Skip to content

Commit

Permalink
Parse ini rule
Browse files Browse the repository at this point in the history
  • Loading branch information
yichengchen committed Oct 7, 2018
1 parent 5d6b481 commit d760c49
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 56 deletions.
8 changes: 8 additions & 0 deletions ClashX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
4989F98E20D0AE990001E564 /* sampleConfig.ini in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.ini */; };
4997732520D251A60009B136 /* SWBApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4997732320D251A60009B136 /* SWBApplication.m */; };
499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; };
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B10869216A356D0064FFCE /* String+Extension.swift */; };
49B7F9A02157C1DC00484470 /* SpeedTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B7F99F2157C1DC00484470 /* SpeedTestViewController.swift */; };
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; };
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -141,6 +142,7 @@
4997732320D251A60009B136 /* SWBApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWBApplication.m; sourceTree = "<group>"; };
4997732420D251A60009B136 /* SWBApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWBApplication.h; sourceTree = "<group>"; };
499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = "<group>"; };
49B10869216A356D0064FFCE /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
49B7F99F2157C1DC00484470 /* SpeedTestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeedTestViewController.swift; sourceTree = "<group>"; };
49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
49BC061E2129A34D005A0FE7 /* appcast.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = appcast.xml; sourceTree = "<group>"; };
Expand Down Expand Up @@ -293,6 +295,7 @@
495A44D220D267D000888A0A /* LaunchAtLogin.swift */,
4966E9E5211824F300A391FB /* NSImage+extension.swift */,
496BDEDF21196F1E00C5207F /* Logger.swift */,
49B10869216A356D0064FFCE /* String+Extension.swift */,
);
path = Basic;
sourceTree = "<group>";
Expand Down Expand Up @@ -620,6 +623,7 @@
49722FE4211ED56C00650A41 /* parseINI.swift in Sources */,
4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */,
4952C3CE2116EA2E004A4FA8 /* ProxyServerModel.swift in Sources */,
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */,
4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -824,6 +828,7 @@
baseConfigurationReference = 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = MEWHFZ92DY;
Expand All @@ -845,6 +850,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "ClashX/ClashX-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
Expand All @@ -857,6 +863,7 @@
baseConfigurationReference = A1485BCE642059532D01B8BA /* Pods-ClashX.release.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = MEWHFZ92DY;
Expand All @@ -878,6 +885,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "ClashX/ClashX-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.2;
Expand Down
18 changes: 18 additions & 0 deletions ClashX/Basic/String+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// String+Extension.swift
// ClashX
//
// Created by 称一称 on 2018/10/7.
// Copyright © 2018年 west2online. All rights reserved.
//
import Foundation

extension String
{
func trimed() -> String {
let whitespaces = CharacterSet(charactersIn: " \n\r\t")
return self.trimmingCharacters(in: whitespaces)
}


}
168 changes: 137 additions & 31 deletions ClashX/General/ConfigFileFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation
import AppKit
import SwiftyJSON
import Yams

class ConfigFileFactory {
static let shared = ConfigFileFactory()
Expand Down Expand Up @@ -165,49 +166,153 @@ class ConfigFileFactory {
}

static func addProxyToConfig(proxy:ProxyServerModel) {
let targetStr = self.proxyConfigStr(proxy: proxy)
guard let ini = parseConfig(kConfigFilePath),
let currentProxys = ini["Proxy"],
let proxyGroup = ini["Proxy Group"]
else {
self.saveToClashConfigFile(str: self.configFile(proxies: [proxy]))
NotificationCenter.default.post(Notification(name: kShouldUpDateConfig))
return
// let targetStr = self.proxyConfigStr(proxy: proxy)
// guard let ini = parseConfig(kConfigFilePath),
// let currentProxys = ini["Proxy"],
// let proxyGroup = ini["Proxy Group"]
// else {
// self.saveToClashConfigFile(str: self.configFile(proxies: [proxy]))
// NotificationCenter.default.post(Notification(name: kShouldUpDateConfig))
// return
// }
//
// if currentProxys.keys.contains(proxy.remark) {
// NSUserNotificationCenter.default.postProxyRemarkDupNotice(name: proxy.remark)
// return
// }
//
// if self.shared.witness != nil {
// // not watch config file change now.
// self.shared.witness = nil
// defer {
// self.shared.watchConfigFile()
// }
// }
//
// let configData = NSData(contentsOfFile: kConfigFilePath)
// var configStr = String(data: configData! as Data, encoding: .utf8)!
// let spilts = configStr.components(separatedBy: "[Proxy Group]")
// configStr = spilts[0] + targetStr + "[Proxy Group]\n" + spilts[1]
//
// if let selectGroup = proxyGroup["Proxy"] {
// let newSelectGroup = "\(selectGroup),\(proxy.remark)"
// configStr = configStr.replacingOccurrences(of: selectGroup, with: newSelectGroup)
// }
//
// if let autoGroup = proxyGroup["ProxyAuto"] {
// let autoGroupProxys = autoGroup.components(separatedBy: ",").dropLast(2).joined(separator:",")
// let newAutoGroupProxys = "\(autoGroupProxys),\(proxy.remark)"
// configStr = configStr.replacingOccurrences(of: autoGroupProxys, with: newAutoGroupProxys)
// }
//
// self.saveToClashConfigFile(str: configStr)
// NotificationCenter.default.post(Notification(name: kShouldUpDateConfig))
}
}


extension ConfigFileFactory {
static func upgradeIni() {

func parseOptions(options:[String]) -> [String:String] {
var mapping = [String:String]()
for option in options {
let pairs = option.split(separator: "=",maxSplits: 2)
guard pairs.count == 2 else {continue}
mapping[String(pairs[0]).trimed()] = String(pairs[1]).trimed()
}
return mapping
}

if currentProxys.keys.contains(proxy.remark) {
NSUserNotificationCenter.default.postProxyRemarkDupNotice(name: proxy.remark)
guard let ini = parseConfig("\(NSHomeDirectory())/.config/clash/config.ini") else {
return
}
var newConfig = [String:Any]()

newConfig.merge(ini["General"]?.dict as [String : Any]? ?? [:]) { $1 }

if self.shared.witness != nil {
// not watch config file change now.
self.shared.witness = nil
defer {
self.shared.watchConfigFile()
var newProxies = [Any]()

for (proxy,elemsStr) in ini["Proxy"]?.dict ?? [:] {

let elems = elemsStr.split(separator: ",").map { (substring) -> String in
return String(substring).trimed()
}

if elems.count < 3 {continue}

let proxyName = proxy
let proxyType = elems[0]
let proxyAddr = elems[1]
guard let proxyPort = Int(elems[2]) else {continue}
var newProxy = ["name":proxyName,"server":proxyAddr,"port":proxyPort] as [String : Any]

switch proxyType {
case "ss":
if elems.count < 5 {continue}
let otherOptions = parseOptions(options:Array(elems[5..<elems.count]))
print(otherOptions)
newProxy["cipher"] = elems[3]
newProxy["password"] = elems[4]
newProxy.merge(otherOptions) { $1 }

case "vmss":
if elems.count < 6 {continue}
newProxy["uuid"] = elems[3]
guard let alertId = Int(elems[4]) else {continue}
newProxy["alterId"] = alertId
newProxy["cipher"] = elems[5]
let otherOptions = parseOptions(options: Array(elems[6..<elems.count]))
newProxy.merge(otherOptions) { $1 }
case "socks":
if elems.count < 3 {continue}

default:
continue

}
newProxies.append(newProxy)

}

let configData = NSData(contentsOfFile: kConfigFilePath)
var configStr = String(data: configData! as Data, encoding: .utf8)!
let spilts = configStr.components(separatedBy: "[Proxy Group]")
configStr = spilts[0] + targetStr + "[Proxy Group]\n" + spilts[1]

if let selectGroup = proxyGroup["Proxy"] {
let newSelectGroup = "\(selectGroup),\(proxy.remark)"
configStr = configStr.replacingOccurrences(of: selectGroup, with: newSelectGroup)
var newProxyGroup = [Any]()
for (group,groupStr) in ini["Proxy Group"]?.dict ?? [:] {
var elems = groupStr.split(separator: ",").map { (substring) -> String in
return String(substring).trimed()
}
if elems.count<2 {continue}
let groupType = String(elems[0])
var proxyGroup = ["name":group,"type":groupType] as [String:Any]

switch groupType {
case "select":
let proxyNames = Array(elems.dropFirst())
proxyGroup["proxies"] = proxyNames
case "url-test","fallback":
proxyGroup["proxies"] = Array(elems.dropLast(2))
proxyGroup["url"] = elems[elems.count-2]
guard let delay = Int(elems[elems.count-1]) else {continue}
proxyGroup["delay"] = delay
default:
continue
}

newProxyGroup.append(proxyGroup)
}

if let autoGroup = proxyGroup["ProxyAuto"] {
let autoGroupProxys = autoGroup.components(separatedBy: ",").dropLast(2).joined(separator:",")
let newAutoGroupProxys = "\(autoGroupProxys),\(proxy.remark)"
configStr = configStr.replacingOccurrences(of: autoGroupProxys, with: newAutoGroupProxys)
}

self.saveToClashConfigFile(str: configStr)
NotificationCenter.default.post(Notification(name: kShouldUpDateConfig))
newConfig["Proxy"] = newProxies
newConfig["Proxy Group"] = newProxyGroup
newConfig["Rule"] = (ini["Rule"]?.array ?? [])
// let order = ["General","Proxy","Proxy","Rule"]
let yaml = try! Yams.dump(object: newConfig,allowUnicode:true)
try? yaml.write(toFile: kConfigFolderPath+"clash.yml", atomically: true, encoding: .utf8)
print(yaml)
}

}


extension ConfigFileFactory {
static func showReplacingConfigFileAlert() -> Bool{
let alert = NSAlert()
alert.messageText = """
Expand All @@ -220,4 +325,5 @@ class ConfigFileFactory {
alert.addButton(withTitle: "Cancel")
return alert.runModal() == .alertFirstButtonReturn
}

}
28 changes: 14 additions & 14 deletions ClashX/General/ConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,21 @@ class ConfigManager {
}

func refreshApiPort(){
if let ini =
parseConfig("\(NSHomeDirectory())/.config/clash/config.ini"),
let controller = ini["General"]?["external-controller"]{
if controller.contains(":") {
if let port = controller.split(separator: ":").last {
apiPort = String(port)
return;
}
}
}
if (ConfigFileFactory.copySimpleConfigFile()) {
refreshApiPort()
} else {
// if let ini =
// parseConfig("\(NSHomeDirectory())/.config/clash/config.ini"),
// let controller = ini["General"]?["external-controller"]{
// if controller.contains(":") {
// if let port = controller.split(separator: ":").last {
// apiPort = String(port)
// return;
// }
// }
// }
// if (ConfigFileFactory.copySimpleConfigFile()) {
// refreshApiPort()
// } else {
apiPort = "7892"
}
// }
}

func setupNetworkNotifier() {
Expand Down
26 changes: 15 additions & 11 deletions ClashX/Vendor/INIParser/parseINI.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import Foundation

class SectionConfig {
var dict = [String: String]()
var array = [String]()
}

typealias SectionConfig = [String: String]
typealias Config = [String: SectionConfig]


func trim(_ s: String) -> String {
let whitespaces = CharacterSet(charactersIn: " \n\r\t")
return s.trimmingCharacters(in: whitespaces)
}


func stripComment(_ line: String) -> String {
Expand All @@ -33,8 +32,8 @@ func parseSectionHeader(_ line: String) -> String {
func parseLine(_ line: String) -> (String, String)? {
let parts = stripComment(line).split(separator: "=", maxSplits: 1)
if parts.count == 2 {
let k = trim(String(parts[0]))
let v = trim(String(parts[1]))
let k = String(parts[0]).trimed()
let v = String(parts[1]).trimed()
return (k, v)
}
return nil
Expand All @@ -46,13 +45,18 @@ func parseConfig(_ filename : String) -> Config? {
var config = Config()
var currentSectionName = "main"
for line in f.components(separatedBy: "\n") {
let line = trim(line)
let line = line.trimed()
if line.hasPrefix("[") && line.hasSuffix("]") {
currentSectionName = parseSectionHeader(line)
if (config[currentSectionName] == nil) {config[currentSectionName] = SectionConfig()}
} else if let (k, v) = parseLine(line) {
var section = config[currentSectionName] ?? [:]
section[k] = v
config[currentSectionName] = section
config[currentSectionName]?.dict[k] = v
} else if line.hasPrefix("//") || line.hasPrefix("#") || line.count < 1 {
continue
} else {
if (line.split(separator: ",").count > 2) {
config[currentSectionName]?.array.append(line)
}
}
}
return config
Expand Down

0 comments on commit d760c49

Please sign in to comment.