This commit is contained in:
yanue 2019-05-15 22:12:16 +08:00
parent dce29e9ec7
commit d54375fbc8
11 changed files with 247 additions and 206 deletions

View File

@ -30,11 +30,11 @@
66784B0021704E1300AD307F /* V2rayCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66784AFF21704E1300AD307F /* V2rayCore.swift */; };
66973EB721797719001FEA1E /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66973EB621797719001FEA1E /* ServiceManagement.framework */; };
66ACB1A021757D5B005B5881 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66ACB19F21757D5B005B5881 /* MainMenu.swift */; };
66BC2B87228C537F00FBB716 /* PreferencePac.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66BC2B86228C537F00FBB716 /* PreferencePac.xib */; };
66F411B121CA49EE007AAC10 /* pac in Copy Files */ = {isa = PBXBuildFile; fileRef = 6683B1EA21C2AD1A004A1C5F /* pac */; };
66F411B221CA49F4007AAC10 /* v2ray-core in Copy Files */ = {isa = PBXBuildFile; fileRef = 667029CF21AFB5730079EF41 /* v2ray-core */; };
66F411B521CA8921007AAC10 /* cmd.sh in Resources */ = {isa = PBXBuildFile; fileRef = 66F411B421CA8921007AAC10 /* cmd.sh */; };
66FEAD53217EE14C009DECF9 /* ConfigWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66FEAD4E217EE14C009DECF9 /* ConfigWindow.xib */; };
6D6DF06E32A1DC263EDF1A8E /* PacUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF62A40417837EA751D88 /* PacUtils.swift */; };
6D6DF16BC49BBF5F3D4B12CF /* PreferenceGeneral.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DFD8EB18FBEA30274ED0D /* PreferenceGeneral.xib */; };
6D6DF2CF13D9FD3C7C6492A4 /* QrcodeWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */; };
6D6DF2F2DFB2234541488BBD /* PreferenceSubscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF5345E4582EE0EBD468D /* PreferenceSubscript.swift */; };
@ -42,17 +42,14 @@
6D6DF5F67A917F78FF652D45 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DF8F5112A8407EE959998 /* .gitignore */; };
6D6DF6553CF221AEB71913EE /* PreferenceGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFFB29020127E034AD168 /* PreferenceGeneral.swift */; };
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF87840CDA04AF0E9D36E /* Scanner.swift */; };
6D6DF75A00BCDA0C2733A718 /* PreferencePac.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DFBC0F08C2F7D573B93B7 /* PreferencePac.xib */; };
6D6DF7C2F627A8BABECA4C32 /* tmp1.json in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DFCE2F5044A1164CB14E3 /* tmp1.json */; };
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF701C75D8D416096D717 /* PreferenceAdvance.swift */; };
6D6DF872C0DECC082122D783 /* tmp.json in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DF8E42F7E29CCBEFF5468 /* tmp.json */; };
6D6DF87497313615AFE3320A /* PreferencePac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */; };
6D6DF8BFC33F97E9AFCA5A4B /* V2rayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFD9F5991B38B128888D6 /* V2rayConfig.swift */; };
6D6DF93B58B654D86ACC490E /* PreferenceAbout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF870C8E334815FBA78B2 /* PreferenceAbout.swift */; };
6D6DFB41BC9692C90A47F31B /* UserRulesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF587B6AF8CEE029E48BC /* UserRulesWindow.swift */; };
6D6DFBB73E90918ED212F138 /* PreferenceAbout.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DFCD202C8F28474E9ED3C /* PreferenceAbout.xib */; };
6D6DFD181802BAFE2933B9C9 /* PreferenceSubscript.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DFBD3C6A7344A44DDE16D /* PreferenceSubscript.xib */; };
6D6DFD4A3A226788760150F8 /* UserRulesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DF010124783F31646A895 /* UserRulesWindow.xib */; };
F260898E336CFA5EAB07ADC9 /* Pods_V2rayU.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A5CE4521706B5A009B08B2 /* Pods_V2rayU.framework */; };
/* End PBXBuildFile section */
@ -146,6 +143,7 @@
66973EB621797719001FEA1E /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
66A5CE4521706B5A009B08B2 /* Pods_V2rayU.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_V2rayU.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66ACB19F21757D5B005B5881 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
66BC2B86228C537F00FBB716 /* PreferencePac.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencePac.xib; sourceTree = "<group>"; };
66F411B421CA8921007AAC10 /* cmd.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = cmd.sh; sourceTree = "<group>"; };
66FEAD45217D75FC009DECF9 /* release.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = release.sh; sourceTree = "<group>"; };
66FEAD4F217EE14C009DECF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConfigWindow.xib; sourceTree = "<group>"; };
@ -155,16 +153,12 @@
6D6DF3C1564D0EBC5A99C9C1 /* gfwlist.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = gfwlist.txt; sourceTree = "<group>"; };
6D6DF4CCA47DCA0A592BCE60 /* PreferenceAdvance.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferenceAdvance.xib; sourceTree = "<group>"; };
6D6DF5345E4582EE0EBD468D /* PreferenceSubscript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferenceSubscript.swift; sourceTree = "<group>"; };
6D6DF57E4CB6D7998F746068 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UserRulesWindow.xib; sourceTree = "<group>"; };
6D6DF587B6AF8CEE029E48BC /* UserRulesWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserRulesWindow.swift; sourceTree = "<group>"; };
6D6DF62A40417837EA751D88 /* PacUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacUtils.swift; sourceTree = "<group>"; };
6D6DF69C7CA7EBEFFE8E22D1 /* abp.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = abp.js; sourceTree = "<group>"; };
6D6DF701C75D8D416096D717 /* PreferenceAdvance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferenceAdvance.swift; sourceTree = "<group>"; };
6D6DF870C8E334815FBA78B2 /* PreferenceAbout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferenceAbout.swift; sourceTree = "<group>"; };
6D6DF87840CDA04AF0E9D36E /* Scanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scanner.swift; sourceTree = "<group>"; };
6D6DF8E42F7E29CCBEFF5468 /* tmp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tmp.json; sourceTree = "<group>"; };
6D6DF8F5112A8407EE959998 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = file.gitignore; path = .gitignore; sourceTree = "<group>"; };
6D6DFBC0F08C2F7D573B93B7 /* PreferencePac.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferencePac.xib; sourceTree = "<group>"; };
6D6DFBD3C6A7344A44DDE16D /* PreferenceSubscript.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferenceSubscript.xib; sourceTree = "<group>"; };
6D6DFCD202C8F28474E9ED3C /* PreferenceAbout.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferenceAbout.xib; sourceTree = "<group>"; };
6D6DFCE2F5044A1164CB14E3 /* tmp1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tmp1.json; sourceTree = "<group>"; };
@ -301,9 +295,6 @@
664EB37B216C9A5F00B6AE0D /* Info.plist */,
667029D121AFB86E0079EF41 /* QrcodeWindow.xib */,
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */,
6D6DF62A40417837EA751D88 /* PacUtils.swift */,
6D6DF587B6AF8CEE029E48BC /* UserRulesWindow.swift */,
6D6DF010124783F31646A895 /* UserRulesWindow.xib */,
6D6DFBED570A0D1FDED0BD53 /* Preference */,
);
path = V2rayU;
@ -325,7 +316,7 @@
6D6DF870C8E334815FBA78B2 /* PreferenceAbout.swift */,
6D6DFCD202C8F28474E9ED3C /* PreferenceAbout.xib */,
6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */,
6D6DFBC0F08C2F7D573B93B7 /* PreferencePac.xib */,
66BC2B86228C537F00FBB716 /* PreferencePac.xib */,
6D6DF5345E4582EE0EBD468D /* PreferenceSubscript.swift */,
6D6DFBD3C6A7344A44DDE16D /* PreferenceSubscript.xib */,
6D6DFFB29020127E034AD168 /* PreferenceGeneral.swift */,
@ -525,13 +516,12 @@
66F411B521CA8921007AAC10 /* cmd.sh in Resources */,
664EB377216C9A5F00B6AE0D /* Assets.xcassets in Resources */,
66FEAD53217EE14C009DECF9 /* ConfigWindow.xib in Resources */,
6D6DFD4A3A226788760150F8 /* UserRulesWindow.xib in Resources */,
6D6DF872C0DECC082122D783 /* tmp.json in Resources */,
6D6DF7C2F627A8BABECA4C32 /* tmp1.json in Resources */,
6D6DF5F67A917F78FF652D45 /* .gitignore in Resources */,
6D6DF75A00BCDA0C2733A718 /* PreferencePac.xib in Resources */,
6D6DF16BC49BBF5F3D4B12CF /* PreferenceGeneral.xib in Resources */,
6D6DFD181802BAFE2933B9C9 /* PreferenceSubscript.xib in Resources */,
66BC2B87228C537F00FBB716 /* PreferencePac.xib in Resources */,
6D6DFBB73E90918ED212F138 /* PreferenceAbout.xib in Resources */,
6D6DF43A6898B0A8CB50C241 /* PreferenceAdvance.xib in Resources */,
);
@ -638,8 +628,6 @@
6D6DF8BFC33F97E9AFCA5A4B /* V2rayConfig.swift in Sources */,
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */,
6D6DF2CF13D9FD3C7C6492A4 /* QrcodeWindow.swift in Sources */,
6D6DF06E32A1DC263EDF1A8E /* PacUtils.swift in Sources */,
6D6DFB41BC9692C90A47F31B /* UserRulesWindow.swift in Sources */,
6D6DF93B58B654D86ACC490E /* PreferenceAbout.swift in Sources */,
6D6DF87497313615AFE3320A /* PreferencePac.swift in Sources */,
6D6DF2F2DFB2234541488BBD /* PreferenceSubscript.swift in Sources */,
@ -688,14 +676,6 @@
name = ConfigWindow.xib;
sourceTree = "<group>";
};
6D6DF010124783F31646A895 /* UserRulesWindow.xib */ = {
isa = PBXVariantGroup;
children = (
6D6DF57E4CB6D7998F746068 /* Base */,
);
name = UserRulesWindow.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */

View File

@ -28,12 +28,11 @@ let preferencesWindowController = PreferencesWindowController(
PreferenceGeneralViewController(),
PreferenceAdvanceViewController(),
PreferenceSubscriptViewController(),
PreferencePacController(),
PreferenceAboutController(),
PreferencePacViewController(),
PreferenceAboutViewController(),
]
)
var qrcodeWindow = QrcodeWindowController()
var editUserRulesWinCtrl = UserRulesWindowController()
// menu controller
class MenuController: NSObject, NSMenuDelegate {

View File

@ -7,131 +7,3 @@
import Foundation
import Alamofire
let PACRulesDirPath = AppResourcesPath + "/pac/"
let PACUserRuleFilePath = PACRulesDirPath + "user-rule.txt"
let PACFilePath = PACRulesDirPath + "pac.js"
let PACUrl = "http://127.0.0.1:" + String(httpServerPort) + "/pac/pac.js"
let PACAbpFile = PACRulesDirPath + "abp.js"
let GFWListFilePath = PACRulesDirPath + "gfwlist.txt"
let GFWListURL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"
// Because of LocalSocks5.ListenPort may be changed
func GeneratePACFile() -> Bool {
print("GeneratePACFile")
let socks5Address = "127.0.0.1"
let v2ray = V2rayServer.loadSelectedItem()
var sockPort = "1080"
if v2ray != nil && v2ray!.isValid {
let cfg = V2rayConfig()
cfg.parseJson(jsonText: v2ray!.json)
sockPort = cfg.socksPort
}
// permission
_ = shell(launchPath: "/bin/bash", arguments: ["-c", "cd " + AppResourcesPath + " && /bin/chmod -R 755 ./pac"])
do {
let gfwlist = try String(contentsOfFile: GFWListFilePath, encoding: String.Encoding.utf8)
if let data = Data(base64Encoded: gfwlist, options: .ignoreUnknownCharacters) {
let str = String(data: data, encoding: String.Encoding.utf8)
var lines = str!.components(separatedBy: CharacterSet.newlines)
// read userRules from UserDefaults
let userRules = UserDefaults.get(forKey: .userRules)
if userRules != nil {
try userRules!.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACUserRuleFilePath), options: .atomic)
}
do {
let userRuleStr = try String(contentsOfFile: PACUserRuleFilePath, encoding: String.Encoding.utf8)
let userRuleLines = userRuleStr.components(separatedBy: CharacterSet.newlines)
lines = userRuleLines + lines
} catch {
NSLog("Not found user-rule.txt")
}
// Filter empty and comment lines
lines = lines.filter({ (s: String) -> Bool in
if s.isEmpty {
return false
}
let c = s[s.startIndex]
if c == "!" || c == "[" {
return false
}
return true
})
do {
// rule lines to json array
let rulesJsonData: Data = try JSONSerialization.data(withJSONObject: lines, options: .prettyPrinted)
let rulesJsonStr = String(data: rulesJsonData, encoding: String.Encoding.utf8)
// Get raw pac js
let jsData = try? Data(contentsOf: URL.init(fileURLWithPath: PACAbpFile))
var jsStr = String(data: jsData!, encoding: String.Encoding.utf8)
// Replace rules placeholder in pac js
jsStr = jsStr!.replacingOccurrences(of: "__RULES__", with: rulesJsonStr!)
// Replace __SOCKS5PORT__ palcholder in pac js
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5PORT__", with: "\(sockPort)")
// Replace __SOCKS5ADDR__ palcholder in pac js
var sin6 = sockaddr_in6()
if socks5Address.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 {
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5ADDR__", with: "[\(socks5Address)]")
} else {
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5ADDR__", with: socks5Address)
}
// Write the pac js to file.
try jsStr!.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACFilePath), options: .atomic)
return true
} catch {
}
}
} catch {
NSLog("Not found gfwlist.txt")
}
return false
}
func UpdatePACFromGFWList() {
// Make the dir if rulesDirPath is not exesited.
if !FileManager.default.fileExists(atPath: PACRulesDirPath) {
do {
try FileManager.default.createDirectory(atPath: PACRulesDirPath
, withIntermediateDirectories: true, attributes: nil)
} catch {
}
}
Alamofire.request(GFWListURL).responseString {
response in
if response.result.isSuccess {
if let v = response.result.value {
do {
try v.write(toFile: GFWListFilePath, atomically: true, encoding: String.Encoding.utf8)
if GeneratePACFile() {
// Popup a user notification
let notification = NSUserNotification()
notification.title = "PAC has been updated by latest GFW List."
NSUserNotificationCenter.default.deliver(notification)
}
} catch {
}
}
} else {
// Popup a user notification
let notification = NSUserNotification()
notification.title = "Failed to download latest GFW List."
NSUserNotificationCenter.default.deliver(notification)
}
}
}

