├── .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 | [](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 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
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 |
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 |
60 |
61 |
62 |
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 |
--------------------------------------------------------------------------------