diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 151b495..4556c29 100644 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -38,6 +38,9 @@ 9B3FFF4C1D09D8F70019A709 /* install_helper.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9B3FFF4B1D09D8F70019A709 /* install_helper.sh */; }; 9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */; }; 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */ = {isa = PBXBuildFile; fileRef = 9B3FFF441D09CD3B0019A709 /* proxy_conf_helper */; }; + 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */; }; + 9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */; }; + 9BA04B271D24044D005AAD7F /* ProxyPreferencesController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BA04B251D24044D005AAD7F /* ProxyPreferencesController.xib */; }; 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BB706A51D1B982300551F0E /* SWBApplication.m */; }; 9BE8FBBF1D0B211600CAFD01 /* libcrypto.1.0.0.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9BE8FBBD1D0B1FB900CAFD01 /* libcrypto.1.0.0.dylib */; }; 9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9BEEF0651D04CB8500FC52B3 /* install_ss_local.sh */; }; @@ -137,6 +140,10 @@ 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProxyConfHelper.m; sourceTree = ""; }; 9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = proxy_conf_helper_version.h; sourceTree = ""; }; 9B3FFF511D09DBA20019A709 /* ShadowsocksX-NG-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ShadowsocksX-NG-Bridging-Header.h"; sourceTree = ""; }; + 9BA04B211D23D5A5005AAD7F /* ProxyConfTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyConfTool.h; sourceTree = ""; }; + 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProxyConfTool.m; sourceTree = ""; }; + 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyPreferencesController.swift; sourceTree = ""; }; + 9BA04B251D24044D005AAD7F /* ProxyPreferencesController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProxyPreferencesController.xib; sourceTree = ""; }; 9BB706A51D1B982300551F0E /* SWBApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWBApplication.m; sourceTree = ""; }; 9BB706A61D1B982300551F0E /* SWBApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWBApplication.h; sourceTree = ""; }; 9BE8FBBD1D0B1FB900CAFD01 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcrypto.1.0.0.dylib; sourceTree = ""; }; @@ -272,6 +279,8 @@ 9B3FFF4D1D09D9D50019A709 /* ProxyConfHelper.h */, 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */, 9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */, + 9BA04B211D23D5A5005AAD7F /* ProxyConfTool.h */, + 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */, ); path = "ShadowsocksX-NG"; sourceTree = ""; @@ -295,6 +304,8 @@ 9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */, 9BEEF0791D05631500FC52B3 /* AdvPreferencesWindowController.swift */, 9B2491B91D0ACC3E003BBECC /* AdvPreferencesWindowController.xib */, + 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */, + 9BA04B251D24044D005AAD7F /* ProxyPreferencesController.xib */, ); name = UI; sourceTree = ""; @@ -436,6 +447,7 @@ 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */, 9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */, 9B172A6A1D0ADDDD00B87B9A /* Localizable.strings in Resources */, + 9BA04B271D24044D005AAD7F /* ProxyPreferencesController.xib in Resources */, 9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */, 9B3FFF291D08A1DF0019A709 /* user-rule.txt in Resources */, 9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */, @@ -602,6 +614,7 @@ 9B3FFF1E1D0732660019A709 /* Utils.m in Sources */, 9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */, 9B3FFF211D08826E0019A709 /* PACUtils.swift in Sources */, + 9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */, 9B3FFF141D0705810019A709 /* Notifications.swift in Sources */, 9BEEF0701D04DDB100FC52B3 /* ServerProfileManager.swift in Sources */, 9BEEF07B1D05631500FC52B3 /* AdvPreferencesWindowController.swift in Sources */, @@ -609,6 +622,7 @@ 9B3FFF0D1D05FEB30019A709 /* Utils.swift in Sources */, 9BEEF0751D04EF3E00FC52B3 /* PreferencesWindowController.swift in Sources */, 9B0BFFE91D0460A70040E62B /* AppDelegate.swift in Sources */, + 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */, 9BEEF0781D04FE8A00FC52B3 /* LaunchAgentUtils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index ad6ea00..74b1b1a 100644 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -15,6 +15,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele var qrcodeWinCtrl: SWBQRCodeWindowController! var preferencesWinCtrl: PreferencesWindowController! var advPreferencesWinCtrl: AdvPreferencesWindowController! + var proxyPreferencesWinCtrl: ProxyPreferencesController! var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController() @@ -52,7 +53,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele "LocalSocks5.Timeout": NSNumber(unsignedInteger: 60), "LocalSocks5.EnableUDPRelay": NSNumber(bool: false), "LocalSocks5.EnableVerboseMode": NSNumber(bool: false), - "GFWListURL": "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt" + "GFWListURL": "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt", + "AutoConfigureNetworkServices": NSNumber(bool: true) ]) statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(20) @@ -63,6 +65,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele let notifyCenter = NSNotificationCenter.defaultCenter() + notifyCenter.addObserverForName(NOTIFY_ADV_PROXY_CONF_CHANGED, object: nil, queue: nil + , usingBlock: { + (note) in + self.applyConfig() + } + ) notifyCenter.addObserverForName(NOTIFY_SERVER_PROFILES_CHANGED, object: nil, queue: nil , usingBlock: { (note) in @@ -272,6 +280,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele ctrl.window?.makeKeyAndOrderFront(self) } + @IBAction func editProxyPreferences(sender: NSObject) { + if proxyPreferencesWinCtrl != nil { + proxyPreferencesWinCtrl.close() + } + proxyPreferencesWinCtrl = ProxyPreferencesController(windowNibName: "ProxyPreferencesController") + proxyPreferencesWinCtrl.showWindow(self) + NSApp.activateIgnoringOtherApps(true) + proxyPreferencesWinCtrl.window?.makeKeyAndOrderFront(self) + } + @IBAction func selectServer(sender: NSMenuItem) { let index = sender.tag let spMgr = ServerProfileManager.instance diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 965ad69..79c059f 100644 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -14,10 +14,10 @@ - - + + - + @@ -38,23 +38,37 @@ - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShadowsocksX-NG/Notifications.swift b/ShadowsocksX-NG/Notifications.swift index bdfac67..69b798f 100644 --- a/ShadowsocksX-NG/Notifications.swift +++ b/ShadowsocksX-NG/Notifications.swift @@ -9,4 +9,5 @@ import Foundation let NOTIFY_SERVER_PROFILES_CHANGED = "NOTIFY_SERVER_PROFILES_CHANGED" +let NOTIFY_ADV_PROXY_CONF_CHANGED = "NOTIFY_ADV_PROXY_CONF_CHANGED" let NOTIFY_ADV_CONF_CHANGED = "NOTIFY_ADV_CONF_CHANGED" \ No newline at end of file diff --git a/ShadowsocksX-NG/ProxyConfHelper.m b/ShadowsocksX-NG/ProxyConfHelper.m index 1b1fbe6..282bbc7 100644 --- a/ShadowsocksX-NG/ProxyConfHelper.m +++ b/ShadowsocksX-NG/ProxyConfHelper.m @@ -99,19 +99,46 @@ } } ++ (void)addArguments4ManualSpecifyNetworkServices:(NSMutableArray*) args { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + + if (![defaults boolForKey:@"AutoConfigureNetworkServices"]) { + NSArray* serviceKeys = [defaults arrayForKey:@"Proxy4NetworkServices"]; + if (serviceKeys) { + for (NSString* key in serviceKeys) { + [args addObject:@"--network-service"]; + [args addObject:key]; + } + } + } +} + + (void)enablePACProxy { NSString* urlString = [NSString stringWithFormat:@"%@/.ShadowsocksX-NG/gfwlist.js", NSHomeDirectory()]; NSURL* url = [NSURL fileURLWithPath:urlString]; - [self callHelper:@[@"--mode", @"auto", @"--pac-url", [url absoluteString]]]; + + NSMutableArray* args = [@[@"--mode", @"auto", @"--pac-url", [url absoluteString]]mutableCopy]; + + [self addArguments4ManualSpecifyNetworkServices:args]; + + [self callHelper:args]; } + (void)enableGlobalProxy { NSUInteger port = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalSocks5.ListenPort"]; - [self callHelper:@[@"--mode", @"global", @"--port", [NSString stringWithFormat:@"%lu", (unsigned long)port] ]]; + + NSMutableArray* args = [@[@"--mode", @"global", @"--port" + , [NSString stringWithFormat:@"%lu", (unsigned long)port]]mutableCopy]; + + [self addArguments4ManualSpecifyNetworkServices:args]; + + [self callHelper:args]; } + (void)disableProxy { - [self callHelper:@[@"--mode", @"off"]]; + NSMutableArray* args = [@[@"--mode", @"off"]mutableCopy]; + [self addArguments4ManualSpecifyNetworkServices:args]; + [self callHelper:args]; } @end diff --git a/ShadowsocksX-NG/ProxyConfTool.h b/ShadowsocksX-NG/ProxyConfTool.h new file mode 100644 index 0000000..467abdf --- /dev/null +++ b/ShadowsocksX-NG/ProxyConfTool.h @@ -0,0 +1,16 @@ +// +// ProxyConfTool.h +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 16/6/29. +// Copyright © 2016年 qiuyuzhou. All rights reserved. +// + +#import + + +@interface ProxyConfTool : NSObject + ++(NSArray*)networkServicesList; + +@end diff --git a/ShadowsocksX-NG/ProxyConfTool.m b/ShadowsocksX-NG/ProxyConfTool.m new file mode 100644 index 0000000..69705dd --- /dev/null +++ b/ShadowsocksX-NG/ProxyConfTool.m @@ -0,0 +1,52 @@ +// +// ProxyConfTool.m +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 16/6/29. +// Copyright © 2016年 qiuyuzhou. All rights reserved. +// + +#import "ProxyConfTool.h" +#import + +//https://developer.apple.com/library/mac/documentation/Networking/Conceptual/SystemConfigFrameworks/SC_Intro/SC_Intro.html + +@implementation ProxyConfTool + + ++(NSArray*)networkServicesList { + NSMutableArray* results = [NSMutableArray array]; + + SCPreferencesRef prefRef = SCPreferencesCreate(nil, CFSTR("Shadowsocks"), nil); + NSDictionary *sets = (__bridge NSDictionary *)SCPreferencesGetValue(prefRef, kSCPrefNetworkServices); + // 遍历系统中的网络设备列表 + for (NSString *key in [sets allKeys]) { + NSMutableDictionary *service = [sets objectForKey:key]; + NSString *userDefinedName = [service valueForKey:(__bridge NSString *)kSCPropUserDefinedName]; +// NSString *hardware = [service valueForKeyPath:@"Interface.Hardware"]; +// NSString *deviceName = [service valueForKeyPath:@"Interface.DeviceName"]; +// NSString *deviceType = [service valueForKeyPath:@"Interface.Type"]; + + BOOL isActive = ![service objectForKey:(NSString *)kSCResvInactive]; + // NSLog(@"%@", hardware); +// NSLog(@"%@-------------------", key); +// for(NSString* key in service) { +// NSLog(@"key=%@ value=%@", key, [service objectForKey:key]); +// } +// + if (isActive) { + if (isActive && userDefinedName) { + NSDictionary* v = @{ + @"key": key, + @"userDefinedName": userDefinedName, + }; + [results addObject:v]; + } + } + } + + return results; +} + + +@end diff --git a/ShadowsocksX-NG/ProxyPreferencesController.swift b/ShadowsocksX-NG/ProxyPreferencesController.swift new file mode 100644 index 0000000..8fc867e --- /dev/null +++ b/ShadowsocksX-NG/ProxyPreferencesController.swift @@ -0,0 +1,93 @@ +// +// ProxyPreferencesController.swift +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 16/6/29. +// Copyright © 2016年 qiuyuzhou. All rights reserved. +// + +import Cocoa + +class ProxyPreferencesController: NSWindowController, NSTableViewDataSource, NSTableViewDelegate { + + var networkServices: NSArray! + var selectedNetworkServices: NSMutableSet! + + var autoConfigureNetworkServices: Bool = true + + @IBOutlet var autoConfigCheckBox: NSButton! + + @IBOutlet var tableView: NSTableView! + + override func windowDidLoad() { + super.windowDidLoad() + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + let defaults = NSUserDefaults.standardUserDefaults() + self.setValue(defaults.boolForKey("AutoConfigureNetworkServices"), forKey: "autoConfigureNetworkServices") + + if let services = defaults.arrayForKey("Proxy4NetworkServices") { + selectedNetworkServices = NSMutableSet(array: services) + } else { + selectedNetworkServices = NSMutableSet() + } + + networkServices = ProxyConfTool.networkServicesList() + tableView.reloadData() + } + + @IBAction func ok(sender: NSObject){ + ProxyConfHelper.disableProxy() + + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setValue(selectedNetworkServices.allObjects, forKeyPath: "Proxy4NetworkServices") + defaults.setBool(autoConfigureNetworkServices, forKey: "AutoConfigureNetworkServices") + + defaults.synchronize() + + window?.performClose(self) + + NSNotificationCenter.defaultCenter() + .postNotificationName(NOTIFY_ADV_PROXY_CONF_CHANGED, object: nil) + } + + @IBAction func cancel(sender: NSObject){ + window?.performClose(self) + } + + //-------------------------------------------------- + // For NSTableViewDataSource + func numberOfRowsInTableView(tableView: NSTableView) -> Int { + if networkServices != nil { + return networkServices.count + } + return 0; + } + + func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn? + , row: Int) -> AnyObject? { + let cell = tableColumn!.dataCell as! NSButtonCell + + let key = networkServices[row]["key"] as! String + if selectedNetworkServices.containsObject(key) { + cell.state = 1 + } else { + cell.state = 0 + } + let userDefinedName = networkServices[row]["userDefinedName"] as! String + cell.title = userDefinedName + return cell + } + + func tableView(tableView: NSTableView, setObjectValue object: AnyObject? + , forTableColumn tableColumn: NSTableColumn?, row: Int) { + let key = networkServices[row]["key"] as! String + +// NSLog("%d", object!.integerValue) + if object!.integerValue == 1 { + selectedNetworkServices.addObject(key) + } else { + selectedNetworkServices.removeObject(key) + } + } +} diff --git a/ShadowsocksX-NG/ProxyPreferencesController.xib b/ShadowsocksX-NG/ProxyPreferencesController.xib new file mode 100644 index 0000000..0272dcc --- /dev/null +++ b/ShadowsocksX-NG/ProxyPreferencesController.xib @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h b/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h index 186788a..c4ff6cc 100644 --- a/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h +++ b/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h @@ -7,4 +7,5 @@ #import "LaunchAtLoginController.h" #import "SWBQRCodeWindowController.h" #import "Utils.h" -#import "ProxyConfHelper.h" \ No newline at end of file +#import "ProxyConfHelper.h" +#import "ProxyConfTool.h" \ No newline at end of file diff --git a/ShadowsocksX-NG/proxy_conf_helper_version.h b/ShadowsocksX-NG/proxy_conf_helper_version.h index fc73b0f..95c2bbc 100644 --- a/ShadowsocksX-NG/proxy_conf_helper_version.h +++ b/ShadowsocksX-NG/proxy_conf_helper_version.h @@ -9,6 +9,6 @@ #ifndef proxy_conf_helper_version_h #define proxy_conf_helper_version_h -#define kProxyConfHelperVersion @"1.1.0" +#define kProxyConfHelperVersion @"1.2.0" #endif /* proxy_conf_helper_version_h */ diff --git a/proxy_conf_helper/main.m b/proxy_conf_helper/main.m index 9765c38..600cfe2 100644 --- a/proxy_conf_helper/main.m +++ b/proxy_conf_helper/main.m @@ -45,6 +45,11 @@ int main(int argc, const char * argv[]) [options addOption:"pac-url" flag:'u' description:@"PAC file url for auto mode." argument:&pacURL]; [options addOption:"port" flag:'p' description:@"Listen port for global mode." argument:&portString]; + NSMutableSet* networkServiceKeys = [NSMutableSet set]; + [options addOption:"network-service" flag:'n' description:@"Manual specify the network profile need to set proxy." blockWithArgument:^(NSString* value){ + [networkServiceKeys addObject:value]; + }]; + NSError *error = nil; if (![options parseArgc:argc argv:argv error:&error]) { const char * message = error.localizedDescription.UTF8String; @@ -108,7 +113,18 @@ int main(int argc, const char * argv[]) NSMutableDictionary *dict = [sets objectForKey:key]; NSString *hardware = [dict valueForKeyPath:@"Interface.Hardware"]; // NSLog(@"%@", hardware); - if ([hardware isEqualToString:@"AirPort"] || [hardware isEqualToString:@"Wi-Fi"] || [hardware isEqualToString:@"Ethernet"]) { + BOOL modify = NO; + if ([networkServiceKeys count] > 0) { + if ([networkServiceKeys containsObject:key]) { + modify = YES; + } + } else if ([hardware isEqualToString:@"AirPort"] + || [hardware isEqualToString:@"Wi-Fi"] + || [hardware isEqualToString:@"Ethernet"]) { + modify = YES; + } + + if (modify) { if ([mode isEqualToString:@"auto"]) {