Merge pull request #434 from timothyqiu/qr-code

Fixes #433 Implements SIP002 QR code
This commit is contained in:
Qiu Yuzhou
2017-08-26 00:52:03 +08:00
committed by GitHub
6 changed files with 121 additions and 27 deletions

View File

@ -265,6 +265,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
}
qrcodeWinCtrl = SWBQRCodeWindowController(windowNibName: "SWBQRCodeWindowController")
qrcodeWinCtrl.qrCode = profile.URL()!.absoluteString
qrcodeWinCtrl.legacyQRCode = profile.URL(legacy: true)!.absoluteString
qrcodeWinCtrl.title = profile.title()
qrcodeWinCtrl.showWindow(self)
NSApp.activate(ignoringOtherApps: true)

View File

@ -11,6 +11,7 @@
@interface SWBQRCodeWindowController : NSWindowController
@property (nonatomic, copy) NSString *legacyQRCode;
@property (nonatomic, copy) NSString *qrCode;
@property (nonatomic, copy) NSString *title;

View File

@ -78,4 +78,13 @@
[pasteboard writeObjects:copiedObjects];
}
- (void)flagsChanged:(NSEvent *)event {
NSUInteger modifiers = event.modifierFlags & NSDeviceIndependentModifierFlagsMask;
if (modifiers & NSAlternateKeyMask) {
[self setQRCode:self.legacyQRCode];
} else {
[self setQRCode:self.qrCode];
}
}
@end

View File

@ -31,7 +31,7 @@ class ServerProfile: NSObject, NSCopying {
self.uuid = uuid
}
convenience init?(url: URL?) {
convenience init?(url: URL) {
self.init()
func padBase64(string: String) -> String {
@ -44,14 +44,12 @@ class ServerProfile: NSObject, NSCopying {
}
}
func decodeUrl(url: URL?) -> String? {
guard let urlStr = url?.absoluteString else {
return nil
}
func decodeUrl(url: URL) -> String? {
let urlStr = url.absoluteString
let index = urlStr.index(urlStr.startIndex, offsetBy: 5)
let encodedStr = urlStr.substring(from: index)
guard let data = Data(base64Encoded: padBase64(string: encodedStr)) else {
return url?.absoluteString
return url.absoluteString
}
guard let decoded = String(data: data, encoding: String.Encoding.utf8) else {
return nil
@ -67,17 +65,40 @@ class ServerProfile: NSObject, NSCopying {
return nil
}
guard let host = parsedUrl.host, let port = parsedUrl.port,
let method = parsedUrl.user, let password = parsedUrl.password else {
let user = parsedUrl.user else {
return nil
}
self.serverHost = host
self.serverPort = UInt16(port)
self.method = method.lowercased()
self.password = password
// This can be overriden by the fragment part of SIP002 URL
remark = parsedUrl.queryItems?
.filter({ $0.name == "Remark" }).first?.value ?? ""
if let password = parsedUrl.password {
self.method = user.lowercased()
self.password = password
} else {
// SIP002 URL have no password section
guard let data = Data(base64Encoded: padBase64(string: user)),
let userInfo = String(data: data, encoding: .utf8) else {
return nil
}
let parts = userInfo.characters.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
if parts.count != 2 {
return nil
}
self.method = String(parts[0]).lowercased()
self.password = String(parts[1])
// SIP002 defines where to put the profile name
if let profileName = parsedUrl.fragment {
self.remark = profileName
}
}
if let otaStr = parsedUrl.queryItems?
.filter({ $0.name == "OTA" }).first?.value {
ota = NSString(string: otaStr).boolValue
@ -225,7 +246,7 @@ class ServerProfile: NSObject, NSCopying {
return true
}
func URL() -> Foundation.URL? {
private func makeLegacyURL() -> URL? {
var url = URLComponents()
url.host = serverHost
@ -254,6 +275,39 @@ class ServerProfile: NSObject, NSCopying {
}
return nil
}
func URL(legacy: Bool = false) -> URL? {
// If you want the URL from <= 1.5.1
if (legacy) {
return self.makeLegacyURL()
}
guard let rawUserInfo = "\(method):\(password)".data(using: .utf8) else {
return nil
}
let paddings = CharacterSet(charactersIn: "=")
let userInfo = rawUserInfo.base64EncodedString().trimmingCharacters(in: paddings)
var items = [URLQueryItem(name: "OTA", value: ota.description)]
if enabledKcptun {
items.append(URLQueryItem(name: "Kcptun", value: enabledKcptun.description))
items.append(contentsOf: kcptunProfile.urlQueryItems())
}
var comps = URLComponents()
comps.scheme = "ss"
comps.host = serverHost
comps.port = Int(serverPort)
comps.user = userInfo
comps.path = "/" // This is required by SIP0002 for URLs with fragment or query
comps.fragment = remark
comps.queryItems = items
let url = try? comps.asURL()
return url
}
func title() -> String {
if remark.isEmpty {

View File

@ -56,7 +56,10 @@ void ScanQRCodeOnScreen() {
NSLog(@"%@", feature.messageString);
if ( [feature.messageString hasPrefix:@"ss://"] )
{
[foundSSUrls addObject:[NSURL URLWithString:feature.messageString]];
NSURL *url = [NSURL URLWithString:feature.messageString];
if (url) {
[foundSSUrls addObject:url];
}
}
}
CGImageRelease(image);