diff --git a/Common/ACPreferences.h b/Common/ACPreferences.h index 7c157fe..cd0a663 100644 --- a/Common/ACPreferences.h +++ b/Common/ACPreferences.h @@ -27,12 +27,14 @@ Return the list of ignored SSIDs for the current service identifier */ -(NSArray*)ignoredSSIDs; +-(void)setIgnoredSSIDs:(NSArray *)ignoredSSIDs; /** Return the list of VPN to ignore */ -(NSArray*)ignoredVPNs; +-(void)setIgnoredVPNs:(NSArray *)ignoredVPNs; /** diff --git a/Common/ACPreferences.m b/Common/ACPreferences.m index 4a4817c..630b5ae 100644 --- a/Common/ACPreferences.m +++ b/Common/ACPreferences.m @@ -103,7 +103,27 @@ -(void)setAlwaysConnected:(BOOL)inAlwaysConnected forServicesIdentifier:(NSStrin -(NSArray *)ignoredSSIDs { NSString *ignoredSSIDsString = [[NSUserDefaults standardUserDefaults] stringForKey:kServiceIgnoredSSIDsKey]; - return [ignoredSSIDsString componentsSeparatedByString:@","]; + if([ignoredSSIDsString length] > 0) + { + return [ignoredSSIDsString componentsSeparatedByString:@","]; + } + else + { + return @[]; + } +} + +-(void)setIgnoredSSIDs:(NSArray *)ignoredSSIDs +{ + if([ignoredSSIDs count] <= 0) + { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kServiceIgnoredSSIDsKey]; + } + else + { + NSString *value = [ignoredSSIDs componentsJoinedByString:@","]; + [[NSUserDefaults standardUserDefaults] setValue:value forKey:kServiceIgnoredSSIDsKey]; + } } /** @@ -121,10 +141,23 @@ -(void)setAlwaysConnected:(BOOL)inAlwaysConnected forServicesIdentifier:(NSStrin // Don't display the Little Snitch Content Filter Configuration ignoredVPNsString = @"Little Snitch"; } - + return [ignoredVPNsString componentsSeparatedByString:@","]; } +-(void)setIgnoredVPNs:(NSArray *)ignoredVPNs +{ + if([ignoredVPNs count] <= 0) + { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kServiceIgnoredVPNsKey]; + } + else + { + NSString *value = [ignoredVPNs componentsJoinedByString:@","]; + [[NSUserDefaults standardUserDefaults] setValue:value forKey:kServiceIgnoredVPNsKey]; + } +} + -(NSInteger)alwaysConnectedRetryDelay { NSInteger retryDelay = [[NSUserDefaults standardUserDefaults] integerForKey:kAlwaysConnectedRetryDelayPrefKey]; diff --git a/VPN.xcodeproj/project.pbxproj b/VPN.xcodeproj/project.pbxproj index ee94277..9a28d0c 100644 --- a/VPN.xcodeproj/project.pbxproj +++ b/VPN.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 024D90122BC72567005BF977 /* GitHubRelease.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024D90002BC71157005BF977 /* GitHubRelease.swift */; }; 024D903C2BC7B230005BF977 /* ACCheckForUpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024D903B2BC7B230005BF977 /* ACCheckForUpdateView.swift */; }; 024D903E2BC7B904005BF977 /* ACCheckForUpdateViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024D903D2BC7B904005BF977 /* ACCheckForUpdateViewFactory.swift */; }; + 024D90552BC9AE8F005BF977 /* ACPreferencesIgnoredView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 024D90532BC9AE8F005BF977 /* ACPreferencesIgnoredView.xib */; }; + 024D90582BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 024D90572BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.m */; }; 02619DE820F7DF2200099652 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 02619DE720F7DF2200099652 /* AppDelegate.m */; }; 02619DEA20F7DF2400099652 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02619DE920F7DF2400099652 /* Assets.xcassets */; }; 02619DED20F7DF2400099652 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 02619DEB20F7DF2400099652 /* MainMenu.xib */; }; @@ -112,6 +114,9 @@ 024D90082BC71C04005BF977 /* GitHubReleaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubReleaseTests.swift; sourceTree = ""; }; 024D903B2BC7B230005BF977 /* ACCheckForUpdateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACCheckForUpdateView.swift; sourceTree = ""; }; 024D903D2BC7B904005BF977 /* ACCheckForUpdateViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACCheckForUpdateViewFactory.swift; sourceTree = ""; }; + 024D90542BC9AE8F005BF977 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ACPreferencesIgnoredView.xib; sourceTree = ""; }; + 024D90562BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACPreferencesIgnoredViewController.h; sourceTree = ""; }; + 024D90572BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ACPreferencesIgnoredViewController.m; sourceTree = ""; }; 02619DE420F7DF2200099652 /* VPNStatus.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VPNStatus.app; sourceTree = BUILT_PRODUCTS_DIR; }; 02619DE620F7DF2200099652 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 02619DE720F7DF2200099652 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -207,13 +212,15 @@ children = ( 024D8FE02BC50D28005BF977 /* NSBundle+ACAppInfo.h */, 024D8FDF2BC50D28005BF977 /* NSBundle+ACAppInfo.m */, + 024D8FD42BC1DF32005BF977 /* ACPreferencesWindowControllerProtocol.h */, + 024D8FD22BC1DF32005BF977 /* ACPreferencesWindowController.h */, + 024D8FD32BC1DF32005BF977 /* ACPreferencesWindowController.m */, 024D8FD02BC1DF32005BF977 /* ACPreferencesGeneralViewController.h */, 024D8FD12BC1DF32005BF977 /* ACPreferencesGeneralViewController.m */, 024D8FD82BC1E6FB005BF977 /* ACPreferencesAboutViewController.h */, 024D8FD92BC1E6FC005BF977 /* ACPreferencesAboutViewController.m */, - 024D8FD22BC1DF32005BF977 /* ACPreferencesWindowController.h */, - 024D8FD32BC1DF32005BF977 /* ACPreferencesWindowController.m */, - 024D8FD42BC1DF32005BF977 /* ACPreferencesWindowControllerProtocol.h */, + 024D90562BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.h */, + 024D90572BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.m */, ); path = PreferencesUI; sourceTree = ""; @@ -260,6 +267,7 @@ 024D8FE22BC51118005BF977 /* ACCrossPromotionWindow.xib */, 024D8FCA2BC1DE38005BF977 /* ACPreferencesGeneralView.xib */, 024D8FDB2BC1E719005BF977 /* ACPreferencesAboutView.xib */, + 024D90532BC9AE8F005BF977 /* ACPreferencesIgnoredView.xib */, 024D8FCC2BC1DE38005BF977 /* ACPreferencesWindow.xib */, 02619DEE20F7DF2400099652 /* Info.plist */, 02619DEF20F7DF2400099652 /* main.m */, @@ -516,6 +524,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 024D90552BC9AE8F005BF977 /* ACPreferencesIgnoredView.xib in Resources */, 024D8FE42BC51118005BF977 /* ACCrossPromotionWindow.xib in Resources */, 024D8FCE2BC1DE38005BF977 /* ACPreferencesGeneralView.xib in Resources */, 024D8FCF2BC1DE38005BF977 /* ACPreferencesWindow.xib in Resources */, @@ -579,6 +588,7 @@ 02619DFF20F7F7BF00099652 /* ACPreferences.m in Sources */, 028DF1172AC7FA200084A822 /* ACLocationManager.m in Sources */, 0231297C20F9F8CF003F7540 /* ACConnectionManager.m in Sources */, + 024D90582BC9AEB2005BF977 /* ACPreferencesIgnoredViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -649,6 +659,14 @@ name = ACCrossPromotionWindow.xib; sourceTree = ""; }; + 024D90532BC9AE8F005BF977 /* ACPreferencesIgnoredView.xib */ = { + isa = PBXVariantGroup; + children = ( + 024D90542BC9AE8F005BF977 /* Base */, + ); + name = ACPreferencesIgnoredView.xib; + sourceTree = ""; + }; 02619DEB20F7DF2400099652 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/VPNStatus/Base.lproj/ACPreferencesIgnoredView.xib b/VPNStatus/Base.lproj/ACPreferencesIgnoredView.xib new file mode 100644 index 0000000..f983293 --- /dev/null +++ b/VPNStatus/Base.lproj/ACPreferencesIgnoredView.xib @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VPNStatus/Base.lproj/ACPreferencesWindow.xib b/VPNStatus/Base.lproj/ACPreferencesWindow.xib index e96e615..8e186f1 100644 --- a/VPNStatus/Base.lproj/ACPreferencesWindow.xib +++ b/VPNStatus/Base.lproj/ACPreferencesWindow.xib @@ -33,6 +33,11 @@ + + + + + @@ -57,5 +62,6 @@ + diff --git a/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.h b/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.h new file mode 100644 index 0000000..fa04356 --- /dev/null +++ b/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.h @@ -0,0 +1,17 @@ +// +// ACPreferencesIgnoredViewController.h +// +// Created by Alexandre Colucci on 12.04.2024. +// Copyright © 2024 Alexandre Colucci. All rights reserved. +// + +#import + +#import "ACPreferencesWindowControllerProtocol.h" + +@interface ACPreferencesIgnoredViewController : NSViewController + +- (instancetype)initViewController; + +@end + diff --git a/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.m b/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.m new file mode 100644 index 0000000..1764524 --- /dev/null +++ b/VPNStatus/PreferencesUI/ACPreferencesIgnoredViewController.m @@ -0,0 +1,275 @@ +// +// ACPreferencesIgnoredViewController.m +// +// Created by Alexandre Colucci on 12.04.2024. +// Copyright © 2024 Alexandre Colucci. All rights reserved. + +#import "ACPreferencesIgnoredViewController.h" +#import "ACPreferences.h" + +@interface ACPreferencesIgnoredViewController () + +@property (weak) IBOutlet NSTabView *tabView; + +@property (weak) IBOutlet NSTableView *ignoredSSIDsTableView; +@property (weak) IBOutlet NSButton *ignoredSSIDsRemoveButton; + +@property (weak) IBOutlet NSTableView *ignoredVPNsTableView; +@property (weak) IBOutlet NSButton *ignoredVPNsRemoveButton; + +@property (strong) NSMutableArray *ignoredSSIDs; +@property (strong) NSMutableArray *ignoredVPNs; + +@end + + +@implementation ACPreferencesIgnoredViewController + +- (instancetype)initViewController +{ + self = [super initWithNibName:@"ACPreferencesIgnoredView" bundle:nil]; + if (self) + { + self.ignoredSSIDs = [[[ACPreferences sharedPreferences] ignoredSSIDs] mutableCopy]; + self.ignoredVPNs = [[[ACPreferences sharedPreferences] ignoredVPNs] mutableCopy]; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self updateIgnoredSSIDsRemoveButtons]; + [self updateIgnoredVPNsRemoveButtons]; +} + +-(NSString*)identifier +{ + return [[self title] lowercaseString]; +} + +-(NSString*)title +{ + return @"Ignored"; +} + + +// MARK: - Ignored SSIDs + +- (void)saveIgnoredSSIDsToPrefs +{ + [[ACPreferences sharedPreferences] setIgnoredSSIDs:self.ignoredSSIDs]; +} + +-(void)reloadIgnoredSSIDs +{ + [self.ignoredSSIDsTableView reloadData]; + [self updateIgnoredSSIDsRemoveButtons]; +} + +-(void)updateIgnoredSSIDsRemoveButtons +{ + [self.ignoredSSIDsRemoveButton setEnabled:([self.ignoredSSIDsTableView selectedRow] >= 0)]; +} + +-(IBAction)doAddSSIDs:(id)sender +{ + [self.ignoredSSIDs addObject:@""]; + [self saveIgnoredSSIDsToPrefs]; + [self reloadIgnoredSSIDs]; + + NSInteger numberOfRows = [self.ignoredSSIDs count]; + if(numberOfRows >= 1) + { + [self.ignoredSSIDsTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:(numberOfRows-1)] byExtendingSelection:NO]; + [self.ignoredSSIDsTableView scrollRowToVisible:numberOfRows-1]; + + NSTableCellView *selectedView = (NSTableCellView *)[self.ignoredSSIDsTableView viewAtColumn:0 row:(numberOfRows-1) makeIfNecessary:YES]; + [selectedView.textField selectText:self]; + [[selectedView.textField currentEditor] setSelectedRange:NSMakeRange([[selectedView.textField stringValue] length], 0)]; + } +} + +-(IBAction)doRemoveSSIDs:(id)sender +{ + NSInteger selectedRow = [self.ignoredSSIDsTableView selectedRow]; + if(selectedRow >= 0 && selectedRow < [self.ignoredSSIDs count]) + { + [self.ignoredSSIDs removeObjectAtIndex:selectedRow]; + [self saveIgnoredSSIDsToPrefs]; + [self reloadIgnoredSSIDs]; + } +} + +// MARK: - Ignored VPNs + +- (void)saveIgnoredVPNsToPrefs +{ + [[ACPreferences sharedPreferences] setIgnoredVPNs:self.ignoredVPNs]; +} + +-(void)reloadIgnoredVPNs +{ + [self.ignoredVPNsTableView reloadData]; + [self updateIgnoredVPNsRemoveButtons]; +} + +-(void)updateIgnoredVPNsRemoveButtons +{ + [self.ignoredVPNsRemoveButton setEnabled:([self.ignoredVPNsTableView selectedRow] >= 0)]; +} + +-(IBAction)doAddVPN:(id)sender +{ + [self.ignoredVPNs addObject:@""]; + [self saveIgnoredVPNsToPrefs]; + [self reloadIgnoredVPNs]; + + NSInteger numberOfRows = [self.ignoredVPNs count]; + if(numberOfRows >= 1) + { + [self.ignoredVPNsTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:(numberOfRows-1)] byExtendingSelection:NO]; + [self.ignoredVPNsTableView scrollRowToVisible:numberOfRows-1]; + + NSTableCellView *selectedView = (NSTableCellView *)[self.ignoredVPNsTableView viewAtColumn:0 row:(numberOfRows-1) makeIfNecessary:YES]; + [selectedView.textField selectText:self]; + [[selectedView.textField currentEditor] setSelectedRange:NSMakeRange([[selectedView.textField stringValue] length], 0)]; + } +} + +-(IBAction)doRemoveVPN:(id)sender +{ + NSInteger selectedRow = [self.ignoredVPNsTableView selectedRow]; + if(selectedRow >= 0 && selectedRow < [self.ignoredVPNs count]) + { + [self.ignoredVPNs removeObjectAtIndex:selectedRow]; + [self saveIgnoredVPNsToPrefs]; + [self reloadIgnoredVPNs]; + } +} + +// MARK: - NSTableViewDataSource + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + if(tableView == self.ignoredSSIDsTableView) + { + return [self.ignoredSSIDs count]; + } + else if(tableView == self.ignoredVPNsTableView) + { + return [self.ignoredVPNs count]; + } + else + { + return 0; + } +} + +- (nullable NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row +{ + if(tableView == self.ignoredSSIDsTableView) + { + NSTableCellView *cellView = (NSTableCellView*)[tableView makeViewWithIdentifier:@"SSIDsRegexID" owner:nil]; + if(row >= 0 && row < [self.ignoredSSIDs count]) + { + NSString *theString = [self.ignoredSSIDs objectAtIndex:row]; + cellView.textField.stringValue = theString; + cellView.textField.toolTip = theString; + cellView.textField.textColor = NSColor.textColor; + + if ([theString length] > 0) + { + // Check if the regex is valid or not + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:theString options:0 error:&error]; + if(regex == nil) + { + cellView.textField.textColor = NSColor.systemRedColor; + cellView.textField.toolTip = @"This string is an invalid regular expression."; + } + } + } + else + { + cellView.textField.stringValue = @""; + cellView.textField.toolTip = nil; + cellView.textField.textColor = NSColor.textColor; + } + + cellView.textField.delegate = self; + return cellView; + } + else if(tableView == self.ignoredVPNsTableView) + { + NSTableCellView *cellView = (NSTableCellView*)[tableView makeViewWithIdentifier:@"VPNRegexID" owner:nil]; + if(row >= 0 && row < [self.ignoredVPNs count]) + { + NSString *theString = [self.ignoredVPNs objectAtIndex:row]; + cellView.textField.stringValue = theString; + cellView.textField.toolTip = theString; + cellView.textField.textColor = NSColor.textColor; + + if ([theString length] > 0) + { + // Check if the regex is valid or not + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:theString options:0 error:&error]; + if(regex == nil) + { + cellView.textField.textColor = NSColor.systemRedColor; + cellView.textField.toolTip = @"This string is an invalid regular expression."; + } + } + } + else + { + cellView.textField.stringValue = @""; + cellView.textField.toolTip = nil; + cellView.textField.textColor = NSColor.textColor; + } + + cellView.textField.delegate = self; + return cellView; + } + else + { + return nil; + } +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [self updateIgnoredSSIDsRemoveButtons]; + [self updateIgnoredVPNsRemoveButtons]; +} + +// MARK: - NSTextFieldDelegate + +- (void)controlTextDidEndEditing:(NSNotification *)notification +{ + NSTextField *textField = (NSTextField*)[notification object]; + if(textField != nil) + { + NSInteger ignoredSSIDsRow = [self.ignoredSSIDsTableView rowForView:textField]; + if(ignoredSSIDsRow >= 0 && ignoredSSIDsRow < [self.ignoredSSIDs count]) + { + NSString *newSSID = textField.stringValue; + [self.ignoredSSIDs replaceObjectAtIndex:ignoredSSIDsRow withObject:newSSID]; + [self saveIgnoredSSIDsToPrefs]; + [self reloadIgnoredSSIDs]; + } + + NSInteger ignoredVPNsRow = [self.ignoredVPNsTableView rowForView:textField]; + if(ignoredVPNsRow >= 0 && ignoredVPNsRow < [self.ignoredVPNs count]) + { + NSString *newVPN = textField.stringValue; + [self.ignoredVPNs replaceObjectAtIndex:ignoredVPNsRow withObject:newVPN]; + [self saveIgnoredVPNsToPrefs]; + [self reloadIgnoredVPNs]; + } + } +} + +@end diff --git a/VPNStatus/PreferencesUI/ACPreferencesWindowController.m b/VPNStatus/PreferencesUI/ACPreferencesWindowController.m index fea28c6..801d8f8 100644 --- a/VPNStatus/PreferencesUI/ACPreferencesWindowController.m +++ b/VPNStatus/PreferencesUI/ACPreferencesWindowController.m @@ -8,6 +8,7 @@ #import "ACPreferencesGeneralViewController.h" #import "ACPreferencesAboutViewController.h" +#import "ACPreferencesIgnoredViewController.h" @interface ACPreferencesWindowController () @@ -15,6 +16,7 @@ @interface ACPreferencesWindowController () @property (strong) ACPreferencesGeneralViewController *generalViewController; @property (strong) ACPreferencesAboutViewController *aboutViewController; +@property (strong) ACPreferencesIgnoredViewController *ignoredViewController; @end @@ -39,6 +41,7 @@ -(instancetype)initPreferencesWindow { _generalViewController = [[ACPreferencesGeneralViewController alloc] initViewController]; _aboutViewController = [[ACPreferencesAboutViewController alloc] initViewController]; + _ignoredViewController = [[ACPreferencesIgnoredViewController alloc] initViewController]; } [self.window setReleasedWhenClosed:NO]; @@ -57,6 +60,7 @@ - (void)windowDidLoad - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar { return @[ [self.generalViewController identifier], + [self.ignoredViewController identifier], [self.aboutViewController identifier], @"NSToolbarFlexibleSpaceItem" ]; @@ -137,6 +141,11 @@ -(IBAction)doGeneral:(id)sender [self changeToViewController:self.generalViewController animated:YES]; } +-(IBAction)doIgnored:(id)sender +{ + [self changeToViewController:self.ignoredViewController animated:YES]; +} + -(IBAction)doAbout:(id)sender { [self changeToViewController:self.aboutViewController animated:YES];