diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 602d488a..9e3a3486 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 1C82DBA81FA96C7500B32551 /* obfs-local in Resources */ = {isa = PBXBuildFile; fileRef = 1C82DBA51FA96C7400B32551 /* obfs-local */; }; 1C82DBAA1FA96FB600B32551 /* install_simple_obfs.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C82DBA91FA96F0300B32551 /* install_simple_obfs.sh */; }; 258E511BA910B0521B24DAB8 /* Pods_ShadowsocksX_NG.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 283ED1A8E9B711AC65670031 /* Pods_ShadowsocksX_NG.framework */; }; + 8E0D71432132B9DA00CE0EBB /* ApiServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E0D71422132B9DA00CE0EBB /* ApiServer.swift */; }; 9B07EFA71D048BBB0052D9DF /* ss-local in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA61D048BBB0052D9DF /* ss-local */; }; 9B07EFAC1D048E880052D9DF /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA81D048E880052D9DF /* menu_icon@2x.png */; }; 9B07EFAD1D048E880052D9DF /* menu_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA91D048E880052D9DF /* menu_icon.png */; }; @@ -150,6 +151,7 @@ 50D54926AA21B0D4D8DD9C4F /* Pods-ShadowsocksX-NGUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShadowsocksX-NGUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ShadowsocksX-NGUITests/Pods-ShadowsocksX-NGUITests.release.xcconfig"; sourceTree = ""; }; 58907E7F50405104B42CB189 /* Pods-ShadowsocksX-NGUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShadowsocksX-NGUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ShadowsocksX-NGUITests/Pods-ShadowsocksX-NGUITests.debug.xcconfig"; sourceTree = ""; }; 5B6203C1228FCD3D365814AC /* Pods-ShadowsocksX-NGTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShadowsocksX-NGTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ShadowsocksX-NGTests/Pods-ShadowsocksX-NGTests.debug.xcconfig"; sourceTree = ""; }; + 8E0D71422132B9DA00CE0EBB /* ApiServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiServer.swift; sourceTree = ""; }; 9B07EFA61D048BBB0052D9DF /* ss-local */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "ss-local"; sourceTree = ""; }; 9B07EFA81D048E880052D9DF /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = ""; }; 9B07EFA91D048E880052D9DF /* menu_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_icon.png; sourceTree = ""; }; @@ -396,6 +398,7 @@ 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */, 9B5831F41E7302F8009D5B7D /* ShortcutsController.h */, 9B5831F51E7302F8009D5B7D /* ShortcutsController.m */, + 8E0D71422132B9DA00CE0EBB /* ApiServer.swift */, ); path = "ShadowsocksX-NG"; sourceTree = ""; @@ -848,6 +851,7 @@ 9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */, 9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */, 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */, + 8E0D71432132B9DA00CE0EBB /* ApiServer.swift in Sources */, 9B3FFF1E1D0732660019A709 /* Utils.m in Sources */, 9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */, 9B3FFF211D08826E0019A709 /* PACUtils.swift in Sources */, diff --git a/ShadowsocksX-NG/ApiServer.swift b/ShadowsocksX-NG/ApiServer.swift new file mode 100644 index 00000000..58f7ede2 --- /dev/null +++ b/ShadowsocksX-NG/ApiServer.swift @@ -0,0 +1,88 @@ +// +// ApiServer.swift +// ShadowsocksX-R +// +// Created by CYC on 2016/10/9. +// Copyright © 2016年 qiuyuzhou. All rights reserved. +// + +import Foundation +import GCDWebServer + + + +class APIServer{ + static let shard = APIServer() + + let apiserver = GCDWebServer() + let SerMgr = ServerProfileManager.instance + let defaults = UserDefaults.standard + let appdeleget = NSApplication.shared.delegate as! AppDelegate + let api_port:UInt = 9528 + + func start(){ + setRouter() + do{ + try apiserver.start(options: [GCDWebServerOption_Port:api_port,"BindToLocalhost":true]) + }catch{ + NSLog("Error:ApiServ start fail") + } + } + + func setRouter(){ + apiserver.addHandler(forMethod: "GET", path: "/status", request: GCDWebServerRequest.self, processBlock: {request in + let isOn = self.defaults.bool(forKey: "ShadowsocksOn") + return GCDWebServerDataResponse(jsonObject: ["enable":isOn], contentType: "json") + }) + + apiserver.addHandler(forMethod: "POST", path: "/toggle", request: GCDWebServerRequest.self, processBlock: {request in + self.appdeleget.doToggleRunning(showToast: false) + return GCDWebServerDataResponse(jsonObject: ["Status":1], contentType: "json") + }) + + apiserver.addHandler(forMethod: "GET", path: "/servers", request: GCDWebServerRequest.self, processBlock: {request in + + var data = [[String:String]]() + + for each in self.SerMgr.profiles{ + data.append(["id":each.uuid,"note":each.remark, + "active":self.SerMgr.activeProfileId == each.uuid ? "1" : "0"]) + } + + return GCDWebServerDataResponse(jsonObject: data, contentType: "json") + }) + + apiserver.addHandler(forMethod: "POST", path: "/servers", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in + + let uuid = ((request as! GCDWebServerURLEncodedFormRequest).arguments["uuid"])as? String + for each in self.SerMgr.profiles{ + if (each.uuid == uuid) { + self.appdeleget.changeServer(uuid: uuid!) + return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json") + + } + } + return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json") + }) + + + apiserver.addHandler(forMethod: "GET", path: "/mode", request: GCDWebServerRequest.self, processBlock: {request in + if let current = self.defaults.string(forKey: "ShadowsocksRunningMode"){ + return GCDWebServerDataResponse(jsonObject: ["mode":current], contentType: "json") + } + return GCDWebServerDataResponse(jsonObject: ["mode":"unknow"], contentType: "json") + }) + + apiserver.addHandler(forMethod: "POST", path: "/mode", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in + let arg = ((request as! GCDWebServerURLEncodedFormRequest).arguments["value"])as? String + + if (arg != "auto" && arg != "global" && arg != "manual") { + return GCDWebServerDataResponse(jsonObject: ["Status":0], contentType: "json") + } + + self.appdeleget.changeMode(mode: arg!) + + return GCDWebServerDataResponse(jsonObject: ["Status":1], contentType: "json") + }) + } +} diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index a65cbf94..0817e9f0 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -183,6 +183,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele // Register global hotkey ShortcutsController.bindShortcuts() + + // Start API Server + APIServer.shard.start() } func applicationWillTerminate(_ aNotification: Notification) { @@ -212,6 +215,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele ProxyConfHelper.disableProxy() } } + + func changeMode(mode:String!) { + let defaults = UserDefaults.standard + + switch mode{ + case "auto":defaults.setValue("auto", forKey: "ShadowsocksRunningMode") + case "global":defaults.setValue("global", forKey: "ShadowsocksRunningMode") + case "manual":defaults.setValue("manual", forKey: "ShadowsocksRunningMode") + default: fatalError() + } + + updateRunningModeMenu() + applyConfig() + } // MARK: - UI Methods @IBAction func toggleRunning(_ sender: NSMenuItem) { @@ -299,24 +316,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } @IBAction func selectPACMode(_ sender: NSMenuItem) { - let defaults = UserDefaults.standard - defaults.setValue("auto", forKey: "ShadowsocksRunningMode") - updateRunningModeMenu() - applyConfig() + changeMode(mode: "auto") } @IBAction func selectGlobalMode(_ sender: NSMenuItem) { - let defaults = UserDefaults.standard - defaults.setValue("global", forKey: "ShadowsocksRunningMode") - updateRunningModeMenu() - applyConfig() + changeMode(mode: "global") } @IBAction func selectManualMode(_ sender: NSMenuItem) { - let defaults = UserDefaults.standard - defaults.setValue("manual", forKey: "ShadowsocksRunningMode") - updateRunningModeMenu() - applyConfig() + changeMode(mode: "manual") } @IBAction func editServerPreferences(_ sender: NSMenuItem) { @@ -342,19 +350,27 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele allInOnePreferencesWinCtrl.window?.makeKeyAndOrderFront(self) } - @IBAction func selectServer(_ sender: NSMenuItem) { - let index = sender.tag - kProfileMenuItemIndexBase + func changeServer(@objc uuid: String) { let spMgr = ServerProfileManager.instance - let newProfile = spMgr.profiles[index] - if newProfile.uuid != spMgr.activeProfileId { - spMgr.setActiveProfiledId(newProfile.uuid) + + if uuid != spMgr.activeProfileId { + spMgr.setActiveProfiledId(uuid) updateServersMenu() SyncSSLocal() applyConfig() } + updateRunningModeMenu() } + @IBAction func selectServer(_ sender: NSMenuItem) { + let index = sender.tag - kProfileMenuItemIndexBase + let spMgr = ServerProfileManager.instance + let newProfileId = spMgr.profiles[index].uuid + + changeServer(uuid:newProfileId) + } + @IBAction func copyExportCommand(_ sender: NSMenuItem) { // Get the Http proxy config. let defaults = UserDefaults.standard @@ -400,7 +416,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele func updateRunningModeMenu() { let defaults = UserDefaults.standard - let mode = defaults.string(forKey: "ShadowsocksRunningMode") + let mode = defaults.string(forKey: "ShadowsocksRunningMosde") var serverMenuText = "Servers".localized