Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions HTTPAPI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright
Most code in HTTPUserProxy.swift comes from [yichengchen's fork](https://github.com/yichengchen/ShadowsocksX-R/blob/42b409beb85aee19a4852e09e7c3e4c2f73f49d3/ShadowsocksX-NG/ApiServer.swift), with slight modification to solve some incompatibility due to obsolete methods.

# API Feature
1. Check current status (on/off)
2. Toggle the client
3. Get server list
4. Switch server
5. Get current mode
6. Switch mode

# HTTP API
**Port:** 9528

**Default response**

| Name | Type | Description |
| :----- | :--- | :------------------------ |
| status | int | 1 for success, 0 for fail |

---

### Check current status (on/off)

`GET /status`

**Response**

| Name | Type | Description |
| :----- | :------ | :---------- |
| enable | boolean | |

### Toggle the client
`POST /toggle`

**NO** Parameter

### Get server list
`GET /servers`

**Response**

An **Array** of the following object:

| Name | Type | Description |
| :----- | :----- | :------------------------------------------------------- |
| id | string | internal UUID of the server |
| remark | string | refer to Remarks in Servers Perferences Panel of the app |
| active | int | 1 for active, 0 for inactive |

### Switch server
`POST /servers`

**Parameter**

| Name | Type | Description |
| :--- | :----- | :-------------------------- |
| id | string | internal UUID of the server |

### Get current mode
`GET /mode`

**Response**

| Name | Type | Description |
| :--- | :----- | :----------------- |
| mode | string | auto/manual/global |

### Set current mode
`POST /mode`

**Parameter**

| Name | Type | Description |
| :--- | :----- | :----------------- |
| mode | string | auto/manual/global |
12 changes: 12 additions & 0 deletions ShadowsocksX-NG.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
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 */; };
8E1DA77F2138231800659D99 /* HTTPUserProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E1DA77D2138231800659D99 /* HTTPUserProxy.swift */; };
8EC6307D2141429B002D56A2 /* AppleScriptUserProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC6307C2141429B002D56A2 /* AppleScriptUserProxy.swift */; };
8EC6308421414792002D56A2 /* AppleScriptDefinition.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8EC6308221414751002D56A2 /* AppleScriptDefinition.sdef */; };
9B07EFA71D048BBB0052D9DF /* ss-local in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA61D048BBB0052D9DF /* ss-local */; };
9B07EFAC1D048E880052D9DF /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA81D048E880052D9DF /* [email protected] */; };
9B07EFAD1D048E880052D9DF /* menu_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA91D048E880052D9DF /* menu_icon.png */; };
Expand Down Expand Up @@ -145,6 +148,9 @@
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
8E1DA77D2138231800659D99 /* HTTPUserProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUserProxy.swift; sourceTree = "<group>"; };
8EC6307C2141429B002D56A2 /* AppleScriptUserProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptUserProxy.swift; sourceTree = "<group>"; };
8EC6308221414751002D56A2 /* AppleScriptDefinition.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = AppleScriptDefinition.sdef; sourceTree = "<group>"; };
9B07EFA61D048BBB0052D9DF /* ss-local */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "ss-local"; sourceTree = "<group>"; };
9B07EFA81D048E880052D9DF /* [email protected] */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "[email protected]"; sourceTree = "<group>"; };
9B07EFA91D048E880052D9DF /* menu_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_icon.png; sourceTree = "<group>"; };
Expand Down Expand Up @@ -358,6 +364,8 @@
9B0BFFE71D0460A70040E62B /* ShadowsocksX-NG */ = {
isa = PBXGroup;
children = (
8EC6308221414751002D56A2 /* AppleScriptDefinition.sdef */,
8EC6307C2141429B002D56A2 /* AppleScriptUserProxy.swift */,
9BB706A51D1B982300551F0E /* SWBApplication.m */,
9BB706A61D1B982300551F0E /* SWBApplication.h */,
9B3FFF511D09DBA20019A709 /* ShadowsocksX-NG-Bridging-Header.h */,
Expand All @@ -378,6 +386,7 @@
9B3FFF1D1D0732660019A709 /* Utils.m */,
9B3FFF1F1D0734060019A709 /* Utils.h */,
9B3FFF4D1D09D9D50019A709 /* ProxyConfHelper.h */,
8E1DA77D2138231800659D99 /* HTTPUserProxy.swift */,
9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */,
9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */,
9BA04B211D23D5A5005AAD7F /* ProxyConfTool.h */,
Expand Down Expand Up @@ -608,6 +617,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8EC6308421414792002D56A2 /* AppleScriptDefinition.sdef in Resources */,
9B7297E7214D69C300FD24AA /* libmbedcrypto.2.12.0.dylib in Resources */,
9BCB1D6C20A15B0E005BABE7 /* libsodium.23.dylib in Resources */,
9BCB1D6D20A15B0E005BABE7 /* libcares.2.dylib in Resources */,
Expand Down Expand Up @@ -819,8 +829,10 @@
9B3FFF171D072FDE0019A709 /* LaunchAtLoginController.m in Sources */,
9B86459D1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift in Sources */,
9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */,
8EC6307D2141429B002D56A2 /* AppleScriptUserProxy.swift in Sources */,
9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */,
9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */,
8E1DA77F2138231800659D99 /* HTTPUserProxy.swift in Sources */,
9B3FFF1E1D0732660019A709 /* Utils.m in Sources */,
9B7297EA214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift in Sources */,
9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */,
Expand Down
52 changes: 34 additions & 18 deletions ShadowsocksX-NG/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele

// Register global hotkey
ShortcutsController.bindShortcuts()

// Start API Server
HTTPUserProxy.shard.start()
}

func applicationWillTerminate(_ aNotification: Notification) {
Expand Down Expand Up @@ -211,6 +214,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) {
Expand Down Expand Up @@ -326,24 +343,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) {
Expand All @@ -369,19 +377,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
Expand Down Expand Up @@ -427,7 +443,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

Expand Down
51 changes: 51 additions & 0 deletions ShadowsocksX-NG/AppleScriptDefinition.sdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">

<dictionary title="ShadowsocksX-NG">

<suite name="ShadowsocksX-NG Suite" code="sktc">
<enumeration name="Mode" code="mode">
<enumerator name="auto" code="auto">
<cocoa string-value="auto"/>
</enumerator>
<enumerator name="manual" code="manu">
<cocoa string-value="manual"/>
</enumerator>
<enumerator name="global" code="glbl">
<cocoa string-value="global"/>
</enumerator>
</enumeration>

<command name="isRunning" code="isruning">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
<result type="boolean"/>
</command>

<command name="toggle" code="sstoggle" description="Turn on/off the client.">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
</command>

<command name="mode" code="currmode">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
<result type="Mode"/>
</command>

<command name="change mode" code="chagmode">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
<direct-parameter type="Mode"/>
</command>

<command name="servers" code="sevrlist">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
<result>
<type type="text" list="yes"/>
</result>
</command>

<command name="change server" code="chagServ">
<cocoa class="ShadowsocksX_NG.AppleScriptUserProxy"/>
<direct-parameter type="text"/>
</command>
</suite>

</dictionary>
72 changes: 72 additions & 0 deletions ShadowsocksX-NG/AppleScriptUserProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// AppleScriptCommand.swift
// ShadowsocksX-NG
//
// Created by melonEater on 2018/9/6.
// Copyright © 2018 qiuyuzhou. All rights reserved.
//

import Cocoa


class AppleScriptUserProxy: NSScriptCommand {
let appdeleget = NSApplication.shared.delegate as! AppDelegate
let SerMgr = ServerProfileManager.instance

override func performDefaultImplementation() -> Any? {
switch(self.commandDescription.commandName) {
case "isRunning":
return isRunning()
case "toggle":
toggle()
case "mode":
return getMode()
case "change mode":
changeMode(mode: self.directParameter as! String)
case "servers":
return getServerList();
case "change server":
setServer(remark: self.directParameter as! String)
default:
return nil;
}
return nil
}

func toggle() {
self.appdeleget.doToggleRunning(showToast: false)
}

func isRunning() -> Bool {
let isOn = UserDefaults.standard.bool(forKey: "ShadowsocksOn")
return isOn
}

func getMode() -> String {
return UserDefaults.standard.string(forKey: "ShadowsocksRunningMode") as! String
}

func changeMode(mode:String) {
appdeleget.changeMode(mode: mode)
}

func getServerList() -> [String] {
var data = [String]()

for each in self.SerMgr.profiles{
data.append(each.remark)
}

return data
}

func setServer(remark: String) {
for each in self.SerMgr.profiles{
if (each.remark == remark) {
self.appdeleget.changeServer(uuid: each.uuid)
return
}
}
}
}

Loading