diff --git a/README.md b/README.md index cd33292..60eb8c0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # ShadowsocksX-NG -Current version is 1.1 +Current version is 1.2 [![Build Status](https://travis-ci.org/qiuyuzhou/ShadowsocksX-NG.svg?branch=develop)](https://travis-ci.org/qiuyuzhou/ShadowsocksX-NG) Next Generation of [ShadowsocksX](https://github.com/shadowsocks/shadowsocks-iOS) -## Why Another Implement +## Why? It's hard to maintaine the original implement. There are too many unused code in it. It also embed ss-local source. It's crazy to maitaine depandences of ss-local. @@ -42,8 +42,10 @@ Then I rewrite the gui code by swift. - Local socks5 timeout. - If enable UDP relay. - GFW List url. +- Manual spesify network service profiles which would be configure the proxy. +- Could reorder shadowsocks profiles by drag & drop in servers preferences panel. -## Diferences with orignal ShadowsocksX +## Different from orignal ShadowsocksX Run ss-local as backgroud service through launchd, not in app process. So after you quit the app, the ss-local maybe is still running. @@ -51,9 +53,15 @@ So after you quit the app, the ss-local maybe is still running. Add a manual mode which won't configure the system proxy settings. Then you could configure your apps to use socks5 proxy manual. +## Contributing + +Contributions must be available on a separately named branch based on the latest version of the main branch develop. + +ref: [GitFlow](http://nvie.com/posts/a-successful-git-branching-model/) + ## TODO List -- Embed the http proxy server [privoxy](http://www.privoxy.org/). +- [ ] Embed the http proxy server [privoxy](http://www.privoxy.org/). ## License diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 479739d..d160b0b 100644 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 9B0BFFEE1D0460A70040E62B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B0BFFEC1D0460A70040E62B /* MainMenu.xib */; }; 9B0BFFF91D0460A70040E62B /* ShadowsocksX_NGTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0BFFF81D0460A70040E62B /* ShadowsocksX_NGTests.swift */; }; 9B0D55461D2CC85400A4A8E2 /* ProxyPreferencesController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B0D55481D2CC85400A4A8E2 /* ProxyPreferencesController.xib */; }; - 9B172A6A1D0ADDDD00B87B9A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */; }; 9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */; }; 9B2491B71D0ACC3E003BBECC /* AdvPreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B2491B91D0ACC3E003BBECC /* AdvPreferencesWindowController.xib */; }; 9B3FFF0D1D05FEB30019A709 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B3FFF0C1D05FEB30019A709 /* Utils.swift */; }; @@ -42,6 +41,7 @@ 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */; }; 9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */; }; 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BB706A51D1B982300551F0E /* SWBApplication.m */; }; + 9BC70EDC1D2E3E3100EDA4CA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */; }; 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 */; }; 9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9BEEF0661D04CE8D00FC52B3 /* start_ss_local.sh */; }; @@ -52,6 +52,8 @@ 9BEEF0751D04EF3E00FC52B3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEEF0731D04EF3E00FC52B3 /* PreferencesWindowController.swift */; }; 9BEEF0781D04FE8A00FC52B3 /* LaunchAgentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEEF0771D04FE8A00FC52B3 /* LaunchAgentUtils.swift */; }; 9BEEF07B1D05631500FC52B3 /* AdvPreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEEF0791D05631500FC52B3 /* AdvPreferencesWindowController.swift */; }; + C8E42A6C1D4F270A0074C7EA /* UserRulesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E42A6A1D4F270A0074C7EA /* UserRulesController.swift */; }; + C8E42A6E1D4F2CAF0074C7EA /* UserRulesController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C8E42A701D4F2CAF0074C7EA /* UserRulesController.xib */; }; E0E57CCA7EB34B90F9D340F2 /* Pods_ShadowsocksX_NGTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 388120F062D7EB7DD0D8DDCA /* Pods_ShadowsocksX_NGTests.framework */; }; F0809FF1595BE2966343D3C7 /* libPods-proxy_conf_helper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7783AEDB4A3BDDC9FF16AC /* libPods-proxy_conf_helper.a */; }; /* End PBXBuildFile section */ @@ -159,6 +161,9 @@ 9BEEF0771D04FE8A00FC52B3 /* LaunchAgentUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LaunchAgentUtils.swift; sourceTree = ""; }; 9BEEF0791D05631500FC52B3 /* AdvPreferencesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvPreferencesWindowController.swift; sourceTree = ""; }; B4E6A97CA843F3943524B686 /* Pods-proxy_conf_helper.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-proxy_conf_helper.debug.xcconfig"; path = "Pods/Target Support Files/Pods-proxy_conf_helper/Pods-proxy_conf_helper.debug.xcconfig"; sourceTree = ""; }; + C8E42A6A1D4F270A0074C7EA /* UserRulesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserRulesController.swift; sourceTree = ""; }; + C8E42A6F1D4F2CAF0074C7EA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UserRulesController.xib; sourceTree = ""; }; + C8E42A721D4F2CB10074C7EA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/UserRulesController.strings"; sourceTree = ""; }; E9E9FB3855DA55D0710EE7BD /* Pods-ShadowsocksX-NG.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShadowsocksX-NG.release.xcconfig"; path = "Pods/Target Support Files/Pods-ShadowsocksX-NG/Pods-ShadowsocksX-NG.release.xcconfig"; sourceTree = ""; }; FE3237E9FB24D9B924A0E630 /* Pods-ShadowsocksX-NG.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShadowsocksX-NG.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ShadowsocksX-NG/Pods-ShadowsocksX-NG.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -307,6 +312,8 @@ 9B2491B91D0ACC3E003BBECC /* AdvPreferencesWindowController.xib */, 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */, 9B0D55481D2CC85400A4A8E2 /* ProxyPreferencesController.xib */, + C8E42A6A1D4F270A0074C7EA /* UserRulesController.swift */, + C8E42A701D4F2CAF0074C7EA /* UserRulesController.xib */, ); name = UI; sourceTree = ""; @@ -447,10 +454,10 @@ 9BE8FBBF1D0B211600CAFD01 /* libcrypto.1.0.0.dylib in Resources */, 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */, 9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */, - 9B172A6A1D0ADDDD00B87B9A /* Localizable.strings in Resources */, 9B0D55461D2CC85400A4A8E2 /* ProxyPreferencesController.xib in Resources */, 9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */, 9B3FFF291D08A1DF0019A709 /* user-rule.txt in Resources */, + C8E42A6E1D4F2CAF0074C7EA /* UserRulesController.xib in Resources */, 9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */, 9B3FFF391D08CF110019A709 /* qrcode.min.js in Resources */, 9B3FFF3A1D08CF110019A709 /* qrcode.htm in Resources */, @@ -463,6 +470,7 @@ 9B2491B71D0ACC3E003BBECC /* AdvPreferencesWindowController.xib in Resources */, 9B3FFF381D08CF110019A709 /* jquery.min.js in Resources */, 9B3FFF271D0898EB0019A709 /* gfwlist.txt in Resources */, + 9BC70EDC1D2E3E3100EDA4CA /* Localizable.strings in Resources */, 9B0BFFEE1D0460A70040E62B /* MainMenu.xib in Resources */, 9B3FFF4C1D09D8F70019A709 /* install_helper.sh in Resources */, 9B07EFAC1D048E880052D9DF /* menu_icon@2x.png in Resources */, @@ -625,6 +633,7 @@ 9B0BFFE91D0460A70040E62B /* AppDelegate.swift in Sources */, 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */, 9BEEF0781D04FE8A00FC52B3 /* LaunchAgentUtils.swift in Sources */, + C8E42A6C1D4F270A0074C7EA /* UserRulesController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -705,6 +714,15 @@ name = AdvPreferencesWindowController.xib; sourceTree = ""; }; + C8E42A701D4F2CAF0074C7EA /* UserRulesController.xib */ = { + isa = PBXVariantGroup; + children = ( + C8E42A6F1D4F2CAF0074C7EA /* Base */, + C8E42A721D4F2CB10074C7EA /* zh-Hans */, + ); + name = UserRulesController.xib; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 74b1b1a..3fc0af2 100644 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -16,7 +16,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele var preferencesWinCtrl: PreferencesWindowController! var advPreferencesWinCtrl: AdvPreferencesWindowController! var proxyPreferencesWinCtrl: ProxyPreferencesController! - + var editUserRulesWinCtrl: UserRulesController! + var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController() @IBOutlet weak var window: NSWindow! @@ -24,11 +25,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele @IBOutlet weak var runningStatusMenuItem: NSMenuItem! @IBOutlet weak var toggleRunningMenuItem: NSMenuItem! + @IBOutlet weak var proxyMenuItem: NSMenuItem! @IBOutlet weak var autoModeMenuItem: NSMenuItem! @IBOutlet weak var globalModeMenuItem: NSMenuItem! @IBOutlet weak var manualModeMenuItem: NSMenuItem! @IBOutlet weak var serversMenuItem: NSMenuItem! + @IBOutlet var showQRCodeMenuItem: NSMenuItem! + @IBOutlet var scanQRCodeMenuItem: NSMenuItem! @IBOutlet var serversPreferencesMenuItem: NSMenuItem! @IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem! @@ -178,23 +182,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } @IBAction func editUserRulesForPAC(sender: NSMenuItem) { - let url = NSURL(fileURLWithPath: PACUserRuleFilePath) - NSWorkspace.sharedWorkspace().openURL(url) - } - - @IBAction func applyUserRulesForPAC(sender: NSMenuItem) { - if GeneratePACFile() { - // Popup a user notification - let notification = NSUserNotification() - notification.title = "PAC has been updated by User Rules.".localized - NSUserNotificationCenter.defaultUserNotificationCenter() - .deliverNotification(notification) - } else { - let notification = NSUserNotification() - notification.title = "It's failed to update PAC by User Rules.".localized - NSUserNotificationCenter.defaultUserNotificationCenter() - .deliverNotification(notification) + if editUserRulesWinCtrl != nil { + editUserRulesWinCtrl.close() } + let ctrl = UserRulesController(windowNibName: "UserRulesController") + editUserRulesWinCtrl = ctrl + + ctrl.showWindow(self) + NSApp.activateIgnoringOtherApps(true) + ctrl.window?.makeKeyAndOrderFront(self) } @IBAction func showQRCodeForCurrentServer(sender: NSMenuItem) { @@ -331,14 +327,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele let defaults = NSUserDefaults.standardUserDefaults() let mode = defaults.stringForKey("ShadowsocksRunningMode") if mode == "auto" { + proxyMenuItem.title = "Proxy - Auto By PAC".localized autoModeMenuItem.state = 1 globalModeMenuItem.state = 0 manualModeMenuItem.state = 0 } else if mode == "global" { + proxyMenuItem.title = "Proxy - Global".localized autoModeMenuItem.state = 0 globalModeMenuItem.state = 1 manualModeMenuItem.state = 0 } else if mode == "manual" { + proxyMenuItem.title = "Proxy - Manual".localized autoModeMenuItem.state = 0 globalModeMenuItem.state = 0 manualModeMenuItem.state = 1 @@ -364,6 +363,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele func updateServersMenu() { let mgr = ServerProfileManager.instance serversMenuItem.submenu?.removeAllItems() + let showQRItem = showQRCodeMenuItem + let scanQRItem = scanQRCodeMenuItem let preferencesItem = serversPreferencesMenuItem var i = 0 @@ -389,6 +390,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele if !mgr.profiles.isEmpty { serversMenuItem.submenu?.addItem(NSMenuItem.separatorItem()) } + serversMenuItem.submenu?.addItem(showQRItem) + serversMenuItem.submenu?.addItem(scanQRItem) + serversMenuItem.submenu?.addItem(NSMenuItem.separatorItem()) serversMenuItem.submenu?.addItem(preferencesItem) } diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Contents.json b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Contents.json index 7cd4f8e..db41bc0 100755 --- a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -3,61 +3,61 @@ { "size" : "16x16", "idiom" : "mac", - "filename" : "icon_16x16.png", + "filename" : "Icon_16x16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", - "filename" : "icon_16x16@2x.png", + "filename" : "Icon_16x16@2x.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "icon_32x32.png", + "filename" : "Icon_32x32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "icon_32x32@2x.png", + "filename" : "Icon_32x32@2x.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "icon_128x128.png", + "filename" : "Icon_128x128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "icon_128x128@2x.png", + "filename" : "Icon_128x128@2x.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "icon_256x256.png", + "filename" : "Icon_256x256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "icon_256x256@2x.png", + "filename" : "Icon_256x256@2x.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "icon_512x512.png", + "filename" : "Icon_512x512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "icon_512x512@2x.png", + "filename" : "Icon_512x512@2x.png", "scale" : "2x" } ], diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png b/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png old mode 100755 new mode 100644 diff --git a/ShadowsocksX-NG/Base.lproj/Localizable.strings b/ShadowsocksX-NG/Base.lproj/Localizable.strings index f92197d..4ca70bf 100644 --- a/ShadowsocksX-NG/Base.lproj/Localizable.strings +++ b/ShadowsocksX-NG/Base.lproj/Localizable.strings @@ -39,3 +39,9 @@ "Shadowsocks: Off" = "Shadowsocks: Off"; "Turn Shadowsocks On" = "Turn Shadowsocks On"; + +"Proxy - Auto By PAC" = "Proxy - Auto By PAC"; + +"Proxy - Global" = "Proxy - Global"; + +"Proxy - Manual" = "Proxy - Manual"; diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 79c059f..381559a 100644 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -18,9 +18,12 @@ + + + @@ -60,6 +63,19 @@ + + + + + + + + + + + + + @@ -70,11 +86,23 @@ - + + + + + + + + + + + + + @@ -84,46 +112,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib index 07f4c19..8142d87 100644 --- a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib +++ b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib @@ -1,5 +1,5 @@ - + @@ -24,16 +24,16 @@ - + - + - + - + @@ -84,7 +84,7 @@ - + @@ -241,39 +241,10 @@ - - - + - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + diff --git a/ShadowsocksX-NG/Base.lproj/UserRulesController.xib b/ShadowsocksX-NG/Base.lproj/UserRulesController.xib new file mode 100644 index 0000000..08a10c6 --- /dev/null +++ b/ShadowsocksX-NG/Base.lproj/UserRulesController.xib @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShadowsocksX-NG/Info.plist b/ShadowsocksX-NG/Info.plist index 7e4cccf..f350173 100644 --- a/ShadowsocksX-NG/Info.plist +++ b/ShadowsocksX-NG/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleSignature ???? CFBundleURLTypes diff --git a/ShadowsocksX-NG/LaunchAgentUtils.swift b/ShadowsocksX-NG/LaunchAgentUtils.swift index a00a9c1..ca0deac 100644 --- a/ShadowsocksX-NG/LaunchAgentUtils.swift +++ b/ShadowsocksX-NG/LaunchAgentUtils.swift @@ -26,7 +26,14 @@ func getFileSHA1Sum(filepath: String) -> String { func generateSSLocalLauchAgentPlist() -> Bool { let sslocalPath = NSHomeDirectory() + APP_SUPPORT_DIR + "ss-local" let logFilePath = NSHomeDirectory() + "/Library/Logs/ss-local.log" - let plistFilepath = NSHomeDirectory() + LAUNCH_AGENT_DIR + LAUNCH_AGENT_CONF_NAME + let launchAgentDirPath = NSHomeDirectory() + LAUNCH_AGENT_DIR + let plistFilepath = launchAgentDirPath + LAUNCH_AGENT_CONF_NAME + + // Ensure launch agent directory is existed. + let fileMgr = NSFileManager.defaultManager() + if !fileMgr.fileExistsAtPath(launchAgentDirPath) { + try! fileMgr.createDirectoryAtPath(launchAgentDirPath, withIntermediateDirectories: true, attributes: nil) + } let oldSha1Sum = getFileSHA1Sum(plistFilepath) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index 45fc92a..6cff2ce 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -26,6 +26,8 @@ class PreferencesWindowController: NSWindowController @IBOutlet weak var copyURLBtn: NSButton! + let tableViewDragType: String = "ss.server.profile.data" + var defaults: NSUserDefaults! var profileMgr: ServerProfileManager! @@ -56,6 +58,10 @@ class PreferencesWindowController: NSWindowController updateProfileBoxVisible() } + override func awakeFromNib() { + profilesTableView.registerForDraggedTypes([tableViewDragType]) + } + @IBAction func addProfile(sender: NSButton) { if editingProfile != nil && !editingProfile.isValid(){ return @@ -203,6 +209,58 @@ class PreferencesWindowController: NSWindowController return "" } + // Drag & Drop reorder rows + + func tableView(tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { + let item = NSPasteboardItem() + item.setString(String(row), forType: tableViewDragType) + return item + } + + func tableView(tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int + , proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation { + if dropOperation == .Above { + return .Move + } + return .None + } + + func tableView(tableView: NSTableView, acceptDrop info: NSDraggingInfo + , row: Int, dropOperation: NSTableViewDropOperation) -> Bool { + if let mgr = profileMgr { + var oldIndexes = [Int]() + info.enumerateDraggingItemsWithOptions([], forView: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { + if let str = ($0.0.item as! NSPasteboardItem).stringForType(self.tableViewDragType), index = Int(str) { + oldIndexes.append(index) + } + } + + var oldIndexOffset = 0 + var newIndexOffset = 0 + + // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly. + // You may want to move rows in your content array and then call `tableView.reloadData()` instead. + tableView.beginUpdates() + for oldIndex in oldIndexes { + if oldIndex < row { + let o = mgr.profiles.removeAtIndex(oldIndex + oldIndexOffset) + mgr.profiles.insert(o, atIndex:row - 1) + tableView.moveRowAtIndex(oldIndex + oldIndexOffset, toIndex: row - 1) + oldIndexOffset -= 1 + } else { + let o = mgr.profiles.removeAtIndex(oldIndex) + mgr.profiles.insert(o, atIndex:row + newIndexOffset) + tableView.moveRowAtIndex(oldIndex, toIndex: row + newIndexOffset) + newIndexOffset += 1 + } + } + tableView.endUpdates() + + return true + } + return false + } + //-------------------------------------------------- // For NSTableViewDelegate diff --git a/ShadowsocksX-NG/ProxyConfHelper.m b/ShadowsocksX-NG/ProxyConfHelper.m index 282bbc7..d18303d 100644 --- a/ShadowsocksX-NG/ProxyConfHelper.m +++ b/ShadowsocksX-NG/ProxyConfHelper.m @@ -136,7 +136,15 @@ } + (void)disableProxy { - NSMutableArray* args = [@[@"--mode", @"off"]mutableCopy]; + // 带上所有参数是为了判断是否原有代理设置是否由ssx-ng设置的。如果是用户手工设置的其他配置,则不进行清空。 + NSString* urlString = [NSString stringWithFormat:@"%@/.ShadowsocksX-NG/gfwlist.js", NSHomeDirectory()]; + NSURL* url = [NSURL fileURLWithPath:urlString]; + NSUInteger port = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalSocks5.ListenPort"]; + + NSMutableArray* args = [@[@"--mode", @"off" + , @"--port", [NSString stringWithFormat:@"%lu", (unsigned long)port] + , @"--pac-url", [url absoluteString] + ]mutableCopy]; [self addArguments4ManualSpecifyNetworkServices:args]; [self callHelper:args]; } diff --git a/ShadowsocksX-NG/UserRulesController.swift b/ShadowsocksX-NG/UserRulesController.swift new file mode 100644 index 0000000..3e020cc --- /dev/null +++ b/ShadowsocksX-NG/UserRulesController.swift @@ -0,0 +1,53 @@ +// +// UserRulesController.swift +// ShadowsocksX-NG +// +// Created by 周斌佳 on 16/8/1. +// Copyright © 2016年 qiuyuzhou. All rights reserved. +// + +import Cocoa + +class UserRulesController: NSWindowController { + + @IBOutlet var userRulesView: NSTextView! + + override func windowDidLoad() { + super.windowDidLoad() + + let fileMgr = NSFileManager.defaultManager() + if !fileMgr.fileExistsAtPath(PACUserRuleFilePath) { + let src = NSBundle.mainBundle().pathForResource("user-rule", ofType: "txt") + try! fileMgr.copyItemAtPath(src!, toPath: PACUserRuleFilePath) + } + + let str = try? String(contentsOfFile: PACUserRuleFilePath, encoding: NSUTF8StringEncoding) + userRulesView.string = str + } + + @IBAction func didCancel(sender: AnyObject) { + window?.performClose(self) + } + + @IBAction func didOK(sender: AnyObject) { + if let str = userRulesView.string { + do { + try str.dataUsingEncoding(NSUTF8StringEncoding)?.writeToFile(PACUserRuleFilePath, options: .DataWritingAtomic) + + if GeneratePACFile() { + // Popup a user notification + let notification = NSUserNotification() + notification.title = "PAC has been updated by User Rules.".localized + NSUserNotificationCenter.defaultUserNotificationCenter() + .deliverNotification(notification) + } else { + let notification = NSUserNotification() + notification.title = "It's failed to update PAC by User Rules.".localized + NSUserNotificationCenter.defaultUserNotificationCenter() + .deliverNotification(notification) + } + } catch {} + } + window?.performClose(self) + } +} diff --git a/ShadowsocksX-NG/proxy_conf_helper_version.h b/ShadowsocksX-NG/proxy_conf_helper_version.h index 95c2bbc..da95803 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.2.0" +#define kProxyConfHelperVersion @"1.3.1" #endif /* proxy_conf_helper_version_h */ diff --git a/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings index 045517c..47d528d 100644 --- a/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings +++ b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings @@ -44,3 +44,9 @@ "Turn Shadowsocks On" = "打开 Shadowsocks"; +"Proxy - Auto By PAC" = "代理 - PAC自动"; + +"Proxy - Global" = "代理 - 全局"; + +"Proxy - Manual" = "代理 - 手动"; + diff --git a/ShadowsocksX-NG/zh-Hans.lproj/UserRulesController.strings b/ShadowsocksX-NG/zh-Hans.lproj/UserRulesController.strings new file mode 100644 index 0000000..c432a05 --- /dev/null +++ b/ShadowsocksX-NG/zh-Hans.lproj/UserRulesController.strings @@ -0,0 +1,9 @@ + +/* Class = "NSButtonCell"; title = "OK"; ObjectID = "0Sc-cm-Vyu"; */ +"0Sc-cm-Vyu.title" = "确定"; + +/* Class = "NSWindow"; title = "User Rules"; ObjectID = "F0z-JX-Cv5"; */ +"F0z-JX-Cv5.title" = "用户规则"; + +/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "QAR-9i-kmv"; */ +"QAR-9i-kmv.title" = "取消"; diff --git a/proxy_conf_helper/main.m b/proxy_conf_helper/main.m index 600cfe2..039846b 100644 --- a/proxy_conf_helper/main.m +++ b/proxy_conf_helper/main.m @@ -67,10 +67,6 @@ int main(int argc, const char * argv[]) if (!portString) { return 1; } - port = [portString integerValue]; - if (0 == port) { - return 1; - } } else if (![@"off" isEqualToString:mode]) { return 1; } @@ -79,6 +75,12 @@ int main(int argc, const char * argv[]) return 0; } + if (portString) { + port = [portString integerValue]; + if (0 == port) { + return 1; + } + } static AuthorizationRef authRef; static AuthorizationFlags authFlags; @@ -126,11 +128,16 @@ int main(int argc, const char * argv[]) if (modify) { + NSString* prefPath = [NSString stringWithFormat:@"/%@/%@/%@", kSCPrefNetworkServices + , key, kSCEntNetProxies]; + if ([mode isEqualToString:@"auto"]) { [proxies setObject:pacURL forKey:(NSString *)kCFNetworkProxiesProxyAutoConfigURLString]; [proxies setObject:[NSNumber numberWithInt:1] forKey:(NSString *)kCFNetworkProxiesProxyAutoConfigEnable]; + SCPreferencesPathSetValue(prefRef, (__bridge CFStringRef)prefPath + , (__bridge CFDictionaryRef)proxies); } else if ([mode isEqualToString:@"global"]) { @@ -142,9 +149,29 @@ int main(int argc, const char * argv[]) kCFNetworkProxiesSOCKSEnable]; [proxies setObject:@[@"127.0.0.1", @"localhost"] forKey:(NSString *)kCFNetworkProxiesExceptionsList]; + SCPreferencesPathSetValue(prefRef, (__bridge CFStringRef)prefPath + , (__bridge CFDictionaryRef)proxies); + } else if ([mode isEqualToString:@"off"]) { + if (pacURL != nil && portString != nil) { + // 取原来的配置,判断是否为shadowsocksX-NG设置的 + NSDictionary* oldProxies + = (__bridge NSDictionary*)SCPreferencesPathGetValue(prefRef + , (__bridge CFStringRef)prefPath); + + if (([oldProxies[(NSString *)kCFNetworkProxiesProxyAutoConfigURLString] isEqualToString:pacURL] + &&[oldProxies[(NSString *)kCFNetworkProxiesProxyAutoConfigEnable] isEqual:[NSNumber numberWithInt:1]]) + ||([oldProxies[(NSString*)kCFNetworkProxiesSOCKSProxy] isEqualToString:@"127.0.0.1"] + &&[oldProxies[(NSString*)kCFNetworkProxiesSOCKSPort] isEqualTo:[NSNumber numberWithInteger:port]] + &&[oldProxies[(NSString*)kCFNetworkProxiesSOCKSEnable] isEqual:[NSNumber numberWithInt:1]]) + ) { + SCPreferencesPathSetValue(prefRef, (__bridge CFStringRef)prefPath + , (__bridge CFDictionaryRef)proxies); + } + } else { + SCPreferencesPathSetValue(prefRef, (__bridge CFStringRef)prefPath + , (__bridge CFDictionaryRef)proxies); + } } - - SCPreferencesPathSetValue(prefRef, (__bridge CFStringRef)[NSString stringWithFormat:@"/%@/%@/%@", kSCPrefNetworkServices, key, kSCEntNetProxies], (__bridge CFDictionaryRef)proxies); } }