diff --git a/README.md b/README.md index 32dc8a2..755835e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ Then I rewrite the GUI code by swift. - XCode 8.0+ - cocoapod 1.0.1+ +## Download + +From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/) + ## Fetures - Use ss-local from shadowsocks-libev 2.4.6 diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 2afa29a..d4c1550 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -96,18 +96,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 083BF8311D82731900831C68 /* SimplePing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimplePing.h; sourceTree = ""; }; - 083BF8321D82731900831C68 /* SimplePing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimplePing.m; sourceTree = ""; }; - 083BF8341D82742200831C68 /* NetWorkMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetWorkMonitor.swift; sourceTree = ""; }; - 083BF8351D82742200831C68 /* PingClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PingClient.swift; sourceTree = ""; }; - 083BF8381D82759600831C68 /* StatusItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = ""; }; - 083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemThemeChangeHelper.swift; sourceTree = ""; }; - 085641E91D7188C400116B27 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; - 08805F181D878CA5009B53E7 /* PingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PingTest.swift; sourceTree = ""; }; - 0880CE3F1D6FE6D900BD39E2 /* example-gui-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "example-gui-config.json"; sourceTree = ""; }; - 088EC3961D5F5B8600E40791 /* whitelist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whitelist.pac; sourceTree = ""; }; - 088EC3981D5F5BA300E40791 /* whiteiplist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whiteiplist.pac; sourceTree = ""; }; - 08AF56C01D6AFA7C00DC4F46 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PreferencesWindowController.strings"; sourceTree = ""; }; 19083CFCED87354F006967FF /* Pods_ShadowsocksX_NGUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NGUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1E7783AEDB4A3BDDC9FF16AC /* libPods-proxy_conf_helper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-proxy_conf_helper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 283ED1A8E9B711AC65670031 /* Pods_ShadowsocksX_NG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NG.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -505,7 +493,6 @@ 9B3FFF3A1D08CF110019A709 /* qrcode.htm in Resources */, C6D429941DA75988002A5711 /* privoxy in Resources */, C6D429991DA76FBC002A5711 /* privoxy.config.example in Resources */, - 0880CE401D6FE6D900BD39E2 /* example-gui-config.json in Resources */, 9BEEF06B1D04D4D500FC52B3 /* stop_ss_local.sh in Resources */, 9B3FFF341D08CEF70019A709 /* SWBQRCodeWindowController.xib in Resources */, 9BEEF06C1D04D4D500FC52B3 /* reload_conf_ss_local.sh in Resources */, diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index dcff1ca..c1ca927 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -58,7 +58,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele "ShadowsocksRunningMode": "auto", "LocalSocks5.ListenPort": NSNumber(value: 1086 as UInt16), "LocalSocks5.ListenAddress": "127.0.0.1", - "PacServer.ListenAddress": "127.0.0.1", "PacServer.ListenPort":NSNumber(value: 8090 as UInt16), "LocalSocks5.Timeout": NSNumber(value: 60 as UInt), "LocalSocks5.EnableUDPRelay": NSNumber(value: false as Bool), @@ -163,6 +162,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele updateLaunchAtLoginMenu() ProxyConfHelper.install() + ProxyConfHelper.startMonitorPAC() applyConfig() SyncSSLocal() } @@ -171,10 +171,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele // Insert code here to tear down your application StopSSLocal() StopPrivoxy() - ProxyConfHelper.disableProxy("hi") + ProxyConfHelper.disableProxy() let defaults = UserDefaults.standard defaults.set(false, forKey: "ShadowsocksOn") - ProxyConfHelper.stopPACServer() } func applyConfig() { @@ -186,16 +185,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele StartSSLocal() StartPrivoxy() if mode == "auto" { - ProxyConfHelper.enablePACProxy("hi") + ProxyConfHelper.enablePACProxy() } else if mode == "global" { ProxyConfHelper.enableGlobalProxy() } else if mode == "manual" { - ProxyConfHelper.disableProxy("hi") + ProxyConfHelper.disableProxy() } } else { StopSSLocal() StopPrivoxy() - ProxyConfHelper.disableProxy("hi") + ProxyConfHelper.disableProxy() } } @@ -385,18 +384,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele showRunningModeMenuItem.state = defaults.bool(forKey: "ShowRunningModeOnStatusBar") ? 1 : 0 var serverMenuText = "Servers".localized - if let serverProfiles = defaults.array(forKey: "ServerProfiles") { - for v in serverProfiles { - let profile = v as! [String:Any] - if profile["Id"] as! String == defaults.string(forKey: "ActiveServerProfileId")! { - var profileName :String - if profile["Remark"] as! String != "" { - profileName = profile["Remark"] as! String - } else { - profileName = profile["ServerHost"] as! String - } - serverMenuText = "\(serverMenuText) - \(profileName)" + + let mgr = ServerProfileManager.instance + for p in mgr.profiles { + if mgr.activeProfileId == p.uuid { + var profileName :String + if !p.remark.isEmpty { + profileName = p.remark + } else { + profileName = p.serverHost } + serverMenuText = "\(serverMenuText) - \(profileName)" } } serversMenuItem.title = serverMenuText diff --git a/ShadowsocksX-NG/Base.lproj/AdvPreferencesWindowController.xib b/ShadowsocksX-NG/Base.lproj/AdvPreferencesWindowController.xib index 35784f1..9e90f71 100644 --- a/ShadowsocksX-NG/Base.lproj/AdvPreferencesWindowController.xib +++ b/ShadowsocksX-NG/Base.lproj/AdvPreferencesWindowController.xib @@ -14,30 +14,19 @@ - + - + - + - - - - - - - - - - - - + - + @@ -53,7 +42,7 @@ - + @@ -61,10 +50,10 @@ - + - + @@ -72,7 +61,7 @@ - + @@ -80,7 +69,7 @@ - + @@ -95,7 +84,7 @@ - + @@ -103,7 +92,7 @@ - + @@ -119,7 +108,7 @@ - + - + @@ -148,10 +137,7 @@ - - - - + @@ -168,18 +154,16 @@ - - - + + - - - + + @@ -194,22 +178,12 @@ - - - - - - - - - - - - - - - - + + + + + + @@ -217,51 +191,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/ShadowsocksX-NG/Base.lproj/UserRulesController.xib b/ShadowsocksX-NG/Base.lproj/UserRulesController.xib index 0d53d39..621b98d 100644 --- a/ShadowsocksX-NG/Base.lproj/UserRulesController.xib +++ b/ShadowsocksX-NG/Base.lproj/UserRulesController.xib @@ -58,7 +58,7 @@ Gw - + diff --git a/ShadowsocksX-NG/Info.plist b/ShadowsocksX-NG/Info.plist index 04f0ded..0c35489 100644 --- a/ShadowsocksX-NG/Info.plist +++ b/ShadowsocksX-NG/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3 + 1.3.1 CFBundleSignature ???? CFBundleURLTypes diff --git a/ShadowsocksX-NG/ProxyConfHelper.h b/ShadowsocksX-NG/ProxyConfHelper.h index c00ade2..65e34a0 100644 --- a/ShadowsocksX-NG/ProxyConfHelper.h +++ b/ShadowsocksX-NG/ProxyConfHelper.h @@ -14,14 +14,12 @@ + (void)install; -+ (void)enablePACProxy:(NSString*) PACFilePath; ++ (void)enablePACProxy; + (void)enableGlobalProxy; -+ (void)disableProxy:(NSString*) PACFilePath; ++ (void)disableProxy; -+ (NSString*)startPACServer:(NSString*) PACFilePath; - -+ (void)stopPACServer; ++ (void)startMonitorPAC; @end diff --git a/ShadowsocksX-NG/ProxyConfHelper.m b/ShadowsocksX-NG/ProxyConfHelper.m index 6089ae0..05187c6 100644 --- a/ShadowsocksX-NG/ProxyConfHelper.m +++ b/ShadowsocksX-NG/ProxyConfHelper.m @@ -14,6 +14,7 @@ @implementation ProxyConfHelper GCDWebServer *webServer =nil; +FSEventStreamRef fsEventStream; + (BOOL)isVersionOk { NSTask *task; @@ -114,14 +115,18 @@ GCDWebServer *webServer =nil; } } -+ (void)enablePACProxy:(NSString*) PACFilePath { ++ (NSString*)getPACFilePath { + return [NSString stringWithFormat:@"%@/%@", NSHomeDirectory(), @".ShadowsocksX-NG/gfwlist.js"]; +} + ++ (void)enablePACProxy { //start server here and then using the string next line //next two lines can open gcdwebserver and work around pac file - NSString *PACURLString = [self startPACServer: PACFilePath];//hi 可以切换成定制pac文件路径来达成使用定制文件路径 - NSURL* url = [NSURL URLWithString: PACURLString]; -// NSString* urlString = [NSString stringWithFormat:@"%@/.ShadowsocksX-NG/gfwlist.js", NSHomeDirectory()]; -// NSURL* url = [NSURL fileURLWithPath:urlString]; - + NSString* PACFilePath = [self getPACFilePath]; + [self startPACServer: PACFilePath]; + + NSURL* url = [NSURL URLWithString: [self getHttpPACUrl]]; + NSMutableArray* args = [@[@"--mode", @"auto", @"--pac-url", [url absoluteString]]mutableCopy]; [self addArguments4ManualSpecifyNetworkServices:args]; @@ -146,12 +151,9 @@ GCDWebServer *webServer =nil; [self stopPACServer]; } -+ (void)disableProxy:(NSString*) PACFilePath { -// 带上所有参数是为了判断是否原有代理设置是否由ssx-ng设置的。如果是用户手工设置的其他配置,则不进行清空。 -// NSString* urlString = [NSString stringWithFormat:@"%@/.ShadowsocksX-NG/gfwlist.js", NSHomeDirectory()]; -// NSURL* url = [NSURL fileURLWithPath:urlString]; - NSString *PACURLString = [self startPACServer: PACFilePath];//hi 可以切换成定制pac文件路径来达成使用定制文件路径 - NSURL* url = [NSURL URLWithString: PACURLString]; ++ (void)disableProxy { + // 带上所有参数是为了判断是否原有代理设置是否由ssx-ng设置的。如果是用户手工设置的其他配置,则不进行清空。 + NSURL* url = [NSURL URLWithString: [self getHttpPACUrl]]; NSUInteger port = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalSocks5.ListenPort"]; NSMutableArray* args = [@[@"--mode", @"off" @@ -163,28 +165,41 @@ GCDWebServer *webServer =nil; [self stopPACServer]; } -+ (NSString*)startPACServer:(NSString*) PACFilePath { - //接受参数为以后使用定制PAC文件 - NSData * originalPACData; ++ (NSString*)getHttpPACUrl { NSString * routerPath = @"/proxy.pac"; - if ([PACFilePath isEqual: @"hi"]) {//用默认路径来代替 - PACFilePath = [NSString stringWithFormat:@"%@/%@", NSHomeDirectory(), @".ShadowsocksX-NG/gfwlist.js"]; - originalPACData = [NSData dataWithContentsOfFile: [NSString stringWithFormat:@"%@/%@", NSHomeDirectory(), @".ShadowsocksX-NG/gfwlist.js"]]; - }else{//用定制路径来代替 - originalPACData = [NSData dataWithContentsOfFile: [NSString stringWithFormat:@"%@/%@/%@", NSHomeDirectory(), @".ShadowsocksX-NG", PACFilePath]]; - routerPath = [NSString stringWithFormat:@"/%@",PACFilePath]; - } + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + NSString * address = @"127.0.0.1"; + int port = (short)[defaults integerForKey:@"PacServer.ListenPort"]; + + return [NSString stringWithFormat:@"%@%@:%d%@",@"http://",address,port,routerPath]; +} + ++ (void)startPACServer:(NSString*) PACFilePath { [self stopPACServer]; + + NSString * routerPath = @"/proxy.pac"; + + NSData* originalPACData = [NSData dataWithContentsOfFile:PACFilePath]; + webServer = [[GCDWebServer alloc] init]; - [webServer addHandlerForMethod:@"GET" path:routerPath requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest *request) { - return [GCDWebServerDataResponse responseWithData: originalPACData contentType:@"application/x-ns-proxy-autoconfig"]; + [webServer addHandlerForMethod:@"GET" + path:routerPath + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest *request) + { + GCDWebServerDataResponse* resp = [GCDWebServerDataResponse responseWithData:originalPACData + contentType:@"application/x-ns-proxy-autoconfig"]; + return resp; } ]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSString * address = [defaults stringForKey:@"PacServer.ListenAddress"]; + int port = (short)[defaults integerForKey:@"PacServer.ListenPort"]; + [webServer startWithOptions:@{@"BindToLocalhost":@YES, @"Port":@(port)} error:nil]; - return [NSString stringWithFormat:@"%@%@:%d%@",@"http://",address,port,routerPath]; } + (void)stopPACServer { @@ -194,4 +209,45 @@ GCDWebServer *webServer =nil; } } +void onPACChange( + ConstFSEventStreamRef streamRef, + void *clientCallBackInfo, + size_t numEvents, + void *eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) +{ + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults boolForKey:@"ShadowsocksOn"]) { + if ([[defaults stringForKey:@"ShadowsocksRunningMode"] isEqualToString:@"auto"]) { + [ProxyConfHelper disableProxy]; + [ProxyConfHelper enablePACProxy]; + } + } +} + ++ (void)startMonitorPAC { + NSString* PACFilePath = [self getPACFilePath]; + + if (fsEventStream) { + return; + } + CFStringRef mypath = (__bridge CFStringRef)(PACFilePath); + CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL); + void *callbackInfo = NULL; // could put stream-specific data here. + CFAbsoluteTime latency = 3.0; /* Latency in seconds */ + + /* Create the stream, passing in a callback */ + fsEventStream = FSEventStreamCreate(NULL, + &onPACChange, + callbackInfo, + pathsToWatch, + kFSEventStreamEventIdSinceNow, /* Or a previous event ID */ + latency, + kFSEventStreamCreateFlagNone /* Flags explained in reference */ + ); + FSEventStreamScheduleWithRunLoop(fsEventStream, [[NSRunLoop mainRunLoop] getCFRunLoop], (__bridge CFStringRef)NSDefaultRunLoopMode); + FSEventStreamStart(fsEventStream); +} + @end diff --git a/ShadowsocksX-NG/ProxyPreferencesController.swift b/ShadowsocksX-NG/ProxyPreferencesController.swift index 4c215d6..82bab46 100644 --- a/ShadowsocksX-NG/ProxyPreferencesController.swift +++ b/ShadowsocksX-NG/ProxyPreferencesController.swift @@ -37,7 +37,7 @@ class ProxyPreferencesController: NSWindowController, NSTableViewDataSource, NST } @IBAction func ok(_ sender: NSObject){ - ProxyConfHelper.disableProxy("hi") + ProxyConfHelper.disableProxy() let defaults = UserDefaults.standard defaults.setValue(selectedNetworkServices.allObjects, forKeyPath: "Proxy4NetworkServices")