From f356cd2d5c3bf741e0762caf255c06bc4c86eafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Sat, 15 Apr 2017 09:03:42 +0800 Subject: [PATCH 01/30] =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E5=8F=AF=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShadowsocksX-NG/PreferencesWindowController.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index 9cb0ddb..5957831 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -107,6 +107,7 @@ class PreferencesWindowController: NSWindowController override func awakeFromNib() { profilesTableView.register(forDraggedTypes: [tableViewDragType]) + profilesTableView.allowsMultipleSelection = true } @IBAction func addProfile(_ sender: NSButton) { @@ -129,11 +130,14 @@ class PreferencesWindowController: NSWindowController } @IBAction func removeProfile(_ sender: NSButton) { - let index = profilesTableView.selectedRow + let index = Int(profilesTableView.selectedRowIndexes.first!) if index >= 0 { profilesTableView.beginUpdates() - profileMgr.profiles.remove(at: index) - profilesTableView.removeRows(at: IndexSet(integer: index), withAnimation: .effectFade) + for (_, _) in profilesTableView.selectedRowIndexes.enumerated() { + print(profileMgr.profiles.count) + profileMgr.profiles.remove(at: index) + profilesTableView.removeRows(at: IndexSet(integer: index), withAnimation: .effectFade) + } profilesTableView.endUpdates() } updateProfileBoxVisible() From 3df85772b016726a7cc62f3515eb499650b5c7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Sat, 15 Apr 2017 09:10:42 +0800 Subject: [PATCH 02/30] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8D=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E5=A4=9A=E9=80=89=E5=88=A0=E9=99=A4=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShadowsocksX-NG/PreferencesWindowController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index 5957831..a4f3082 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -131,12 +131,14 @@ class PreferencesWindowController: NSWindowController @IBAction func removeProfile(_ sender: NSButton) { let index = Int(profilesTableView.selectedRowIndexes.first!) + var deleteCount = 0 if index >= 0 { profilesTableView.beginUpdates() - for (_, _) in profilesTableView.selectedRowIndexes.enumerated() { + for (_, toDeleteIndex) in profilesTableView.selectedRowIndexes.enumerated() { print(profileMgr.profiles.count) - profileMgr.profiles.remove(at: index) - profilesTableView.removeRows(at: IndexSet(integer: index), withAnimation: .effectFade) + profileMgr.profiles.remove(at: toDeleteIndex - deleteCount) + profilesTableView.removeRows(at: IndexSet(integer: toDeleteIndex - deleteCount), withAnimation: .effectFade) + deleteCount += 1 } profilesTableView.endUpdates() } From f6a46311c39a1ef9807aa69b8d550a6837be0010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Sat, 15 Apr 2017 22:40:25 +0800 Subject: [PATCH 03/30] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E9=85=8D=E7=BD=AE=E5=90=8E=E6=BB=9A=E5=8A=A8=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShadowsocksX-NG/PreferencesWindowController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index a4f3082..b8deeb2 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -142,6 +142,8 @@ class PreferencesWindowController: NSWindowController } profilesTableView.endUpdates() } + self.profilesTableView.scrollRowToVisible(index-1) + self.profilesTableView.selectRowIndexes(IndexSet(integer: index-1), byExtendingSelection: false) updateProfileBoxVisible() } From 923aad5d94d9ad0920d8a18a7863a56545d89f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Mon, 24 Apr 2017 15:36:08 +0800 Subject: [PATCH 04/30] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9C=A8=E5=A4=9A?= =?UTF-8?q?=E9=80=89=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?profile=E5=A4=8D=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PreferencesWindowController.swift | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index b8deeb2..883af37 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -168,16 +168,23 @@ class PreferencesWindowController: NSWindowController } @IBAction func duplicate(_ sender: Any) { - let profile = profileMgr.profiles[profilesTableView.clickedRow] - let duplicateProfile = profile.copy() as! ServerProfile - duplicateProfile.uuid = UUID().uuidString - profileMgr.profiles.insert(duplicateProfile, at: profilesTableView.clickedRow+1) - profilesTableView.beginUpdates() - let index = IndexSet(integer: profileMgr.profiles.count-1) - profilesTableView.insertRows(at: index, withAnimation: .effectFade) - self.profilesTableView.scrollRowToVisible(profilesTableView.clickedRow+1) - self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) - profilesTableView.endUpdates() + var copyCount = 0 + for (_, toDuplicateIndex) in profilesTableView.selectedRowIndexes.enumerated() { + print(profileMgr.profiles.count) + let profile = profileMgr.profiles[toDuplicateIndex + copyCount] + let duplicateProfile = profile.copy() as! ServerProfile + duplicateProfile.uuid = UUID().uuidString + profileMgr.profiles.insert(duplicateProfile, at:toDuplicateIndex + copyCount) + + profilesTableView.beginUpdates() + let index = IndexSet(integer: toDuplicateIndex + copyCount) + profilesTableView.insertRows(at: index, withAnimation: .effectFade) + self.profilesTableView.scrollRowToVisible(toDuplicateIndex + copyCount) + self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) + profilesTableView.endUpdates() + + copyCount += 1 + } updateProfileBoxVisible() } From efd0c767631746219875d6cb4a0eddeec0ea5d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Mon, 24 Apr 2017 17:24:11 +0800 Subject: [PATCH 05/30] =?UTF-8?q?Revert=20"=E5=AE=9E=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E5=A4=9A=E9=80=89=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E5=A4=9A?= =?UTF-8?q?=E4=B8=AAprofile=E5=A4=8D=E5=88=B6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 923aad5d94d9ad0920d8a18a7863a56545d89f91. --- .../PreferencesWindowController.swift | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index 883af37..b8deeb2 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -168,23 +168,16 @@ class PreferencesWindowController: NSWindowController } @IBAction func duplicate(_ sender: Any) { - var copyCount = 0 - for (_, toDuplicateIndex) in profilesTableView.selectedRowIndexes.enumerated() { - print(profileMgr.profiles.count) - let profile = profileMgr.profiles[toDuplicateIndex + copyCount] - let duplicateProfile = profile.copy() as! ServerProfile - duplicateProfile.uuid = UUID().uuidString - profileMgr.profiles.insert(duplicateProfile, at:toDuplicateIndex + copyCount) - - profilesTableView.beginUpdates() - let index = IndexSet(integer: toDuplicateIndex + copyCount) - profilesTableView.insertRows(at: index, withAnimation: .effectFade) - self.profilesTableView.scrollRowToVisible(toDuplicateIndex + copyCount) - self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) - profilesTableView.endUpdates() - - copyCount += 1 - } + let profile = profileMgr.profiles[profilesTableView.clickedRow] + let duplicateProfile = profile.copy() as! ServerProfile + duplicateProfile.uuid = UUID().uuidString + profileMgr.profiles.insert(duplicateProfile, at: profilesTableView.clickedRow+1) + profilesTableView.beginUpdates() + let index = IndexSet(integer: profileMgr.profiles.count-1) + profilesTableView.insertRows(at: index, withAnimation: .effectFade) + self.profilesTableView.scrollRowToVisible(profilesTableView.clickedRow+1) + self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) + profilesTableView.endUpdates() updateProfileBoxVisible() } From b89d9879f2f00faf226aa82ad3ee4af233fb6028 Mon Sep 17 00:00:00 2001 From: Julien Schmidt Date: Sat, 29 Apr 2017 01:00:39 +0800 Subject: [PATCH 06/30] README: Mention AEAD Ciphers instead of OTA Support OTA is deprecated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ed86d4..3f4b37e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/) - Scan QRCode from screen. - Auto launch at login. - User rules for PAC. -- Support OTA +- Support for [AEAD Ciphers](https://shadowsocks.org/en/spec/AEAD-Ciphers.html) - HTTP Proxy by [privoxy](http://www.privoxy.org/) - Over [kcptun](https://github.com/xtaci/kcptun). Version 20170322 - Export/Import configure file. From 0f050f6e442d820a8da1799ea367677732a679f8 Mon Sep 17 00:00:00 2001 From: Gino Zhao Date: Thu, 11 May 2017 13:52:07 +0800 Subject: [PATCH 07/30] remove keeplive key from plist, update shell script adding launchctl start/stop command keeplive key makes the services of shadowsocks-ng automatically start when logining system Replace the original way with launchctl start/stop, then start the services when needed as shadowsocks-ng using --- ShadowsocksX-NG/LaunchAgentUtils.swift | 3 --- ShadowsocksX-NG/start_kcptun.sh | 1 + ShadowsocksX-NG/start_privoxy.sh | 1 + ShadowsocksX-NG/start_ss_local.sh | 3 ++- ShadowsocksX-NG/stop_kcptun.sh | 1 + ShadowsocksX-NG/stop_privoxy.sh | 3 +-- ShadowsocksX-NG/stop_ss_local.sh | 5 ++--- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ShadowsocksX-NG/LaunchAgentUtils.swift b/ShadowsocksX-NG/LaunchAgentUtils.swift index 9e7dfba..d74116d 100644 --- a/ShadowsocksX-NG/LaunchAgentUtils.swift +++ b/ShadowsocksX-NG/LaunchAgentUtils.swift @@ -60,7 +60,6 @@ func generateSSLocalLauchAgentPlist() -> Bool { let dict: NSMutableDictionary = [ "Label": "com.qiuyuzhou.shadowsocksX-NG.local", "WorkingDirectory": NSHomeDirectory() + APP_SUPPORT_DIR, - "KeepAlive": true, "StandardOutPath": logFilePath, "StandardErrorPath": logFilePath, "ProgramArguments": arguments, @@ -203,7 +202,6 @@ func generatePrivoxyLauchAgentPlist() -> Bool { let dict: NSMutableDictionary = [ "Label": "com.qiuyuzhou.shadowsocksX-NG.http", "WorkingDirectory": NSHomeDirectory() + APP_SUPPORT_DIR, - "KeepAlive": true, "StandardOutPath": logFilePath, "StandardErrorPath": logFilePath, "ProgramArguments": arguments @@ -358,7 +356,6 @@ func generateKcptunLauchAgentPlist() -> Bool { let dict: NSMutableDictionary = [ "Label": "com.qiuyuzhou.shadowsocksX-NG.kcptun", "WorkingDirectory": NSHomeDirectory() + APP_SUPPORT_DIR, - "KeepAlive": true, "StandardOutPath": logFilePath, "StandardErrorPath": logFilePath, "ProgramArguments": arguments, diff --git a/ShadowsocksX-NG/start_kcptun.sh b/ShadowsocksX-NG/start_kcptun.sh index 580d40e..3ca2463 100755 --- a/ShadowsocksX-NG/start_kcptun.sh +++ b/ShadowsocksX-NG/start_kcptun.sh @@ -7,3 +7,4 @@ # Copyright © 2017年 qiuyuzhou. All rights reserved. launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" +launchctl start com.qiuyuzhou.shadowsocksX-NG.kcptun diff --git a/ShadowsocksX-NG/start_privoxy.sh b/ShadowsocksX-NG/start_privoxy.sh index bedef6a..dbc7d7b 100755 --- a/ShadowsocksX-NG/start_privoxy.sh +++ b/ShadowsocksX-NG/start_privoxy.sh @@ -7,3 +7,4 @@ # Copyright © 2016年 zhfish. All rights reserved. launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" +launchctl start com.qiuyuzhou.shadowsocksX-NG.http diff --git a/ShadowsocksX-NG/start_ss_local.sh b/ShadowsocksX-NG/start_ss_local.sh index b4047d3..a59973e 100755 --- a/ShadowsocksX-NG/start_ss_local.sh +++ b/ShadowsocksX-NG/start_ss_local.sh @@ -6,4 +6,5 @@ # Created by 邱宇舟 on 16/6/6. # Copyright © 2016年 qiuyuzhou. All rights reserved. -launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" \ No newline at end of file +launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" +launchctl start com.qiuyuzhou.shadowsocksX-NG.local diff --git a/ShadowsocksX-NG/stop_kcptun.sh b/ShadowsocksX-NG/stop_kcptun.sh index b192aea..68536fc 100755 --- a/ShadowsocksX-NG/stop_kcptun.sh +++ b/ShadowsocksX-NG/stop_kcptun.sh @@ -6,4 +6,5 @@ # Created by 邱宇舟 on 2017/1/11. # Copyright © 2017年 qiuyuzhou. All rights reserved. +launchctl stop com.qiuyuzhou.shadowsocksX-NG.kcptun launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" diff --git a/ShadowsocksX-NG/stop_privoxy.sh b/ShadowsocksX-NG/stop_privoxy.sh index 8cb6a2b..e3ebb94 100755 --- a/ShadowsocksX-NG/stop_privoxy.sh +++ b/ShadowsocksX-NG/stop_privoxy.sh @@ -6,6 +6,5 @@ # Created by 王晨 on 16/10/7. # Copyright © 2016年 zhfish. All rights reserved. - - +launchctl stop com.qiuyuzhou.shadowsocksX-NG.http launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" diff --git a/ShadowsocksX-NG/stop_ss_local.sh b/ShadowsocksX-NG/stop_ss_local.sh index 5a6cf57..15d59b3 100755 --- a/ShadowsocksX-NG/stop_ss_local.sh +++ b/ShadowsocksX-NG/stop_ss_local.sh @@ -6,6 +6,5 @@ # Created by 邱宇舟 on 16/6/6. # Copyright © 2016年 qiuyuzhou. All rights reserved. - - -launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" \ No newline at end of file +launchctl stop com.qiuyuzhou.shadowsocksX-NG.local +launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" From e8e4f9ad11a4b72b831eb245d352a3db4afc2549 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Thu, 20 Jul 2017 15:09:54 +0800 Subject: [PATCH 08/30] =?UTF-8?q?Revert=20"Revert=20"=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=9C=A8=E5=A4=9A=E9=80=89=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84?= =?UTF-8?q?=E5=A4=9A=E4=B8=AAprofile=E5=A4=8D=E5=88=B6""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit efd0c767631746219875d6cb4a0eddeec0ea5d9a. --- .../PreferencesWindowController.swift | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index b8deeb2..883af37 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -168,16 +168,23 @@ class PreferencesWindowController: NSWindowController } @IBAction func duplicate(_ sender: Any) { - let profile = profileMgr.profiles[profilesTableView.clickedRow] - let duplicateProfile = profile.copy() as! ServerProfile - duplicateProfile.uuid = UUID().uuidString - profileMgr.profiles.insert(duplicateProfile, at: profilesTableView.clickedRow+1) - profilesTableView.beginUpdates() - let index = IndexSet(integer: profileMgr.profiles.count-1) - profilesTableView.insertRows(at: index, withAnimation: .effectFade) - self.profilesTableView.scrollRowToVisible(profilesTableView.clickedRow+1) - self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) - profilesTableView.endUpdates() + var copyCount = 0 + for (_, toDuplicateIndex) in profilesTableView.selectedRowIndexes.enumerated() { + print(profileMgr.profiles.count) + let profile = profileMgr.profiles[toDuplicateIndex + copyCount] + let duplicateProfile = profile.copy() as! ServerProfile + duplicateProfile.uuid = UUID().uuidString + profileMgr.profiles.insert(duplicateProfile, at:toDuplicateIndex + copyCount) + + profilesTableView.beginUpdates() + let index = IndexSet(integer: toDuplicateIndex + copyCount) + profilesTableView.insertRows(at: index, withAnimation: .effectFade) + self.profilesTableView.scrollRowToVisible(toDuplicateIndex + copyCount) + self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) + profilesTableView.endUpdates() + + copyCount += 1 + } updateProfileBoxVisible() } From 997fd749eb4fc86fef34c66d542622e62c8700e8 Mon Sep 17 00:00:00 2001 From: Gong Zhang Date: Fri, 21 Jul 2017 10:10:11 +0800 Subject: [PATCH 09/30] Increase status bar icon width from 20pt to 30pt --- ShadowsocksX-NG/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 841c235..75d4f6f 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -48,7 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele let kProfileMenuItemIndexBase = 100 var statusItem: NSStatusItem! - static let StatusItemIconWidth:CGFloat = 20 + static let StatusItemIconWidth:CGFloat = 30 func applicationDidFinishLaunching(_ aNotification: Notification) { From 5f83aad324b3dc2cd2455143909cd2ad32323d94 Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Sat, 29 Jul 2017 14:27:47 +0800 Subject: [PATCH 10/30] Fixes #426 Save network interface configuration --- ShadowsocksX-NG/ProxyInterfacesViewCtrl.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ShadowsocksX-NG/ProxyInterfacesViewCtrl.swift b/ShadowsocksX-NG/ProxyInterfacesViewCtrl.swift index 06d21d0..6173794 100644 --- a/ShadowsocksX-NG/ProxyInterfacesViewCtrl.swift +++ b/ShadowsocksX-NG/ProxyInterfacesViewCtrl.swift @@ -68,5 +68,8 @@ class ProxyInterfacesViewCtrl: NSViewController, NSTableViewDataSource, NSTableV } else { selectedNetworkServices.remove(key) } + + UserDefaults.standard.set(selectedNetworkServices.allObjects, + forKey: "Proxy4NetworkServices") } } From 0ebbaec244c3828675e4d9a2887ba840ac464949 Mon Sep 17 00:00:00 2001 From: Gong Zhang Date: Mon, 31 Jul 2017 11:07:03 +0800 Subject: [PATCH 11/30] Change status item width to NSVariableStatusItemLength --- ShadowsocksX-NG/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 75d4f6f..f1926d4 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -48,7 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele let kProfileMenuItemIndexBase = 100 var statusItem: NSStatusItem! - static let StatusItemIconWidth:CGFloat = 30 + static let StatusItemIconWidth: CGFloat = NSVariableStatusItemLength func applicationDidFinishLaunching(_ aNotification: Notification) { From f1db8fff419adfc564dfc78b63ea88285d13930d Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Tue, 1 Aug 2017 19:31:12 +0800 Subject: [PATCH 12/30] Cleanup: Don't accept optional in init --- ShadowsocksX-NG/ServerProfile.swift | 10 ++++---- ShadowsocksX-NGTests/ServerProfileTests.swift | 24 +++++++------------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index dfb1fa0..f5f01f5 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -31,7 +31,7 @@ class ServerProfile: NSObject, NSCopying { self.uuid = uuid } - convenience init?(url: URL?) { + convenience init?(url: URL) { self.init() func padBase64(string: String) -> String { @@ -44,14 +44,12 @@ class ServerProfile: NSObject, NSCopying { } } - func decodeUrl(url: URL?) -> String? { - guard let urlStr = url?.absoluteString else { - return nil - } + func decodeUrl(url: URL) -> String? { + let urlStr = url.absoluteString let index = urlStr.index(urlStr.startIndex, offsetBy: 5) let encodedStr = urlStr.substring(from: index) guard let data = Data(base64Encoded: padBase64(string: encodedStr)) else { - return url?.absoluteString + return url.absoluteString } guard let decoded = String(data: data, encoding: String.Encoding.utf8) else { return nil diff --git a/ShadowsocksX-NGTests/ServerProfileTests.swift b/ShadowsocksX-NGTests/ServerProfileTests.swift index d942895..8204f42 100644 --- a/ShadowsocksX-NGTests/ServerProfileTests.swift +++ b/ShadowsocksX-NGTests/ServerProfileTests.swift @@ -31,7 +31,7 @@ class ServerProfileTests: XCTestCase { } func testInitWithSelfGeneratedURL() { - let newProfile = ServerProfile.init(url: profile.URL()) + let newProfile = ServerProfile.init(url: profile.URL()!) XCTAssertEqual(newProfile?.serverHost, profile.serverHost) XCTAssertEqual(newProfile?.serverPort, profile.serverPort) @@ -42,7 +42,7 @@ class ServerProfileTests: XCTestCase { } func testInitWithPlainURL() { - let url = URL(string: "ss://aes-256-cfb:password@example.com:8388") + let url = URL(string: "ss://aes-256-cfb:password@example.com:8388")! let profile = ServerProfile(url: url) @@ -57,7 +57,7 @@ class ServerProfileTests: XCTestCase { } func testInitWithPlainURLandQuery() { - let url = URL(string: "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=true") + let url = URL(string: "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=true")! let profile = ServerProfile(url: url) @@ -72,7 +72,7 @@ class ServerProfileTests: XCTestCase { } func testInitWithPlainURLandAnotherQuery() { - let url = URL(string: "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=0") + let url = URL(string: "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=0")! let profile = ServerProfile(url: url) @@ -88,7 +88,7 @@ class ServerProfileTests: XCTestCase { func testInitWithBase64EncodedURL() { // "ss://aes-256-cfb:password@example.com:8388" - let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAZXhhbXBsZS5jb206ODM4OA") + let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAZXhhbXBsZS5jb206ODM4OA")! let profile = ServerProfile(url: url) @@ -104,7 +104,7 @@ class ServerProfileTests: XCTestCase { func testInitWithBase64EncodedURLandQuery() { // "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=true" - let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAZXhhbXBsZS5jb206ODM4OD9SZW1hcms9UHJpc20mT1RBPXRydWU") + let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAZXhhbXBsZS5jb206ODM4OD9SZW1hcms9UHJpc20mT1RBPXRydWU")! let profile = ServerProfile(url: url) @@ -119,15 +119,7 @@ class ServerProfileTests: XCTestCase { } func testInitWithEmptyURL() { - let url = URL(string: "ss://") - - let profile = ServerProfile(url: url) - - XCTAssertNil(profile) - } - - func testInitWithInvalidURL() { - let url = URL(string: "ss://invalid url") + let url = URL(string: "ss://")! let profile = ServerProfile(url: url) @@ -136,7 +128,7 @@ class ServerProfileTests: XCTestCase { func testInitWithBase64EncodedInvalidURL() { // "ss://invalid url" - let url = URL(string: "ss://aW52YWxpZCB1cmw") + let url = URL(string: "ss://aW52YWxpZCB1cmw")! let profile = ServerProfile(url: url) From b1e95babaa9da0e29789cce85de6a65f21285b5a Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Tue, 1 Aug 2017 19:39:46 +0800 Subject: [PATCH 13/30] Fixes possible crash if url is malicious Try scan a QR code with content `ss://sdf invalid`, an uncaught exception raises. --- ShadowsocksX-NG/Utils.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ShadowsocksX-NG/Utils.m b/ShadowsocksX-NG/Utils.m index c77a07a..92912c9 100644 --- a/ShadowsocksX-NG/Utils.m +++ b/ShadowsocksX-NG/Utils.m @@ -56,7 +56,10 @@ void ScanQRCodeOnScreen() { NSLog(@"%@", feature.messageString); if ( [feature.messageString hasPrefix:@"ss://"] ) { - [foundSSUrls addObject:[NSURL URLWithString:feature.messageString]]; + NSURL *url = [NSURL URLWithString:feature.messageString]; + if (url) { + [foundSSUrls addObject:url]; + } } } CGImageRelease(image); From 72c52047f46e9407c46002071b51a6dfdb6bc253 Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Tue, 1 Aug 2017 20:02:58 +0800 Subject: [PATCH 14/30] Adds SIP002 URL for QR scaner --- ShadowsocksX-NG/ServerProfile.swift | 29 ++++++++++++++-- ShadowsocksX-NGTests/ServerProfileTests.swift | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index f5f01f5..2fde458 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -65,17 +65,40 @@ class ServerProfile: NSObject, NSCopying { return nil } guard let host = parsedUrl.host, let port = parsedUrl.port, - let method = parsedUrl.user, let password = parsedUrl.password else { + let user = parsedUrl.user else { return nil } self.serverHost = host self.serverPort = UInt16(port) - self.method = method.lowercased() - self.password = password + // This can be overriden by the fragment part of SIP002 URL remark = parsedUrl.queryItems? .filter({ $0.name == "Remark" }).first?.value ?? "" + + if let password = parsedUrl.password { + self.method = user.lowercased() + self.password = password + } else { + // SIP002 URL have no password section + guard let data = Data(base64Encoded: padBase64(string: user)), + let userInfo = String(data: data, encoding: .utf8) else { + return nil + } + + let parts = userInfo.characters.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) + if parts.count != 2 { + return nil + } + self.method = String(parts[0]).lowercased() + self.password = String(parts[1]) + + // SIP002 defines where to put the profile name + if let profileName = parsedUrl.fragment { + self.remark = profileName + } + } + if let otaStr = parsedUrl.queryItems? .filter({ $0.name == "OTA" }).first?.value { ota = NSString(string: otaStr).boolValue diff --git a/ShadowsocksX-NGTests/ServerProfileTests.swift b/ShadowsocksX-NGTests/ServerProfileTests.swift index 8204f42..c3b4d42 100644 --- a/ShadowsocksX-NGTests/ServerProfileTests.swift +++ b/ShadowsocksX-NGTests/ServerProfileTests.swift @@ -135,6 +135,40 @@ class ServerProfileTests: XCTestCase { XCTAssertNil(profile) } + func testInitWithSIP002URL() { + // "ss://aes-256-cfb:password@example.com:8388?Remark=Prism&OTA=true" + let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ=@example.com:8388/?Remark=Prism&OTA=true")! + + let profile = ServerProfile(url: url) + + XCTAssertNotNil(profile) + + XCTAssertEqual(profile?.serverHost, "example.com") + XCTAssertEqual(profile?.serverPort, 8388) + XCTAssertEqual(profile?.method, "aes-256-cfb") + XCTAssertEqual(profile?.password, "password") + XCTAssertEqual(profile?.remark, "Prism") + XCTAssertEqual(profile?.ota, true) + } + + func testInitWithSIP002URLProfileName() { + let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ=@example.com:8388/#Name")! + + let profile = ServerProfile(url: url) + + XCTAssertNotNil(profile) + XCTAssertEqual(profile?.remark, "Name") + } + + func testInitWithSIP002URLProfileNameOverride() { + let url = URL(string: "ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ=@example.com:8388/?Remark=Name#Overriden")! + + let profile = ServerProfile(url: url) + + XCTAssertNotNil(profile) + XCTAssertEqual(profile?.remark, "Overriden") + } + func testPerformanceExample() { // This is an example of a performance test case. self.measure { From e7cc26f81dd2b82aa2e5898d357b785e8d9938bf Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Tue, 1 Aug 2017 21:40:09 +0800 Subject: [PATCH 15/30] Generates SIP002 QR code --- ShadowsocksX-NG/AppDelegate.swift | 1 + ShadowsocksX-NG/SWBQRCodeWindowController.h | 1 + ShadowsocksX-NG/SWBQRCodeWindowController.m | 9 ++++++ ShadowsocksX-NG/ServerProfile.swift | 34 ++++++++++++++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 841c235..97883e6 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -241,6 +241,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } qrcodeWinCtrl = SWBQRCodeWindowController(windowNibName: "SWBQRCodeWindowController") qrcodeWinCtrl.qrCode = profile.URL()!.absoluteString + qrcodeWinCtrl.legacyQRCode = profile.URL(legacy: true)!.absoluteString qrcodeWinCtrl.title = profile.title() qrcodeWinCtrl.showWindow(self) NSApp.activate(ignoringOtherApps: true) diff --git a/ShadowsocksX-NG/SWBQRCodeWindowController.h b/ShadowsocksX-NG/SWBQRCodeWindowController.h index 85a3e9a..ff9b194 100644 --- a/ShadowsocksX-NG/SWBQRCodeWindowController.h +++ b/ShadowsocksX-NG/SWBQRCodeWindowController.h @@ -11,6 +11,7 @@ @interface SWBQRCodeWindowController : NSWindowController +@property (nonatomic, copy) NSString *legacyQRCode; @property (nonatomic, copy) NSString *qrCode; @property (nonatomic, copy) NSString *title; diff --git a/ShadowsocksX-NG/SWBQRCodeWindowController.m b/ShadowsocksX-NG/SWBQRCodeWindowController.m index a7e97e9..e123be0 100644 --- a/ShadowsocksX-NG/SWBQRCodeWindowController.m +++ b/ShadowsocksX-NG/SWBQRCodeWindowController.m @@ -78,4 +78,13 @@ [pasteboard writeObjects:copiedObjects]; } +- (void)flagsChanged:(NSEvent *)event { + NSUInteger modifiers = event.modifierFlags & NSDeviceIndependentModifierFlagsMask; + if (modifiers & NSAlternateKeyMask) { + [self setQRCode:self.legacyQRCode]; + } else { + [self setQRCode:self.qrCode]; + } +} + @end diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index 2fde458..af86a89 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -246,7 +246,7 @@ class ServerProfile: NSObject, NSCopying { return true } - func URL() -> Foundation.URL? { + private func makeLegacyURL() -> URL? { var url = URLComponents() url.host = serverHost @@ -275,6 +275,38 @@ class ServerProfile: NSObject, NSCopying { } return nil } + + func URL(legacy: Bool = false) -> URL? { + // If you want the URL from <= 1.5.1 + if (legacy) { + return self.makeLegacyURL() + } + + guard let rawUserInfo = "\(method):\(password)".data(using: .utf8) else { + return nil + } + let paddings = CharacterSet(charactersIn: "=") + let userInfo = rawUserInfo.base64EncodedString().trimmingCharacters(in: paddings) + + var items = [URLQueryItem(name: "OTA", value: ota.description)] + if enabledKcptun { + items.append(URLQueryItem(name: "Kcptun", value: enabledKcptun.description)) + items.append(contentsOf: kcptunProfile.urlQueryItems()) + } + + var comps = URLComponents() + + comps.scheme = "ss" + comps.host = serverHost + comps.port = Int(serverPort) + comps.user = userInfo + comps.fragment = remark + comps.queryItems = items + + let url = try? comps.asURL() + + return url + } func title() -> String { if remark.isEmpty { From f54a108d7ec8acb5b24c332e693682becb4a0e4c Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Tue, 1 Aug 2017 23:22:33 +0800 Subject: [PATCH 16/30] Sets path for URL to meet SIP002 requirement --- ShadowsocksX-NG/ServerProfile.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index af86a89..aff8b4e 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -300,6 +300,7 @@ class ServerProfile: NSObject, NSCopying { comps.host = serverHost comps.port = Int(serverPort) comps.user = userInfo + comps.path = "/" // This is required by SIP0002 for URLs with fragment or query comps.fragment = remark comps.queryItems = items From 8e0a284bee3ad9991034f3a842676aa21a9eaad0 Mon Sep 17 00:00:00 2001 From: imnotpopo Date: Wed, 2 Aug 2017 17:06:58 +1000 Subject: [PATCH 17/30] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f4b37e..3304850 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ShadowsocksX-NG -Current version is 1.5 +Current version is 1.5.1 [![Build Status](https://travis-ci.org/shadowsocks/ShadowsocksX-NG.svg?branch=develop)](https://travis-ci.org/shadowsocks/ShadowsocksX-NG) @@ -59,7 +59,7 @@ Run ss-local as a background service through launchd, not as an in-app process. So after you quit the app, the ss-local maybe be still running. Added a manual mode which won't configure the system proxy settings. -Then you could configure your apps to use socks5 proxy manual. +Then you could configure your apps to use the socks5 proxy manually. ## Contributing From ad038d2cc4dc126cac5a1e8c9e97f2329e8261d4 Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Wed, 2 Aug 2017 20:49:11 +0800 Subject: [PATCH 18/30] Fixes #348 Prevent cascading toast window --- ShadowsocksX-NG/ToastWindowController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ShadowsocksX-NG/ToastWindowController.swift b/ShadowsocksX-NG/ToastWindowController.swift index b688721..161a105 100644 --- a/ShadowsocksX-NG/ToastWindowController.swift +++ b/ShadowsocksX-NG/ToastWindowController.swift @@ -30,6 +30,8 @@ class ToastWindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() + self.shouldCascadeWindows = false + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. if let win = self.window { win.isOpaque = false From 0367a4cd742fe799e6ec5248b38fd844ce91c407 Mon Sep 17 00:00:00 2001 From: Timothy Qiu Date: Thu, 3 Aug 2017 18:56:17 +0800 Subject: [PATCH 19/30] Fixes #359 L10n typo fix --- ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings index 1839eb1..4785d17 100755 --- a/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings +++ b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings @@ -26,7 +26,7 @@ * ./AppDelegate.swift */ -"Add Shadowsocks Server Profile" = "已添加新Shaodwsocks服务器配置"; +"Add Shadowsocks Server Profile" = "已添加新Shadowsocks服务器配置"; "By scan QR Code" = "通过扫描二维码"; From dc44449046a6fe73c90b60ae94ba7fb97ed4e748 Mon Sep 17 00:00:00 2001 From: Dig Para Date: Fri, 11 Aug 2017 13:40:58 +0800 Subject: [PATCH 20/30] Fix ss_local start issue. The `launchctl load` failed with "Path had bad ownership/permissions". Add chmod 444 command for the plist file. Remove plist write permission. --- ShadowsocksX-NG/start_kcptun.sh | 1 + ShadowsocksX-NG/start_privoxy.sh | 1 + ShadowsocksX-NG/start_ss_local.sh | 1 + 3 files changed, 3 insertions(+) diff --git a/ShadowsocksX-NG/start_kcptun.sh b/ShadowsocksX-NG/start_kcptun.sh index 3ca2463..64e98f4 100755 --- a/ShadowsocksX-NG/start_kcptun.sh +++ b/ShadowsocksX-NG/start_kcptun.sh @@ -6,5 +6,6 @@ # Created by 邱宇舟 on 2017/1/11. # Copyright © 2017年 qiuyuzhou. All rights reserved. +chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.kcptun diff --git a/ShadowsocksX-NG/start_privoxy.sh b/ShadowsocksX-NG/start_privoxy.sh index dbc7d7b..ed1ab64 100755 --- a/ShadowsocksX-NG/start_privoxy.sh +++ b/ShadowsocksX-NG/start_privoxy.sh @@ -6,5 +6,6 @@ # Created by 王晨 on 16/10/7. # Copyright © 2016年 zhfish. All rights reserved. +chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.http diff --git a/ShadowsocksX-NG/start_ss_local.sh b/ShadowsocksX-NG/start_ss_local.sh index a59973e..31b3593 100755 --- a/ShadowsocksX-NG/start_ss_local.sh +++ b/ShadowsocksX-NG/start_ss_local.sh @@ -6,5 +6,6 @@ # Created by 邱宇舟 on 16/6/6. # Copyright © 2016年 qiuyuzhou. All rights reserved. +chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.local From b0310cd42aea0f146d3f3ab2ee8ce525e05b1466 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Fri, 25 Aug 2017 23:05:05 +0800 Subject: [PATCH 21/30] chmod 644 instead of 444. --- ShadowsocksX-NG/start_kcptun.sh | 2 +- ShadowsocksX-NG/start_privoxy.sh | 2 +- ShadowsocksX-NG/start_ss_local.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ShadowsocksX-NG/start_kcptun.sh b/ShadowsocksX-NG/start_kcptun.sh index 64e98f4..929c64d 100755 --- a/ShadowsocksX-NG/start_kcptun.sh +++ b/ShadowsocksX-NG/start_kcptun.sh @@ -6,6 +6,6 @@ # Created by 邱宇舟 on 2017/1/11. # Copyright © 2017年 qiuyuzhou. All rights reserved. -chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" +chmod 644 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.kcptun.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.kcptun diff --git a/ShadowsocksX-NG/start_privoxy.sh b/ShadowsocksX-NG/start_privoxy.sh index ed1ab64..3018dd6 100755 --- a/ShadowsocksX-NG/start_privoxy.sh +++ b/ShadowsocksX-NG/start_privoxy.sh @@ -6,6 +6,6 @@ # Created by 王晨 on 16/10/7. # Copyright © 2016年 zhfish. All rights reserved. -chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" +chmod 644 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.http diff --git a/ShadowsocksX-NG/start_ss_local.sh b/ShadowsocksX-NG/start_ss_local.sh index 31b3593..5a16145 100755 --- a/ShadowsocksX-NG/start_ss_local.sh +++ b/ShadowsocksX-NG/start_ss_local.sh @@ -6,6 +6,6 @@ # Created by 邱宇舟 on 16/6/6. # Copyright © 2016年 qiuyuzhou. All rights reserved. -chmod 444 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" +chmod 644 "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" launchctl start com.qiuyuzhou.shadowsocksX-NG.local From 948f7d469157a2b67dc9e2396d7ca471064fd864 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Fri, 25 Aug 2017 23:06:56 +0800 Subject: [PATCH 22/30] Bump version. --- README.md | 2 +- ShadowsocksX-NG/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3304850..c259c38 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ShadowsocksX-NG -Current version is 1.5.1 +Current version is 1.5.2 [![Build Status](https://travis-ci.org/shadowsocks/ShadowsocksX-NG.svg?branch=develop)](https://travis-ci.org/shadowsocks/ShadowsocksX-NG) diff --git a/ShadowsocksX-NG/Info.plist b/ShadowsocksX-NG/Info.plist index 034917c..cf7d7d9 100644 --- a/ShadowsocksX-NG/Info.plist +++ b/ShadowsocksX-NG/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.1 + 1.5.2 CFBundleSignature ???? CFBundleURLTypes From af73e0e3ca8bfd773d6f07087a795e02ff6ded6e Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sat, 26 Aug 2017 00:46:33 +0800 Subject: [PATCH 23/30] Ensure the owner of launch agents dir is current user. --- ShadowsocksX-NG.xcodeproj/project.pbxproj | 4 ++++ ShadowsocksX-NG/AppDelegate.swift | 24 +++++++++++++++++++++++ ShadowsocksX-NG/fix_dir_owner.sh | 12 ++++++++++++ 3 files changed, 40 insertions(+) create mode 100755 ShadowsocksX-NG/fix_dir_owner.sh diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 4d1e918..8b4850a 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */; }; 9BAFE2E21E83ED7F00F71CCE /* PreferencesWinController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BAFE2E41E83ED7F00F71CCE /* PreferencesWinController.xib */; }; 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BB706A51D1B982300551F0E /* SWBApplication.m */; }; + 9BBE7B751F508A0E00E8FFE5 /* fix_dir_owner.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9BBE7B711F50790500E8FFE5 /* fix_dir_owner.sh */; }; 9BC70EDC1D2E3E3100EDA4CA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */; }; 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 */; }; @@ -216,6 +217,7 @@ 9BAFE2EB1E83F91D00F71CCE /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; 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 = ""; }; + 9BBE7B711F50790500E8FFE5 /* fix_dir_owner.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = fix_dir_owner.sh; sourceTree = ""; }; 9BE8FBC11D0B71CF00CAFD01 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PreferencesWindowController.strings"; sourceTree = ""; }; 9BEEF0651D04CB8500FC52B3 /* install_ss_local.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = install_ss_local.sh; sourceTree = ""; }; 9BEEF0661D04CE8D00FC52B3 /* start_ss_local.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = start_ss_local.sh; sourceTree = ""; }; @@ -324,6 +326,7 @@ C6D429981DA76FBC002A5711 /* privoxy.config.example */, 9B9CBCB01E2644DC00FC61AA /* start_kcptun.sh */, 9B9CBCB11E26450D00FC61AA /* stop_kcptun.sh */, + 9BBE7B711F50790500E8FFE5 /* fix_dir_owner.sh */, ); name = "Support Files"; sourceTree = ""; @@ -577,6 +580,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9BBE7B751F508A0E00E8FFE5 /* fix_dir_owner.sh in Resources */, 9B3F7BFF1E82BF5B00C68B75 /* libev.4.dylib in Resources */, 9B3F7C001E82BF5B00C68B75 /* libmbedcrypto.2.4.2.dylib in Resources */, 9B3F7C011E82BF5B00C68B75 /* libsodium.18.dylib in Resources */, diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index f1926d4..e2459c8 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -50,12 +50,36 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele var statusItem: NSStatusItem! static let StatusItemIconWidth: CGFloat = NSVariableStatusItemLength + func ensureLaunchAgentsDirOwner () { + let dirPath = NSHomeDirectory() + "/Library/LaunchAgents" + let fileMgr = FileManager.default + if fileMgr.fileExists(atPath: dirPath) { + do { + let attrs = try fileMgr.attributesOfItem(atPath: dirPath) + if attrs[FileAttributeKey.ownerAccountName] as! String != NSUserName() { + //try fileMgr.setAttributes([FileAttributeKey.ownerAccountName: NSUserName()], ofItemAtPath: dirPath) + let bashFilePath = Bundle.main.path(forResource: "fix_dir_owner.sh", ofType: nil)! + let script = "do shell script \"bash \(bashFilePath) \(NSUserName()) \" with administrator privileges" + if let appleScript = NSAppleScript(source: script) { + var err: NSDictionary? = nil + appleScript.executeAndReturnError(&err) + } + } + } + catch { + NSLog("Error when ensure the owner of $HOME/Library/LaunchAgents, \(error.localizedDescription)") + } + } + } + func applicationDidFinishLaunching(_ aNotification: Notification) { _ = LaunchAtLoginController()// Ensure set when launch NSUserNotificationCenter.default.delegate = self + self.ensureLaunchAgentsDirOwner() + // Prepare ss-local InstallSSLocal() InstallKcptunClient() diff --git a/ShadowsocksX-NG/fix_dir_owner.sh b/ShadowsocksX-NG/fix_dir_owner.sh new file mode 100755 index 0000000..60f0fa5 --- /dev/null +++ b/ShadowsocksX-NG/fix_dir_owner.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# fix_dir_owner.sh +# ShadowsocksX-NG +# +# Created by 邱宇舟 on 2017/8/25. +# Copyright © 2017年 qiuyuzhou. All rights reserved. + + +LAUNCH_AGENTS_DIR="$HOME/Library/LaunchAgents" +sudo chown $@ "$HOME/Library/LaunchAgents" + From edaa2117eb1c103ba88597564ec9f93af6826601 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sat, 26 Aug 2017 02:08:27 +0800 Subject: [PATCH 24/30] Show overlay text 'Legacy'/'SIP002' on qrcode. --- ShadowsocksX-NG/SWBQRCodeWindowController.m | 35 ++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/ShadowsocksX-NG/SWBQRCodeWindowController.m b/ShadowsocksX-NG/SWBQRCodeWindowController.m index e123be0..a11cf5b 100644 --- a/ShadowsocksX-NG/SWBQRCodeWindowController.m +++ b/ShadowsocksX-NG/SWBQRCodeWindowController.m @@ -19,13 +19,32 @@ [super windowDidLoad]; // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. - [self setQRCode:self.qrCode]; + [self setQRCode:self.qrCode withOverlayText:@"Shadowsocks-NG SIP002"]; } -- (void)setQRCode:(NSString*) qrCode { +- (void)setQRCode:(NSString*) qrCode withOverlayText: (NSString*) text { CGImageRef cgImgRef = [self createQRImageForString:qrCode size:CGSizeMake(250, 250)]; NSImage *image = [[NSImage alloc]initWithCGImage:cgImgRef size:CGSizeMake(250, 250)]; + if (text) { + // Draw overlay text + NSDictionary* attrs = @{ + NSForegroundColorAttributeName: [NSColor colorWithRed:28/255.0 green:155/255.0 blue:71/255.0 alpha:1], + NSBackgroundColorAttributeName: [NSColor whiteColor], + NSFontAttributeName: [NSFont fontWithName:@"Helvetica" size:(CGFloat)16], + }; + NSMutableAttributedString* attrsText = [[NSMutableAttributedString alloc] initWithString: text + attributes: attrs]; + [attrsText setAttributes:@{ + NSForegroundColorAttributeName: [NSColor darkGrayColor], + NSBackgroundColorAttributeName: [NSColor whiteColor], + NSFontAttributeName: [NSFont fontWithName:@"Helvetica" size:(CGFloat)16], + } range: NSMakeRange(0, 14)]; + + [image lockFocus]; + [attrsText drawAtPoint: NSMakePoint(45, 8)]; + [image unlockFocus]; + } self.imageView.image = image; } @@ -36,6 +55,14 @@ NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; [filter setValue:data forKey:@"inputMessage"]; + /* + L: 7% + M: 15% + Q: 25% + H: 30% + */ + [filter setValue:@"Q" forKey:@"inputCorrectionLevel"]; + CIImage *image = [filter valueForKey:@"outputImage"]; // Calculate the size of the generated image and the scale for the desired image size @@ -81,9 +108,9 @@ - (void)flagsChanged:(NSEvent *)event { NSUInteger modifiers = event.modifierFlags & NSDeviceIndependentModifierFlagsMask; if (modifiers & NSAlternateKeyMask) { - [self setQRCode:self.legacyQRCode]; + [self setQRCode:self.legacyQRCode withOverlayText:@"Shadowsocks-NG Legacy"]; } else { - [self setQRCode:self.qrCode]; + [self setQRCode:self.qrCode withOverlayText:@"Shadowsocks-NG SIP002"]; } } From b4fa7fc9933c7fc3ecfed2e74d261e2253274b09 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sat, 26 Aug 2017 02:13:59 +0800 Subject: [PATCH 25/30] Add a tip on qrcode view. The tip: 'Press option/alt key to show legacy' --- ShadowsocksX-NG/SWBQRCodeWindowController.xib | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ShadowsocksX-NG/SWBQRCodeWindowController.xib b/ShadowsocksX-NG/SWBQRCodeWindowController.xib index ea35673..96a94e9 100644 --- a/ShadowsocksX-NG/SWBQRCodeWindowController.xib +++ b/ShadowsocksX-NG/SWBQRCodeWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -17,14 +17,14 @@ - - + + - + - + @@ -32,7 +32,7 @@ - + @@ -44,7 +44,7 @@ + + + + + + + + + - + From 7b1c3b6aacc7668b9b5caaea0990035e12a86aca Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Sat, 26 Aug 2017 12:17:38 +0800 Subject: [PATCH 26/30] Change profile section only when updating menu --- ShadowsocksX-NG/AppDelegate.swift | 54 +++++++++++-------------- ShadowsocksX-NG/Base.lproj/MainMenu.xib | 11 +++-- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 059671a..a90f763 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -32,10 +32,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele @IBOutlet weak var serversMenuItem: NSMenuItem! @IBOutlet var showQRCodeMenuItem: NSMenuItem! @IBOutlet var scanQRCodeMenuItem: NSMenuItem! - @IBOutlet var showBunchJsonExampleFileItem: NSMenuItem! - @IBOutlet var importBunchJsonFileItem: NSMenuItem! - @IBOutlet var exportAllServerProfileItem: NSMenuItem! - @IBOutlet var serversPreferencesMenuItem: NSMenuItem! + @IBOutlet var serverProfilesBeginSeparatorMenuItem: NSMenuItem! + @IBOutlet var serverProfilesEndSeparatorMenuItem: NSMenuItem! @IBOutlet weak var copyHttpProxyExportCmdLineMenuItem: NSMenuItem! @@ -478,39 +476,33 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } func updateServersMenu() { + guard let menu = serversMenuItem.submenu else { return } + let mgr = ServerProfileManager.instance - serversMenuItem.submenu?.removeAllItems() - let preferencesItem = serversPreferencesMenuItem - let showBunch = showBunchJsonExampleFileItem - let importBuntch = importBunchJsonFileItem - let exportAllServer = exportAllServerProfileItem - - serversMenuItem.submenu?.addItem(preferencesItem!) - serversMenuItem.submenu?.addItem(NSMenuItem.separator()) - - var i = 0 - for p in mgr.profiles { + let profiles = mgr.profiles + + // Remove all profile menu items + let beginIndex = menu.index(of: serverProfilesBeginSeparatorMenuItem) + 1 + let endIndex = menu.index(of: serverProfilesEndSeparatorMenuItem) + // Remove from end to begin, so the index won't change :) + for index in (beginIndex.. - + - + @@ -16,15 +16,13 @@ - - + + - - @@ -73,6 +71,7 @@ + From fb97b7b91f95b594798db7eb7f88bb6469159649 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Sun, 27 Aug 2017 02:29:34 +0800 Subject: [PATCH 27/30] Fixes typos and styling in README --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c259c38..279a954 100644 --- a/README.md +++ b/README.md @@ -9,23 +9,23 @@ Next Generation of [ShadowsocksX](https://github.com/shadowsocks/shadowsocks-iOS ## Why? It's hard to maintain the original implementation as there is too much unused code in it. -It also embeds the ss-local source. It's crazy to maintain dependencies of ss-local. -So it's hard to update the ss-local version. +It also embeds the `ss-local` source. It's crazy to maintain dependencies of `ss-local`. +So it's hard to update the `ss-local` version. -Now I just copied the ss-local from homebrew. Run ss-local executable as a Launch Agent in the background. -Serve PAC js file as a file URL. So there is only some source code related to GUI left. +Now I just copied the `ss-local` from Homebrew. Run `ss-local` executable as a Launch Agent in the background. +Serve PAC JS file as a file URL. So there is only some source code related to GUI left. Then I will rewrite the GUI code in Swift. ## Requirements ### Running -- Mac OS X 10.11 + +- macOS 10.11+ ### Building -- XCode 8.3+ -- cocoapod 1.2+ +- Xcode 8.3+ +- CocoaPods 1.2+ ## Download @@ -33,8 +33,8 @@ From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/) ## Features -- Use ss-local from shadowsocks-libev 3.0.5 -- Could Update PAC by download GFW List from GitHub. +- Use `ss-local` from shadowsocks-libev 3.0.5 +- Could update PAC by download GFW List from GitHub. - Show QRCode for current server profile. - Scan QRCode from screen. - Auto launch at login. @@ -44,28 +44,28 @@ From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/) - Over [kcptun](https://github.com/xtaci/kcptun). Version 20170322 - Export/Import configure file. - An advanced preferences panel to configure: - - Local socks5 listen address. - - Local socks5 listen port. - - Local socks5 timeout. + - Local SOCKS5 listen address. + - Local SOCKS5 listen port. + - Local SOCKS5 timeout. - If enable UDP relay. - GFW List URL. - Manual specify network service profiles which would be configure the proxy. - Could reorder shadowsocks profiles by drag & drop in servers preferences panel. - Configurable global shortcuts for toggle running and switch proxy mode. -## Different from orignal ShadowsocksX +## Different from original ShadowsocksX -Run ss-local as a background service through launchd, not as an in-app process. -So after you quit the app, the ss-local maybe be still running. +Run `ss-local` as a background service through launchd, not as an in-app process. +So after you quit the app, the `ss-local` maybe be still running. Added a manual mode which won't configure the system proxy settings. -Then you could configure your apps to use the socks5 proxy manually. +Then you could configure your apps to use the SOCKS5 proxy manually. -## Contributing +## Contributing [![gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ShadowsocksX-NG/Lobby) -Contributions must be available on a separately named branch based on the latest version of the main branch develop. +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/) From cb691db61763acafe79cea02e60986a54be2bb18 Mon Sep 17 00:00:00 2001 From: imnotpopo Date: Thu, 7 Sep 2017 17:29:29 +1000 Subject: [PATCH 28/30] Small typo fixes --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 279a954..20c3b7a 100644 --- a/README.md +++ b/README.md @@ -33,33 +33,33 @@ From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/) ## Features -- Use `ss-local` from shadowsocks-libev 3.0.5 +- Uses `ss-local` from shadowsocks-libev 3.0.5 - Could update PAC by download GFW List from GitHub. -- Show QRCode for current server profile. -- Scan QRCode from screen. +- Shows QRCode for current server profile. +- Scans QRCode from screen. - Auto launch at login. - User rules for PAC. - Support for [AEAD Ciphers](https://shadowsocks.org/en/spec/AEAD-Ciphers.html) - HTTP Proxy by [privoxy](http://www.privoxy.org/) - Over [kcptun](https://github.com/xtaci/kcptun). Version 20170322 - Export/Import configure file. -- An advanced preferences panel to configure: +- An advanced preferences panel for configuring: - Local SOCKS5 listen address. - Local SOCKS5 listen port. - Local SOCKS5 timeout. - If enable UDP relay. - GFW List URL. -- Manual specify network service profiles which would be configure the proxy. -- Could reorder shadowsocks profiles by drag & drop in servers preferences panel. +- Manually specify network service profiles which would be used to configure the proxy. +- Could reorder shadowsocks profiles by drag-&-dropping in servers preferences panel. - Configurable global shortcuts for toggle running and switch proxy mode. -## Different from original ShadowsocksX +## Difference from original ShadowsocksX -Run `ss-local` as a background service through launchd, not as an in-app process. -So after you quit the app, the `ss-local` maybe be still running. +`ss-local` is run as a background service through launchd, not as an in-app process. +So after you quit the app, the `ss-local` might be still running. -Added a manual mode which won't configure the system proxy settings. -Then you could configure your apps to use the SOCKS5 proxy manually. +Added a manual mode which won't configure the system proxy settings, +so that you could configure your apps to use the SOCKS5 proxy manually. ## Contributing From c549454fcd39f996a2c316d9923686cce49abbd0 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Fri, 8 Sep 2017 10:44:28 +0800 Subject: [PATCH 29/30] Add 192.168.0.0/16 and 10.0.0.0/8 to exceptions list for gobal proxy mode. --- ShadowsocksX-NG/proxy_conf_helper_version.h | 2 +- proxy_conf_helper/main.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ShadowsocksX-NG/proxy_conf_helper_version.h b/ShadowsocksX-NG/proxy_conf_helper_version.h index 4525a5c..9a39b4b 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.5.0" +#define kProxyConfHelperVersion @"1.6.0" #endif /* proxy_conf_helper_version_h */ diff --git a/proxy_conf_helper/main.m b/proxy_conf_helper/main.m index e1f39db..f45a6c5 100644 --- a/proxy_conf_helper/main.m +++ b/proxy_conf_helper/main.m @@ -158,7 +158,7 @@ int main(int argc, const char * argv[]) kCFNetworkProxiesSOCKSPort]; [proxies setObject:[NSNumber numberWithInt:1] forKey:(NSString*) kCFNetworkProxiesSOCKSEnable]; - [proxies setObject:@[@"127.0.0.1", @"localhost"] forKey:(NSString *)kCFNetworkProxiesExceptionsList]; + [proxies setObject:@[@"127.0.0.1", @"localhost", @"192.168.0.0/16", @"10.0.0.0/8"] forKey:(NSString *)kCFNetworkProxiesExceptionsList]; if (privoxyPort != 0) { [proxies setObject:@"127.0.0.1" forKey:(NSString *) From 071739d562cba13e2fdb057facafab26a44c3050 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Fri, 8 Sep 2017 15:19:03 +0800 Subject: [PATCH 30/30] Bump version to 1.6.0 --- ShadowsocksX-NG/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ShadowsocksX-NG/Info.plist b/ShadowsocksX-NG/Info.plist index cf7d7d9..96dbd73 100644 --- a/ShadowsocksX-NG/Info.plist +++ b/ShadowsocksX-NG/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.2 + 1.6.0 CFBundleSignature ???? CFBundleURLTypes @@ -44,7 +44,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2016年 qiuyuzhou. All rights reserved. License GPLv3. + Copyright © 2016-2017 qiuyuzhou. All rights reserved. License GPLv3. NSMainNibFile MainMenu NSPrincipalClass