Parse SS URL with much cleaner URL struct methods
This commit is contained in:
@ -11,6 +11,4 @@
|
|||||||
|
|
||||||
void ScanQRCodeOnScreen();
|
void ScanQRCodeOnScreen();
|
||||||
|
|
||||||
NSDictionary<NSString *, id>* ParseSSURL(NSURL* url);
|
|
||||||
|
|
||||||
#endif /* QRCodeUtils_h */
|
#endif /* QRCodeUtils_h */
|
||||||
|
@ -71,63 +71,3 @@ void ScanQRCodeOnScreen() {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析SS URL,如果成功则返回一个与ServerProfile类兼容的dict
|
|
||||||
NSDictionary<NSString *, id>* ParseSSURL(NSURL* url) {
|
|
||||||
if (!url.host) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *urlString = [url absoluteString];
|
|
||||||
int i = 0;
|
|
||||||
NSString *errorReason = nil;
|
|
||||||
while(i < 2) {
|
|
||||||
if (i == 1) {
|
|
||||||
NSString* host = url.host;
|
|
||||||
if ([host length]%4!=0) {
|
|
||||||
int n = 4 - [host length]%4;
|
|
||||||
if (1==n) {
|
|
||||||
host = [host stringByAppendingString:@"="];
|
|
||||||
} else if (2==n) {
|
|
||||||
host = [host stringByAppendingString:@"=="];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NSData *data = [[NSData alloc] initWithBase64EncodedString:host options:0];
|
|
||||||
NSString *decodedString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
urlString = decodedString;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
urlString = [urlString stringByReplacingOccurrencesOfString:@"ss://" withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, urlString.length)];
|
|
||||||
NSRange firstColonRange = [urlString rangeOfString:@":"];
|
|
||||||
NSRange lastColonRange = [urlString rangeOfString:@":" options:NSBackwardsSearch];
|
|
||||||
NSRange lastAtRange = [urlString rangeOfString:@"@" options:NSBackwardsSearch];
|
|
||||||
if (firstColonRange.length == 0) {
|
|
||||||
errorReason = @"colon not found";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (firstColonRange.location == lastColonRange.location) {
|
|
||||||
errorReason = @"only one colon";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (lastAtRange.length == 0) {
|
|
||||||
errorReason = @"at not found";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!((firstColonRange.location < lastAtRange.location) && (lastAtRange.location < lastColonRange.location))) {
|
|
||||||
errorReason = @"wrong position";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NSString *method = [urlString substringWithRange:NSMakeRange(0, firstColonRange.location)];
|
|
||||||
NSString *password = [urlString substringWithRange:NSMakeRange(firstColonRange.location + 1, lastAtRange.location - firstColonRange.location - 1)];
|
|
||||||
NSString *IP = [urlString substringWithRange:NSMakeRange(lastAtRange.location + 1, lastColonRange.location - lastAtRange.location - 1)];
|
|
||||||
NSString *port = [urlString substringWithRange:NSMakeRange(lastColonRange.location + 1, urlString.length - lastColonRange.location - 1)];
|
|
||||||
|
|
||||||
|
|
||||||
return @{@"ServerHost": IP,
|
|
||||||
@"ServerPort": @([port integerValue]),
|
|
||||||
@"Method": method,
|
|
||||||
@"Password": password,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
@ -8,14 +8,12 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
var localized: String {
|
var localized: String {
|
||||||
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
func sha1() -> String {
|
func sha1() -> String {
|
||||||
let data = self
|
let data = self
|
||||||
@ -25,3 +23,36 @@ extension Data {
|
|||||||
return hexBytes.joined(separator: "")
|
return hexBytes.joined(separator: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseSSURL(_ url: URL?) -> [String: Any?]? {
|
||||||
|
|
||||||
|
func padBase64(string: String) -> String {
|
||||||
|
var length = string.characters.count
|
||||||
|
length = 4 - length % 4 + length
|
||||||
|
return string.padding(toLength: length, withPad: "=", startingAt: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if url?.host == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var plainUrl: URL! = url
|
||||||
|
|
||||||
|
let data = Data(base64Encoded: padBase64(string: url!.host!),
|
||||||
|
options: Data.Base64DecodingOptions())
|
||||||
|
|
||||||
|
if data != nil {
|
||||||
|
let decoded = String(data: data!, encoding: String.Encoding.utf8)
|
||||||
|
plainUrl = URL(string: "ss://\(decoded!)")
|
||||||
|
|
||||||
|
if plainUrl == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["ServerHost": plainUrl.host,
|
||||||
|
"ServerPort": UInt16(plainUrl.port!),
|
||||||
|
"Method": plainUrl.user,
|
||||||
|
"Password": plainUrl.password,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
@testable import ShadowsocksX_NG
|
||||||
|
|
||||||
class UtilsTests: XCTestCase {
|
class UtilsTests: XCTestCase {
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user