Skip to content

Commit

Permalink
Add HTTP API & AppleScript support.
Browse files Browse the repository at this point in the history
Refactor in class AppDelegate.
  • Loading branch information
yangziy committed Sep 17, 2018
1 parent 9508af2 commit 739b040
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 34 deletions.
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 */; };
8EE2EDD8214F7CEC00FB4562 /* AppleScriptUserProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE2EDD4214F7CEC00FB4562 /* AppleScriptUserProxy.swift */; };
8EE2EDD9214F7CEC00FB4562 /* HTTPUserProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE2EDD6214F7CEC00FB4562 /* HTTPUserProxy.swift */; };
8EE2EDDA214F7CEC00FB4562 /* AppleScriptDefinition.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8EE2EDD7214F7CEC00FB4562 /* AppleScriptDefinition.sdef */; };
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 */; };
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>"; };
8EE2EDD4214F7CEC00FB4562 /* AppleScriptUserProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleScriptUserProxy.swift; sourceTree = "<group>"; };
8EE2EDD6214F7CEC00FB4562 /* HTTPUserProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUserProxy.swift; sourceTree = "<group>"; };
8EE2EDD7214F7CEC00FB4562 /* 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 /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = "<group>"; };
9B07EFA91D048E880052D9DF /* menu_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_icon.png; sourceTree = "<group>"; };
Expand Down Expand Up @@ -359,6 +365,9 @@
isa = PBXGroup;
children = (
9BB706A51D1B982300551F0E /* SWBApplication.m */,
8EE2EDD7214F7CEC00FB4562 /* AppleScriptDefinition.sdef */,
8EE2EDD4214F7CEC00FB4562 /* AppleScriptUserProxy.swift */,
8EE2EDD6214F7CEC00FB4562 /* HTTPUserProxy.swift */,
9BB706A61D1B982300551F0E /* SWBApplication.h */,
9B3FFF511D09DBA20019A709 /* ShadowsocksX-NG-Bridging-Header.h */,
9B3FFF151D072FDE0019A709 /* LaunchAtLoginController.h */,
Expand Down Expand Up @@ -635,6 +644,7 @@
9B3FFF341D08CEF70019A709 /* SWBQRCodeWindowController.xib in Resources */,
9B3FFF231D088E8D0019A709 /* abp.js in Resources */,
9B07EFAD1D048E880052D9DF /* menu_icon.png in Resources */,
8EE2EDDA214F7CEC00FB4562 /* AppleScriptDefinition.sdef in Resources */,
9BAFE2E21E83ED7F00F71CCE /* PreferencesWinController.xib in Resources */,
9B0BFFEB1D0460A70040E62B /* Assets.xcassets in Resources */,
08FCA0FF1E24BE1A0070984F /* example-gui-config.json in Resources */,
Expand Down Expand Up @@ -823,10 +833,12 @@
9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */,
9B3FFF1E1D0732660019A709 /* Utils.m in Sources */,
9B7297EA214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift in Sources */,
8EE2EDD9214F7CEC00FB4562 /* HTTPUserProxy.swift in Sources */,
9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */,
9B3FFF211D08826E0019A709 /* PACUtils.swift in Sources */,
9B3FFF141D0705810019A709 /* Notifications.swift in Sources */,
9BEEF0701D04DDB100FC52B3 /* ServerProfileManager.swift in Sources */,
8EE2EDD8214F7CEC00FB4562 /* AppleScriptUserProxy.swift in Sources */,
9BEEF06E1D04DCE400FC52B3 /* ServerProfile.swift in Sources */,
9B3FFF0D1D05FEB30019A709 /* Utils.swift in Sources */,
9BEEF0751D04EF3E00FC52B3 /* PreferencesWindowController.swift in Sources */,
Expand Down
85 changes: 51 additions & 34 deletions ShadowsocksX-NG/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele

@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!


@IBOutlet weak var hudWindow: NSPanel!
@IBOutlet weak var panelView: NSView!
@IBOutlet weak var isNameTextField: NSTextField!

let kProfileMenuItemIndexBase = 100

var statusItem: NSStatusItem!
static let StatusItemIconWidth: CGFloat = NSStatusItem.variableLength

Expand Down Expand Up @@ -133,7 +134,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
self.updateServersMenu()
self.updateRunningModeMenu()
SyncSSLocal()
}
}
)
_ = notifyCenter.rx.notification(NOTIFY_TOGGLE_RUNNING_SHORTCUT)
.subscribe(onNext: { noti in
Expand Down Expand Up @@ -180,9 +181,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.install()
ProxyConfHelper.startMonitorPAC()
applyConfig()

// Register global hotkey
ShortcutsController.bindShortcuts()

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

func applicationWillTerminate(_ aNotification: Notification) {
Expand All @@ -191,7 +195,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
StopPrivoxy()
ProxyConfHelper.disableProxy()
}

func applyConfig() {
SyncSSLocal()

Expand All @@ -211,7 +215,21 @@ 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) {
self.doToggleRunning(showToast: false)
Expand Down Expand Up @@ -324,26 +342,17 @@ 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 +378,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 +444,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 Expand Up @@ -468,12 +485,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
if isOn {
if let m = mode {
switch m {
case "auto":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_p_icon"))
case "global":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_g_icon"))
case "manual":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_m_icon"))
case "auto":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_p_icon"))
case "global":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_g_icon"))
case "manual":
statusItem.image = NSImage(named: NSImage.Name(rawValue: "menu_m_icon"))
default: break
}
statusItem.image?.isTemplate = true
Expand Down Expand Up @@ -512,17 +529,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
func updateServersMenu() {
guard let menu = serversMenuItem.submenu else { return }


let mgr = ServerProfileManager.instance
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..<endIndex).reversed() {
menu.removeItem(at: index)
}

// Insert all profile menu items
for (i, profile) in profiles.enumerated().reversed() {
let item = NSMenuItem()
Expand All @@ -534,7 +551,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele

menu.insertItem(item, at: beginIndex)
}

// End separator is redundant if profile section is empty
serverProfilesEndSeparatorMenuItem.isHidden = profiles.isEmpty
}
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
}
}
}
}

0 comments on commit 739b040

Please sign in to comment.