View File

@ -11,7 +11,7 @@ import Preferences
import ServiceManagement
import Sparkle
final class PreferenceAboutController: NSViewController, PreferencePane {
final class PreferenceAboutViewController: NSViewController, PreferencePane {
let preferencePaneIdentifier = PreferencePane.Identifier.aboutTab
let preferencePaneTitle = "About"
let toolbarItemIcon = NSImage(named: NSImage.infoName)!

View File

@ -6,7 +6,7 @@
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceAboutController" customModule="V2rayU" customModuleProvider="target">
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceAboutViewController" customModule="V2rayU" customModuleProvider="target">
<connections>
<outlet property="V2rayCoreVersion" destination="gws-ge-QLa" id="0Gu-lx-VkO"/>
<outlet property="VersionLabel" destination="GfP-Xl-Rdf" id="tPf-dh-zDO"/>
@ -112,6 +112,5 @@
<point key="canvasLocation" x="-190" y="-220.5"/>
</view>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<userDefaultsController representsSharedInstance="YES" id="HAt-Ds-MVV"/>
</objects>
</document>

View File

@ -6,7 +6,7 @@
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceGeneralViewController" customModule="V2rayU" customModuleProvider="target">
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceAdvanceViewController" customModule="V2rayU" customModuleProvider="target">
<connections>
<outlet property="view" destination="bXz-rK-jao" id="xYY-dt-fo2"/>
</connections>

View File

@ -52,20 +52,26 @@
</customSpacing>
</stackView>
<button horizontalHuggingPriority="500" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cPC-Na-pRr">
<rect key="frame" x="14" y="143" width="165" height="32"/>
<rect key="frame" x="80" y="70" width="165" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Check for Updates..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="2p9-GL-XZ9">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="checkVersion:" target="-2" id="4u0-Zb-Xfz"/>
</connections>
</button>
<button horizontalHuggingPriority="500" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="liJ-Uu-8HQ">
<rect key="frame" x="14" y="110" width="111" height="32"/>
<rect key="frame" x="269" y="70" width="111" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Feedback..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QjX-NH-23u">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goFeedback:" target="-2" id="dvI-qJ-3yN"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-143" y="-199"/>

View File

@ -6,12 +6,19 @@
// Copyright © 2018 yanue. All rights reserved.
//
import Foundation
import Cocoa
import Preferences
import ServiceManagement
import Sparkle
import Alamofire
final class PreferencePacController: NSViewController, PreferencePane {
let PACRulesDirPath = AppResourcesPath + "/pac/"
let PACUserRuleFilePath = PACRulesDirPath + "user-rule.txt"
let PACFilePath = PACRulesDirPath + "pac.js"
let PACUrl = "http://127.0.0.1:" + String(httpServerPort) + "/pac/pac.js"
let PACAbpFile = PACRulesDirPath + "abp.js"
let GFWListFilePath = PACRulesDirPath + "gfwlist.txt"
let GFWListURL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"
final class PreferencePacViewController: NSViewController, PreferencePane {
let preferencePaneIdentifier = PreferencePane.Identifier.pacTab
let preferencePaneTitle = "Pac File"
let toolbarItemIcon = NSImage(named: NSImage.bookmarksTemplateName)!
@ -20,9 +27,181 @@ final class PreferencePacController: NSViewController, PreferencePane {
return "PreferencePac"
}
@IBOutlet weak var gfwPacListUrl: NSTextField!
@IBOutlet var userRulesView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// fix: https://github.com/sindresorhus/Preferences/issues/31
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height);
let gfwUrl = UserDefaults.get(forKey: .gfwPacListUrl)
if gfwUrl != nil {
gfwPacListUrl.stringValue = gfwUrl!
} else {
gfwPacListUrl.stringValue = GFWListURL
}
// read userRules from UserDefaults
let txt = UserDefaults.get(forKey: .userRules)
var userRuleTxt = """
! Put user rules line by line in this file.
! See https://adblockplus.org/en/filter-cheatsheet
"""
if txt != nil {
if txt!.count>0 {
userRuleTxt = txt!
}
} else {
let str = try? String(contentsOfFile: PACUserRuleFilePath, encoding: String.Encoding.utf8)
if str!.count>0 {
userRuleTxt = str!
}
}
userRulesView.string = userRuleTxt
}
@IBAction func updatePac(_ sender: Any) {
if let str = userRulesView?.string {
do {
// save user rules into UserDefaults
UserDefaults.set(forKey: .userRules, value: str)
print("updatePac str",str)
try str.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACUserRuleFilePath), options: .atomic)
UpdatePACFromGFWList()
if GeneratePACFile() {
// Popup a user notification
let notification = NSUserNotification()
notification.title = "PAC has been updated by User Rules."
NSUserNotificationCenter.default.deliver(notification)
} else {
let notification = NSUserNotification()
notification.title = "It's failed to update PAC by User Rules."
NSUserNotificationCenter.default.deliver(notification)
}
} catch {
}
}
}
}
// Because of LocalSocks5.ListenPort may be changed
func GeneratePACFile() -> Bool {
let socks5Address = "127.0.0.1"
let v2ray = V2rayServer.loadSelectedItem()
var sockPort = "1080"
if v2ray != nil && v2ray!.isValid {
let cfg = V2rayConfig()
cfg.parseJson(jsonText: v2ray!.json)
sockPort = cfg.socksPort
}
// permission
_ = shell(launchPath: "/bin/bash", arguments: ["-c", "cd " + AppResourcesPath + " && /bin/chmod -R 755 ./pac"])
do {
let gfwlist = try String(contentsOfFile: GFWListFilePath, encoding: String.Encoding.utf8)
if let data = Data(base64Encoded: gfwlist, options: .ignoreUnknownCharacters) {
let str = String(data: data, encoding: String.Encoding.utf8)
var lines = str!.components(separatedBy: CharacterSet.newlines)
// read userRules from UserDefaults
let userRules = UserDefaults.get(forKey: .userRules)
if userRules != nil {
try userRules!.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACUserRuleFilePath), options: .atomic)
}
do {
let userRuleStr = try String(contentsOfFile: PACUserRuleFilePath, encoding: String.Encoding.utf8)
let userRuleLines = userRuleStr.components(separatedBy: CharacterSet.newlines)
lines = userRuleLines + lines
} catch {
NSLog("Not found user-rule.txt")
}
// Filter empty and comment lines
lines = lines.filter({ (s: String) -> Bool in
if s.isEmpty {
return false
}
let c = s[s.startIndex]
if c == "!" || c == "[" {
return false
}
return true
})
do {
// rule lines to json array
let rulesJsonData: Data = try JSONSerialization.data(withJSONObject: lines, options: .prettyPrinted)
let rulesJsonStr = String(data: rulesJsonData, encoding: String.Encoding.utf8)
// Get raw pac js
let jsData = try? Data(contentsOf: URL.init(fileURLWithPath: PACAbpFile))
var jsStr = String(data: jsData!, encoding: String.Encoding.utf8)
// Replace rules placeholder in pac js
jsStr = jsStr!.replacingOccurrences(of: "__RULES__", with: rulesJsonStr!)
// Replace __SOCKS5PORT__ palcholder in pac js
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5PORT__", with: "\(sockPort)")
// Replace __SOCKS5ADDR__ palcholder in pac js
var sin6 = sockaddr_in6()
if socks5Address.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 {
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5ADDR__", with: "[\(socks5Address)]")
} else {
jsStr = jsStr!.replacingOccurrences(of: "__SOCKS5ADDR__", with: socks5Address)
}
// Write the pac js to file.
try jsStr!.data(using: String.Encoding.utf8)?.write(to: URL(fileURLWithPath: PACFilePath), options: .atomic)
return true
} catch {
}
}
} catch {
NSLog("Not found gfwlist.txt")
}
return false
}
func UpdatePACFromGFWList() {
// Make the dir if rulesDirPath is not exesited.
if !FileManager.default.fileExists(atPath: PACRulesDirPath) {
do {
try FileManager.default.createDirectory(atPath: PACRulesDirPath
, withIntermediateDirectories: true, attributes: nil)
} catch {
}
}
Alamofire.request(GFWListURL).responseString {
response in
if response.result.isSuccess {
if let v = response.result.value {
do {
try v.write(toFile: GFWListFilePath, atomically: true, encoding: String.Encoding.utf8)
if GeneratePACFile() {
// Popup a user notification
let notification = NSUserNotification()
notification.title = "PAC has been updated by latest GFW List."
NSUserNotificationCenter.default.deliver(notification)
}
} catch {
}
}
} else {
// Popup a user notification
let notification = NSUserNotification()
notification.title = "Failed to download latest GFW List."
NSUserNotificationCenter.default.deliver(notification)
}
}
}

