mirror of
https://github.com/yanue/V2rayU.git
synced 2026-01-07 05:50:44 +00:00
share qrcode
This commit is contained in:
parent
40cd322ad0
commit
b8d2a80eef
2
Podfile
2
Podfile
@ -12,6 +12,6 @@ target 'V2rayU' do
|
||||
pod 'SwiftyJSON'
|
||||
pod 'Preferences'
|
||||
pod 'Sparkle'
|
||||
pod 'EFQRCode'
|
||||
pod 'QRCoder'
|
||||
|
||||
end
|
||||
|
||||
14
Podfile.lock
14
Podfile.lock
@ -1,36 +1,32 @@
|
||||
PODS:
|
||||
- Alamofire (4.7.3)
|
||||
- EFQRCode (4.4.2):
|
||||
- swift_qrcodejs (~> 1.0.1)
|
||||
- Preferences (0.2.0)
|
||||
- QRCoder (1.0.0)
|
||||
- Sparkle (1.20.0)
|
||||
- swift_qrcodejs (1.0.1)
|
||||
- SwiftyJSON (4.2.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire
|
||||
- EFQRCode
|
||||
- Preferences
|
||||
- QRCoder
|
||||
- Sparkle
|
||||
- SwiftyJSON
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- Alamofire
|
||||
- EFQRCode
|
||||
- Preferences
|
||||
- QRCoder
|
||||
- Sparkle
|
||||
- swift_qrcodejs
|
||||
- SwiftyJSON
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568
|
||||
EFQRCode: e65a9eb6c7137ed6db4cfce37f0cee47938e44f0
|
||||
Preferences: ebc036a176a298cde835155c73251173768a2f8d
|
||||
QRCoder: 488f6869fb89ffcc3de3b4de2dabd200a6573f3c
|
||||
Sparkle: 48999e7ee032f05ca05e28451eadf4af8ede6b44
|
||||
swift_qrcodejs: c181fe5c849d30c699546a23762d7e3dd143ab37
|
||||
SwiftyJSON: c4bcba26dd9ec7a027fc8eade48e2c911f229e96
|
||||
|
||||
PODFILE CHECKSUM: fde745e82410567a545d4aa0c3f453959d468f5b
|
||||
PODFILE CHECKSUM: c8bfc44adeae40100cae3f20ce703eaa6666d947
|
||||
|
||||
COCOAPODS: 1.6.0.beta.1
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
6608D9E22182C9C100A0E0DD /* v2rayStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608D9E12182C9C100A0E0DD /* v2rayStream.swift */; };
|
||||
660C8CA22181CBAA00896361 /* V2rayUTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660C8CA12181CBAA00896361 /* V2rayUTests.swift */; };
|
||||
660D0E5A216E0158000C2922 /* V2rayServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660D0E59216E0158000C2922 /* V2rayServer.swift */; };
|
||||
660F9A7521A94C7600AEB4F7 /* QrcodeWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 660F9A7421A94C7600AEB4F7 /* QrcodeWindow.xib */; };
|
||||
6610ECB721742AFD008FC401 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 664EB378216C9A5F00B6AE0D /* MainMenu.xib */; };
|
||||
66406AF42183320000B56041 /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66406AF32183320000B56041 /* Sample.swift */; };
|
||||
664EB375216C9A5E00B6AE0D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB374216C9A5E00B6AE0D /* AppDelegate.swift */; };
|
||||
@ -30,6 +31,7 @@
|
||||
66FEAD432179A969009DECF9 /* PreferenceGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FEAD422179A969009DECF9 /* PreferenceGeneral.swift */; };
|
||||
66FEAD53217EE14C009DECF9 /* ConfigWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66FEAD4E217EE14C009DECF9 /* ConfigWindow.xib */; };
|
||||
66FEAD54217EE14C009DECF9 /* PreferenceGeneral.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66FEAD50217EE14C009DECF9 /* PreferenceGeneral.xib */; };
|
||||
6D6DF2CF13D9FD3C7C6492A4 /* QrcodeWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */; };
|
||||
6D6DF4711EE7CB7CA552CC89 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF04B14482D1536ADDA7D /* Test.swift */; };
|
||||
6D6DF6AEAA13C0DB0065BE3B /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF821D34F8B06A62F1936 /* Authorization.swift */; };
|
||||
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF87840CDA04AF0E9D36E /* Scanner.swift */; };
|
||||
@ -74,6 +76,7 @@
|
||||
660C8CA12181CBAA00896361 /* V2rayUTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2rayUTests.swift; sourceTree = "<group>"; };
|
||||
660C8CA32181CBAA00896361 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
660D0E59216E0158000C2922 /* V2rayServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = V2rayServer.swift; sourceTree = "<group>"; usesTabs = 0; wrapsLines = 0; };
|
||||
660F9A7421A94C7600AEB4F7 /* QrcodeWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QrcodeWindow.xib; sourceTree = "<group>"; };
|
||||
66406AF32183320000B56041 /* Sample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = "<group>"; };
|
||||
664B95DD217062A500DBC941 /* Alamofire */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Alamofire; path = Pods/Alamofire; sourceTree = "<group>"; };
|
||||
664EB371216C9A5E00B6AE0D /* V2rayU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2rayU.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -96,6 +99,7 @@
|
||||
66FEAD4F217EE14C009DECF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConfigWindow.xib; sourceTree = "<group>"; };
|
||||
66FEAD51217EE14C009DECF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreferenceGeneral.xib; sourceTree = "<group>"; };
|
||||
6D6DF04B14482D1536ADDA7D /* Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Test.swift; sourceTree = "<group>"; };
|
||||
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QrcodeWindow.swift; sourceTree = "<group>"; };
|
||||
6D6DF821D34F8B06A62F1936 /* Authorization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = "<group>"; };
|
||||
6D6DF87840CDA04AF0E9D36E /* Scanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scanner.swift; sourceTree = "<group>"; };
|
||||
6D6DFD9F5991B38B128888D6 /* V2rayConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = V2rayConfig.swift; sourceTree = "<group>"; };
|
||||
@ -208,6 +212,8 @@
|
||||
6D6DF821D34F8B06A62F1936 /* Authorization.swift */,
|
||||
6D6DF87840CDA04AF0E9D36E /* Scanner.swift */,
|
||||
664EB37B216C9A5F00B6AE0D /* Info.plist */,
|
||||
660F9A7421A94C7600AEB4F7 /* QrcodeWindow.xib */,
|
||||
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */,
|
||||
);
|
||||
path = V2rayU;
|
||||
sourceTree = "<group>";
|
||||
@ -373,6 +379,7 @@
|
||||
66FEAD54217EE14C009DECF9 /* PreferenceGeneral.xib in Resources */,
|
||||
664EB377216C9A5F00B6AE0D /* Assets.xcassets in Resources */,
|
||||
66FEAD53217EE14C009DECF9 /* ConfigWindow.xib in Resources */,
|
||||
660F9A7521A94C7600AEB4F7 /* QrcodeWindow.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -389,22 +396,20 @@
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/EFQRCode/EFQRCode.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Preferences/Preferences.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/QRCoder/QRCoder.framework",
|
||||
"${PODS_ROOT}/Sparkle/Sparkle.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/swift_qrcodejs/swift_qrcodejs.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EFQRCode.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Preferences.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QRCoder.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/swift_qrcodejs.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@ -473,6 +478,7 @@
|
||||
6D6DF6AEAA13C0DB0065BE3B /* Authorization.swift in Sources */,
|
||||
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */,
|
||||
6D6DF4711EE7CB7CA552CC89 /* Test.swift in Sources */,
|
||||
6D6DF2CF13D9FD3C7C6492A4 /* QrcodeWindow.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<key>Launcher.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
<integer>11</integer>
|
||||
</dict>
|
||||
<key>V2rayU.xcscheme</key>
|
||||
<dict>
|
||||
@ -17,17 +17,17 @@
|
||||
<key>V2rayUHelper.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>13</integer>
|
||||
<integer>17</integer>
|
||||
</dict>
|
||||
<key>V2rayULauncher.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>20</integer>
|
||||
<integer>21</integer>
|
||||
</dict>
|
||||
<key>V2rayULauncher.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>17</integer>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
<key>V2rayUTests.test.xcscheme</key>
|
||||
<dict>
|
||||
@ -39,7 +39,7 @@
|
||||
<key>isShown</key>
|
||||
<false />
|
||||
<key>orderHint</key>
|
||||
<integer>12</integer>
|
||||
<integer>13</integer>
|
||||
</dict>
|
||||
<key>V2rayUTests.testDecodeVmess.xcscheme</key>
|
||||
<dict>
|
||||
@ -60,17 +60,17 @@
|
||||
<key>isShown</key>
|
||||
<false />
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>V2rayuHelper.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>18</integer>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>proxyHelper.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>19</integer>
|
||||
<integer>20</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
@ -2,4 +2,22 @@
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "V2rayUTests/V2rayUTests.swift"
|
||||
timestampString = "564742299.928"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "23"
|
||||
endingLineNumber = "23"
|
||||
landmarkName = "testQrcode()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import WebKit
|
||||
import Alamofire
|
||||
|
||||
class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDelegate {
|
||||
@ -430,16 +429,20 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
|
||||
let errMsg = V2rayServer.save(idx: self.serversTableView.selectedRow, isValid: self.v2rayConfig.isValid, jsonData: text)
|
||||
self.errTip.stringValue = errMsg
|
||||
|
||||
self.refreshServerList(ok: errMsg.count > 0)
|
||||
}
|
||||
|
||||
func refreshServerList(ok: Bool = true) {
|
||||
// refresh menu
|
||||
menuController.showServers()
|
||||
// if server is current
|
||||
if let curName = UserDefaults.get(forKey: .v2rayCurrentServerName) {
|
||||
let v2rayItemList = V2rayServer.list()
|
||||
if curName == v2rayItemList[self.serversTableView.selectedRow].name {
|
||||
if errMsg != "" {
|
||||
menuController.stopV2rayCore()
|
||||
} else {
|
||||
if ok {
|
||||
menuController.startV2rayCore()
|
||||
} else {
|
||||
menuController.stopV2rayCore()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -481,6 +484,20 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
|
||||
self.importJson()
|
||||
}
|
||||
|
||||
func saveImport(importUri: ImportUri) {
|
||||
if importUri.isValid {
|
||||
self.configText.string = importUri.json
|
||||
if importUri.remark.count > 0 {
|
||||
V2rayServer.edit(rowIndex: self.serversTableView.selectedRow, remark: importUri.remark)
|
||||
}
|
||||
|
||||
// refresh
|
||||
self.refreshServerList(ok: true)
|
||||
} else {
|
||||
self.errTip.stringValue = importUri.error
|
||||
}
|
||||
}
|
||||
|
||||
func importJson() {
|
||||
let text = self.configText.string
|
||||
let uri = jsonUrl.stringValue.trimmingCharacters(in: .whitespaces)
|
||||
@ -489,19 +506,11 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
|
||||
if uri.contains("vmess://") {
|
||||
let importUri = ImportUri()
|
||||
importUri.importVmessUri(uri: uri)
|
||||
if importUri.isValid {
|
||||
self.configText.string = importUri.json
|
||||
} else {
|
||||
self.errTip.stringValue = importUri.error
|
||||
}
|
||||
self.saveImport(importUri: importUri)
|
||||
} else if uri.contains("ss://") {
|
||||
let importUri = ImportUri()
|
||||
importUri.importSSUri(uri: uri)
|
||||
if importUri.isValid {
|
||||
self.configText.string = importUri.json
|
||||
} else {
|
||||
self.errTip.stringValue = importUri.error
|
||||
}
|
||||
self.saveImport(importUri: importUri)
|
||||
} else {
|
||||
// download json file
|
||||
Alamofire.request(jsonUrl.stringValue).responseString { DataResponse in
|
||||
|
||||
@ -56,6 +56,7 @@ class MenuController: NSObject, NSMenuDelegate {
|
||||
statusItem.menu = statusMenu
|
||||
|
||||
configWindow = ConfigWindowController()
|
||||
|
||||
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
|
||||
// start
|
||||
// on status
|
||||
@ -291,13 +292,33 @@ class MenuController: NSObject, NSMenuDelegate {
|
||||
V2rayLaunch.setSystemProxy(enabled: true, httpPort: httpPort, sockPort: sockPort)
|
||||
}
|
||||
|
||||
@IBAction func generateQrcode(_ sender: NSMenuItem) {
|
||||
guard let v2ray = V2rayServer.loadSelectedItem() else {
|
||||
NSLog("v2ray config not found")
|
||||
self.notice(title: "generate Qrcode fail", subtitle: "", informativeText: "no available servers")
|
||||
return
|
||||
}
|
||||
|
||||
let share = ShareUri()
|
||||
share.qrcode(item: v2ray)
|
||||
if share.error.count > 0 {
|
||||
self.notice(title: "generate Qrcode fail", subtitle: "", informativeText: share.error)
|
||||
return
|
||||
}
|
||||
|
||||
let qrcodeWindow = QrcodeWindowController()
|
||||
qrcodeWindow.showWindow(nil)
|
||||
qrcodeWindow.setShareUri(uri: share.uri)
|
||||
// bring to front
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
@IBAction func scanQrcode(_ sender: NSMenuItem) {
|
||||
let uri: String = Scanner.scanQRCodeFromScreen()
|
||||
print("scanQrcode", uri)
|
||||
if uri.count > 0 {
|
||||
self.importUri(url: uri)
|
||||
} else {
|
||||
print("not found")
|
||||
self.notice(title: "import server fail", subtitle: "", informativeText: "no found qrcode")
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,44 +326,57 @@ class MenuController: NSObject, NSMenuDelegate {
|
||||
if let uri = NSPasteboard.general.string(forType: .string), uri.count > 0 {
|
||||
self.importUri(url: uri)
|
||||
} else {
|
||||
print("not found")
|
||||
self.notice(title: "import server fail", subtitle: "", informativeText: "no found ss:// or vmess:// from Pasteboard")
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func generateQrcode(_ sender: NSMenuItem) {
|
||||
print("generateQrcode")
|
||||
}
|
||||
|
||||
func importUri(url: String) {
|
||||
let uri = url.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
if uri.count == 0 {
|
||||
print("import error: uri not found")
|
||||
self.notice(title: "import server fail", subtitle: "", informativeText: "import error: uri not found")
|
||||
return
|
||||
}
|
||||
|
||||
if uri.contains("ss://") && URL(string: uri) != nil {
|
||||
let importUri = ImportUri()
|
||||
importUri.importSSUri(uri: uri)
|
||||
if importUri.isValid {
|
||||
V2rayServer.add(remark: importUri.remark, json: importUri.json, isValid: true, url: uri)
|
||||
// todo tip
|
||||
// todo refresh server list
|
||||
} else {
|
||||
print("import error", importUri.error)
|
||||
}
|
||||
self.saveServer(importUri: importUri)
|
||||
return
|
||||
}
|
||||
|
||||
if uri.contains("vmess://") && URL(string: uri) != nil {
|
||||
let importUri = ImportUri()
|
||||
importUri.importVmessUri(uri: uri)
|
||||
if importUri.isValid {
|
||||
V2rayServer.add(remark: importUri.remark, json: importUri.json, isValid: true, url: uri)
|
||||
// todo tip
|
||||
// todo refresh server list
|
||||
} else {
|
||||
print("import error", importUri.error)
|
||||
}
|
||||
self.saveServer(importUri: importUri)
|
||||
return
|
||||
}
|
||||
|
||||
self.notice(title: "import server fail", subtitle: "", informativeText: "no found ss:// or vmess://")
|
||||
}
|
||||
|
||||
func notice(title: String = "", subtitle: String = "", informativeText: String = "") {
|
||||
// 定义NSUserNotification
|
||||
let userNotification = NSUserNotification()
|
||||
userNotification.title = title
|
||||
userNotification.subtitle = subtitle
|
||||
userNotification.informativeText = informativeText
|
||||
// 使用NSUserNotificationCenter发送NSUserNotification
|
||||
let userNotificationCenter = NSUserNotificationCenter.default
|
||||
userNotificationCenter.scheduleNotification(userNotification)
|
||||
}
|
||||
|
||||
func saveServer(importUri: ImportUri) {
|
||||
if importUri.isValid {
|
||||
// add server
|
||||
V2rayServer.add(remark: importUri.remark, json: importUri.json, isValid: true, url: importUri.uri)
|
||||
// refresh server
|
||||
self.showServers()
|
||||
|
||||
self.notice(title: "import server success", subtitle: "", informativeText: importUri.remark)
|
||||
} else {
|
||||
self.notice(title: "import server fail", subtitle: "", informativeText: importUri.error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
29
V2rayU/QrcodeWindow.swift
Normal file
29
V2rayU/QrcodeWindow.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by yanue on 2018/11/24.
|
||||
// Copyright (c) 2018 yanue. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
import QRCoder
|
||||
|
||||
class QrcodeWindowController: NSWindowController {
|
||||
override var windowNibName: String? {
|
||||
return "QrcodeWindow" // no extension .xib here
|
||||
}
|
||||
@IBOutlet weak var shareUri: NSTextField!
|
||||
@IBOutlet weak var shareQrcode: NSImageView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
}
|
||||
|
||||
func setShareUri(uri: String) {
|
||||
self.shareUri.stringValue = uri
|
||||
let generator = QRCodeGenerator()
|
||||
shareQrcode.image = generator.createImage(value: uri, size: CGSize(width: 256, height: 256))
|
||||
}
|
||||
}
|
||||
45
V2rayU/QrcodeWindow.xib
Normal file
45
V2rayU/QrcodeWindow.xib
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="QrcodeWindowController" customModule="V2rayU" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="shareQrcode" destination="A7n-ua-0nz" id="osg-S9-FM3"/>
|
||||
<outlet property="shareUri" destination="u9M-IA-5nP" id="jNY-gA-XtE"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Share QRCode" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="415" height="363"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="415" height="363"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="u9M-IA-5nP">
|
||||
<rect key="frame" x="43" y="287" width="329" height="57"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" toolTip="share server uri" drawsBackground="YES" id="PMU-Ca-6qA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A7n-ua-0nz">
|
||||
<rect key="frame" x="75" y="18" width="256" height="256"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" imageFrameStyle="grayBezel" id="rrw-ad-nML"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="106.5" y="193.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
@ -8,13 +8,133 @@ import CoreGraphics
|
||||
import CoreImage
|
||||
import SwiftyJSON
|
||||
|
||||
struct VmessShare: Codable {
|
||||
var v: String = "2"
|
||||
var ps: String = ""
|
||||
var add: String = ""
|
||||
var port: String = ""
|
||||
var id: String = ""
|
||||
var aid: String = ""
|
||||
var net: String = ""
|
||||
var type: String = "none"
|
||||
var host: String = ""
|
||||
var path: String = ""
|
||||
var tls: String = "none"
|
||||
}
|
||||
|
||||
class ShareUri {
|
||||
var error = ""
|
||||
var remark = ""
|
||||
var uri: String = ""
|
||||
var v2ray = V2rayConfig()
|
||||
var share = VmessShare()
|
||||
|
||||
func qrcode(item: V2rayItem) {
|
||||
v2ray.parseJson(jsonText: item.json)
|
||||
if !v2ray.isValid {
|
||||
self.error = v2ray.errors[0]
|
||||
return
|
||||
}
|
||||
|
||||
self.remark = item.remark
|
||||
|
||||
if v2ray.serverProtocol == V2rayProtocolOutbound.vmess.rawValue {
|
||||
self.genVmessUri()
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
if let data = try? encoder.encode(self.share) {
|
||||
let uri = String(data: data, encoding: .utf8)!
|
||||
self.uri = "vmess://" + uri.base64Encoded()!
|
||||
print("vmess", self.uri)
|
||||
} else {
|
||||
self.error = "encode uri error"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if v2ray.serverProtocol == V2rayProtocolOutbound.shadowsocks.rawValue {
|
||||
print("share shadowsocks")
|
||||
self.genShadowsocksUri()
|
||||
return
|
||||
}
|
||||
|
||||
self.error = "not support"
|
||||
}
|
||||
|
||||
/**s
|
||||
分享的链接(二维码)格式:vmess://(Base64编码的json格式服务器数据
|
||||
json数据如下
|
||||
{
|
||||
"v": "2",
|
||||
"ps": "备注别名",
|
||||
"add": "111.111.111.111",
|
||||
"port": "32000",
|
||||
"id": "1386f85e-657b-4d6e-9d56-78badb75e1fd",
|
||||
"aid": "100",
|
||||
"net": "tcp",
|
||||
"type": "none",
|
||||
"host": "www.bbb.com",
|
||||
"path": "/",
|
||||
"tls": "tls"
|
||||
}
|
||||
v:配置文件版本号,主要用来识别当前配置
|
||||
net :传输协议(tcp\kcp\ws\h2)
|
||||
type:伪装类型(none\http\srtp\utp\wechat-video)
|
||||
host:伪装的域名
|
||||
1)http host中间逗号(,)隔开
|
||||
2)ws host
|
||||
3)h2 host
|
||||
path:path(ws/h2)
|
||||
tls:底层传输安全(tls)
|
||||
*/
|
||||
private func genVmessUri() {
|
||||
self.share.add = self.v2ray.serverVmess.address
|
||||
self.share.ps = self.remark
|
||||
self.share.port = String(self.v2ray.serverVmess.port)
|
||||
self.share.id = self.v2ray.serverVmess.users[0].id
|
||||
self.share.aid = String(self.v2ray.serverVmess.users[0].alterId)
|
||||
self.share.net = self.v2ray.streamNetwork
|
||||
|
||||
if self.v2ray.streamNetwork == "h2" {
|
||||
self.share.host = self.v2ray.streamH2.host[0]
|
||||
self.share.path = self.v2ray.streamH2.path
|
||||
}
|
||||
|
||||
if self.v2ray.streamNetwork == "ws" {
|
||||
self.share.host = self.v2ray.streamWs.headers.host
|
||||
self.share.path = self.v2ray.streamWs.path
|
||||
}
|
||||
|
||||
self.share.tls = self.v2ray.streamTlsSecurity
|
||||
}
|
||||
|
||||
// Shadowsocks
|
||||
func genShadowsocksUri() {
|
||||
var ss = ShadowsockUri()
|
||||
ss.host = self.v2ray.serverShadowsocks.address
|
||||
ss.port = self.v2ray.serverShadowsocks.port
|
||||
ss.password = self.v2ray.serverShadowsocks.password
|
||||
ss.method = self.v2ray.serverShadowsocks.method
|
||||
ss.remark = self.remark
|
||||
self.uri = ss.encode()
|
||||
self.error = ss.error
|
||||
}
|
||||
}
|
||||
|
||||
class ImportUri {
|
||||
var isValid: Bool = false
|
||||
var json: String = ""
|
||||
var remark: String = ""
|
||||
var error: String = ""
|
||||
var uri: String = ""
|
||||
|
||||
func importSSUri(uri: String) {
|
||||
if URL(string: uri) == nil {
|
||||
self.error = "invail ss url"
|
||||
return
|
||||
}
|
||||
self.uri = uri
|
||||
|
||||
let ss = ShadowsockUri()
|
||||
ss.Init(url: URL(string: uri)!)
|
||||
if ss.error.count > 0 {
|
||||
@ -44,6 +164,12 @@ class ImportUri {
|
||||
}
|
||||
|
||||
func importVmessUri(uri: String) {
|
||||
if URL(string: uri) == nil {
|
||||
self.error = "invail vmess url"
|
||||
return
|
||||
}
|
||||
self.uri = uri
|
||||
|
||||
var vmess = VmessUri()
|
||||
vmess.parseType2(url: URL(string: uri)!)
|
||||
if vmess.error.count > 0 {
|
||||
@ -290,6 +416,7 @@ class VmessUri {
|
||||
self.tls = json["tls"].stringValue
|
||||
// type:伪装类型(none\http\srtp\utp\wechat-video)
|
||||
self.type = json["type"].stringValue
|
||||
print("json", json)
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +431,17 @@ class ShadowsockUri {
|
||||
|
||||
var error: String = ""
|
||||
|
||||
// ss://bf-cfb:test@192.168.100.1:8888#remark
|
||||
func encode() -> String {
|
||||
let base64 = self.method + ":" + self.password + "@" + self.host + ":" + String(self.port)
|
||||
var ss = base64.base64Encoded()
|
||||
if ss != nil {
|
||||
return "ss://" + ss! + "#" + self.remark
|
||||
}
|
||||
self.error = "encode base64 fail"
|
||||
return ""
|
||||
}
|
||||
|
||||
func Init(url: URL) {
|
||||
let (_decodedUrl, _tag) = self.decodeUrl(url: url)
|
||||
guard let decodedUrl = _decodedUrl else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user