├── .gitignore ├── LICENSE ├── README.md ├── ShadowsocksX-NG.entitlements ├── ShadowsocksX-NG.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── ShadowsocksX-NG.xcscheme │ ├── ShadowsocksX-NGTests.xcscheme │ └── proxy_conf_helper.xcscheme ├── ShadowsocksX-NG ├── AdvPreferencesWindowController.swift ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon_128x128.png │ │ ├── Icon_128x128@2x.png │ │ ├── Icon_16x16.png │ │ ├── Icon_16x16@2x.png │ │ ├── Icon_256x256.png │ │ ├── Icon_256x256@2x.png │ │ ├── Icon_32x32.png │ │ ├── Icon_32x32@2x.png │ │ ├── Icon_512x512.png │ │ └── Icon_512x512@2x.png │ ├── Contents.json │ ├── copyV1.imageset │ │ ├── Contents.json │ │ └── copyV1.png │ ├── copyV2.imageset │ │ ├── Contents.json │ │ └── copyV2.png │ ├── copyV3.imageset │ │ ├── Contents.json │ │ └── copyV3.png │ ├── menu_icon.imageset │ │ ├── Contents.json │ │ ├── menu_icon.png │ │ ├── menu_icon@2x.png │ │ ├── menu_icon_dark_mode.png │ │ └── menu_icon_dark_mode@2x.png │ ├── menu_icon.psd │ ├── menu_icon@2x.psd │ ├── menu_icon_acl.imageset │ │ ├── Contents.json │ │ ├── menu_icon_acl-1.png │ │ ├── menu_icon_acl.png │ │ ├── menu_icon_acl@2x-1.png │ │ └── menu_icon_acl@2x.png │ ├── menu_icon_disabled.imageset │ │ ├── Contents.json │ │ ├── menu_icon_disabled.png │ │ ├── menu_icon_disabled@2x.png │ │ ├── menu_icon_disabled_dark_mode.png │ │ └── menu_icon_disabled_dark_mode@2x.png │ ├── menu_icon_global.imageset │ │ ├── Contents.json │ │ ├── menu_icon_global-1.png │ │ ├── menu_icon_global.png │ │ ├── menu_icon_global@2x-1.png │ │ └── menu_icon_global@2x.png │ ├── menu_icon_manual.imageset │ │ ├── Contents.json │ │ ├── menu_icon_manual-1.png │ │ ├── menu_icon_manual.png │ │ ├── menu_icon_manual@2x-1.png │ │ └── menu_icon_manual@2x.png │ ├── menu_icon_pac.imageset │ │ ├── Contents.json │ │ ├── menu_icon_pac-1.png │ │ ├── menu_icon_pac.png │ │ ├── menu_icon_pac@2x-1.png │ │ └── menu_icon_pac@2x.png │ └── menu_icon_white.imageset │ │ ├── Contents.json │ │ ├── menu_icon_white-1.png │ │ ├── menu_icon_white.png │ │ ├── menu_icon_white@2x-1.png │ │ └── menu_icon_white@2x.png ├── Base.lproj │ ├── AdvPreferencesWindowController.xib │ ├── HTTPPreferencesWindowController.xib │ ├── Localizable.strings │ ├── MainMenu.xib │ ├── PreferencesWindowController.xib │ ├── ProxyPreferencesController.xib │ ├── SWBQRCodeWindowController.xib │ ├── SubscribePreferenceWindowController.xib │ └── UserRulesController.xib ├── HTTPPreferencesWindowController.swift ├── IconUtils.swift ├── Info.plist ├── LaunchAgentUtils.swift ├── NetWorkMonitor.swift ├── Notifications.swift ├── PACUtils.swift ├── PingClient.swift ├── PreferencesWindowController.swift ├── ProxyConfHelper.swift ├── ProxyConfHelperVersion.swift ├── ProxyConfTool.swift ├── ProxyPreferencesController.swift ├── QRCodeUtils.swift ├── SWBApplication.swift ├── SWBQRCodeWindowController.swift ├── ServerProfile.swift ├── ServerProfileManager.swift ├── StatusItemView.swift ├── Subscribe.swift ├── SubscribeManager.swift ├── SubscribePreferenceWindowController.swift ├── SystemThemeChangeHelper.swift ├── UserRulesController.swift ├── Utils.swift ├── VersionChecker.swift ├── abp.js ├── backchn.acl ├── chn.acl ├── en.lproj │ ├── AdvPreferencesWindowController.strings │ ├── HTTPPreferencesWindowController.strings │ ├── MainMenu.strings │ ├── PreferencesWindowController.strings │ ├── ProxyPreferencesController.strings │ ├── SWBQRCodeWindowController.strings │ ├── SubscribePreferenceWindowController.strings │ └── UserRulesController.strings ├── example-gui-config.json ├── gfwlist.acl ├── gfwlist.txt ├── install_helper.sh ├── install_privoxy.sh ├── install_ss_local.sh ├── libcrypto.1.0.0.dylib ├── menu_icon.png ├── menu_icon@2x.png ├── menu_icon_dark_mode.png ├── menu_icon_dark_mode@2x.png ├── menu_icon_disabled.png ├── menu_icon_disabled@2x.png ├── menu_icon_disabled_dark_mode.png ├── menu_icon_disabled_dark_mode@2x.png ├── privoxy ├── privoxy.config.example ├── proxy.pac ├── reload_conf_privoxy.sh ├── reload_conf_ss_local.sh ├── ss-local ├── start_privoxy.sh ├── start_ss_local.sh ├── stop_privoxy.sh ├── stop_ss_local.sh ├── user-rule.txt ├── whiteiplist.pac ├── whitelist.pac └── zh-Hans.lproj │ ├── AdvPreferencesWindowController.strings │ ├── HTTPPreferencesWindowController.strings │ ├── Localizable.strings │ ├── MainMenu.strings │ ├── PreferencesWindowController.strings │ ├── ProxyPreferencesController.strings │ ├── SWBQRCodeWindowController.strings │ ├── SubscribePreferenceWindowController.strings │ └── UserRulesController.strings ├── ShadowsocksX-NGTests ├── Info.plist └── ShadowsocksX_NGTests.swift └── proxy_conf_helper ├── main.swift └── proxy_conf_helper.entitlements /.gitignore: -------------------------------------------------------------------------------- 1 | ##### 2 | # OS X temporary files that should never be committed 3 | .DS_Store 4 | *.swp 5 | *.lock 6 | profile 7 | 8 | #### 9 | # Xcode temporary files that should never be committed 10 | *~.nib 11 | 12 | #### 13 | # Objective-C/Swift specific 14 | *.hmap 15 | *.ipa 16 | 17 | #### 18 | # Xcode build files 19 | DerivedData/ 20 | build/ 21 | Builds/ 22 | 23 | ##### 24 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 25 | *.pbxuser 26 | *.mode1v3 27 | *.mode2v3 28 | *.perspectivev3 29 | !default.pbxuser 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.perspectivev3 33 | 34 | 35 | xcuserdata 36 | xcshareddata 37 | 38 | #### 39 | # Other Xcode files 40 | profile 41 | *.hmap 42 | *.ipa 43 | 44 | #### 45 | # Carthage 46 | Carthage/Build 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShadowsocksX-NG 2 | 3 | [![Build Status](https://travis-ci.org/shadowsocksr/ShadowsocksX-NG.svg?branches=develop)](https://travis-ci.org/shadowsocksr/ShadowsocksX-NG) 4 | 5 | Next Generation of Shadowsocks(R) macOS client. 6 | 7 | ## About 8 | 9 | The original implement embed ss-local source. This makes it hard to update ss-local. 10 | 11 | Now I just copy the [ss-local](https://formulae.brew.sh/formula/shadowsocks-libev) from Homebrew. Run ss-local executable as a Launch Agent in background. 12 | Serve pac js file as a file url. So only some souce code related to GUI left. 13 | 14 | ## Requirements 15 | 16 | ### Dependencies 17 | ```shell 18 | brew install libev; 19 | brew install c-ares; 20 | brew install libsodium; 21 | brew install mbedtls@2; 22 | brew install pcre; 23 | ``` 24 | 25 | ### Running 26 | 27 | - macOS Catalina(v10.15+) 28 | 29 | ### Building 30 | 31 | - Xcode 13.0+ 32 | 33 | ## Features 34 | 35 | - New Subscription URL support (Clash and Shadowrocket) 36 | - Limited SSR support 37 | - White domain list & white IP list 38 | - Use ss-local from [shadowsocks-libev](https://formulae.brew.sh/formula/shadowsocks-libev) 39 | - Auto update PAC by download GFW List from GitHub. (You can even customize your list) 40 | - Auto update ACL white list from GutHub. (You can even customize your list) 41 | - Show QRCode for current server profile 42 | - Scan QRCode from screen 43 | - Import config.json to config all your servers 44 | - Auto launch at login 45 | - User rules for PAC 46 | 47 | ## Differences from original ShadowsocksX 48 | 49 | Run ss-local as backgroud service through launchd, not in app process. 50 | So after you quit the app, the ss-local maybe is still running. 51 | 52 | Add a manual mode which won't configure the system proxy settings. 53 | Then you could configure your apps to use socks5 proxy manual. 54 | 55 | ## License 56 | 57 | The project is released under the terms of GPLv3. 58 | 59 | -------------------------------------------------------------------------------- /ShadowsocksX-NG.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-dyld-environment-variables 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ShadowsocksX-NG.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShadowsocksX-NG.xcodeproj/xcshareddata/xcschemes/ShadowsocksX-NG.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 57 | 63 | 64 | 65 | 71 | 77 | 78 | 79 | 85 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 107 | 108 | 109 | 110 | 112 | 118 | 119 | 120 | 121 | 122 | 132 | 134 | 140 | 141 | 142 | 143 | 149 | 151 | 157 | 158 | 159 | 160 | 162 | 163 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /ShadowsocksX-NG.xcodeproj/xcshareddata/xcschemes/ShadowsocksX-NGTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ShadowsocksX-NG.xcodeproj/xcshareddata/xcschemes/proxy_conf_helper.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/AdvPreferencesWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AdvPreferencesWindowController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 邱宇舟 on 16/6/6. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AdvPreferencesWindowController: NSWindowController, NSWindowDelegate { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 17 | self.window?.delegate = self 18 | } 19 | 20 | //------------------------------------------------------------ 21 | // NSWindowDelegate 22 | func windowWillClose(_ notification: Notification) { 23 | NotificationCenter.default 24 | .post(name: Notification.Name(rawValue: NOTIFY_ADV_CONF_CHANGED), object: nil) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "Icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "Icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "Icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "Icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "Icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "Icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "Icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "Icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "Icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "Icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "copyV1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV1.imageset/copyV1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/copyV1.imageset/copyV1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "copyV2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV2.imageset/copyV2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/copyV2.imageset/copyV2.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "copyV3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/copyV3.imageset/copyV3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/copyV3.imageset/copyV3.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_dark_mode.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_dark_mode@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon_dark_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon_dark_mode.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon_dark_mode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon.imageset/menu_icon_dark_mode@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon.psd -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon@2x.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon@2x.psd -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_acl.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_acl-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_acl@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_acl@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl@2x-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_acl.imageset/menu_icon_acl@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_disabled.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_disabled_dark_mode.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_disabled@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_disabled_dark_mode@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled_dark_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled_dark_mode.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled_dark_mode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_disabled.imageset/menu_icon_disabled_dark_mode@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_global.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_global-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_global@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_global@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global@2x-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_global.imageset/menu_icon_global@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_manual.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_manual-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_manual@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_manual@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual@2x-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_manual.imageset/menu_icon_manual@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_pac.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_pac-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_pac@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_pac@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | }, 54 | "properties" : { 55 | "template-rendering-intent" : "template" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac@2x-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_pac.imageset/menu_icon_pac@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "menu_icon_white.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "menu_icon_white-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "menu_icon_white@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "menu_icon_white@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "universal", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "appearances" : [ 41 | { 42 | "appearance" : "luminosity", 43 | "value" : "dark" 44 | } 45 | ], 46 | "idiom" : "universal", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "author" : "xcode", 52 | "version" : 1 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white@2x-1.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/Assets.xcassets/menu_icon_white.imageset/menu_icon_white@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/Base.lproj/HTTPPreferencesWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Base.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | /* 8 | * SHARED STRINGS 9 | */ 10 | 11 | /* 12 | * ./ShadowsocksX-NG/PACUtils.swift 13 | */ 14 | 15 | "PAC has been updated by latest GFW List." = "PAC has been updated by latest GFW List."; 16 | 17 | "Failed to download latest GFW List." = "Failed to download latest GFW List."; 18 | 19 | "White List update succeed." = "White List update succeed."; 20 | 21 | "Failed to download latest White List update succeed." = "Failed to download latest White List update succeed."; 22 | 23 | /* 24 | * ./ShadowsocksX-NG/BunchImportUtils.swift 25 | */ 26 | 27 | "Import Server Profile succeed!" = "Import Server Profile succeed!"; 28 | 29 | "Successful import \(configsCount) items" = "Successful import \(configsCount) items"; 30 | 31 | "Import Server Profile failed!" = "Import Server Profile failed!"; 32 | 33 | "Invalid config file!" = "Invalid config file!"; 34 | 35 | /* 36 | * ./ShadowsocksX-NG/AppDelegate.swift 37 | */ 38 | 39 | "Add Shadowsocks Server Profile" = "Add Shadowsocks Server Profile"; 40 | 41 | "By scan QR Code" = "By scan QR Code"; 42 | 43 | "By Handle SS URL" = "By Handle SS URL"; 44 | 45 | " Encryption Method: \(profile.method)" = " Encryption Method: \(profile.method)"; 46 | 47 | "Current server profile is not valid." = "Current server profile is not valid."; 48 | 49 | "No current server profile." = "No current server profile."; 50 | 51 | "Proxy - Auto By PAC" = "Proxy - Auto By PAC"; 52 | 53 | "Proxy - Global" = "Proxy - Global"; 54 | 55 | "Proxy - Manual" = "Proxy - Manual"; 56 | 57 | "Proxy - White List Domain" = "Proxy - White List Domain"; 58 | 59 | "Proxy - White List IP" = "Proxy - White List IP"; 60 | 61 | "Shadowsocks: On" = "Shadowsocks: On"; 62 | 63 | "Turn Shadowsocks Off" = "Turn Shadowsocks Off"; 64 | 65 | "Shadowsocks: Off" = "Shadowsocks: Off"; 66 | 67 | "Turn Shadowsocks On" = "Turn Shadowsocks On"; 68 | 69 | /* 70 | * ./ShadowsocksX-NG/PreferencesWindowController.swift 71 | */ 72 | 73 | "New Server" = "New Server"; 74 | 75 | /* 76 | * ./ShadowsocksX-NG/UserRulesController.swift 77 | */ 78 | 79 | "PAC has been updated by User Rules." = "PAC has been updated by User Rules."; 80 | 81 | "It's failed to update PAC by User Rules." = "It's failed to update PAC by User Rules."; 82 | 83 | "Proxy - Manual" = "Proxy - Manual"; 84 | 85 | "Servers" = "Servers"; 86 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Base.lproj/ProxyPreferencesController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 69 | 73 | 74 | 90 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Base.lproj/SWBQRCodeWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Base.lproj/UserRulesController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | 66 | 67 | 68 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/HTTPPreferencesWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPPreferencesWindowController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 王晨 on 2016/10/7. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class HTTPPreferencesWindowController: NSWindowController, NSWindowDelegate { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 17 | self.window?.delegate = self 18 | } 19 | 20 | //------------------------------------------------------------ 21 | // NSWindowDelegate 22 | func windowWillClose(_ notification: Notification) { 23 | NotificationCenter.default 24 | .post(name: Notification.Name(rawValue: NOTIFY_HTTP_CONF_CHANGED), object: nil) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/IconUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IconUtils.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/5/4. 6 | // 7 | 8 | import Foundation 9 | 10 | class IconUtils{ 11 | public class func getIconImageName() -> String { 12 | var iconImageName = ""; 13 | let defaults = UserDefaults.standard 14 | let mode = defaults.string(forKey: "ShadowsocksRunningMode") 15 | if mode == "auto" { 16 | iconImageName = "menu_icon_pac" 17 | } else if mode == "global" { 18 | iconImageName = "menu_icon_global" 19 | } else if mode == "manual" { 20 | iconImageName = "menu_icon_manual" 21 | } else if mode == "whiteList" { 22 | if defaults.string(forKey: "ACLFileName")! == "chn.acl" { 23 | iconImageName = "menu_icon_white" 24 | } else { 25 | iconImageName = "menu_icon_acl" 26 | } 27 | } 28 | 29 | return iconImageName; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Viewer 26 | CFBundleURLIconFile 27 | menu_icon@2x 28 | CFBundleURLName 29 | shadowsocks 30 | CFBundleURLSchemes 31 | 32 | ss 33 | 34 | 35 | 36 | CFBundleTypeRole 37 | Viewer 38 | CFBundleURLIconFile 39 | menu_icon@2x 40 | CFBundleURLName 41 | shadowsocks 42 | CFBundleURLSchemes 43 | 44 | ssr 45 | 46 | 47 | 48 | CFBundleVersion 49 | $(CURRENT_PROJECT_VERSION) 50 | LSApplicationCategoryType 51 | public.app-category.utilities 52 | LSMinimumSystemVersion 53 | $(MACOSX_DEPLOYMENT_TARGET) 54 | LSUIElement 55 | 56 | NSHumanReadableCopyright 57 | Copyright © 2016年 qiuyuzhou. All rights reserved. License GPLv3. 58 | NSMainNibFile 59 | MainMenu 60 | NSPrincipalClass 61 | SWBApplication 62 | 63 | 64 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/NetWorkMonitor.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// MonitorTask.swift 3 | //// Up&Down 4 | //// 5 | //// Created by 郭佳哲 on 6/3/16. 6 | //// Copyright © 2016 郭佳哲. All rights reserved. 7 | //// 8 | // 9 | //import Cocoa 10 | // 11 | //open class NetWorkMonitor: NSObject { 12 | // let statusItemView: StatusItemView 13 | // 14 | // var thread: Thread? 15 | // var timer: Timer? 16 | // let interval: Double = 2 17 | // var preBytesIn: Double = -1 18 | // var preBytesOut: Double = -1 19 | // 20 | // init(statusItemView view: StatusItemView) { 21 | // statusItemView = view 22 | // } 23 | // 24 | // func start() { 25 | // thread = Thread(target: self, selector: #selector(startUpdateTimer), object: nil) 26 | // thread?.start() 27 | // statusItemView.showSpeed = true 28 | // } 29 | // 30 | // @objc func startUpdateTimer() { 31 | // timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateNetWorkData), userInfo: nil, repeats: true) 32 | // RunLoop.current.run() 33 | // } 34 | // 35 | // func stop() { 36 | // thread?.cancel() 37 | // statusItemView.showSpeed = false 38 | // } 39 | // 40 | // // nettop -x -k state -k interface -k rx_dupe -k rx_ooo -k re-tx -k rtt_avg -k rcvsize -k tx_win -k tc_class -k tc_mgt -k cc_algo -k P -k C -k R -k W -l 1 -t wifi -t wired 41 | // @objc func updateNetWorkData() { 42 | // if Thread.current.isCancelled { 43 | // timer?.invalidate() 44 | // timer = nil 45 | // thread = nil 46 | // Thread.exit() 47 | // } 48 | // 49 | // let task = Process() 50 | // task.launchPath = "/usr/bin/nettop" 51 | // task.arguments = ["-x", "-k", "state", "-k", "interface", "-k", "rx_dupe", "-k", "rx_ooo", "-k", "re-tx", "-k", "rtt_avg", "-k", "rcvsize", "-k", "tx_win", "-k", "tc_class", "-k", "tc_mgt", "-k", "cc_algo", "-k", "P", "-k", "C", "-k", "R", "-k", "W", "-l", "1", "-t", "wifi", "-t", "wired"] 52 | // 53 | // let pipe = Pipe() 54 | // task.standardOutput = pipe 55 | // 56 | // task.launch() 57 | // 58 | // pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() 59 | // NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: pipe.fileHandleForReading, queue: nil) { 60 | // notification in 61 | // 62 | // let output = pipe.fileHandleForReading.availableData 63 | // let outputString = String(data: output, encoding: String.Encoding.utf8) ?? "" 64 | // 65 | // self.handleNetWorkData(outputString) 66 | // } 67 | // } 68 | // 69 | // func handleNetWorkData(_ string: String) { 70 | // var bytesIn: Double = 0 71 | // var bytesOut: Double = 0 72 | // 73 | // let pattern = "\\.\\d+\\s+(\\d+)\\s+(\\d+)\\n" 74 | // do { 75 | // let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive) 76 | // let results = regex.matches(in: string, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, string.count)) 77 | // for result in results { 78 | // bytesIn += Double((string as NSString).substring(with: result.range(at: 1)))! 79 | // bytesOut += Double((string as NSString).substring(with: result.range(at: 2)))! 80 | // } 81 | // bytesIn /= interval 82 | // bytesOut /= interval 83 | // 84 | // if (preBytesOut != -1) { 85 | // statusItemView.setRateData(up: bytesOut-preBytesOut, down: bytesIn-preBytesIn) 86 | // } 87 | // preBytesOut = bytesOut 88 | // preBytesIn = bytesIn 89 | // } 90 | // catch { } 91 | // } 92 | // 93 | //} 94 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 邱宇舟 on 16/6/7. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let NOTIFY_SERVER_PROFILES_CHANGED = "NOTIFY_SERVER_PROFILES_CHANGED" 12 | let NOTIFY_ADV_PROXY_CONF_CHANGED = "NOTIFY_ADV_PROXY_CONF_CHANGED" 13 | let NOTIFY_ADV_CONF_CHANGED = "NOTIFY_ADV_CONF_CHANGED" 14 | let NOTIFY_HTTP_CONF_CHANGED = "NOTIFY_HTTP_CONF_CHANGED" 15 | let NOTIFY_INVALIDE_QR = "NOTIFY_INVALIDE_QR" 16 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/PingClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PingClient.swift 3 | // ShadowsocksX-R 4 | // 5 | // Created by 称一称 on 16/9/5. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 12 | switch (lhs, rhs) { 13 | case let (l?, r?): 14 | return l < r 15 | case (nil, _?): 16 | return true 17 | default: 18 | return false 19 | } 20 | } 21 | 22 | 23 | public typealias SimplePingClientCallback = (String?)->() 24 | 25 | 26 | class PingServers:NSObject{ 27 | static let instance = PingServers() 28 | 29 | let SerMgr = ServerProfileManager.instance 30 | var fastest:String? 31 | var fastest_id : Int=0 32 | 33 | // func ping(_ i:Int=0){ 34 | // if i == 0{ 35 | // fastest_id = 0 36 | // fastest = nil 37 | // } 38 | // 39 | // if i >= SerMgr.profiles.count{ 40 | // DispatchQueue.main.async { 41 | // // do the UI update HERE 42 | // let notice = NSUserNotification() 43 | // notice.title = "Ping测试完成!" 44 | // notice.subtitle = "最快的是\(self.SerMgr.profiles[self.fastest_id].remark) \(self.SerMgr.profiles[self.fastest_id].serverHost) \(self.SerMgr.profiles[self.fastest_id].latency!)ms" 45 | // NSUserNotificationCenter.default.deliver(notice) 46 | // } 47 | // return 48 | // } 49 | // let host = self.SerMgr.profiles[i].serverHost 50 | // SimplePingClient.pingHostname(host) { latency in 51 | // DispatchQueue.global().async { 52 | // print("[Ping Result]-\(host) latency is \(latency ?? "fail")") 53 | // self.SerMgr.profiles[i].latency = latency ?? "fail" 54 | // 55 | // if latency != nil { 56 | // if self.fastest == nil{ 57 | // self.fastest = latency 58 | // self.fastest_id = i 59 | // }else{ 60 | // if Int(latency!) < Int(self.fastest!) { 61 | // self.fastest = latency 62 | // self.fastest_id = i 63 | // } 64 | // } 65 | // DispatchQueue.main.async { 66 | // // do the UI update HERE 67 | // (NSApplication.shared().delegate as! AppDelegate).updateServersMenu() 68 | // (NSApplication.shared().delegate as! AppDelegate).updateRunningModeMenu() 69 | // } 70 | // } 71 | // } 72 | // self.ping(i+1) 73 | // } 74 | // } 75 | 76 | func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) { 77 | 78 | var output : [String] = [] 79 | var error : [String] = [] 80 | 81 | let task = Process() 82 | task.launchPath = cmd 83 | task.arguments = args 84 | 85 | let outpipe = Pipe() 86 | task.standardOutput = outpipe 87 | let errpipe = Pipe() 88 | task.standardError = errpipe 89 | 90 | task.launch() 91 | 92 | let outdata = outpipe.fileHandleForReading.readDataToEndOfFile() 93 | if var string = String(data: outdata, encoding: .utf8) { 94 | string = string.trimmingCharacters(in: .newlines) 95 | output = string.components(separatedBy: "\n") 96 | } 97 | 98 | let errdata = errpipe.fileHandleForReading.readDataToEndOfFile() 99 | if var string = String(data: errdata, encoding: .utf8) { 100 | string = string.trimmingCharacters(in: .newlines) 101 | error = string.components(separatedBy: "\n") 102 | } 103 | 104 | task.waitUntilExit() 105 | let status = task.terminationStatus 106 | 107 | return (output, error, status) 108 | } 109 | 110 | func getlatencyFromString(result:String) -> Double?{ 111 | var res = result 112 | if !result.contains("round-trip min/avg/max/stddev =") { 113 | return nil 114 | } 115 | res.removeSubrange(res.range(of: "round-trip min/avg/max/stddev = ")!) 116 | res = String(res.dropLast(3)) 117 | res = res.components(separatedBy: "/")[1] 118 | let latency = Double(res) 119 | return latency 120 | } 121 | 122 | func pingSingleHost(host:String,completionHandler:@escaping (Double?) -> Void){ 123 | DispatchQueue.global(qos: .userInteractive).async { 124 | if let outputString = self.runCommand(cmd: "/sbin/ping", args: "-c","1","-t","1.5",host).output.last{ 125 | completionHandler(self.getlatencyFromString(result: outputString)) 126 | } 127 | } 128 | } 129 | 130 | 131 | 132 | func ping(_ i:Int=0){ 133 | 134 | neverSpeedTestBefore = false 135 | 136 | var result:[(Int,Double)] = [] 137 | 138 | for k in 0.. Void 184 | 185 | @discardableResult func delay(_ time: TimeInterval, task: @escaping ()->()) -> Task? { 186 | 187 | func dispatch_later(block: @escaping ()->()) { 188 | let t = DispatchTime.now() + time 189 | DispatchQueue.main.asyncAfter(deadline: t, execute: block) 190 | } 191 | 192 | 193 | 194 | var closure: (()->Void)? = task 195 | var result: Task? 196 | 197 | let delayedClosure: Task = { 198 | cancel in 199 | if let internalClosure = closure { 200 | if (cancel == false) { 201 | DispatchQueue.main.async(execute: internalClosure) 202 | } 203 | } 204 | closure = nil 205 | result = nil 206 | } 207 | 208 | result = delayedClosure 209 | 210 | dispatch_later { 211 | if let delayedClosure = result { 212 | delayedClosure(false) 213 | } 214 | } 215 | 216 | return result; 217 | 218 | } 219 | 220 | 221 | func cancel(_ task: Task?) { 222 | task?(true) 223 | } 224 | 225 | var neverSpeedTestBefore:Bool = true 226 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ProxyConfHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyConfHelper.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/4/20. 6 | // 7 | 8 | import Foundation 9 | import GCDWebServers 10 | 11 | class ProxyConfHelper { 12 | 13 | let kShadowsocksHelper = "/Library/Application Support/ShadowsocksX-NG/proxy_conf_helper" 14 | 15 | var webServer: GCDWebServer? = nil 16 | 17 | func isVersionOk() -> Bool { 18 | var task: Process? 19 | task = Process() 20 | task?.launchPath = kShadowsocksHelper 21 | 22 | var args: [AnyHashable]? 23 | args = ["-v"] 24 | task?.arguments = args as? [String] 25 | 26 | var pipe: Pipe? 27 | pipe = Pipe() 28 | task?.standardOutput = pipe 29 | 30 | var fd: FileHandle? 31 | fd = pipe?.fileHandleForReading 32 | 33 | task?.launch() 34 | 35 | var data: Data? 36 | data = fd?.readDataToEndOfFile() 37 | 38 | var str: String? 39 | if let data = data { 40 | str = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .newlines) 41 | } 42 | 43 | if str != kProxyConfHelperVersion { 44 | return false 45 | } 46 | return true 47 | } 48 | 49 | func install() { 50 | let fileManager = FileManager.default 51 | if !fileManager.fileExists(atPath: kShadowsocksHelper) || !isVersionOk() { 52 | let helperPath = "\(Bundle.main.resourcePath ?? "")/\("install_helper.sh")" 53 | NSLog("run install script: \(helperPath)") 54 | var error: NSDictionary? 55 | let script = "do shell script \"bash \(helperPath)\" with administrator privileges" 56 | let appleScript = type(of: NSAppleScript()).init(source: script) 57 | if appleScript?.executeAndReturnError(&error) != nil { 58 | print("installation success") 59 | } else { 60 | print("installation failure") 61 | } 62 | } 63 | } 64 | 65 | func callHelper(_ arguments: [AnyHashable]?) { 66 | var task: Process? 67 | task = Process() 68 | task?.launchPath = kShadowsocksHelper 69 | 70 | // this log is very important 71 | print("run shadowsocks helper: \(kShadowsocksHelper)") 72 | task?.arguments = arguments as? [String] 73 | 74 | var stdoutpipe: Pipe? 75 | stdoutpipe = Pipe() 76 | task?.standardOutput = stdoutpipe 77 | 78 | var stderrpipe: Pipe? 79 | stderrpipe = Pipe() 80 | task?.standardError = stderrpipe 81 | 82 | var file: FileHandle? 83 | file = stdoutpipe?.fileHandleForReading 84 | 85 | task?.launch() 86 | 87 | var data: Data? 88 | data = file?.readDataToEndOfFile() 89 | 90 | var string: String? 91 | if let data = data { 92 | string = String(data: data, encoding: .utf8) 93 | } 94 | if (string?.count ?? 0) > 0 { 95 | print("\(string ?? "")") 96 | } 97 | 98 | file = stderrpipe?.fileHandleForReading 99 | data = file?.readDataToEndOfFile() 100 | if let data = data { 101 | string = String(data: data, encoding: .utf8) 102 | } 103 | if (string?.count ?? 0) > 0 { 104 | print("\(string ?? "")") 105 | } 106 | } 107 | 108 | func addArguments4ManualSpecifyNetworkServices(_ args: inout [String]) { 109 | let defaults = UserDefaults.standard 110 | 111 | if !defaults.bool(forKey: "AutoConfigureNetworkServices") { 112 | let serviceKeys = defaults.array(forKey: "Proxy4NetworkServices") 113 | if let serviceKeys = serviceKeys { 114 | for key in serviceKeys { 115 | guard let key = key as? String else { 116 | continue 117 | } 118 | args.append("--network-service") 119 | args.append(key) 120 | } 121 | } 122 | } 123 | } 124 | 125 | func enablePACProxy(_ PACFilePath: String) { 126 | //start server here and then using the string next line 127 | //next two lines can open gcdwebserver and work around pac file 128 | let PACURLString = startPACServer(PACFilePath) //hi 可以切换成定制pac文件路径来达成使用定制文件路径 129 | let url = URL(string: PACURLString!) 130 | 131 | var args = ["--mode", "auto", "--pac-url", url!.absoluteString] 132 | 133 | addArguments4ManualSpecifyNetworkServices(&args) 134 | callHelper(args) 135 | } 136 | 137 | 138 | func enableGlobalProxy() { 139 | let port = UserDefaults.standard.integer(forKey: "LocalSocks5.ListenPort") 140 | 141 | var args = [ 142 | "--mode", 143 | "global", 144 | "--port", 145 | String(format: "%lu", UInt(port)) 146 | ] 147 | 148 | if UserDefaults.standard.bool(forKey: "LocalHTTPOn") && UserDefaults.standard.bool(forKey: "LocalHTTP.FollowGlobal") { 149 | let privoxyPort = UserDefaults.standard.integer(forKey: "LocalHTTP.ListenPort") 150 | 151 | args.append("--privoxy-port") 152 | args.append(String(format: "%lu", UInt(privoxyPort))) 153 | } 154 | 155 | addArguments4ManualSpecifyNetworkServices(&args) 156 | callHelper(args) 157 | stopPACServer() 158 | } 159 | 160 | func enableWhiteListProxy() { 161 | // 基于全局socks5代理下使用ACL文件来进行白名单代理 不需要使用pac文件 162 | let port = UserDefaults.standard.integer(forKey: "LocalSocks5.ListenPort") 163 | 164 | var args = [ 165 | "--mode", 166 | "global", 167 | "--port", 168 | String(format: "%lu", UInt(port)) 169 | ] 170 | 171 | if UserDefaults.standard.bool(forKey: "LocalHTTPOn") && UserDefaults.standard.bool(forKey: "LocalHTTP.FollowGlobal") { 172 | let privoxyPort = UserDefaults.standard.integer(forKey: "LocalHTTP.ListenPort") 173 | 174 | args.append("--privoxy-port") 175 | args.append(String(format: "%lu", UInt(privoxyPort))) 176 | } 177 | 178 | addArguments4ManualSpecifyNetworkServices(&args) 179 | callHelper(args) 180 | stopPACServer() 181 | } 182 | 183 | func disableProxy(_ PACFilePath: String?) { 184 | var args = ["--mode", "off"] 185 | addArguments4ManualSpecifyNetworkServices(&args) 186 | callHelper(args) 187 | stopPACServer() 188 | } 189 | 190 | func startPACServer(_ PACFilePath: String?) -> String? { 191 | var PACFilePath = PACFilePath 192 | //接受参数为以后使用定制PAC文件 193 | var originalPACData: Data? 194 | var routerPath = "/proxy.pac" 195 | if PACFilePath == "hi" { 196 | //用默认路径来代替 197 | PACFilePath = "\(NSHomeDirectory())/\(".ShadowsocksX-NG/gfwlist.js")" 198 | originalPACData = NSData(contentsOfFile: "\(NSHomeDirectory())/\(".ShadowsocksX-NG/gfwlist.js")") as Data? 199 | } else { 200 | //用定制路径来代替 201 | originalPACData = NSData(contentsOfFile: "\(NSHomeDirectory())/\(".ShadowsocksX-NG")/\(PACFilePath ?? "")") as Data? 202 | routerPath = "/\(PACFilePath ?? "")" 203 | } 204 | stopPACServer() 205 | webServer = GCDWebServer() 206 | webServer?.addHandler(forMethod: "GET", path: routerPath, request: GCDWebServerRequest.self, processBlock: { request in 207 | return GCDWebServerDataResponse(data: originalPACData!, contentType: "application/x-ns-proxy-autoconfig") 208 | }) 209 | let defaults = UserDefaults.standard 210 | let address = defaults.string(forKey: "PacServer.ListenAddress") 211 | let port = Int(Int16(defaults.integer(forKey: "PacServer.ListenPort"))) 212 | 213 | do { 214 | try webServer!.start(options: [ 215 | "BindToLocalhost": NSNumber(value: true), 216 | "Port": NSNumber(value: port) 217 | ]) 218 | } catch { 219 | } 220 | 221 | return "\("http://")\(address ?? ""):\(port)\(routerPath)" 222 | } 223 | 224 | func stopPACServer() { 225 | if ((webServer?.isRunning) != nil) { 226 | webServer?.stop() 227 | webServer = nil 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ProxyConfHelperVersion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyConfHelperVersion.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/4/20. 6 | // 7 | 8 | import Foundation 9 | 10 | let kProxyConfHelperVersion = "1.3.2" 11 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ProxyConfTool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyConfTool.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/4/24. 6 | // 7 | 8 | import Foundation 9 | import SystemConfiguration 10 | 11 | class ProxyConfTool { 12 | func networkServicesList() -> NSArray { 13 | let results: NSArray = [] 14 | 15 | let prefRef = SCPreferencesCreate(nil, "Shadowsocks" as CFString, nil) 16 | let sets: NSDictionary? = SCPreferencesGetValue(prefRef!, kSCPrefNetworkServices) as? NSDictionary 17 | // 遍历系统中的网络设备列表 18 | for key in sets?.allKeys ?? [] { 19 | guard let key = key as? String else { 20 | continue 21 | } 22 | let service: NSDictionary = sets?[key] as! NSDictionary 23 | let userDefinedName = service[kSCPropUserDefinedName as String] as? String 24 | let isActive = service[kSCResvInactive as String] as! Bool 25 | if isActive && userDefinedName != nil { 26 | let v = [ 27 | "key": key, 28 | "userDefinedName": userDefinedName ?? "" 29 | ] 30 | results.adding(v) 31 | } 32 | } 33 | 34 | return results 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ProxyPreferencesController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyPreferencesController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 邱宇舟 on 16/6/29. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ProxyPreferencesController: NSWindowController, NSTableViewDataSource, NSTableViewDelegate { 12 | 13 | @IBOutlet var autoConfigCheckBox: NSButton! 14 | @IBOutlet var tableView: NSTableView! 15 | 16 | var networkServices: NSArray! 17 | var selectedNetworkServices: NSMutableSet! 18 | var autoConfigureNetworkServices: Bool = true 19 | var proxyConfHelper: ProxyConfHelper = ProxyConfHelper() 20 | var proxyConfTool: ProxyConfTool = ProxyConfTool() 21 | 22 | override func windowDidLoad() { 23 | super.windowDidLoad() 24 | 25 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 26 | let defaults = UserDefaults.standard 27 | self.setValue(defaults.bool(forKey: "AutoConfigureNetworkServices"), forKey: "autoConfigureNetworkServices") 28 | 29 | if let services = defaults.array(forKey: "Proxy4NetworkServices") { 30 | selectedNetworkServices = NSMutableSet(array: services) 31 | } else { 32 | selectedNetworkServices = NSMutableSet() 33 | } 34 | 35 | networkServices = proxyConfTool.networkServicesList() 36 | tableView.reloadData() 37 | } 38 | 39 | @IBAction func ok(_ sender: NSObject){ 40 | proxyConfHelper.disableProxy("hi") 41 | 42 | let defaults = UserDefaults.standard 43 | defaults.setValue(selectedNetworkServices.allObjects, forKeyPath: "Proxy4NetworkServices") 44 | defaults.set(autoConfigureNetworkServices, forKey: "AutoConfigureNetworkServices") 45 | 46 | defaults.synchronize() 47 | 48 | window?.performClose(self) 49 | 50 | NotificationCenter.default 51 | .post(name: Notification.Name(rawValue: NOTIFY_ADV_PROXY_CONF_CHANGED), object: nil) 52 | } 53 | 54 | @IBAction func cancel(_ sender: NSObject){ 55 | window?.performClose(self) 56 | } 57 | 58 | //-------------------------------------------------- 59 | // For NSTableViewDataSource 60 | func numberOfRows(in tableView: NSTableView) -> Int { 61 | if networkServices != nil { 62 | return networkServices.count 63 | } 64 | return 0; 65 | } 66 | 67 | func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn? 68 | , row: Int) -> Any? { 69 | let cell = tableColumn!.dataCell as! NSButtonCell 70 | 71 | let key = (networkServices[row] as AnyObject)["key"] as! String 72 | if selectedNetworkServices.contains(key) { 73 | cell.state = NSControl.StateValue(rawValue: 1) 74 | } else { 75 | cell.state = NSControl.StateValue(rawValue: 0) 76 | } 77 | let userDefinedName = (networkServices[row] as AnyObject)["userDefinedName"] as! String 78 | cell.title = userDefinedName 79 | return cell 80 | } 81 | 82 | func tableView(_ tableView: NSTableView, setObjectValue object: Any? 83 | , for tableColumn: NSTableColumn?, row: Int) { 84 | let key = (networkServices[row] as AnyObject)["key"] as! String 85 | 86 | // NSLog("%d", object!.integerValue) 87 | if (object! as AnyObject).intValue == 1 { 88 | selectedNetworkServices.add(key) 89 | } else { 90 | selectedNetworkServices.remove(key) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/SWBApplication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWBApplication.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/4/13. 6 | // 7 | 8 | import Cocoa 9 | 10 | @objc(SWBApplication) 11 | class SWBApplication: NSApplication { 12 | open override func sendEvent(_ event: NSEvent) { 13 | if event.type == .keyDown { 14 | if event.modifierFlags.contains(.command) && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.command) { 15 | if event.modifierFlags.contains(.shift) && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.shift) { 16 | if event.charactersIgnoringModifiers == "Z" { 17 | if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return } 18 | } 19 | } 20 | guard let key = event.charactersIgnoringModifiers else { return super.sendEvent(event) } 21 | switch key { 22 | case "x": 23 | if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return } 24 | case "c": 25 | if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return } 26 | case "v": 27 | if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return } 28 | case "z": 29 | if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return } 30 | case "a": 31 | if NSApp.sendAction(#selector(NSStandardKeyBindingResponding.selectAll(_:)), to: nil, from: self) { return } 32 | default: 33 | break 34 | } 35 | } 36 | } 37 | super.sendEvent(event) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/SWBQRCodeWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWBQRCodeWindowController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 钟增强 on 2021/4/28. 6 | // 7 | 8 | import Cocoa 9 | import WebKit 10 | import CoreImage 11 | 12 | class SWBQRCodeWindowController: NSWindowController { 13 | 14 | @IBOutlet weak var titleTextField: NSTextField! 15 | @IBOutlet weak var imageView: NSImageView! 16 | 17 | var qrCode: String? 18 | var title: String? 19 | 20 | @IBAction func copyQRCode(_ sender: Any) { 21 | let pasteboard = NSPasteboard.general 22 | pasteboard.clearContents() 23 | let copiedObjects = [imageView?.image] 24 | pasteboard.writeObjects(copiedObjects.compactMap { $0 }) 25 | } 26 | 27 | override func windowDidLoad() { 28 | super.windowDidLoad() 29 | 30 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 31 | titleTextField.stringValue = title ?? "" 32 | setQRCode(qrCode) 33 | } 34 | 35 | func setQRCode(_ qrCode: String?) { 36 | let cgImgRef = createQRImage(for: qrCode, size: CGSize(width: 250, height: 250)) 37 | 38 | var image: NSImage? = nil 39 | if let cgImgRef = cgImgRef { 40 | image = NSImage(cgImage: cgImgRef, size: CGSize(width: 250, height: 250)) 41 | } 42 | imageView?.image = image 43 | } 44 | 45 | func createQRImage(for string: String?, size: CGSize) -> CGImage? { 46 | // Setup the QR filter with our string 47 | let filter = CIFilter(name: "CIQRCodeGenerator") 48 | filter?.setDefaults() 49 | 50 | let data = string?.data(using: .utf8) 51 | filter?.setValue(data, forKey: "inputMessage") 52 | let image = filter?.value(forKey: "outputImage") as? CIImage 53 | 54 | // Calculate the size of the generated image and the scale for the desired image size 55 | let extent = image?.extent.integral 56 | let scale = CGFloat(min(size.width / (extent?.width ?? 0.0), size.height / (extent?.height ?? 0.0))) 57 | 58 | // Since CoreImage nicely interpolates, we need to create a bitmap image that we'll draw into 59 | // a bitmap context at the desired size; 60 | let width = size_t((extent?.width ?? 0.0) * scale) 61 | let height = size_t((extent?.height ?? 0.0) * scale) 62 | let cs = CGColorSpaceCreateDeviceGray() 63 | let bitmapRef = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).rawValue ) 64 | 65 | #if os(iOS) 66 | let context = CIContext(options: [ 67 | CIContextOption.useSoftwareRenderer: true 68 | ]) 69 | #else 70 | var context: CIContext? = nil 71 | if let bitmapRef = bitmapRef { 72 | context = CIContext(cgContext: bitmapRef, options: [ 73 | CIContextOption.useSoftwareRenderer: NSNumber(value: true) 74 | ]) 75 | } 76 | #endif 77 | 78 | var bitmapImage: CGImage? = nil 79 | if let image = image { 80 | bitmapImage = context?.createCGImage(image, from: extent ?? CGRect.zero) 81 | } 82 | 83 | bitmapRef!.interpolationQuality = CGInterpolationQuality.none 84 | bitmapRef?.scaleBy(x: scale, y: scale) 85 | bitmapRef?.draw(bitmapImage!, in: extent!) 86 | 87 | // Create an image with the contents of our bitmap 88 | let scaledImage = bitmapRef?.makeImage() 89 | 90 | return scaledImage 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ServerProfile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerProfile.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 邱宇舟 on 16/6/6. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ServerProfile: NSObject { 12 | var uuid: String 13 | 14 | @objc var serverHost: String = "" 15 | @objc var serverPort: uint16 = 8379 16 | @objc var method: String = "aes-128-cfb" 17 | @objc var password: String = "" 18 | @objc var remark: String = "" 19 | 20 | @objc var ssrProtocol: String = "origin" 21 | @objc var ssrProtocolParam: String = "" 22 | @objc var ssrObfs: String = "plain" 23 | @objc var ssrObfsParam: String = "" 24 | @objc var ssrGroup: String = "" 25 | 26 | var latency: String? 27 | 28 | override init() { 29 | uuid = UUID().uuidString 30 | } 31 | 32 | init(uuid: String) { 33 | self.uuid = uuid 34 | } 35 | 36 | static func fromDictionary(_ data: [String: Any]) -> ServerProfile { 37 | let cp = { 38 | (profile: ServerProfile) in 39 | profile.serverHost = data["ServerHost"] as! String 40 | profile.serverPort = (data["ServerPort"] as! NSNumber).uint16Value 41 | profile.method = (data["Method"] as! String).lowercased() 42 | profile.password = data["Password"] as! String 43 | 44 | if let remark = data["Remark"] { 45 | profile.remark = remark as! String 46 | } 47 | if let ssrObfs = data["ssrObfs"] { 48 | profile.ssrObfs = (ssrObfs as! String).lowercased() 49 | } 50 | if let ssrObfsParam = data["ssrObfsParam"] { 51 | profile.ssrObfsParam = ssrObfsParam as! String 52 | } 53 | if let ssrProtocol = data["ssrProtocol"] { 54 | profile.ssrProtocol = (ssrProtocol as! String).lowercased() 55 | } 56 | if let ssrProtocolParam = data["ssrProtocolParam"] { 57 | profile.ssrProtocolParam = ssrProtocolParam as! String 58 | } 59 | if let ssrGroup = data["ssrGroup"] { 60 | profile.ssrGroup = ssrGroup as! String 61 | } 62 | } 63 | 64 | if let id = data["Id"] as? String { 65 | let profile = ServerProfile(uuid: id) 66 | cp(profile) 67 | return profile 68 | } else { 69 | let profile = ServerProfile() 70 | cp(profile) 71 | return profile 72 | } 73 | } 74 | 75 | func toDictionary() -> [String: AnyObject] { 76 | var d = [String: AnyObject]() 77 | d["Id"] = uuid as AnyObject? 78 | d["ServerHost"] = serverHost as AnyObject? 79 | d["ServerPort"] = NSNumber(value: serverPort as UInt16) 80 | d["Method"] = method as AnyObject? 81 | d["Password"] = password as AnyObject? 82 | d["Remark"] = remark as AnyObject? 83 | d["ssrProtocol"] = ssrProtocol as AnyObject? 84 | d["ssrProtocolParam"] = ssrProtocolParam as AnyObject? 85 | d["ssrObfs"] = ssrObfs as AnyObject? 86 | d["ssrObfsParam"] = ssrObfsParam as AnyObject? 87 | d["ssrGroup"] = ssrGroup as AnyObject? 88 | return d 89 | } 90 | 91 | func toJsonConfig() -> [String: AnyObject] { 92 | // supply json file for ss-local only export vital param 93 | var conf: [String: AnyObject] = ["server": serverHost as AnyObject, 94 | "server_port": NSNumber(value: serverPort as UInt16), 95 | "password": password as AnyObject, 96 | "method": method as AnyObject,] 97 | 98 | let defaults = UserDefaults.standard 99 | conf["local_port"] = NSNumber(value: UInt16(defaults.integer(forKey: "LocalSocks5.ListenPort")) as UInt16) 100 | conf["local_address"] = defaults.string(forKey: "LocalSocks5.ListenAddress") as AnyObject? 101 | conf["timeout"] = NSNumber(value: UInt32(defaults.integer(forKey: "LocalSocks5.Timeout")) as UInt32) 102 | 103 | if(!ssrObfs.isEmpty) { 104 | conf["protocol"] = ssrProtocol as AnyObject? 105 | conf["protocol_param"] = ssrProtocolParam as AnyObject?// do not muta here 106 | conf["obfs"] = ssrObfs as AnyObject? 107 | conf["obfs_param"] = ssrObfsParam as AnyObject? 108 | } 109 | 110 | return conf 111 | } 112 | 113 | func isValid() -> Bool { 114 | func validateIpAddress(_ ipToValidate: String) -> Bool { 115 | 116 | var sin = sockaddr_in() 117 | var sin6 = sockaddr_in6() 118 | 119 | if ipToValidate.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 { 120 | // IPv6 peer. 121 | return true 122 | } 123 | else if ipToValidate.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1 { 124 | // IPv4 peer. 125 | return true 126 | } 127 | 128 | return false; 129 | } 130 | 131 | func validateDomainName(_ value: String) -> Bool { 132 | let validHostnameRegex = "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$" 133 | 134 | if (value.range(of: validHostnameRegex, options: .regularExpression) != nil) { 135 | return true 136 | } else { 137 | return false 138 | } 139 | } 140 | 141 | if !(validateIpAddress(serverHost) || validateDomainName(serverHost)) { 142 | return false 143 | } 144 | 145 | if password.isEmpty { 146 | return false 147 | } 148 | 149 | if (ssrProtocol.isEmpty && !ssrObfs.isEmpty) || (!ssrProtocol.isEmpty && ssrObfs.isEmpty) { 150 | return false 151 | } 152 | 153 | return true 154 | } 155 | 156 | func URL() -> Foundation.URL? { 157 | if(ssrObfs == "plain") { 158 | let parts = "\(method):\(password)@\(serverHost):\(serverPort)" 159 | let base64String = parts.data(using: String.Encoding.utf8)? 160 | .base64EncodedString(options: NSData.Base64EncodingOptions()) 161 | if var s = base64String { 162 | s = s.trimmingCharacters(in: CharacterSet(charactersIn: "=")) 163 | return Foundation.URL(string: "ss://\(s)") 164 | } 165 | } else { 166 | let firstParts = "\(serverHost):\(serverPort):\(ssrProtocol):\(method):\(ssrObfs):" 167 | let secondParts = "\(password)" 168 | // ssr:// + base64(abc.xyz:12345:auth_sha1_v2:rc4-md5:tls1.2_ticket_auth:{base64(password)}/?obfsparam={base64(混淆参数(网址))}&protoparam={base64(混淆协议)}&remarks={base64(节点名称)}&group={base64(分组名)}) 169 | let base64PasswordString = encode64(secondParts) 170 | let base64ssrObfsParamString = encode64(ssrObfsParam) 171 | let base64ssrProtocolParamString = encode64(ssrProtocolParam) 172 | let base64RemarkString = encode64(remark) 173 | let base64GroupString = encode64(ssrGroup) 174 | 175 | let password = firstParts + base64PasswordString! + "/?" 176 | let obfsparam = "obfsparam=" + base64ssrObfsParamString! + "&" 177 | let protoparam = "protoparam=" + base64ssrProtocolParamString! + "&" 178 | let remarks = "remarks=" + base64RemarkString! + "&" 179 | let group = "group=" + base64GroupString! 180 | let s = encode64(password + obfsparam + protoparam + remarks + group) 181 | return Foundation.URL(string: "ssr://\(s ?? "")") 182 | } 183 | return nil 184 | } 185 | 186 | func title() -> String { 187 | if remark.isEmpty { 188 | return "\(serverHost):\(serverPort)" 189 | } else { 190 | return "\(remark) (\(serverHost):\(serverPort))" 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/StatusItemView.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// StatusItemView.swift 3 | //// Up&Down 4 | //// 5 | //// Created by 郭佳哲 on 5/16/16. 6 | //// Copyright © 2016 郭佳哲. All rights reserved. 7 | //// 8 | // 9 | //import AppKit 10 | //import Foundation 11 | // 12 | //open class StatusItemView: NSControl { 13 | // static let KB: Double = 1024 14 | // static let MB: Double = KB * 1024 15 | // static let GB: Double = MB * 1024 16 | // static let TB: Double = GB * 1024 17 | // 18 | // var fontSize: CGFloat = 9 19 | // var fontColor = NSColor.black 20 | // var darkMode = false 21 | // var mouseDown = false 22 | // var statusItem: NSStatusItem 23 | // 24 | // var upRate = "- - KB/s" 25 | // var downRate = "- - KB/s" 26 | // 27 | // var image = NSImage(named: NSImage.Name("menu_icon")) 28 | // 29 | // var showSpeed: Bool = false 30 | // 31 | // init(statusItem aStatusItem: NSStatusItem, menu aMenu: NSMenu) { 32 | // statusItem = aStatusItem 33 | // super.init(frame: NSMakeRect(0, 0, statusItem.length, 30)) 34 | // menu = aMenu 35 | // menu?.delegate = self 36 | // 37 | // darkMode = SystemThemeChangeHelper.isCurrentDark() 38 | // 39 | // SystemThemeChangeHelper.addRespond(target: self, selector: #selector(changeMode as () -> Void)) 40 | // 41 | // let iconImageName = IconUtils.getIconImageName() 42 | // if iconImageName != "" { 43 | // image = NSImage(named: NSImage.Name(iconImageName))! 44 | // } 45 | // } 46 | // 47 | // @available(*, unavailable) 48 | // public required init?(coder: NSCoder) { 49 | // fatalError("init(coder:) has not been implemented") 50 | // } 51 | // 52 | // override open func draw(_ dirtyRect: NSRect) { 53 | // statusItem.drawStatusBarBackground(in: dirtyRect, withHighlight: mouseDown) 54 | // 55 | // fontColor = (darkMode || mouseDown) ? NSColor.white : NSColor.black 56 | // let fontAttributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize), NSAttributedString.Key.foregroundColor: fontColor] as [NSAttributedString.Key: Any] 57 | // 58 | // if showSpeed { 59 | // let upRateString = NSAttributedString(string: upRate + " ↑", attributes: fontAttributes) 60 | // let upRateRect = upRateString.boundingRect(with: NSSize(width: 100, height: 100), options: .usesLineFragmentOrigin) 61 | // upRateString.draw(at: NSMakePoint(bounds.width - upRateRect.width - 5, 10)) 62 | // 63 | // let downRateString = NSAttributedString(string: downRate + " ↓", attributes: fontAttributes) 64 | // let downRateRect = downRateString.boundingRect(with: NSSize(width: 100, height: 100), options: .usesLineFragmentOrigin) 65 | // downRateString.draw(at: NSMakePoint(bounds.width - downRateRect.width - 5, 0)) 66 | // } 67 | // 68 | // image?.draw(at: NSPoint(x: 0, y: 0), from: NSRect(x: 0, y: 0, width: bounds.height, height: bounds.height), operation: NSCompositingOperation.sourceOver, fraction: 1.0) 69 | // } 70 | // 71 | // open func setRateData(up: Double, down: Double) { 72 | // upRate = formatRateData(up) 73 | // downRate = formatRateData(down) 74 | // 75 | // DispatchQueue.main.async { 76 | // self.needsDisplay = true 77 | // } 78 | // } 79 | // 80 | // func formatRateData(_ data: Double) -> String { 81 | // var result: Double 82 | // var unit: String 83 | // 84 | // if data < StatusItemView.KB / 100 { 85 | // result = 0 86 | // return "0 KB/s" 87 | // } 88 | // 89 | // else if data < StatusItemView.MB { 90 | // result = data / StatusItemView.KB 91 | // unit = " KB/s" 92 | // } 93 | // 94 | // else if data < StatusItemView.GB { 95 | // result = data / StatusItemView.MB 96 | // unit = " MB/s" 97 | // } 98 | // 99 | // else if data < StatusItemView.TB { 100 | // result = data / StatusItemView.GB 101 | // unit = " GB/s" 102 | // } 103 | // 104 | // else { 105 | // result = 1023 106 | // unit = " GB/s" 107 | // } 108 | // 109 | // if result < 100 { 110 | // return String(format: "%0.2f", result) + unit 111 | // } 112 | // else if result < 999 { 113 | // return String(format: "%0.1f", result) + unit 114 | // } 115 | // else { 116 | // return String(format: "%0.0f", result) + unit 117 | // } 118 | // } 119 | // 120 | // @objc func changeMode() { 121 | // darkMode = SystemThemeChangeHelper.isCurrentDark() 122 | // needsDisplay = true 123 | // } 124 | //} 125 | // 126 | //// action 127 | //extension StatusItemView: NSMenuDelegate { 128 | // override open func mouseDown(with theEvent: NSEvent) { 129 | // statusItem.popUpMenu(menu!) 130 | // } 131 | // 132 | // public func menuWillOpen(_ menu: NSMenu) { 133 | // mouseDown = true 134 | // needsDisplay = true 135 | // } 136 | // 137 | // public func menuDidClose(_ menu: NSMenu) { 138 | // mouseDown = false 139 | // needsDisplay = true 140 | // } 141 | //} 142 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Subscribe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Subscribe.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 秦宇航 on 2017/6/15. 6 | // Copyright © 2017年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Alamofire 11 | import Yams 12 | 13 | class Subscribe: NSObject { 14 | 15 | @objc var subscribeFeed = "" 16 | var isActive = true 17 | @objc var maxCount = 0 // -1 is not limited 18 | @objc var groupName = "" 19 | @objc var token = "" 20 | var cache = "" 21 | 22 | var profileMgr: ServerProfileManager! 23 | 24 | init(initUrlString: String, initGroupName: String, initToken: String, initMaxCount: Int) { 25 | super.init() 26 | subscribeFeed = initUrlString 27 | 28 | token = initToken 29 | 30 | setMaxCount(initMaxCount: initMaxCount) 31 | setGroupName(newGroupName: initGroupName) 32 | profileMgr = ServerProfileManager.instance 33 | } 34 | func getFeed() -> String { 35 | return subscribeFeed 36 | } 37 | func setFeed(newFeed: String) { 38 | subscribeFeed = newFeed 39 | } 40 | func diactivateSubscribe() { 41 | isActive = false 42 | } 43 | func activateSubscribe() { 44 | isActive = true 45 | } 46 | func setGroupName(newGroupName: String) { 47 | func getGroupNameFromRes(resString: String) { 48 | let decodeRes = decode64(resString)! 49 | let ssrregexp = "ssr://([A-Za-z0-9_-]+)" 50 | let urls = splitor(url: decodeRes, regexp: ssrregexp) 51 | let profile = ServerProfile.fromDictionary(parseAppURLSchemes(URL(string: urls[0]))! as [String: AnyObject]) 52 | self.groupName = profile.ssrGroup 53 | } 54 | if newGroupName != "" { return groupName = newGroupName } 55 | if self.cache != "" { return getGroupNameFromRes(resString: cache) } 56 | sendRequest(url: self.subscribeFeed, options: "", callback: { resString, contentType in 57 | if resString == "" { return self.groupName = "New Subscribe" } 58 | getGroupNameFromRes(resString: resString) 59 | self.cache = resString 60 | }) 61 | } 62 | func getGroupName() -> String { 63 | return groupName 64 | } 65 | func getMaxCount() -> Int { 66 | return maxCount 67 | } 68 | static func fromDictionary(_ data: [String: AnyObject]) -> Subscribe { 69 | var feed: String = "" 70 | var group: String = "" 71 | var token: String = "" 72 | var maxCount: Int = -1 73 | for (key, value) in data { 74 | switch key { 75 | case "feed": 76 | feed = value as! String 77 | case "group": 78 | group = value as! String 79 | case "token": 80 | token = value as! String 81 | case "maxCount": 82 | maxCount = value as! Int 83 | default: 84 | print("") 85 | } 86 | } 87 | return Subscribe.init(initUrlString: feed, initGroupName: group, initToken: token, initMaxCount: maxCount) 88 | } 89 | static func toDictionary(_ data: Subscribe) -> [String: AnyObject] { 90 | var ret: [String: AnyObject] = [:] 91 | ret["feed"] = data.subscribeFeed as AnyObject 92 | ret["group"] = data.groupName as AnyObject 93 | ret["token"] = data.token as AnyObject 94 | ret["maxCount"] = data.maxCount as AnyObject 95 | return ret 96 | } 97 | fileprivate func sendRequest(url: String, options: Any, callback: @escaping (String, String) -> Void) { 98 | let headers: HTTPHeaders = [ 99 | "token": self.token, 100 | "User-Agent": "ShadowsocksX-NG " + (getLocalInfo()["CFBundleShortVersionString"] as! String) 101 | ] 102 | 103 | AF.request(url, headers: headers) 104 | .responseString { response in 105 | switch response.result { 106 | case .success: 107 | let contentType = response.response?.allHeaderFields["Content-Type"] as? String ; 108 | callback(response.value!, contentType!) 109 | case .failure(_): 110 | callback("", "") 111 | self.pushNotification(title: "请求失败", subtitle: "", info: "发送到\(url)的请求失败,请检查您的网络") 112 | } 113 | } 114 | } 115 | func setMaxCount(initMaxCount: Int) { 116 | func getMaxFromRes(resString: String) { 117 | let maxCountReg = "MAX=[0-9]+" 118 | let decodeRes = decode64(resString)! 119 | let range = decodeRes.range(of: maxCountReg, options: .regularExpression) 120 | if range != nil { 121 | let result = String(decodeRes[range!]) 122 | self.maxCount = Int(result.replacingOccurrences(of: "MAX=", with: ""))! 123 | } 124 | else { 125 | self.maxCount = -1 126 | } 127 | } 128 | if initMaxCount != 0 { return self.maxCount = initMaxCount } 129 | if cache != "" { return getMaxFromRes(resString: cache) } 130 | sendRequest(url: self.subscribeFeed, options: "", callback: { resString, contentType in 131 | if resString == "" { return }// Also should hold if token is wrong feedback 132 | getMaxFromRes(resString: resString) 133 | self.cache = resString 134 | }) 135 | } 136 | func updateServerFromFeed() { 137 | func updateServerHandler(resString: String, _ contentType: String) { 138 | if(!contentType.contains("application/octet-stream") && !contentType.contains("application/yaml")) { 139 | NSLog("unsupport content type") 140 | return 141 | } 142 | 143 | var proxiesOrUrls: [Any] = [] 144 | if(contentType.contains("application/octet-stream")) { 145 | let decodeRes = decode64(resString)! 146 | if decodeRes.hasPrefix("ss://") { 147 | NSLog("unsupport ss type") 148 | } else if decodeRes.hasPrefix("ssr://") { 149 | proxiesOrUrls = splitor(url: decodeRes, regexp: "ssr://([A-Za-z0-9_-]+)") 150 | } 151 | } else if(contentType.contains("application/yaml")) { 152 | do { 153 | proxiesOrUrls = try YAMLDecoder().decode(YamlContent.self, from: resString).proxies 154 | } catch { 155 | NSLog("parse yaml failed") 156 | } 157 | } 158 | 159 | let maxN = (self.maxCount > proxiesOrUrls.count) ? proxiesOrUrls.count : (self.maxCount == -1) ? proxiesOrUrls.count : self.maxCount 160 | 161 | var profileDict: [String: Any] = [:] 162 | for index in 0.. Bool { 206 | // is the right format 207 | // should be http or https reg 208 | // but we should not support http only feed 209 | // TODO refine the regular expression 210 | let feedRegExp = "http[s]?://[A-Za-z0-9-_/.=?]*" 211 | return subscribeFeed.range(of: feedRegExp, options: .regularExpression) != nil 212 | } 213 | fileprivate func pushNotification(title: String, subtitle: String, info: String) { 214 | let userNote = NSUserNotification() 215 | userNote.title = title 216 | userNote.subtitle = subtitle 217 | userNote.informativeText = info 218 | userNote.soundName = NSUserNotificationDefaultSoundName 219 | 220 | NSUserNotificationCenter.default 221 | .deliver(userNote); 222 | } 223 | class func isSame(source: Subscribe, target: Subscribe) -> Bool { 224 | return source.subscribeFeed == target.subscribeFeed && source.token == target.token && source.maxCount == target.maxCount 225 | } 226 | func isExist(_ target: Subscribe) -> Bool { 227 | return self.subscribeFeed == target.subscribeFeed 228 | } 229 | 230 | //proxies: 231 | //- {"name":"🇭🇰 Hong Kong 01","type":"ss","server":"hk.it.dev","port":1344,"cipher":"chacha20","password":"xoW","udp":true} 232 | //- {"name":"🇭🇰 Hong Kong 02","type":"ss","server":"hk.kit.dev","port":1044,"cipher":"chacha20","password":"xoW","udp":true} 233 | struct YamlContent: Codable { 234 | var proxies: [Proxy] 235 | } 236 | 237 | struct Proxy: Codable { 238 | var name: String 239 | var type: String 240 | var server: String 241 | var port: Int 242 | var cipher: String 243 | var password: String 244 | var udp: Bool 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/SubscribeManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubscribeManager.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 秦宇航 on 2017/6/19. 6 | // Copyright © 2017年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class SubscribeManager:NSObject{ 12 | static let instance:SubscribeManager = SubscribeManager() 13 | 14 | var subscribes:[Subscribe] 15 | var subscribesDefault : [[String: AnyObject]] 16 | let defaults = UserDefaults.standard 17 | 18 | fileprivate override init() { 19 | subscribes = [] 20 | subscribesDefault = [[:]] 21 | if let subscribesDefault = defaults.array(forKey: "Subscribes") { 22 | for value in subscribesDefault{ 23 | subscribes.append(Subscribe.fromDictionary(value as! [String : AnyObject])) 24 | } 25 | } 26 | } 27 | func addSubscribe(oneSubscribe: Subscribe) -> Bool { 28 | for (index, value) in subscribes.enumerated() { 29 | if Subscribe.isSame(source: oneSubscribe, target: value) { 30 | return true 31 | } 32 | if value.isExist(oneSubscribe) { 33 | subscribes.replaceSubrange(index.. Bool { 41 | subscribes.remove(at: atIndex) 42 | return true 43 | } 44 | func save() { 45 | defaults.set(subscribesToDefaults(data: subscribes), forKey: "Subscribes") 46 | } 47 | fileprivate func subscribesToDefaults(data: [Subscribe]) -> [[String: AnyObject]]{ 48 | var ret : [[String: AnyObject]] = [] 49 | for value in data { 50 | ret.append(Subscribe.toDictionary(value)) 51 | } 52 | return ret 53 | } 54 | fileprivate func DefaultsToSubscribes(data:[[String: AnyObject]]) -> [Subscribe] { 55 | var ret : [Subscribe] = [] 56 | for value in data{ 57 | ret.append(Subscribe.fromDictionary(value)) 58 | } 59 | return ret 60 | } 61 | func updateAllServerFromSubscribe(){ 62 | subscribes.forEach{ value in 63 | value.updateServerFromFeed() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/SubscribePreferenceWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubscribePreferenceWindowController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 秦宇航 on 2017/6/15. 6 | // Copyright © 2017年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SubscribePreferenceWindowController: NSWindowController 12 | , NSTableViewDataSource, NSTableViewDelegate { 13 | 14 | @IBOutlet weak var FeedLabel: NSTextField! 15 | @IBOutlet weak var OKButton: NSButton! 16 | 17 | @IBOutlet weak var FeedTextField: NSTextField! 18 | @IBOutlet weak var TokenTextField: NSTextField! 19 | @IBOutlet weak var GroupTextField: NSTextField! 20 | @IBOutlet weak var MaxCountTextField: NSTextField! 21 | @IBOutlet weak var SubscribeTableView: NSTableView! 22 | 23 | @IBOutlet weak var AddSubscribeBtn: NSButton! 24 | @IBOutlet weak var DeleteSubscribeBtn: NSButton! 25 | 26 | var sbMgr: SubscribeManager! 27 | var defaults: UserDefaults! 28 | let tableViewDragType: String = "subscribe.host" 29 | var editingSubscribe: Subscribe! 30 | 31 | override func windowDidLoad() { 32 | super.windowDidLoad() 33 | 34 | sbMgr = SubscribeManager.instance 35 | defaults = UserDefaults.standard 36 | SubscribeTableView.reloadData() 37 | updateSubscribeBoxVisible() 38 | } 39 | 40 | override func awakeFromNib() { 41 | SubscribeTableView.registerForDraggedTypes([NSPasteboard.PasteboardType.init(tableViewDragType)]) 42 | SubscribeTableView.allowsMultipleSelection = true 43 | } 44 | 45 | @IBAction func onOk(_ sender: NSButton) { 46 | if editingSubscribe != nil { 47 | if !editingSubscribe.feedValidator() { 48 | // Done Shake window 49 | shakeWindows() 50 | return 51 | } 52 | } 53 | sbMgr.save() 54 | window?.performClose(self) 55 | } 56 | 57 | @IBAction func onAdd(_ sender: NSButton) { 58 | if editingSubscribe != nil && !editingSubscribe.feedValidator(){ 59 | shakeWindows() 60 | return 61 | } 62 | SubscribeTableView.beginUpdates() 63 | let subscribe = Subscribe(initUrlString: "", initGroupName: "", initToken: "", initMaxCount: -1) 64 | sbMgr.subscribes.append(subscribe) 65 | 66 | let index = IndexSet(integer: sbMgr.subscribes.count-1) 67 | SubscribeTableView.insertRows(at: index, withAnimation: .effectFade) 68 | 69 | self.SubscribeTableView.scrollRowToVisible(self.sbMgr.subscribes.count-1) 70 | self.SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false) 71 | SubscribeTableView.endUpdates() 72 | updateSubscribeBoxVisible() 73 | } 74 | 75 | @IBAction func onDelete(_ sender: NSButton) { 76 | let index = Int(SubscribeTableView.selectedRowIndexes.first!) 77 | var deleteCount = 0 78 | if index >= 0 { 79 | SubscribeTableView.beginUpdates() 80 | for (_, toDeleteIndex) in SubscribeTableView.selectedRowIndexes.enumerated() { 81 | _ = sbMgr.deleteSubscribe(atIndex: toDeleteIndex - deleteCount) 82 | SubscribeTableView.removeRows(at: IndexSet(integer: toDeleteIndex - deleteCount), withAnimation: .effectFade) 83 | deleteCount += 1 84 | if sbMgr.subscribes.count == 0 { 85 | cleanField() 86 | } 87 | } 88 | SubscribeTableView.endUpdates() 89 | } 90 | self.SubscribeTableView.scrollRowToVisible(index - 1) 91 | self.SubscribeTableView.selectRowIndexes(IndexSet(integer: index - 1), byExtendingSelection: false) 92 | updateSubscribeBoxVisible() 93 | } 94 | 95 | func updateSubscribeBoxVisible() { 96 | if sbMgr.subscribes.count <= 0 { 97 | DeleteSubscribeBtn.isEnabled = false 98 | FeedTextField.isEnabled = false 99 | TokenTextField.isEnabled = false 100 | GroupTextField.isEnabled = false 101 | MaxCountTextField.isEnabled = false 102 | }else{ 103 | DeleteSubscribeBtn.isEnabled = true 104 | FeedTextField.isEnabled = true 105 | TokenTextField.isEnabled = true 106 | GroupTextField.isEnabled = true 107 | MaxCountTextField.isEnabled = true 108 | } 109 | } 110 | 111 | func bindSubscribe(_ index:Int) { 112 | if index >= 0 && index < sbMgr.subscribes.count { 113 | editingSubscribe = sbMgr.subscribes[index] 114 | 115 | FeedTextField.bind(NSBindingName(rawValue: "value"), to: editingSubscribe!, withKeyPath: "subscribeFeed", options: [NSBindingOption.continuouslyUpdatesValue: true]) 116 | TokenTextField.bind(NSBindingName(rawValue: "value"), to: editingSubscribe!, withKeyPath: "token", options: [NSBindingOption.continuouslyUpdatesValue: true]) 117 | GroupTextField.bind(NSBindingName(rawValue: "value"), to: editingSubscribe!, withKeyPath: "groupName", options: [NSBindingOption.continuouslyUpdatesValue: true]) 118 | MaxCountTextField.bind(NSBindingName(rawValue: "value"), to: editingSubscribe!, withKeyPath: "maxCount", options: [NSBindingOption.continuouslyUpdatesValue: true]) 119 | 120 | } else { 121 | editingSubscribe = nil 122 | FeedTextField.unbind(NSBindingName(rawValue: "value")) 123 | TokenTextField.unbind(NSBindingName(rawValue: "value")) 124 | GroupTextField.unbind(NSBindingName(rawValue: "value")) 125 | MaxCountTextField.unbind(NSBindingName(rawValue: "value")) 126 | } 127 | } 128 | 129 | func getDataAtRow(_ index:Int) -> String { 130 | if sbMgr.subscribes[index].groupName != "" { 131 | return sbMgr.subscribes[index].groupName 132 | } 133 | return sbMgr.subscribes[index].subscribeFeed 134 | } 135 | 136 | // MARK: For NSTableViewDataSource 137 | 138 | func numberOfRows(in tableView: NSTableView) -> Int { 139 | if let mgr = sbMgr { 140 | return mgr.subscribes.count 141 | } 142 | return 0 143 | } 144 | 145 | func tableView(_ tableView: NSTableView 146 | , objectValueFor tableColumn: NSTableColumn? 147 | , row: Int) -> Any? { 148 | 149 | let title = getDataAtRow(row) 150 | 151 | if tableColumn?.identifier == NSUserInterfaceItemIdentifier("main") { 152 | if title != "" {return title} 153 | else {return "S"} 154 | } else if tableColumn?.identifier == NSUserInterfaceItemIdentifier("status") { 155 | return NSImage(named: NSImage.Name("menu_icon")) 156 | } 157 | return "" 158 | } 159 | 160 | // MARK: Drag & Drop reorder rows 161 | 162 | func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { 163 | let item = NSPasteboardItem() 164 | item.setString(String(row), forType: NSPasteboard.PasteboardType(rawValue: tableViewDragType)) 165 | return item 166 | } 167 | 168 | func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int 169 | , proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { 170 | if dropOperation == .above { 171 | return .move 172 | } 173 | return NSDragOperation() 174 | } 175 | 176 | func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo 177 | , row: Int, dropOperation: NSTableView.DropOperation) -> Bool { 178 | if let mgr = sbMgr { 179 | var oldIndexes = [Int]() 180 | info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { draggingItem, _, _ in 181 | if let str = (draggingItem.item as! NSPasteboardItem).string(forType: NSPasteboard.PasteboardType(rawValue: self.tableViewDragType)), let index = Int(str) { 182 | oldIndexes.append(index) 183 | } 184 | } 185 | 186 | var oldIndexOffset = 0 187 | var newIndexOffset = 0 188 | 189 | // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly. 190 | // You may want to move rows in your content array and then call `tableView.reloadData()` instead. 191 | tableView.beginUpdates() 192 | for oldIndex in oldIndexes { 193 | if oldIndex < row { 194 | let o = mgr.subscribes.remove(at: oldIndex + oldIndexOffset) 195 | mgr.subscribes.insert(o, at:row - 1) 196 | tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1) 197 | oldIndexOffset -= 1 198 | } else { 199 | let o = mgr.subscribes.remove(at: oldIndex) 200 | mgr.subscribes.insert(o, at:row + newIndexOffset) 201 | tableView.moveRow(at: oldIndex, to: row + newIndexOffset) 202 | newIndexOffset += 1 203 | } 204 | } 205 | tableView.endUpdates() 206 | 207 | return true 208 | } 209 | return false 210 | } 211 | 212 | //-------------------------------------------------- 213 | // For NSTableViewDelegate 214 | 215 | func tableView(_ tableView: NSTableView 216 | , shouldEdit tableColumn: NSTableColumn?, row: Int) -> Bool { 217 | return false 218 | } 219 | 220 | func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { 221 | if row < 0 { 222 | editingSubscribe = nil 223 | return true 224 | } 225 | // if editingSubscribe != nil { 226 | // if !editingSubscribe.isValid() { 227 | // return false 228 | // } 229 | // } 230 | 231 | return true 232 | } 233 | 234 | func tableViewSelectionDidChange(_ notification: Notification) { 235 | if SubscribeTableView.selectedRow >= 0 { 236 | bindSubscribe(SubscribeTableView.selectedRow) 237 | if (SubscribeTableView.selectedRowIndexes.count > 1){ 238 | // duplicateProfileButton.isEnabled = false 239 | } else { 240 | // duplicateProfileButton.isEnabled = true 241 | } 242 | } else { 243 | if !sbMgr.subscribes.isEmpty { 244 | let index = IndexSet(integer: sbMgr.subscribes.count - 1) 245 | SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false) 246 | } 247 | } 248 | } 249 | 250 | func cleanField(){ 251 | FeedTextField.stringValue = "" 252 | TokenTextField.stringValue = "" 253 | GroupTextField.stringValue = "" 254 | MaxCountTextField.stringValue = "" 255 | } 256 | 257 | func shakeWindows(){ 258 | let numberOfShakes:Int = 8 259 | let durationOfShake:Float = 0.5 260 | let vigourOfShake:Float = 0.05 261 | 262 | let frame:CGRect = (window?.frame)! 263 | let shakeAnimation = CAKeyframeAnimation() 264 | 265 | let shakePath = CGMutablePath() 266 | 267 | shakePath.move(to: CGPoint(x:NSMinX(frame), y:NSMinY(frame))) 268 | 269 | for _ in 1...numberOfShakes{ 270 | shakePath.addLine(to: CGPoint(x: NSMinX(frame) - frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame))) 271 | shakePath.addLine(to: CGPoint(x: NSMinX(frame) + frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame))) 272 | } 273 | 274 | shakePath.closeSubpath() 275 | shakeAnimation.path = shakePath 276 | shakeAnimation.duration = CFTimeInterval(durationOfShake) 277 | window?.animations = [NSAnimatablePropertyKey.init("frameOrigin"):shakeAnimation] 278 | window?.animator().setFrameOrigin(window!.frame.origin) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/SystemThemeChangeHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemThemeChangeHelper.swift 3 | // Up&Down 4 | // 5 | // Created by 郭佳哲 on 5/20/16. 6 | // Copyright © 2016 郭佳哲. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class SystemThemeChangeHelper { 12 | 13 | static func addRespond(target aTarget: AnyObject, selector aSelector: Selector) { 14 | DistributedNotificationCenter.default().addObserver(aTarget, selector: aSelector, name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil) 15 | } 16 | 17 | static func isCurrentDark() -> Bool { 18 | var result = false 19 | let dict = UserDefaults.standard.persistentDomain(forName: UserDefaults.globalDomain) 20 | if let style:AnyObject = dict!["AppleInterfaceStyle"] as AnyObject? { 21 | if (style as! String).caseInsensitiveCompare("dark") == ComparisonResult.orderedSame { 22 | result = true 23 | } 24 | } 25 | return result 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/UserRulesController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserRulesController.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 周斌佳 on 16/8/1. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class UserRulesController: NSWindowController { 12 | 13 | @IBOutlet var userRulesView: NSTextView! 14 | 15 | override func windowDidLoad() { 16 | super.windowDidLoad() 17 | 18 | let fileMgr = FileManager.default 19 | if !fileMgr.fileExists(atPath: PACUserRuleFilePath) { 20 | let src = Bundle.main.path(forResource: "user-rule", ofType: "txt") 21 | try! fileMgr.copyItem(atPath: src!, toPath: PACUserRuleFilePath) 22 | } 23 | 24 | let str = try? String(contentsOfFile: PACUserRuleFilePath, encoding: String.Encoding.utf8) 25 | userRulesView.string = str ?? "" 26 | } 27 | 28 | @IBAction func didCancel(_ sender: AnyObject) { 29 | window?.performClose(self) 30 | } 31 | 32 | @IBAction func didOK(_ sender: AnyObject) { 33 | let str = userRulesView.string 34 | if str != "" { 35 | do { 36 | try str.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACUserRuleFilePath), options: .atomic) 37 | 38 | if GeneratePACFile() { 39 | // Popup a user notification 40 | let notification = NSUserNotification() 41 | notification.title = "PAC has been updated by User Rules.".localized 42 | NSUserNotificationCenter.default 43 | .deliver(notification) 44 | } else { 45 | let notification = NSUserNotification() 46 | notification.title = "It's failed to update PAC by User Rules.".localized 47 | NSUserNotificationCenter.default 48 | .deliver(notification) 49 | } 50 | } catch {} 51 | } 52 | window?.performClose(self) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 邱宇舟 on 16/6/7. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CommonCrypto 11 | 12 | extension String { 13 | var localized: String { 14 | return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") 15 | } 16 | } 17 | 18 | 19 | extension Data { 20 | func sha1() -> String { 21 | let data = self 22 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) 23 | CC_SHA1((data as NSData).bytes, CC_LONG(data.count), &digest) 24 | let hexBytes = digest.map { String(format: "%02hhx", $0) } 25 | return hexBytes.joined(separator: "") 26 | } 27 | } 28 | 29 | func splitProfile(url: String, max: Int) -> [String] { 30 | let ssrregexp = "ssr://([A-Za-z0-9_-]+)" 31 | let ssregexp = "ss://([A-Za-z0-9_-]+" 32 | 33 | 34 | if url.hasPrefix("ss://"){ 35 | return splitor(url: url, regexp: ssregexp) 36 | }else if url.hasPrefix("ssr://"){ 37 | return splitor(url: url, regexp: ssrregexp) 38 | } 39 | return [""] 40 | } 41 | 42 | func splitor(url: String, regexp: String) -> [String] { 43 | var ret: [String] = [] 44 | var ssrUrl = url 45 | while ssrUrl.range(of:regexp, options: .regularExpression) != nil { 46 | let range = ssrUrl.range(of:regexp, options: .regularExpression) 47 | let result = String(ssrUrl[range!]) 48 | ssrUrl.replaceSubrange(range!, with: "") 49 | ret.append(result) 50 | } 51 | return ret 52 | } 53 | 54 | func getLocalInfo() -> [String: Any] { 55 | let InfoDict = Bundle.main.infoDictionary 56 | return InfoDict! 57 | } 58 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/VersionChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VersionChecker.swift 3 | // ShadowsocksX-NG 4 | // 5 | // Created by 秦宇航 on 2017/1/9. 6 | // Copyright © 2017年 qinyuhang. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Alamofire 11 | 12 | let LATEST_RELEASE_URL = "https://api.github.com/repos/jack80342/ShadowsocksX-NG/releases/latest" 13 | let _VERSION_XML_LOCAL: String = Bundle.main.bundlePath + "/Contents/Info.plist" 14 | 15 | class VersionChecker: NSObject { 16 | var haveNewVersion: Bool = false 17 | enum versionError: Error { 18 | case CanNotGetOnlineData 19 | } 20 | func saveFile(fromURL: String, toPath: String, withName: String) -> Bool { 21 | let manager = FileManager.default 22 | let url = URL(string: fromURL)! 23 | do { 24 | let st = try String(contentsOf: url, encoding: String.Encoding.utf8) 25 | print(st) 26 | let data = st.data(using: String.Encoding.utf8) 27 | manager.createFile(atPath: toPath + withName, contents: data, attributes: nil) 28 | return true 29 | 30 | } catch { 31 | print(error) 32 | return false 33 | } 34 | } 35 | func showAlertView(Title: String, SubTitle: String, ConfirmBtn: String, CancelBtn: String) -> Int { 36 | let alertView = NSAlert() 37 | alertView.messageText = Title 38 | alertView.informativeText = SubTitle 39 | alertView.addButton(withTitle: ConfirmBtn) 40 | if CancelBtn != "" { 41 | alertView.addButton(withTitle: CancelBtn) 42 | } 43 | let action = alertView.runModal() 44 | return action.rawValue 45 | } 46 | func parserVersionString(strIn: String) -> Array { 47 | let version: Range? = strIn.range(of: "-") 48 | var strTmp = "" 49 | if(version != nil) { 50 | strTmp = String(strIn[.. Void) { 69 | // return 70 | // newVersion: Bool, 71 | // error: String, 72 | // alertTitle: String, 73 | // alertSubtitle: String, 74 | // alertConfirmBtn: String, 75 | // alertCancelBtn: String 76 | 77 | struct Version: Decodable { 78 | let tagName: String 79 | enum CodingKeys: String, CodingKey { 80 | case tagName = "tag_name" 81 | } 82 | } 83 | AF.request(LATEST_RELEASE_URL).responseDecodable(of: Version.self) { response in 84 | debugPrint("Response: \(response)") 85 | switch response.result { 86 | case .success: 87 | callback(check(onlineData: response.value)) 88 | case .failure(let error): 89 | print("Request failed with error: \(error)") 90 | callback(["newVersion": false, 91 | "error": "network error", 92 | "Title": "网络错误", 93 | "SubTitle": "由于网络错误无法检查更新", 94 | "ConfirmBtn": "确认", 95 | "CancelBtn": "" 96 | ]) 97 | } 98 | } 99 | 100 | func check(onlineData: Version?) -> NSDictionary { 101 | // 已发布的最新版本,请求频率限制:https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting 102 | let tagName = onlineData?.tagName; 103 | if(tagName == nil) { 104 | return ["newVersion": false, 105 | "error": "", 106 | "Title": "请求过于频繁", 107 | "SubTitle": "请稍后重试!", 108 | "ConfirmBtn": "确认", 109 | "CancelBtn": "" 110 | ] 111 | } 112 | var versionString: String = tagName! 113 | // 去掉版本前缀:v 114 | versionString = String(versionString[versionString.range(of: "v")!.upperBound...]) 115 | 116 | let localData = NSDictionary(contentsOfFile: _VERSION_XML_LOCAL)! 117 | // 用户的软件版本 118 | let currentVersionString: String = localData["CFBundleShortVersionString"] as! String 119 | 120 | var subtitle: String 121 | if (versionString == currentVersionString) { 122 | // 版本号相同 123 | subtitle = "当前版本 " + currentVersionString 124 | return ["newVersion": false, 125 | "error": "", 126 | "Title": "已是最新版本!", 127 | "SubTitle": subtitle, 128 | "ConfirmBtn": "确认", 129 | "CancelBtn": "" 130 | ] 131 | } else { 132 | // 版本号不同 133 | var versionArr = parserVersionString(strIn: versionString) 134 | var currentVersionArr = parserVersionString(strIn: currentVersionString) 135 | 136 | // 做补0处理 137 | while (max(versionArr.count, currentVersionArr.count) != min(versionArr.count, currentVersionArr.count)) { 138 | if (versionArr.count < currentVersionArr.count) { 139 | versionArr.append(0) 140 | } 141 | else { 142 | currentVersionArr.append(0) 143 | } 144 | } 145 | 146 | for i in 0.. currentVersionArr[i] { 148 | haveNewVersion = true 149 | subtitle = "新版本为 " + versionString + "\n" + "当前版本 " + currentVersionString 150 | return ["newVersion": true, 151 | "error": "", 152 | "Title": "软件有更新!", 153 | "SubTitle": subtitle, 154 | "ConfirmBtn": "前往下载", 155 | "CancelBtn": "取消" 156 | ] 157 | } 158 | } 159 | subtitle = "当前版本 " + currentVersionString 160 | return ["newVersion": false, 161 | "error": "", 162 | "Title": "已是最新版本!", 163 | "SubTitle": subtitle, 164 | "ConfirmBtn": "确认", 165 | "CancelBtn": "" 166 | ] 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/AdvPreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "Enable Udp Replay"; ObjectID = "1C3-4s-Apd"; */ 3 | "1C3-4s-Apd.title" = "Enable Udp Replay"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Don't change these preferences if you have no idea what they are."; ObjectID = "8wL-qv-5nD"; */ 6 | "8wL-qv-5nD.title" = "Don't change these preferences if you have no idea what they are."; 7 | 8 | /* Class = "NSWindow"; title = "Advance Preferences"; ObjectID = "F0z-JX-Cv5"; */ 9 | "F0z-JX-Cv5.title" = "Advance Preferences"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Seconds"; ObjectID = "Gd3-Fm-sWh"; */ 12 | "Gd3-Fm-sWh.title" = "Seconds"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Local PAC Listen Port:"; ObjectID = "PSg-XK-qhl"; */ 15 | "PSg-XK-qhl.title" = "Local PAC Listen Port:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "ACL White List URL:"; ObjectID = "aif-uV-Lxk"; */ 18 | "aif-uV-Lxk.title" = "ACL White List URL:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Local Socks5 Listen Address:"; ObjectID = "cwr-n0-zwn"; */ 21 | "cwr-n0-zwn.title" = "Local Socks5 Listen Address:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "ACL Back China URL:"; ObjectID = "cwt-37-PW1"; */ 24 | "cwt-37-PW1.title" = "ACL Back China URL:"; 25 | 26 | /* Class = "NSButtonCell"; title = "Enable Verbose Mode"; ObjectID = "dCD-gg-I2i"; */ 27 | "dCD-gg-I2i.title" = "Enable Verbose Mode"; 28 | 29 | /* Class = "NSTextFieldCell"; title = "PAC GFW List URL:"; ObjectID = "dg0-gS-z5V"; */ 30 | "dg0-gS-z5V.title" = "PAC GFW List URL:"; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Local PAC Listen Address:"; ObjectID = "dvL-Ic-Wae"; */ 33 | "dvL-Ic-Wae.title" = "Local PAC Listen Address:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "Timeout:"; ObjectID = "i4l-2S-gOQ"; */ 36 | "i4l-2S-gOQ.title" = "Timeout:"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Local Socks5 Listen Port:"; ObjectID = "qRs-ow-vVB"; */ 39 | "qRs-ow-vVB.title" = "Local Socks5 Listen Port:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "ACL Auto List URL:"; ObjectID = "qhu-d1-qgq"; */ 42 | "qhu-d1-qgq.title" = "ACL Auto List URL:"; 43 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/HTTPPreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "HTTP Proxy Listen Port:"; ObjectID = "2Sd-yn-gdY"; */ 3 | "2Sd-yn-gdY.title" = "HTTP Proxy Listen Port:"; 4 | 5 | /* Class = "NSButtonCell"; title = "HTTP Proxy Enable"; ObjectID = "dqU-MG-Sum"; */ 6 | "dqU-MG-Sum.title" = "HTTP Proxy Enable"; 7 | 8 | /* Class = "NSButtonCell"; title = "Follow Global Mode"; ObjectID = "ofk-Pc-c8f"; */ 9 | "ofk-Pc-c8f.title" = "Follow Global Mode"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "HTTP Proxy Listen Address:"; ObjectID = "qd0-kc-ttB"; */ 12 | "qd0-kc-ttB.title" = "HTTP Proxy Listen Address:"; 13 | 14 | /* Class = "NSWindow"; title = "HTTP Proxy Preferences"; ObjectID = "vS3-DL-Nq3"; */ 15 | "vS3-DL-Nq3.title" = "HTTP Proxy Preferences"; 16 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/MainMenu.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Check Updates At Launch"; ObjectID = "3nJ-zQ-Agj"; */ 3 | "3nJ-zQ-Agj.title" = "Check Updates At Launch"; 4 | 5 | /* Class = "NSMenuItem"; title = "Export All Server To Json..."; ObjectID = "6k0-gn-DQv"; */ 6 | "6k0-gn-DQv.title" = "Export All Server To Json..."; 7 | 8 | /* Class = "NSMenuItem"; title = "Manual Mode"; ObjectID = "8PR-gs-c5N"; */ 9 | "8PR-gs-c5N.title" = "Manual Mode"; 10 | 11 | /* Class = "NSMenu"; title = "Servers"; ObjectID = "9Y1-db-3HK"; */ 12 | "9Y1-db-3HK.title" = "Servers"; 13 | 14 | /* Class = "NSMenuItem"; title = "Turn ShadowsocksX On"; ObjectID = "GSu-Tf-StS"; */ 15 | "GSu-Tf-StS.title" = "Turn ShadowsocksX On"; 16 | 17 | /* Class = "NSMenu"; title = "ShadowsocksX-NG"; ObjectID = "Hob-KD-bx9"; */ 18 | "Hob-KD-bx9.title" = "ShadowsocksX-NG"; 19 | 20 | /* Class = "NSMenuItem"; title = "Show Logs..."; ObjectID = "Jfy-sf-Fhl"; */ 21 | "Jfy-sf-Fhl.title" = "Show Logs..."; 22 | 23 | /* Class = "NSMenuItem"; title = "About"; ObjectID = "LgB-6g-Gba"; */ 24 | "LgB-6g-Gba.title" = "About"; 25 | 26 | /* Class = "NSMenuItem"; title = "Server Preferences..."; ObjectID = "M5r-E7-44f"; */ 27 | "M5r-E7-44f.title" = "Server Preferences..."; 28 | 29 | /* Class = "NSMenuItem"; title = "Global Mode"; ObjectID = "Mw3-Jm-eXA"; */ 30 | "Mw3-Jm-eXA.title" = "Global Mode"; 31 | 32 | /* Class = "NSMenuItem"; title = "Manual Update Subscribe"; ObjectID = "NnE-Ad-1An"; */ 33 | "NnE-Ad-1An.title" = "Manual Update Subscribe"; 34 | 35 | /* Class = "NSMenuItem"; title = "Edit Subscribe Feed"; ObjectID = "QJ6-eg-mzv"; */ 36 | "QJ6-eg-mzv.title" = "Edit Subscribe Feed"; 37 | 38 | /* Class = "NSMenuItem"; title = "Scan QR Code From Screen..."; ObjectID = "Qe6-bF-paT"; */ 39 | "Qe6-bF-paT.title" = "Scan QR Code From Screen..."; 40 | 41 | /* Class = "NSMenuItem"; title = "Show QR Code For Current Server..."; ObjectID = "R6A-96-Zcb"; */ 42 | "R6A-96-Zcb.title" = "Show QR Code For Current Server..."; 43 | 44 | /* Class = "NSMenuItem"; title = "White List Mode"; ObjectID = "RvZ-Zn-29U"; */ 45 | "RvZ-Zn-29U.title" = "White List Mode"; 46 | 47 | /* Class = "NSMenuItem"; title = "Import Bunch Json File..."; ObjectID = "T9g-gy-gvv"; */ 48 | "T9g-gy-gvv.title" = "Import Bunch Json File..."; 49 | 50 | /* Class = "NSMenuItem"; title = "Update PAC file"; ObjectID = "TFc-Ec-duM"; */ 51 | "TFc-Ec-duM.title" = "Update PAC file"; 52 | 53 | /* Class = "NSMenuItem"; title = "Feedback"; ObjectID = "W7u-7g-Gv4"; */ 54 | "W7u-7g-Gv4.title" = "Feedback"; 55 | 56 | /* Class = "NSMenu"; title = "Proxy Settings"; ObjectID = "YZp-bf-L40"; */ 57 | "YZp-bf-L40.title" = "Proxy Settings"; 58 | 59 | /* Class = "NSMenuItem"; title = "ACL Mode"; ObjectID = "Yub-b0-bni"; */ 60 | "Yub-b0-bni.title" = "ACL Mode"; 61 | 62 | ///* Class = "NSMenuItem"; title = "Show network speed"; ObjectID = "a3h-uQ-DuO"; */ 63 | //"a3h-uQ-DuO.title" = "Show network speed"; 64 | 65 | /* Class = "NSMenuItem"; title = "Connect At Launch"; ObjectID = "aB3-cf-5j0"; */ 66 | "aB3-cf-5j0.title" = "Connect At Launch"; 67 | 68 | /* Class = "NSMenuItem"; title = "Advance Preference ..."; ObjectID = "bZ3-fy-34d"; */ 69 | "bZ3-fy-34d.title" = "Advance Preference ..."; 70 | 71 | /* Class = "NSMenuItem"; title = "Proxy Settings"; ObjectID = "diI-fB-Rss"; */ 72 | "diI-fB-Rss.title" = "Proxy Settings"; 73 | 74 | /* Class = "NSMenuItem"; title = "Launch At Login"; ObjectID = "eUq-p7-ICK"; */ 75 | "eUq-p7-ICK.title" = "Launch At Login"; 76 | 77 | /* Class = "NSMenuItem"; title = "Showsocks: On"; ObjectID = "fzk-mE-CEV"; */ 78 | "fzk-mE-CEV.title" = "Showsocks: On"; 79 | 80 | /* Class = "NSMenuItem"; title = "Update ACL file"; ObjectID = "gde-ZE-086"; */ 81 | "gde-ZE-086.title" = "Update ACL file"; 82 | 83 | /* Class = "NSMenuItem"; title = "Proxy Back China"; ObjectID = "lam-el-r4I"; */ 84 | "lam-el-r4I.title" = "Proxy Back China"; 85 | 86 | /* Class = "NSMenuItem"; title = "Item 2"; ObjectID = "lay-XB-0oV"; */ 87 | "lay-XB-0oV.title" = "Item 2"; 88 | 89 | /* Class = "NSMenuItem"; title = "Item 3"; ObjectID = "m00-ru-6Po"; */ 90 | "m00-ru-6Po.title" = "Item 3"; 91 | 92 | /* Class = "NSMenuItem"; title = "Update Subscribe on App Start"; ObjectID = "nQ4-Nv-tox"; */ 93 | "nQ4-Nv-tox.title" = "Update Subscribe on App Start"; 94 | 95 | /* Class = "NSMenuItem"; title = "Item 1"; ObjectID = "oR3-cT-RTC"; */ 96 | "oR3-cT-RTC.title" = "Item 1"; 97 | 98 | /* Class = "NSMenuItem"; title = "Show Bunch Json Example File..."; ObjectID = "pdy-JE-50Q"; */ 99 | "pdy-JE-50Q.title" = "Show Bunch Json Example File..."; 100 | 101 | /* Class = "NSMenuItem"; title = "Auto Mode By PAC"; ObjectID = "r07-Gu-aEz"; */ 102 | "r07-Gu-aEz.title" = "Auto Mode By PAC"; 103 | 104 | /* Class = "NSMenuItem"; title = "Edit User Rules For PAC..."; ObjectID = "rms-p0-CvB"; */ 105 | "rms-p0-CvB.title" = "Edit User Rules For PAC..."; 106 | 107 | /* Class = "NSMenuItem"; title = "ACL Auto"; ObjectID = "sDG-YV-yuQ"; */ 108 | "sDG-YV-yuQ.title" = "ACL Auto"; 109 | 110 | /* Class = "NSMenuItem"; title = "Advance Proxy Preference..."; ObjectID = "sbx-yz-3lO"; */ 111 | "sbx-yz-3lO.title" = "Advance Proxy Preference..."; 112 | 113 | /* Class = "NSMenu"; title = "ACL Mode"; ObjectID = "tEB-59-fCx"; */ 114 | "tEB-59-fCx.title" = "ACL Mode"; 115 | 116 | /* Class = "NSMenuItem"; title = "Servers"; ObjectID = "u5M-hQ-VSc"; */ 117 | "u5M-hQ-VSc.title" = "Servers"; 118 | 119 | /* Class = "NSMenuItem"; title = "HTTP Proxy Preference ..."; ObjectID = "uEp-Gz-cu0"; */ 120 | "uEp-Gz-cu0.title" = "HTTP Proxy Preference ..."; 121 | 122 | /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "vJS-JW-byz"; */ 123 | "vJS-JW-byz.title" = "Quit"; 124 | 125 | /* Class = "NSMenuItem"; title = "Check Updates"; ObjectID = "wyZ-4G-qg9"; */ 126 | "wyZ-4G-qg9.title" = "Check Updates"; 127 | 128 | /* Class = "NSMenuItem"; title = "Ping server"; ObjectID = "zfR-Jt-GmS"; */ 129 | "zfR-Jt-GmS.title" = "Ping server"; 130 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/PreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Text"; ObjectID = "COZ-Qr-bmU"; */ 3 | "COZ-Qr-bmU.title" = "Text"; 4 | 5 | /* Class = "NSWindow"; title = "Server Preferences"; ObjectID = "F0z-JX-Cv5"; */ 6 | "F0z-JX-Cv5.title" = "Server Preferences"; 7 | 8 | /* Class = "NSBox"; title = "Box"; ObjectID = "Gqv-5O-Wwf"; */ 9 | "Gqv-5O-Wwf.title" = "Box"; 10 | 11 | /* Class = "NSTextFieldCell"; title = ":"; ObjectID = "Ibr-Gs-5Js"; */ 12 | "Ibr-Gs-5Js.title" = ":"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Remarks:"; ObjectID = "MiN-VE-FtC"; */ 15 | "MiN-VE-FtC.title" = "Remarks:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "SSR Settings (Left empty if not sure what it is)"; ObjectID = "Vtp-4J-er7"; */ 18 | "Vtp-4J-er7.title" = "SSR Settings (Left empty if not sure what it is)"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Obfs:"; ObjectID = "aQj-D7-XWZ"; */ 21 | "aQj-D7-XWZ.title" = "Obfs:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "Protocol Param:"; ObjectID = "dCk-qx-L1X"; */ 24 | "dCk-qx-L1X.title" = "Protocol Param:"; 25 | 26 | /* Class = "NSTextFieldCell"; title = "Obfs Param:"; ObjectID = "g7c-fW-7J4"; */ 27 | "g7c-fW-7J4.title" = "Obfs Param:"; 28 | 29 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "j03-9E-uHW"; */ 30 | "j03-9E-uHW.title" = "Cancel"; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Group:"; ObjectID = "j0N-8s-9Z3"; */ 33 | "j0N-8s-9Z3.title" = "Group:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "kil-xC-HPD"; */ 36 | "kil-xC-HPD.title" = "Password:"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Encryption:"; ObjectID = "mOh-nD-7EX"; */ 39 | "mOh-nD-7EX.title" = "Encryption:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Protocol:"; ObjectID = "nmk-Ue-wYb"; */ 42 | "nmk-Ue-wYb.title" = "Protocol:"; 43 | 44 | /* Class = "NSButtonCell"; title = "Copy URL to Clipboard"; ObjectID = "t3T-Kd-5La"; */ 45 | "t3T-Kd-5La.title" = "Copy URL to Clipboard"; 46 | 47 | /* Class = "NSTextFieldCell"; title = "Address:"; ObjectID = "yHs-XE-XEB"; */ 48 | "yHs-XE-XEB.title" = "Address:"; 49 | 50 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "zPE-oD-PwK"; */ 51 | "zPE-oD-PwK.title" = "OK"; 52 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/ProxyPreferencesController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSWindow"; title = "Advance Proxy Preferences"; ObjectID = "F0z-JX-Cv5"; */ 3 | "F0z-JX-Cv5.title" = "Advance Proxy Preferences"; 4 | 5 | /* Class = "NSButtonCell"; title = "Auto Configure"; ObjectID = "FLv-D9-CRw"; */ 6 | "FLv-D9-CRw.title" = "Auto Configure"; 7 | 8 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "lsQ-1C-OhG"; */ 9 | "lsQ-1C-OhG.title" = "Cancel"; 10 | 11 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "rWE-M6-TvV"; */ 12 | "rWE-M6-TvV.title" = "OK"; 13 | 14 | /* Class = "NSButtonCell"; title = "Check"; ObjectID = "ucF-02-bm8"; */ 15 | "ucF-02-bm8.title" = "Check"; 16 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/SWBQRCodeWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "Copy To Pasteboard"; ObjectID = "3q0-EO-Vug"; */ 3 | "3q0-EO-Vug.title" = "Copy To Pasteboard"; 4 | 5 | /* Class = "NSWindow"; title = "QR Code"; ObjectID = "QvC-M9-y7g"; */ 6 | "QvC-M9-y7g.title" = "QR Code"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "Title For Server"; ObjectID = "fHU-xF-zyc"; */ 9 | "fHU-xF-zyc.title" = "Title For Server"; 10 | 11 | /* Class = "NSMenuItem"; title = "Copy QRCode"; ObjectID = "m8q-W6-UjX"; */ 12 | "m8q-W6-UjX.title" = "Copy QRCode"; 13 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/SubscribePreferenceWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; placeholderString = "Optional"; ObjectID = "3kt-n3-uTF"; */ 3 | "3kt-n3-uTF.placeholderString" = "Optional"; 4 | 5 | /* Class = "NSTextFieldCell"; placeholderString = "Optional"; ObjectID = "ASh-mc-Uyt"; */ 6 | "ASh-mc-Uyt.placeholderString" = "Optional"; 7 | 8 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "CfK-8Y-YNP"; */ 9 | "CfK-8Y-YNP.title" = "OK"; 10 | 11 | /* Class = "NSWindow"; title = "Subscribe Preference"; ObjectID = "F0z-JX-Cv5"; */ 12 | "F0z-JX-Cv5.title" = "Subscribe Preference"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Token"; ObjectID = "IrJ-D8-tlX"; */ 15 | "IrJ-D8-tlX.title" = "Token"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Group"; ObjectID = "f2p-qb-uuE"; */ 18 | "f2p-qb-uuE.title" = "Group"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Text"; ObjectID = "ik7-wS-gok"; */ 21 | "ik7-wS-gok.title" = "Text"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "Count"; ObjectID = "wbG-HT-bdN"; */ 24 | "wbG-HT-bdN.title" = "Count"; 25 | 26 | /* Class = "NSTextFieldCell"; title = "URL"; ObjectID = "yBp-oB-gbL"; */ 27 | "yBp-oB-gbL.title" = "URL"; 28 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/en.lproj/UserRulesController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "0Sc-cm-Vyu"; */ 3 | "0Sc-cm-Vyu.title" = "OK"; 4 | 5 | /* Class = "NSWindow"; title = "User Rules"; ObjectID = "F0z-JX-Cv5"; */ 6 | "F0z-JX-Cv5.title" = "User Rules"; 7 | 8 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "QAR-9i-kmv"; */ 9 | "QAR-9i-kmv.title" = "Cancel"; 10 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/example-gui-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": 0, 3 | "random": false, 4 | "global": false, 5 | "enabled": true, 6 | "shareOverLan": false, 7 | "isDefault": false, 8 | "localPort": 1080, 9 | "pacUrl": null, 10 | "useOnlinePac": false, 11 | "reconnectTimes": 3, 12 | "randomAlgorithm": 0, 13 | "TTL": 0, 14 | "proxyEnable": false, 15 | "proxyType": 0, 16 | "proxyHost": null, 17 | "proxyPort": 0, 18 | "proxyAuthUser": null, 19 | "proxyAuthPass": null, 20 | "authUser": null, 21 | "authPass": null, 22 | "autoban": false, 23 | "configs": [{ 24 | "remarks": "example", 25 | "server": "abc.xyz", 26 | "server_port": 1234, 27 | "method": "rc4-md5", 28 | "obfs": "tls1.2_ticket_auth", 29 | "obfsparam": "baidu.com", 30 | "remarks_base64": "ZXhhbXBsZQ==", 31 | "password": "passwd", 32 | "tcp_over_udp": false, 33 | "udp_over_tcp": false, 34 | "protocol": "auth_sha1_v2", 35 | "obfs_udp": false, 36 | "enable": true 37 | },{ 38 | "remarks": "example2", 39 | "server": "xyz.xyz", 40 | "server_port": 1234, 41 | "method": "rc4-md5", 42 | "obfs": "tls1.2_ticket_auth", 43 | "obfsparam": "baidu.com", 44 | "remarks_base64": "ZXhhbXBsZTI=", 45 | "password": "passwd", 46 | "tcp_over_udp": false, 47 | "udp_over_tcp": false, 48 | "protocol": "auth_sha1_v2", 49 | "obfs_udp": false, 50 | "enable": true 51 | }] 52 | } -------------------------------------------------------------------------------- /ShadowsocksX-NG/install_helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install_helper.sh 4 | # shadowsocks 5 | # 6 | # Created by clowwindy on 14-3-15. 7 | 8 | cd `dirname "${BASH_SOURCE[0]}"` 9 | sudo mkdir -p "/Library/Application Support/ShadowsocksX-NG/" 10 | sudo cp proxy_conf_helper "/Library/Application Support/ShadowsocksX-NG/" 11 | sudo chown root:admin "/Library/Application Support/ShadowsocksX-NG/proxy_conf_helper" 12 | sudo chmod +s "/Library/Application Support/ShadowsocksX-NG/proxy_conf_helper" 13 | 14 | echo done 15 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/install_privoxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install_privoxy.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 王晨 on 16/10/7. 7 | # Copyright © 2016年 zhfish. All rights reserved. 8 | 9 | 10 | cd `dirname "${BASH_SOURCE[0]}"` 11 | privoxyVersion=3.0.32.static 12 | mkdir -p "$HOME/Library/Application Support/ShadowsocksX-NG/privoxy-$privoxyVersion" 13 | cp -f privoxy "$HOME/Library/Application Support/ShadowsocksX-NG/privoxy-$privoxyVersion/" 14 | rm -f "$HOME/Library/Application Support/ShadowsocksX-NG/privoxy" 15 | ln -s "$HOME/Library/Application Support/ShadowsocksX-NG/privoxy-$privoxyVersion/privoxy" "$HOME/Library/Application Support/ShadowsocksX-NG/privoxy" 16 | 17 | echo done 18 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/install_ss_local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install_ss_local.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 邱宇舟 on 16/6/6. 7 | # Copyright © 2016年 qiuyuzhou. All rights reserved. 8 | 9 | 10 | cd `dirname "${BASH_SOURCE[0]}"` 11 | ssLocalVersion=3.3.5_4.static 12 | mkdir -p "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion" 13 | cp -f ss-local "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion/" 14 | rm -f "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local" 15 | ln -s "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion/ss-local" "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local" 16 | 17 | cp -f libcrypto.1.0.0.dylib "$HOME/Library/Application Support/ShadowsocksX-NG/" 18 | 19 | echo done 20 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/libcrypto.1.0.0.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/libcrypto.1.0.0.dylib -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_dark_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_dark_mode.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_dark_mode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_dark_mode@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_disabled.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_disabled@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_disabled@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_disabled_dark_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_disabled_dark_mode.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/menu_icon_disabled_dark_mode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/menu_icon_disabled_dark_mode@2x.png -------------------------------------------------------------------------------- /ShadowsocksX-NG/privoxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/privoxy -------------------------------------------------------------------------------- /ShadowsocksX-NG/privoxy.config.example: -------------------------------------------------------------------------------- 1 | listen-address {http} 2 | toggle 1 3 | enable-remote-toggle 1 4 | enable-remote-http-toggle 1 5 | enable-edit-actions 0 6 | enforce-blocks 0 7 | buffer-limit 4096 8 | forwarded-connect-retries 0 9 | accept-intercepted-requests 0 10 | allow-cgi-request-crunching 0 11 | split-large-forms 0 12 | keep-alive-timeout 5 13 | socket-timeout 60 14 | 15 | forward-socks5 / {socks5} . 16 | forward 192.168.*.*/ . 17 | forward 10.*.*.*/ . 18 | forward 127.*.*.*/ . 19 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/reload_conf_privoxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # reload_privoxy.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 王晨 on 16/10/7. 7 | # Copyright © 2016年 zhfish. All rights reserved. 8 | 9 | #launchctl kill SIGHUP "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" 10 | 11 | launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" 12 | launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" 13 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/reload_conf_ss_local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # reload_conf_ss_local.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 邱宇舟 on 16/6/6. 7 | # Copyright © 2016年 qiuyuzhou. All rights reserved. 8 | 9 | #launchctl kill SIGHUP "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" 10 | 11 | launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" 12 | launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" 13 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/ss-local: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack80342/ShadowsocksX-NG/83cfcf8541063548c91f495a39f85edefe756dfd/ShadowsocksX-NG/ss-local -------------------------------------------------------------------------------- /ShadowsocksX-NG/start_privoxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # start_privoxy.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 王晨 on 16/10/7. 7 | # Copyright © 2016年 zhfish. All rights reserved. 8 | 9 | launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" 10 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/start_ss_local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # start_ss_local.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 邱宇舟 on 16/6/6. 7 | # Copyright © 2016年 qiuyuzhou. All rights reserved. 8 | 9 | launchctl load "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" 10 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/stop_privoxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # stop_privoxy.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 王晨 on 16/10/7. 7 | # Copyright © 2016年 zhfish. All rights reserved. 8 | 9 | launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.http.plist" 10 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/stop_ss_local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # stop_ss_local.sh 4 | # ShadowsocksX-NG 5 | # 6 | # Created by 邱宇舟 on 16/6/6. 7 | # Copyright © 2016年 qiuyuzhou. All rights reserved. 8 | 9 | 10 | 11 | launchctl unload "$HOME/Library/LaunchAgents/com.qiuyuzhou.shadowsocksX-NG.local.plist" 12 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/user-rule.txt: -------------------------------------------------------------------------------- 1 | ! Put user rules line by line in this file. 2 | ! See https://adblockplus.org/en/filter-cheatsheet 3 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/AdvPreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "启用 Udp Relay"; ObjectID = "1C3-4s-Apd"; */ 3 | "1C3-4s-Apd.title" = "启用 Udp Relay"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Don't change these preferences if you have no idea what are they."; ObjectID = "8wL-qv-5nD"; */ 6 | "8wL-qv-5nD.title" = "如果你不明白这些选项是什么,请不要修改"; 7 | 8 | /* Class = "NSWindow"; title = "高级设置"; ObjectID = "F0z-JX-Cv5"; */ 9 | "F0z-JX-Cv5.title" = "高级设置"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Seconds"; ObjectID = "Gd3-Fm-sWh"; */ 12 | "Gd3-Fm-sWh.title" = "秒"; 13 | 14 | /* Class = "NSBox"; title = "Box"; ObjectID = "ayu-Tb-kmf"; */ 15 | "ayu-Tb-kmf.title" = "Box"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "本地 Socks5 监听地址:"; ObjectID = "cwr-n0-zwn"; */ 18 | "cwr-n0-zwn.title" = "本地 Socks5 监听地址:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "GFW List URL:"; ObjectID = "dg0-gS-z5V"; */ 21 | "dg0-gS-z5V.title" = "GFW LIST URL:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "Timeout:"; ObjectID = "i4l-2S-gOQ"; */ 24 | "i4l-2S-gOQ.title" = "超时:"; 25 | 26 | /* Class = "NSTextFieldCell"; title = "本地 Socks5 监听端口:"; ObjectID = "qRs-ow-vVB"; */ 27 | "qRs-ow-vVB.title" = "本地 Socks5 监听端口:"; 28 | 29 | /* Class = "NSTextFieldCell"; title = "Local PAC Listen Address:"; ObjectID = "dvL-Ic-Wae"; */ 30 | "dvL-Ic-Wae.title" = "本地 PAC 监听地址:"; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Local PAC Listen Port:"; ObjectID = "PSg-XK-qhl"; */ 33 | "PSg-XK-qhl.title" = "本地 PAC 监听端口:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "White List URL:"; ObjectID = "aif-uV-Lxk"; */ 36 | "aif-uV-Lxk.title" = "ACL 白名单列表 URL:"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "White IP List URL:"; ObjectID = "qhu-d1-qgq"; */ 39 | "qhu-d1-qgq.title" = "ACL GFW List 列表 URL:"; 40 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/HTTPPreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "HTTP Proxy Listen Port:"; ObjectID = "2Sd-yn-gdY"; */ 3 | "2Sd-yn-gdY.title" = "HTTP 代理 监听端口:"; 4 | 5 | /* Class = "NSButtonCell"; title = "HTTP Proxy Enable"; ObjectID = "dqU-MG-Sum"; */ 6 | "dqU-MG-Sum.title" = "HTTP 代理 开启"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "HTTP Proxy Listen Address:"; ObjectID = "qd0-kc-ttB"; */ 9 | "qd0-kc-ttB.title" = "HTTP 代理 监听地址:"; 10 | 11 | /* Class = "NSWindow"; title = "HTTP Proxy Preferences"; ObjectID = "vS3-DL-Nq3"; */ 12 | "vS3-DL-Nq3.title" = "HTTP 代理 设置"; 13 | 14 | /* Class = "NSButtonCell"; title = "Follow Global Mode"; ObjectID = "ofk-Pc-c8f"; */ 15 | "ofk-Pc-c8f.title" = "跟随全局模式"; 16 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | /* 8 | * SHARED STRINGS 9 | */ 10 | 11 | /* 12 | * ./ShadowsocksX-NG/PACUtils.swift 13 | */ 14 | 15 | "PAC has been updated by latest GFW List." = "已经使用最新的 GFW List 更新PAC"; 16 | 17 | "Failed to download latest GFW List." = "下载 GFW List 失败"; 18 | 19 | "White List update succeed." = "白名单更新成功"; 20 | 21 | "Failed to download latest White List update succeed." = "下载白名单失败"; 22 | 23 | /* 24 | * ./ShadowsocksX-NG/BunchImportUtils.swift 25 | */ 26 | 27 | "Import Server Profile succeed!" = "导入服务器配置文件成功!"; 28 | 29 | "Successful import \(configsCount) items" = "成功导入 \(configsCount) 个纪录"; 30 | 31 | "Import Server Profile failed!" = "导入服务器配置文件失败!"; 32 | 33 | "Invalid config file!" = "配置文件无效!"; 34 | 35 | /* 36 | * ./ShadowsocksX-NG/AppDelegate.swift 37 | */ 38 | 39 | "Add Shadowsocks Server Profile" = "已添加新Shaodwsocks服务器配置"; 40 | 41 | "By scan QR Code" = "通过扫描二维码"; 42 | 43 | "By Handle SS URL" = "处理 SS URL"; 44 | 45 | " Encription Method: \(profile.method)" = " 加密方法: \(profile.method)"; 46 | 47 | "Current server profile is not valid." = "当前服务器配置无效"; 48 | 49 | "No current server profile." = "未设置当前服务器配置"; 50 | 51 | "Proxy - Auto By PAC" = "代理 - PAC自动"; 52 | 53 | "Proxy - Global" = "代理 - 全局"; 54 | 55 | "Proxy - Manual" = "代理 - 手动"; 56 | 57 | "Proxy - White List" = "代理 - 白名单"; 58 | 59 | "Proxy - White List Domain" = "代理 - 域名白名单"; 60 | 61 | "Proxy - White List IP" = "代理 - IP白名单"; 62 | 63 | "Shadowsocks: On" = "Shadowsocks: On"; 64 | 65 | "Turn Shadowsocks Off" = "关闭 Shadowsocks"; 66 | 67 | "Shadowsocks: Off" = "Shadowsocks: Off"; 68 | 69 | "Turn Shadowsocks On" = "打开 Shadowsocks"; 70 | 71 | 72 | "Check Updates" = "检查更新"; 73 | 74 | "Check Updates At Launch" = "打开时检查更新"; 75 | 76 | "Proxy Back China" = "代理大陆IP"; 77 | 78 | "ACL Mode" = "ACL模式"; 79 | 80 | "ACL Mode" = "ACL模式"; 81 | 82 | "ACL Auto" = "ACL自动"; 83 | 84 | /* 85 | * ./ShadowsocksX-NG/PreferencesWindowController.swift 86 | */ 87 | 88 | "New Server" = "新服务器"; 89 | 90 | /* 91 | * ./ShadowsocksX-NG/UserRulesController.swift 92 | */ 93 | 94 | "PAC has been updated by User Rules." = "PAC has been updated by User Rules."; 95 | 96 | "It's failed to update PAC by User Rules." = "It's failed to update PAC by User Rules."; 97 | 98 | "Servers" = "服务器"; 99 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Export All Server To Json..."; ObjectID = "6k0-gn-DQv"; */ 3 | "6k0-gn-DQv.title" = "导出全部服务器配置..."; 4 | 5 | /* Class = "NSMenuItem"; title = "Manual Mode"; ObjectID = "8PR-gs-c5N"; */ 6 | "8PR-gs-c5N.title" = "手动模式"; 7 | 8 | /* Class = "NSMenu"; title = "Servers"; ObjectID = "9Y1-db-3HK"; */ 9 | "9Y1-db-3HK.title" = "服务器"; 10 | 11 | /* Class = "NSMenuItem"; title = "Turn ShadowsocksX On"; ObjectID = "GSu-Tf-StS"; */ 12 | "GSu-Tf-StS.title" = "打开ShadowsocksX"; 13 | 14 | /* Class = "NSMenu"; title = "ShadowsocksX-NG"; ObjectID = "Hob-KD-bx9"; */ 15 | "Hob-KD-bx9.title" = "ShadowsocksX-NG"; 16 | 17 | /* Class = "NSMenuItem"; title = "Show Logs..."; ObjectID = "Jfy-sf-Fhl"; */ 18 | "Jfy-sf-Fhl.title" = "显示日志..."; 19 | 20 | /* Class = "NSMenuItem"; title = "White List Domain"; ObjectID = "L27-aH-Wwl"; */ 21 | "L27-aH-Wwl.title" = "域名白名单"; 22 | 23 | /* Class = "NSMenuItem"; title = "About"; ObjectID = "LgB-6g-Gba"; */ 24 | "LgB-6g-Gba.title" = "关于"; 25 | 26 | /* Class = "NSMenuItem"; title = "Server Preferences..."; ObjectID = "M5r-E7-44f"; */ 27 | "M5r-E7-44f.title" = "服务器设置..."; 28 | 29 | /* Class = "NSMenuItem"; title = "Global Mode"; ObjectID = "Mw3-Jm-eXA"; */ 30 | "Mw3-Jm-eXA.title" = "全局模式"; 31 | 32 | /* Class = "NSMenuItem"; title = "Scan QR Code From Screen..."; ObjectID = "Qe6-bF-paT"; */ 33 | "Qe6-bF-paT.title" = "扫描屏幕上的二维码..."; 34 | 35 | /* Class = "NSMenuItem"; title = "Show QR Code For Current Server..."; ObjectID = "R6A-96-Zcb"; */ 36 | "R6A-96-Zcb.title" = "显示当前服务器的二维码..."; 37 | 38 | /* Class = "NSMenuItem"; title = "White List Mode"; ObjectID = "RvZ-Zn-29U"; */ 39 | "RvZ-Zn-29U.title" = "白名单模式"; 40 | 41 | /* Class = "NSMenuItem"; title = "Import Bunch Json File..."; ObjectID = "T9g-gy-gvv"; */ 42 | "T9g-gy-gvv.title" = "导入服务器配置文件..."; 43 | 44 | /* Class = "NSMenuItem"; title = "Update PAC from GFW List"; ObjectID = "TFc-Ec-duM"; */ 45 | "TFc-Ec-duM.title" = "从 GFW List 更新 PAC"; 46 | 47 | /* Class = "NSMenuItem"; title = "Feedback"; ObjectID = "W7u-7g-Gv4"; */ 48 | "W7u-7g-Gv4.title" = "反馈"; 49 | 50 | /* Class = "NSMenuItem"; title = "White List IP"; ObjectID = "Xbu-Kq-BSR"; */ 51 | "Xbu-Kq-BSR.title" = "IP白名单"; 52 | 53 | /* Class = "NSMenu"; title = "Proxy"; ObjectID = "YZp-bf-L40"; */ 54 | "YZp-bf-L40.title" = "代理"; 55 | 56 | /* Class = "NSMenuItem"; title = "Advance Preference ..."; ObjectID = "bZ3-fy-34d"; */ 57 | "bZ3-fy-34d.title" = "高级设置..."; 58 | 59 | /* Class = "NSMenuItem"; title = "Proxy"; ObjectID = "diI-fB-Rss"; */ 60 | "diI-fB-Rss.title" = "代理设置"; 61 | 62 | /* Class = "NSMenuItem"; title = "Launch At Login"; ObjectID = "eUq-p7-ICK"; */ 63 | "eUq-p7-ICK.title" = "登录时自动启动"; 64 | 65 | /* Class = "NSMenuItem"; title = "Showsocks: On"; ObjectID = "fzk-mE-CEV"; */ 66 | "fzk-mE-CEV.title" = "Showsocks: On"; 67 | 68 | /* Class = "NSMenuItem"; title = "Update All Wihte List"; ObjectID = "gde-ZE-086"; */ 69 | "gde-ZE-086.title" = "更新全部白名单"; 70 | 71 | /* Class = "NSMenu"; title = "White List Mode"; ObjectID = "nAe-c2-Q79"; */ 72 | "nAe-c2-Q79.title" = "白名单模式"; 73 | 74 | /* Class = "NSMenuItem"; title = "Show Bunch Json Example File..."; ObjectID = "pdy-JE-50Q"; */ 75 | "pdy-JE-50Q.title" = "显示示例服务器配置文件..."; 76 | 77 | /* Class = "NSMenuItem"; title = "Auto Mode By PAC"; ObjectID = "r07-Gu-aEz"; */ 78 | "r07-Gu-aEz.title" = "PAC自动模式"; 79 | 80 | /* Class = "NSMenuItem"; title = "Edit User Rules For PAC..."; ObjectID = "rms-p0-CvB"; */ 81 | "rms-p0-CvB.title" = "编辑PAC用户自定规则..."; 82 | 83 | /* Class = "NSMenuItem"; title = "Advance Proxy Preference..."; ObjectID = "sbx-yz-3lO"; */ 84 | "sbx-yz-3lO.title" = "高级设置..."; 85 | 86 | /* Class = "NSMenuItem"; title = "Servers"; ObjectID = "u5M-hQ-VSc"; */ 87 | "u5M-hQ-VSc.title" = "服务器"; 88 | 89 | /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "vJS-JW-byz"; */ 90 | "vJS-JW-byz.title" = "退出"; 91 | 92 | /* Class = "NSMenuItem"; title = "Ping server"; ObjectID = "zfR-Jt-GmS"; */ 93 | "zfR-Jt-GmS.title" = "服务器测速"; 94 | 95 | ///* Class = "NSMenuItem"; title = "Show network speed"; ObjectID = "a3h-uQ-DuO"; */ 96 | //"a3h-uQ-DuO.title" = "显示网速"; 97 | 98 | /* Class = "NSMenuItem"; title = "Connect At Launch"; ObjectID = "aB3-cf-5j0"; */ 99 | "aB3-cf-5j0.title" = "打开时自动连接"; 100 | 101 | /* Class = "NSMenuItem"; title = "HTTP Proxy Preference ..."; ObjectID = "uEp-Gz-cu0"; */ 102 | "uEp-Gz-cu0.title" = "HTTP代理设置..."; 103 | 104 | "wyZ-4G-qg9.title" = "检查更新"; 105 | 106 | "3nJ-zQ-Agj.title" = "打开时检查更新"; 107 | 108 | "lam-el-r4I.title" = "代理大陆IP"; 109 | 110 | "tEB-59-fCx.title" = "ACL模式"; 111 | 112 | "Yub-b0-bni.title" = "ACL模式"; 113 | 114 | "sDG-YV-yuQ.title" = "ACL自动"; 115 | 116 | "NnE-Ad-1An.title" = "手动更新订阅"; 117 | 118 | "nQ4-Nv-tox.title" = "打开时自动更新订阅"; 119 | 120 | "QJ6-eg-mzv.title" = "编辑订阅"; 121 | 122 | "QJ6-eg-mzv.title" = "编辑订阅"; 123 | 124 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Text"; ObjectID = "COZ-Qr-bmU"; */ 3 | "COZ-Qr-bmU.title" = "Text"; 4 | 5 | /* Class = "NSWindow"; title = "Server Preferences"; ObjectID = "F0z-JX-Cv5"; */ 6 | "F0z-JX-Cv5.title" = "服务器设置"; 7 | 8 | /* Class = "NSBox"; title = "Box"; ObjectID = "Gqv-5O-Wwf"; */ 9 | "Gqv-5O-Wwf.title" = "Box"; 10 | 11 | /* Class = "NSTextFieldCell"; title = ":"; ObjectID = "Ibr-Gs-5Js"; */ 12 | "Ibr-Gs-5Js.title" = ":"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Remarks:"; ObjectID = "MiN-VE-FtC"; */ 15 | "MiN-VE-FtC.title" = "备注:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "SSR Settings (Left empty if not sure what it is)"; ObjectID = "Vtp-4J-er7"; */ 18 | "Vtp-4J-er7.title" = "SSR设置,不清楚请留空"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Obfs"; ObjectID = "aQj-D7-XWZ"; */ 21 | "aQj-D7-XWZ.title" = "混淆:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "Protocol Param"; ObjectID = "dCk-qx-L1X"; */ 24 | "dCk-qx-L1X.title" = "协议参数:"; 25 | 26 | /* Class = "NSTextFieldCell"; title = "Obfs Param"; ObjectID = "g7c-fW-7J4"; */ 27 | "g7c-fW-7J4.title" = "混淆参数:"; 28 | 29 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "j03-9E-uHW"; */ 30 | "j03-9E-uHW.title" = "取消"; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "kil-xC-HPD"; */ 33 | "kil-xC-HPD.title" = "密码:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "Encryption:"; ObjectID = "mOh-nD-7EX"; */ 36 | "mOh-nD-7EX.title" = "加密方法:"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Protocol:"; ObjectID = "nmk-Ue-wYb"; */ 39 | "nmk-Ue-wYb.title" = "协议"; 40 | 41 | /* Class = "NSButtonCell"; title = "Duplicate"; ObjectID = "s5r-hM-4Gh"; */ 42 | "s5r-hM-4Gh.title" = "复制"; 43 | 44 | /* Class = "NSButtonCell"; title = "Copy URL to Clipboard"; ObjectID = "t3T-Kd-5La"; */ 45 | "t3T-Kd-5La.title" = "复制服务器URL到剪贴板"; 46 | 47 | /* Class = "NSTextFieldCell"; title = "Address:"; ObjectID = "yHs-XE-XEB"; */ 48 | "yHs-XE-XEB.title" = "地址:"; 49 | 50 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "zPE-oD-PwK"; */ 51 | "zPE-oD-PwK.title" = "确定"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "obfs:"; ObjectID = "nXF-vK-BH8"; */ 54 | "nXF-vK-BH8.title" = "协议:"; 55 | 56 | /* Class = "NSTextFieldCell"; title = "protocol:"; ObjectID = "yJ4-7n-yhc"; */ 57 | "yJ4-7n-yhc.title" = "混淆:"; 58 | 59 | /* Class = "NSTextFieldCell"; title = "obfs-param:"; ObjectID = "aQv-HP-iKY"; */ 60 | "aQv-HP-iKY.title" = "混淆参数:"; 61 | /**/ 62 | "j0N-8s-9Z3.title" = "分组:"; 63 | 64 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/ProxyPreferencesController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSWindow"; title = "Advance Proxy Preferences"; ObjectID = "F0z-JX-Cv5"; */ 3 | "F0z-JX-Cv5.title" = "高级代理设置"; 4 | 5 | /* Class = "NSButtonCell"; title = "Auto Configure"; ObjectID = "FLv-D9-CRw"; */ 6 | "FLv-D9-CRw.title" = "自动设置"; 7 | 8 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "lsQ-1C-OhG"; */ 9 | "lsQ-1C-OhG.title" = "取消"; 10 | 11 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "rWE-M6-TvV"; */ 12 | "rWE-M6-TvV.title" = "确定"; 13 | 14 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/SWBQRCodeWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "Copy To Pasteboard"; ObjectID = "3q0-EO-Vug"; */ 3 | "3q0-EO-Vug.title" = "Copy To Pasteboard"; 4 | 5 | /* Class = "NSWindow"; title = "QR Code"; ObjectID = "QvC-M9-y7g"; */ 6 | "QvC-M9-y7g.title" = "QR Code"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "Title For Server"; ObjectID = "fHU-xF-zyc"; */ 9 | "fHU-xF-zyc.title" = "Title For Server"; 10 | 11 | /* Class = "NSMenuItem"; title = "Copy QRCode"; ObjectID = "m8q-W6-UjX"; */ 12 | "m8q-W6-UjX.title" = "Copy QRCode"; 13 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/SubscribePreferenceWindowController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "CfK-8Y-YNP"; */ 3 | "CfK-8Y-YNP.title" = "OK"; 4 | 5 | /* Class = "NSWindow"; title = "Window"; ObjectID = "F0z-JX-Cv5"; */ 6 | "F0z-JX-Cv5.title" = "订阅设置"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "Token"; ObjectID = "IrJ-D8-tlX"; */ 9 | "IrJ-D8-tlX.title" = "口令"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Group"; ObjectID = "f2p-qb-uuE"; */ 12 | "f2p-qb-uuE.title" = "组名"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Text"; ObjectID = "ik7-wS-gok"; */ 15 | "ik7-wS-gok.title" = "Text"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Subscribe Feed URL"; ObjectID = "rMX-5B-Fvs"; */ 18 | "rMX-5B-Fvs.title" = "订阅设置"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Count"; ObjectID = "wbG-HT-bdN"; */ 21 | "wbG-HT-bdN.title" = "最大数量"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "URL"; ObjectID = "yBp-oB-gbL"; */ 24 | "yBp-oB-gbL.title" = "订阅地址"; 25 | -------------------------------------------------------------------------------- /ShadowsocksX-NG/zh-Hans.lproj/UserRulesController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "OK"; ObjectID = "0Sc-cm-Vyu"; */ 3 | "0Sc-cm-Vyu.title" = "确定"; 4 | 5 | /* Class = "NSWindow"; title = "User Rules"; ObjectID = "F0z-JX-Cv5"; */ 6 | "F0z-JX-Cv5.title" = "用户规则"; 7 | 8 | /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "QAR-9i-kmv"; */ 9 | "QAR-9i-kmv.title" = "取消"; 10 | -------------------------------------------------------------------------------- /ShadowsocksX-NGTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ShadowsocksX-NGTests/ShadowsocksX_NGTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShadowsocksX_NGTests.swift 3 | // ShadowsocksX-NGTests 4 | // 5 | // Created by 邱宇舟 on 16/6/5. 6 | // Copyright © 2016年 qiuyuzhou. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Pods_ShadowsocksX_NG 11 | 12 | class ShadowsocksX_NGTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /proxy_conf_helper/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyConfHelper.swift 3 | // proxy_conf_helper 4 | // 5 | // Created by 钟增强 on 2021/5/9. 6 | // 7 | 8 | import Foundation 9 | import ArgumentParser 10 | import SystemConfiguration 11 | 12 | struct ProxyConfHelper: ParsableCommand { 13 | 14 | @Flag(name: .shortAndLong, help: "Print the version number.") 15 | var version = false 16 | 17 | @Option(name: .shortAndLong, help: "Proxy mode,may be:auto,global,off") 18 | var mode: String? 19 | 20 | @Option(name: [.long, .customShort("u")], help: "PAC file url for auto mode.") 21 | var pacUrl: String? 22 | 23 | @Option(name: .shortAndLong, help: "Listen port for global mode.") 24 | var port: Int? 25 | 26 | @Option(name: [.long, .customShort("r")], help: "Privoxy Port for global mode.") 27 | var privoxyPort: Int? 28 | 29 | @Option(name: .shortAndLong, parsing: .upToNextOption, help: "Manual specify the network profile need to set proxy.") 30 | var networkService: [String] = [] 31 | 32 | mutating func run() throws { 33 | if version { 34 | print("\(kProxyConfHelperVersion)") 35 | throw ExitCode.success 36 | } 37 | 38 | // validate 39 | if(mode == "auto" && pacUrl == nil) { 40 | throw ValidationError("Please specify a PAC file url for auto mode.") 41 | } else if(mode == "global") { 42 | if(port == nil) { 43 | throw ValidationError("Please specify a listening port for global mode.") 44 | } else if(port == 0) { 45 | throw ValidationError("Invalid port for global mode.") 46 | } 47 | } 48 | 49 | var authRef: AuthorizationRef? 50 | let authFlags: AuthorizationFlags = [.extendRights, .interactionAllowed, .preAuthorize] 51 | let authErr = AuthorizationCreate(nil, nil, authFlags, &authRef) 52 | if authErr != noErr { 53 | authRef = nil 54 | print("Error when create authorization") 55 | throw ExitCode.failure 56 | } else { 57 | if authRef == nil { 58 | print("No authorization has been granted to modify network configuration") 59 | throw ExitCode.failure 60 | } 61 | 62 | if let prefRef = SCPreferencesCreateWithAuthorization(nil, "Shadowsocks" as CFString, nil, authRef), 63 | let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices) { 64 | var proxies: [String: Any] = [:] 65 | proxies[kCFNetworkProxiesHTTPEnable as String] = NSNumber(value: 0) 66 | proxies[kCFNetworkProxiesHTTPSEnable as String] = NSNumber(value: 0) 67 | proxies[kCFNetworkProxiesProxyAutoConfigEnable as String] = NSNumber(value: 0) 68 | proxies[kCFNetworkProxiesSOCKSEnable as String] = NSNumber(value: 0) 69 | proxies[kCFNetworkProxiesExceptionsList as String] = [] 70 | 71 | // 遍历系统中的网络设备列表,设置 AirPort 和 Ethernet 的代理 72 | for key in sets.allKeys { 73 | guard let key = key as? String else { 74 | continue 75 | } 76 | let dict = sets.object(forKey: key) as? NSDictionary 77 | let hardware = dict?.value(forKeyPath: "Interface.Hardware") as? String 78 | 79 | var modify = false 80 | if networkService.count > 0 { 81 | if networkService.contains(key) { 82 | modify = true 83 | } 84 | } else if (hardware == "AirPort") || (hardware == "Wi-Fi") || (hardware == "Ethernet") { 85 | modify = true 86 | } 87 | 88 | if modify { 89 | let prefPath = "/\(kSCPrefNetworkServices)/\(key)/\(kSCEntNetProxies)" 90 | 91 | if mode == "auto" { 92 | proxies[kCFNetworkProxiesProxyAutoConfigURLString as String] = pacUrl 93 | proxies[kCFNetworkProxiesProxyAutoConfigEnable as String] = NSNumber(value: 1) 94 | SCPreferencesPathSetValue(prefRef, prefPath as CFString, proxies as CFDictionary) 95 | } else if mode == "global" { 96 | proxies[kCFNetworkProxiesSOCKSProxy as String] = "127.0.0.1" 97 | proxies[kCFNetworkProxiesSOCKSPort as String] = NSNumber(value: port!) 98 | proxies[kCFNetworkProxiesSOCKSEnable as String] = NSNumber(value: 1) 99 | proxies[kCFNetworkProxiesExceptionsList as String] = ["127.0.0.1", "localhost"] 100 | 101 | if privoxyPort != 0 { 102 | proxies[kCFNetworkProxiesHTTPProxy as String] = "127.0.0.1" 103 | proxies[kCFNetworkProxiesHTTPPort as String] = NSNumber(value: privoxyPort!) 104 | proxies[kCFNetworkProxiesHTTPEnable as String] = NSNumber(value: 1) 105 | 106 | proxies[kCFNetworkProxiesHTTPSProxy as String] = "127.0.0.1" 107 | proxies[kCFNetworkProxiesHTTPSPort as String] = NSNumber(value: privoxyPort!) 108 | proxies[kCFNetworkProxiesHTTPSEnable as String] = NSNumber(value: 1) 109 | } 110 | SCPreferencesPathSetValue(prefRef, prefPath as CFString, proxies as CFDictionary) 111 | } else if mode == "off" { 112 | if pacUrl != nil && port != nil { 113 | // 取原来的配置,判断是否为shadowsocksX-NG设置的 114 | if let oldProxies = SCPreferencesPathGetValue(prefRef, prefPath as CFString) as? [String: AnyObject] { 115 | 116 | let proxyEnable: Bool = oldProxies[kCFNetworkProxiesProxyAutoConfigURLString as String]?.contains(pacUrl as Any) ?? false && oldProxies[kCFNetworkProxiesProxyAutoConfigEnable as String] === NSNumber(value: 1) 117 | if(proxyEnable) { 118 | if(oldProxies[kCFNetworkProxiesSOCKSProxy as String] as! String == "127.0.0.1" && oldProxies[kCFNetworkProxiesSOCKSPort as String]?.isEqual(to: NSNumber(value: port!)) ?? false && oldProxies[kCFNetworkProxiesSOCKSEnable as String] === NSNumber(value: 1)) { 119 | SCPreferencesPathSetValue(prefRef, prefPath as CFString, proxies as CFDictionary) 120 | } 121 | } 122 | } 123 | } else { 124 | SCPreferencesPathSetValue(prefRef, prefPath as CFString, proxies as CFDictionary) 125 | } 126 | } 127 | } 128 | } 129 | 130 | SCPreferencesCommitChanges(prefRef); 131 | SCPreferencesApplyChanges(prefRef); 132 | SCPreferencesSynchronize(prefRef); 133 | 134 | AuthorizationFree(authRef!, AuthorizationFlags()); 135 | } 136 | } 137 | } 138 | } 139 | 140 | ProxyConfHelper.main() 141 | -------------------------------------------------------------------------------- /proxy_conf_helper/proxy_conf_helper.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-dyld-environment-variables 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------