76
V2rayU/Preference/PreferencePac.xib Executable file → Normal file
View File

@ -6,8 +6,10 @@
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceGeneralViewController" customModule="V2rayU" customModuleProvider="target">
<customObject id="-2" userLabel="File's Owner" customClass="PreferencePacViewController" customModule="V2rayU" customModuleProvider="target">
<connections>
<outlet property="gfwPacListUrl" destination="zr7-bd-EAE" id="Bb8-mK-cmI"/>
<outlet property="userRulesView" destination="TVW-6s-N8F" id="f19-R4-Xf5"/>
<outlet property="view" destination="bXz-rK-jao" id="xYY-dt-fo2"/>
</connections>
</customObject>
@ -17,48 +19,53 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JT2-vd-Ocf">
<rect key="frame" x="0.0" y="258" width="460" height="102"/>
<rect key="frame" x="0.0" y="238" width="460" height="122"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dD5-t7-MtS">
<rect key="frame" x="6" y="73" width="192" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="188" id="meM-bk-dCu"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="GFW List URL:" id="L8s-Y6-NP1">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dD5-t7-MtS">
<rect key="frame" x="18" y="79" width="192" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="GFW List URL" id="L8s-Y6-NP1">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" ambiguous="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zr7-bd-EAE">
<rect key="frame" x="8" y="20" width="384" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="XJO-g5-kGc"/>
</constraints>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zr7-bd-EAE">
<rect key="frame" x="20" y="20" width="420" height="45"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="left" drawsBackground="YES" id="RXK-Mf-gKW">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<connections>
<binding destination="-2" name="displayPatternValue1" keyPath="self.gfwPacListUrl" id="Mxg-5a-CDL">
<dictionary key="options">
<string key="NSDisplayPattern">%{value1}@</string>
</dictionary>
</binding>
</connections>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Cg-gM-BHl">
<rect key="frame" x="243" y="74" width="203" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Update Pac From GFW List" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="fkx-pu-mTv">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="updatePac:" target="-2" id="0Ka-zt-S7w"/>
</connections>
</button>
</subviews>
</customView>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8NJ-ru-j3U">
<rect key="frame" x="0.0" y="30" width="460" height="228"/>
<rect key="frame" x="0.0" y="0.0" width="460" height="238"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<rect key="frame" x="18" y="188" width="192" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="User Rules:" id="W0g-B7-neT">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView fixedFrame="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9KM-Hr-5go">
<rect key="frame" x="20" y="2" width="420" height="178"/>
<rect key="frame" x="20" y="20" width="420" height="178"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="KKI-Gt-Jbl">
<rect key="frame" x="1" y="1" width="418" height="176"/>
@ -84,20 +91,17 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<rect key="frame" x="18" y="206" width="192" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="User Rules" id="W0g-B7-neT">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</customView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ug9-RB-78F">
<rect key="frame" x="371" y="-3" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="save" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oBz-VP-htp">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
</button>
</subviews>
<point key="canvasLocation" x="-130" y="-187"/>
</view>

View File

@ -17,14 +17,14 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mta-JC-Ywa">
<rect key="frame" x="20" y="85" width="420" height="226"/>
<rect key="frame" x="20" y="38" width="420" height="273"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" id="4aC-gI-mNs">
<rect key="frame" x="1" y="0.0" width="418" height="225"/>
<rect key="frame" x="1" y="0.0" width="418" height="272"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="yUk-I2-XZb" viewBased="YES" id="fYa-dn-wCU">
<rect key="frame" x="0.0" y="0.0" width="418" height="200"/>
<rect key="frame" x="0.0" y="0.0" width="418" height="247"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -123,9 +123,9 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3pd-DD-jIz">
<rect key="frame" x="300" y="316" width="146" height="32"/>
<rect key="frame" x="308" y="316" width="131" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="update servers ..." bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ZPU-gS-0li">
<buttonCell key="cell" type="push" title="update servers" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ZPU-gS-0li">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>

View File

@ -29,6 +29,8 @@ extension UserDefaults {
case runMode
// use rules
case userRules
// gfw pac list url
case gfwPacListUrl
}
static func setBool(forKey key: KEY, value: Bool) {