mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
OpenVPN3 pluggable transport cloak for ios (#227)
OpenVPN3 pluggable transport cloak for ios
This commit is contained in:
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -6,7 +6,7 @@
|
||||
url = https://github.com/WireGuard/wireguard-apple
|
||||
[submodule "client/3rd/OpenVPNAdapter"]
|
||||
path = client/3rd/OpenVPNAdapter
|
||||
url = https://github.com/ss-abramchuk/OpenVPNAdapter.git
|
||||
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
||||
[submodule "client/3rd/ShadowPath"]
|
||||
path = client/3rd/ShadowPath
|
||||
url = https://github.com/qman9501/ShadowPath
|
||||
@@ -28,9 +28,6 @@
|
||||
[submodule "client/3rd/zlib"]
|
||||
path = client/3rd/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
[submodule "deploy/amnezia-ios-certificates"]
|
||||
path = deploy/amnezia-ios-certificates
|
||||
url = https://github.com/amnezia-vpn/amnezia-ios-certificates.git
|
||||
[submodule "client/3rd/SortFilterProxyModel"]
|
||||
path = client/3rd/SortFilterProxyModel
|
||||
url = https://github.com/mitchcurtis/SortFilterProxyModel.git
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 3.0.7.1
|
||||
project(${PROJECT} VERSION 3.0.7.2
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
|
||||
Submodule client/3rd/OpenVPNAdapter updated: 0e2db0baa0...7b4046faf3
@@ -48,7 +48,8 @@ endif()
|
||||
qt_standard_project_setup()
|
||||
|
||||
if(IOS)
|
||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/scripts/openvpn.sh args
|
||||
#execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/scripts/run-build-cloak.sh)
|
||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
endif()
|
||||
|
||||
@@ -313,7 +314,6 @@ if(IOS)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/json.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigint.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigintipv6addr.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.h
|
||||
@@ -325,7 +325,6 @@ if(IOS)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/json.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddressrange.cpp
|
||||
@@ -362,7 +361,7 @@ if(IOS)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
set(BUILD_ID ${A_PROJECT_BUILD})
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
@@ -373,12 +372,12 @@ if(IOS)
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${CMAKE_PROJECT_VERSION}-${BUILD_ID}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/ios/app/main.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
|
||||
@@ -39,7 +39,7 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
|
||||
config.insert("UID", cloakBypassUid);
|
||||
config.insert("PublicKey", cloakPublicKey);
|
||||
config.insert("ServerName", "$FAKE_WEB_SITE_ADDRESS");
|
||||
config.insert("NumConn", 4);
|
||||
config.insert("NumConn", 1);
|
||||
config.insert("BrowserSig", "chrome");
|
||||
config.insert("StreamTimeout", 300);
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::OpenVpn: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
// case DockerContainer::ShadowSocks: return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ set_target_properties(networkextension PROPERTIES
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
|
||||
|
||||
14
client/ios/scripts/clangwrap.sh
Executable file
14
client/ios/scripts/clangwrap.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
# go/clangwrap.sh
|
||||
|
||||
SDK_PATH=`xcrun --sdk $SDK --show-sdk-path`
|
||||
CLANG=`xcrun --sdk $SDK --find clang`
|
||||
|
||||
if [ "$GOARCH" == "amd64" ]; then
|
||||
CARCH="x86_64"
|
||||
elif [ "$GOARCH" == "arm64" ]; then
|
||||
CARCH="arm64"
|
||||
fi
|
||||
|
||||
exec $CLANG -arch $CARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@"
|
||||
10
client/scripts/openvpn.sh → client/ios/scripts/openvpn.sh
Normal file → Executable file
10
client/scripts/openvpn.sh → client/ios/scripts/openvpn.sh
Normal file → Executable file
@@ -1,13 +1,12 @@
|
||||
|
||||
XCODEBUILD="/usr/bin/xcodebuild"
|
||||
WORKINGDIR=`pwd`
|
||||
PATCH="/usr/bin/patch"
|
||||
|
||||
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||
|
||||
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||
PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
|
||||
CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
||||
EOF
|
||||
|
||||
|
||||
@@ -18,4 +17,3 @@ EOF
|
||||
echo "OpenVPNAdapter build failed"
|
||||
fi
|
||||
cd ../../
|
||||
|
||||
@@ -236,7 +236,6 @@ public class IOSControllerImpl : NSObject {
|
||||
}
|
||||
|
||||
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check status")
|
||||
assert(tunnel != nil)
|
||||
|
||||
let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol
|
||||
|
||||
@@ -48,7 +48,7 @@ func wg_log(_ type: OSLogType, staticMessage msg: StaticString) {
|
||||
}
|
||||
|
||||
func wg_log(_ type: OSLogType, message msg: String) {
|
||||
os_log("%{public}s", log: OSLog.default, type: type, msg)
|
||||
os_log("%{AMNEZIA}s", log: OSLog.default, type: type, msg)
|
||||
Logger.global?.log(message: msg)
|
||||
NSLog("AMNEZIA: \(msg)")
|
||||
}
|
||||
|
||||
@@ -688,23 +688,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
*/
|
||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
|
||||
wg_log(.info, message: "Inside setupAndlaunchOpenVPN()")
|
||||
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
||||
wg_log(.info, message: "OPENVPN config: \(str)")
|
||||
|
||||
|
||||
let configuration = OpenVPNConfiguration()
|
||||
configuration.fileContent = ovpnConfiguration
|
||||
if viaSS {
|
||||
// configuration.settings = [
|
||||
// "remote": "137.74.6.148 1194",
|
||||
// "proto": "tcp",
|
||||
// "link-mtu": "1480",
|
||||
// "tun-mtu": "1460",
|
||||
// ]
|
||||
if(str.contains("cloak")){
|
||||
configuration.setPTCloak();
|
||||
}
|
||||
|
||||
let evaluation: OpenVPNConfigurationEvaluation
|
||||
do {
|
||||
evaluation = try ovpnAdapter.apply(configuration: configuration)
|
||||
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
return
|
||||
|
||||
@@ -41,8 +41,6 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
super.init()
|
||||
Logger.configureGlobal(tagged: "APP", withFilePath: "")
|
||||
|
||||
print("Config from caller: \(config)")
|
||||
|
||||
vpnBundleID = bundleID;
|
||||
precondition(!vpnBundleID.isEmpty)
|
||||
|
||||
@@ -296,7 +294,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
@objc func connect(ssConfig: String,
|
||||
ovpnConfig: String,
|
||||
failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
Logger.global?.log(message: "Logger Connecting")
|
||||
// assert(tunnel != nil)
|
||||
|
||||
self.openVPNConfig = ovpnConfig
|
||||
@@ -315,7 +313,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
}
|
||||
|
||||
@objc func connect(ovpnConfig: String, failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
Logger.global?.log(message: "Logger Connecting")
|
||||
// assert(tunnel != nil)
|
||||
|
||||
let addr: String = ovpnConfig
|
||||
@@ -331,7 +329,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
}
|
||||
|
||||
@objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, presharedKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array<VPNIPAddressRange>, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
Logger.global?.log(message: "Logger Connecting")
|
||||
// assert(tunnel != nil)
|
||||
|
||||
// Let's remove the previous config if it exists.
|
||||
@@ -525,11 +523,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
}
|
||||
|
||||
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check status")
|
||||
// assert(tunnel != nil)
|
||||
print("check status")
|
||||
let protoType = (tunnel!.localizedDescription ?? "").toTunnelType
|
||||
print(protoType);
|
||||
|
||||
switch protoType {
|
||||
case .wireguard:
|
||||
@@ -545,7 +539,6 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
}
|
||||
|
||||
private func checkShadowSocksStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check ShadowSocks")
|
||||
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||
callback("", "", "")
|
||||
return
|
||||
@@ -557,11 +550,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
print("server IP: \(serverIpv4Gateway)")
|
||||
|
||||
|
||||
let deviceIpv4Address = getWiFiAddress()
|
||||
print("device IP: \(serverIpv4Gateway)")
|
||||
if deviceIpv4Address == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
@@ -591,7 +581,6 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
}
|
||||
|
||||
private func checkOVPNStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check OpenVPN")
|
||||
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||
callback("", "", "")
|
||||
return
|
||||
@@ -607,13 +596,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||
.splitToArray(separator: "\n", trimmingCharacters: nil)
|
||||
.first { $0.starts(with: "remote ") }
|
||||
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
|
||||
|
||||
print("server IP: \(serverIpv4Gateway)")
|
||||
|
||||
|
||||
|
||||
|
||||
let deviceIpv4Address = getWiFiAddress()
|
||||
print("device IP: \(deviceIpv4Address)")
|
||||
if deviceIpv4Address == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
|
||||
@@ -1,732 +0,0 @@
|
||||
/**
|
||||
* QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa.
|
||||
* Copyright (C) 2011 Eeli Reilin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.cpp
|
||||
*/
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QStringList>
|
||||
#include "json.h"
|
||||
|
||||
namespace QtJson {
|
||||
static QString dateFormat, dateTimeFormat;
|
||||
static bool prettySerialize = false;
|
||||
|
||||
static QString sanitizeString(QString str);
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep);
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success);
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success);
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success);
|
||||
static QVariant parseString(const QString &json, int &index, bool &success);
|
||||
static QVariant parseNumber(const QString &json, int &index);
|
||||
static int lastIndexOfNumber(const QString &json, int index);
|
||||
static void eatWhitespace(const QString &json, int &index);
|
||||
static int lookAhead(const QString &json, int index);
|
||||
static int nextToken(const QString &json, int &index);
|
||||
|
||||
template<typename T>
|
||||
QByteArray serializeMap(const T &map, bool &success, int _level = 0) {
|
||||
QByteArray newline;
|
||||
QByteArray tabs;
|
||||
QByteArray tabsFields;
|
||||
if (prettySerialize && !map.isEmpty()) {
|
||||
newline = "\n";
|
||||
for (uint l=1; l<_level; l++) {
|
||||
tabs += " ";
|
||||
}
|
||||
tabsFields = tabs + " ";
|
||||
}
|
||||
|
||||
QByteArray str = "{" + newline;
|
||||
QList<QByteArray> pairs;
|
||||
for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) {
|
||||
bool otherSuccess = true;
|
||||
QByteArray serializedValue = serialize(it.value(), otherSuccess, _level);
|
||||
if (serializedValue.isNull()) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
pairs << tabsFields + sanitizeString(it.key()).toUtf8() + ":" + (prettySerialize ? " " : "") + serializedValue;
|
||||
}
|
||||
|
||||
str += join(pairs, "," + newline) + newline;
|
||||
str += tabs + "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
void insert(QVariant &v, const QString &key, const QVariant &value);
|
||||
void append(QVariant &v, const QVariant &value);
|
||||
|
||||
template<typename T>
|
||||
void cloneMap(QVariant &json, const T &map) {
|
||||
for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) {
|
||||
insert(json, it.key(), (*it));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void cloneList(QVariant &json, const T &list) {
|
||||
for (typename T::const_iterator it = list.begin(), itend = list.end(); it != itend; ++it) {
|
||||
append(json, (*it));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json) {
|
||||
bool success = true;
|
||||
return parse(json, success);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success) {
|
||||
success = true;
|
||||
|
||||
// Return an empty QVariant if the JSON data is either null or empty
|
||||
if (!json.isNull() || !json.isEmpty()) {
|
||||
QString data = json;
|
||||
// We'll start from index 0
|
||||
int index = 0;
|
||||
|
||||
// Parse the first value
|
||||
QVariant value = parseValue(data, index, success);
|
||||
|
||||
// Return the parsed value
|
||||
return value;
|
||||
} else {
|
||||
// Return the empty QVariant
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clone
|
||||
*/
|
||||
QVariant clone(const QVariant &data) {
|
||||
QVariant v;
|
||||
|
||||
if (data.type() == QVariant::Map) {
|
||||
cloneMap(v, data.toMap());
|
||||
} else if (data.type() == QVariant::Hash) {
|
||||
cloneMap(v, data.toHash());
|
||||
} else if (data.type() == QVariant::List) {
|
||||
cloneList(v, data.toList());
|
||||
} else if (data.type() == QVariant::StringList) {
|
||||
cloneList(v, data.toStringList());
|
||||
} else {
|
||||
v = QVariant(data);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert value (map case)
|
||||
*/
|
||||
void insert(QVariant &v, const QString &key, const QVariant &value) {
|
||||
if (!v.canConvert<QVariantMap>()) v = QVariantMap();
|
||||
QVariantMap *p = (QVariantMap *)v.data();
|
||||
p->insert(key, clone(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* append value (list case)
|
||||
*/
|
||||
void append(QVariant &v, const QVariant &value) {
|
||||
if (!v.canConvert<QVariantList>()) v = QVariantList();
|
||||
QVariantList *p = (QVariantList *)v.data();
|
||||
p->append(value);
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data) {
|
||||
bool success = true;
|
||||
return serialize(data, success);
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data, bool &success, int _level /*= 0*/) {
|
||||
QByteArray newline;
|
||||
QByteArray tabs;
|
||||
QByteArray tabsFields;
|
||||
if (prettySerialize) {
|
||||
newline = "\n";
|
||||
for (uint l=0; l<_level; l++) {
|
||||
tabs += " ";
|
||||
}
|
||||
tabsFields = tabs + " ";
|
||||
}
|
||||
|
||||
QByteArray str;
|
||||
success = true;
|
||||
|
||||
if (!data.isValid()) { // invalid or null?
|
||||
str = "null";
|
||||
} else if ((data.type() == QVariant::List) ||
|
||||
(data.type() == QVariant::StringList)) { // variant is a list?
|
||||
QList<QByteArray> values;
|
||||
const QVariantList list = data.toList();
|
||||
Q_FOREACH(const QVariant& v, list) {
|
||||
bool otherSuccess = true;
|
||||
QByteArray serializedValue = serialize(v, otherSuccess, _level+1);
|
||||
if (serializedValue.isNull()) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
values << tabsFields + serializedValue;
|
||||
}
|
||||
|
||||
if (!values.isEmpty()) {
|
||||
str = "[" + newline + join( values, "," + newline ) + newline + tabs + "]";
|
||||
} else {
|
||||
str = "[]";
|
||||
}
|
||||
} else if (data.type() == QVariant::Hash) { // variant is a hash?
|
||||
str = serializeMap<>(data.toHash(), success, _level+1);
|
||||
} else if (data.type() == QVariant::Map) { // variant is a map?
|
||||
str = serializeMap<>(data.toMap(), success, _level+1);
|
||||
} else if ((data.type() == QVariant::String) ||
|
||||
(data.type() == QVariant::ByteArray)) {// a string or a byte array?
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
} else if (data.type() == QVariant::Double) { // double?
|
||||
double value = data.toDouble(&success);
|
||||
if (success) {
|
||||
str = QByteArray::number(value, 'g');
|
||||
if (!str.contains(".") && ! str.contains("e")) {
|
||||
str += ".0";
|
||||
}
|
||||
}
|
||||
} else if (data.type() == QVariant::Bool) { // boolean value?
|
||||
str = data.toBool() ? "true" : "false";
|
||||
} else if (data.type() == QVariant::ULongLong) { // large unsigned number?
|
||||
str = QByteArray::number(data.value<qulonglong>());
|
||||
} else if (data.canConvert<qlonglong>()) { // any signed number?
|
||||
str = QByteArray::number(data.value<qlonglong>());
|
||||
} else if (data.canConvert<long>()) { //TODO: this code is never executed because all smaller types can be converted to qlonglong
|
||||
str = QString::number(data.value<long>()).toUtf8();
|
||||
} else if (data.type() == QVariant::DateTime) { // datetime value?
|
||||
str = sanitizeString(dateTimeFormat.isEmpty()
|
||||
? data.toDateTime().toString()
|
||||
: data.toDateTime().toString(dateTimeFormat)).toUtf8();
|
||||
} else if (data.type() == QVariant::Date) { // date value?
|
||||
str = sanitizeString(dateTimeFormat.isEmpty()
|
||||
? data.toDate().toString()
|
||||
: data.toDate().toString(dateFormat)).toUtf8();
|
||||
} else if (data.canConvert<QString>()) { // can value be converted to string?
|
||||
// this will catch QUrl, ... (all other types which can be converted to string)
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
return str;
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString serializeStr(const QVariant &data) {
|
||||
return QString::fromUtf8(serialize(data));
|
||||
}
|
||||
|
||||
QString serializeStr(const QVariant &data, bool &success) {
|
||||
return QString::fromUtf8(serialize(data, success));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \enum JsonToken
|
||||
*/
|
||||
enum JsonToken {
|
||||
JsonTokenNone = 0,
|
||||
JsonTokenCurlyOpen = 1,
|
||||
JsonTokenCurlyClose = 2,
|
||||
JsonTokenSquaredOpen = 3,
|
||||
JsonTokenSquaredClose = 4,
|
||||
JsonTokenColon = 5,
|
||||
JsonTokenComma = 6,
|
||||
JsonTokenString = 7,
|
||||
JsonTokenNumber = 8,
|
||||
JsonTokenTrue = 9,
|
||||
JsonTokenFalse = 10,
|
||||
JsonTokenNull = 11
|
||||
};
|
||||
|
||||
static QString sanitizeString(QString str) {
|
||||
str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
|
||||
str.replace(QLatin1String("\""), QLatin1String("\\\""));
|
||||
str.replace(QLatin1String("\b"), QLatin1String("\\b"));
|
||||
str.replace(QLatin1String("\f"), QLatin1String("\\f"));
|
||||
str.replace(QLatin1String("\n"), QLatin1String("\\n"));
|
||||
str.replace(QLatin1String("\r"), QLatin1String("\\r"));
|
||||
str.replace(QLatin1String("\t"), QLatin1String("\\t"));
|
||||
return QString(QLatin1String("\"%1\"")).arg(str);
|
||||
}
|
||||
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep) {
|
||||
QByteArray res;
|
||||
Q_FOREACH(const QByteArray &i, list) {
|
||||
if (!res.isEmpty()) {
|
||||
res += sep;
|
||||
}
|
||||
res += i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseValue
|
||||
*/
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success) {
|
||||
// Determine what kind of data we should parse by
|
||||
// checking out the upcoming token
|
||||
switch(lookAhead(json, index)) {
|
||||
case JsonTokenString:
|
||||
return parseString(json, index, success);
|
||||
case JsonTokenNumber:
|
||||
return parseNumber(json, index);
|
||||
case JsonTokenCurlyOpen:
|
||||
return parseObject(json, index, success);
|
||||
case JsonTokenSquaredOpen:
|
||||
return parseArray(json, index, success);
|
||||
case JsonTokenTrue:
|
||||
nextToken(json, index);
|
||||
return QVariant(true);
|
||||
case JsonTokenFalse:
|
||||
nextToken(json, index);
|
||||
return QVariant(false);
|
||||
case JsonTokenNull:
|
||||
nextToken(json, index);
|
||||
return QVariant();
|
||||
case JsonTokenNone:
|
||||
break;
|
||||
}
|
||||
|
||||
// If there were no tokens, flag the failure and return an empty QVariant
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* parseObject
|
||||
*/
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success) {
|
||||
QVariantMap map;
|
||||
int token;
|
||||
|
||||
// Get rid of the whitespace and increment index
|
||||
nextToken(json, index);
|
||||
|
||||
// Loop through all of the key/value pairs of the object
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
// Get the upcoming token
|
||||
token = lookAhead(json, index);
|
||||
|
||||
if (token == JsonTokenNone) {
|
||||
success = false;
|
||||
return QVariantMap();
|
||||
} else if (token == JsonTokenComma) {
|
||||
nextToken(json, index);
|
||||
} else if (token == JsonTokenCurlyClose) {
|
||||
nextToken(json, index);
|
||||
return map;
|
||||
} else {
|
||||
// Parse the key/value pair's name
|
||||
QString name = parseString(json, index, success).toString();
|
||||
|
||||
if (!success) {
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
// Get the next token
|
||||
token = nextToken(json, index);
|
||||
|
||||
// If the next token is not a colon, flag the failure
|
||||
// return an empty QVariant
|
||||
if (token != JsonTokenColon) {
|
||||
success = false;
|
||||
return QVariant(QVariantMap());
|
||||
}
|
||||
|
||||
// Parse the key/value pair's value
|
||||
QVariant value = parseValue(json, index, success);
|
||||
|
||||
if (!success) {
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
// Assign the value to the key in the map
|
||||
map[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the map successfully
|
||||
return QVariant(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseArray
|
||||
*/
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success) {
|
||||
QVariantList list;
|
||||
|
||||
nextToken(json, index);
|
||||
|
||||
bool done = false;
|
||||
while(!done) {
|
||||
int token = lookAhead(json, index);
|
||||
|
||||
if (token == JsonTokenNone) {
|
||||
success = false;
|
||||
return QVariantList();
|
||||
} else if (token == JsonTokenComma) {
|
||||
nextToken(json, index);
|
||||
} else if (token == JsonTokenSquaredClose) {
|
||||
nextToken(json, index);
|
||||
break;
|
||||
} else {
|
||||
QVariant value = parseValue(json, index, success);
|
||||
if (!success) {
|
||||
return QVariantList();
|
||||
}
|
||||
list.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseString
|
||||
*/
|
||||
static QVariant parseString(const QString &json, int &index, bool &success) {
|
||||
QString s;
|
||||
QChar c;
|
||||
|
||||
eatWhitespace(json, index);
|
||||
|
||||
c = json[index++];
|
||||
|
||||
bool complete = false;
|
||||
while(!complete) {
|
||||
if (index == json.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if (c == '\"') {
|
||||
complete = true;
|
||||
break;
|
||||
} else if (c == '\\') {
|
||||
if (index == json.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if (c == '\"') {
|
||||
s.append('\"');
|
||||
} else if (c == '\\') {
|
||||
s.append('\\');
|
||||
} else if (c == '/') {
|
||||
s.append('/');
|
||||
} else if (c == 'b') {
|
||||
s.append('\b');
|
||||
} else if (c == 'f') {
|
||||
s.append('\f');
|
||||
} else if (c == 'n') {
|
||||
s.append('\n');
|
||||
} else if (c == 'r') {
|
||||
s.append('\r');
|
||||
} else if (c == 't') {
|
||||
s.append('\t');
|
||||
} else if (c == 'u') {
|
||||
int remainingLength = json.size() - index;
|
||||
if (remainingLength >= 4) {
|
||||
QString unicodeStr = json.mid(index, 4);
|
||||
|
||||
int symbol = unicodeStr.toInt(0, 16);
|
||||
|
||||
s.append(QChar(symbol));
|
||||
|
||||
index += 4;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseNumber
|
||||
*/
|
||||
static QVariant parseNumber(const QString &json, int &index) {
|
||||
eatWhitespace(json, index);
|
||||
|
||||
int lastIndex = lastIndexOfNumber(json, index);
|
||||
int charLength = (lastIndex - index) + 1;
|
||||
QString numberStr;
|
||||
|
||||
numberStr = json.mid(index, charLength);
|
||||
|
||||
index = lastIndex + 1;
|
||||
bool ok;
|
||||
|
||||
if (numberStr.contains('.')) {
|
||||
return QVariant(numberStr.toDouble(NULL));
|
||||
} else if (numberStr.startsWith('-')) {
|
||||
int i = numberStr.toInt(&ok);
|
||||
if (!ok) {
|
||||
qlonglong ll = numberStr.toLongLong(&ok);
|
||||
return ok ? ll : QVariant(numberStr);
|
||||
}
|
||||
return i;
|
||||
} else {
|
||||
uint u = numberStr.toUInt(&ok);
|
||||
if (!ok) {
|
||||
qulonglong ull = numberStr.toULongLong(&ok);
|
||||
return ok ? ull : QVariant(numberStr);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lastIndexOfNumber
|
||||
*/
|
||||
static int lastIndexOfNumber(const QString &json, int index) {
|
||||
int lastIndex;
|
||||
|
||||
for(lastIndex = index; lastIndex < json.size(); lastIndex++) {
|
||||
if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastIndex -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* eatWhitespace
|
||||
*/
|
||||
static void eatWhitespace(const QString &json, int &index) {
|
||||
for(; index < json.size(); index++) {
|
||||
if (QString(" \t\n\r").indexOf(json[index]) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lookAhead
|
||||
*/
|
||||
static int lookAhead(const QString &json, int index) {
|
||||
int saveIndex = index;
|
||||
return nextToken(json, saveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* nextToken
|
||||
*/
|
||||
static int nextToken(const QString &json, int &index) {
|
||||
eatWhitespace(json, index);
|
||||
|
||||
if (index == json.size()) {
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
QChar c = json[index];
|
||||
index++;
|
||||
switch(c.toLatin1()) {
|
||||
case '{': return JsonTokenCurlyOpen;
|
||||
case '}': return JsonTokenCurlyClose;
|
||||
case '[': return JsonTokenSquaredOpen;
|
||||
case ']': return JsonTokenSquaredClose;
|
||||
case ',': return JsonTokenComma;
|
||||
case '"': return JsonTokenString;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '-': return JsonTokenNumber;
|
||||
case ':': return JsonTokenColon;
|
||||
}
|
||||
index--; // ^ WTF?
|
||||
|
||||
int remainingLength = json.size() - index;
|
||||
|
||||
// True
|
||||
if (remainingLength >= 4) {
|
||||
if (json[index] == 't' && json[index + 1] == 'r' &&
|
||||
json[index + 2] == 'u' && json[index + 3] == 'e') {
|
||||
index += 4;
|
||||
return JsonTokenTrue;
|
||||
}
|
||||
}
|
||||
|
||||
// False
|
||||
if (remainingLength >= 5) {
|
||||
if (json[index] == 'f' && json[index + 1] == 'a' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 's' &&
|
||||
json[index + 4] == 'e') {
|
||||
index += 5;
|
||||
return JsonTokenFalse;
|
||||
}
|
||||
}
|
||||
|
||||
// Null
|
||||
if (remainingLength >= 4) {
|
||||
if (json[index] == 'n' && json[index + 1] == 'u' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 'l') {
|
||||
index += 4;
|
||||
return JsonTokenNull;
|
||||
}
|
||||
}
|
||||
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
void setDateTimeFormat(const QString &format) {
|
||||
dateTimeFormat = format;
|
||||
}
|
||||
|
||||
void setDateFormat(const QString &format) {
|
||||
dateFormat = format;
|
||||
}
|
||||
|
||||
QString getDateTimeFormat() {
|
||||
return dateTimeFormat;
|
||||
}
|
||||
|
||||
QString getDateFormat() {
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
void setPrettySerialize(bool enabled) {
|
||||
prettySerialize = enabled;
|
||||
}
|
||||
|
||||
bool isPrettySerialize() {
|
||||
return prettySerialize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QQueue<BuilderJsonObject *> BuilderJsonObject::created_list;
|
||||
|
||||
BuilderJsonObject::BuilderJsonObject() {
|
||||
// clean objects previous "created"
|
||||
while (!BuilderJsonObject::created_list.isEmpty()) {
|
||||
delete BuilderJsonObject::created_list.dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
BuilderJsonObject::BuilderJsonObject(JsonObject &json) {
|
||||
BuilderJsonObject();
|
||||
|
||||
obj = json;
|
||||
}
|
||||
|
||||
BuilderJsonObject *BuilderJsonObject::set(const QString &key, const QVariant &value) {
|
||||
obj[key] = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonObject *builder) {
|
||||
return set(key, builder->create());
|
||||
}
|
||||
|
||||
BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonArray *builder) {
|
||||
return set(key, builder->create());
|
||||
}
|
||||
|
||||
JsonObject BuilderJsonObject::create() {
|
||||
BuilderJsonObject::created_list.enqueue(this);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
QQueue<BuilderJsonArray *> BuilderJsonArray::created_list;
|
||||
|
||||
BuilderJsonArray::BuilderJsonArray() {
|
||||
// clean objects previous "created"
|
||||
while (!BuilderJsonArray::created_list.isEmpty()) {
|
||||
delete BuilderJsonArray::created_list.dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
BuilderJsonArray::BuilderJsonArray(JsonArray &json) {
|
||||
BuilderJsonArray();
|
||||
|
||||
array = json;
|
||||
}
|
||||
|
||||
BuilderJsonArray *BuilderJsonArray::add(const QVariant &element) {
|
||||
array.append(element);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
BuilderJsonArray *BuilderJsonArray::add(BuilderJsonObject *builder) {
|
||||
return add(builder->create());
|
||||
}
|
||||
|
||||
BuilderJsonArray *BuilderJsonArray::add(BuilderJsonArray *builder) {
|
||||
return add(builder->create());
|
||||
}
|
||||
|
||||
JsonArray BuilderJsonArray::create() {
|
||||
BuilderJsonArray::created_list.enqueue(this);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BuilderJsonObject *objectBuilder() {
|
||||
return new BuilderJsonObject();
|
||||
}
|
||||
|
||||
BuilderJsonObject *objectBuilder(JsonObject &json) {
|
||||
return new BuilderJsonObject(json);
|
||||
}
|
||||
|
||||
BuilderJsonArray *arrayBuilder() {
|
||||
return new BuilderJsonArray();
|
||||
}
|
||||
|
||||
BuilderJsonArray *arrayBuilder(JsonArray &json) {
|
||||
return new BuilderJsonArray(json);
|
||||
}
|
||||
|
||||
} //end namespace
|
||||
@@ -1,265 +0,0 @@
|
||||
/**
|
||||
* QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa.
|
||||
* Copyright (C) 2011 Eeli Reilin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.h
|
||||
*/
|
||||
|
||||
#ifndef JSON_H
|
||||
#define JSON_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QQueue>
|
||||
|
||||
|
||||
/**
|
||||
* \namespace QtJson
|
||||
* \brief A JSON data parser
|
||||
*
|
||||
* Json parses a JSON data into a QVariant hierarchy.
|
||||
*/
|
||||
namespace QtJson {
|
||||
typedef QVariantMap JsonObject;
|
||||
typedef QVariantList JsonArray;
|
||||
|
||||
/**
|
||||
* Clone a JSON object (makes a deep copy)
|
||||
*
|
||||
* \param data The JSON object
|
||||
*/
|
||||
QVariant clone(const QVariant &data);
|
||||
|
||||
/**
|
||||
* Insert value to JSON object (QVariantMap)
|
||||
*
|
||||
* \param v The JSON object
|
||||
* \param key The key
|
||||
* \param value The value
|
||||
*/
|
||||
void insert(QVariant &v, const QString &key, const QVariant &value);
|
||||
|
||||
/**
|
||||
* Append value to JSON array (QVariantList)
|
||||
*
|
||||
* \param v The JSON array
|
||||
* \param value The value
|
||||
*/
|
||||
void append(QVariant &v, const QVariant &value);
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
*/
|
||||
QVariant parse(const QString &json);
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
* \param success The success of the parsing
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
*
|
||||
* \return QByteArray Textual JSON representation in UTF-8
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
* \param success The success of the serialization
|
||||
*
|
||||
* \return QByteArray Textual JSON representation in UTF-8
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data, bool &success, int _level = 0);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
*
|
||||
* \return QString Textual JSON representation
|
||||
*/
|
||||
QString serializeStr(const QVariant &data);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
* \param success The success of the serialization
|
||||
*
|
||||
* \return QString Textual JSON representation
|
||||
*/
|
||||
QString serializeStr(const QVariant &data, bool &success, int _level = 0);
|
||||
|
||||
/**
|
||||
* This method sets date(time) format to be used for QDateTime::toString
|
||||
* If QString is empty, Qt::TextDate is used.
|
||||
*
|
||||
* \param format The JSON data generated by the parser.
|
||||
*/
|
||||
void setDateTimeFormat(const QString& format);
|
||||
void setDateFormat(const QString& format);
|
||||
|
||||
/**
|
||||
* This method gets date(time) format to be used for QDateTime::toString
|
||||
* If QString is empty, Qt::TextDate is used.
|
||||
*/
|
||||
QString getDateTimeFormat();
|
||||
QString getDateFormat();
|
||||
|
||||
/**
|
||||
* @brief setPrettySerialize enable/disabled pretty-print when serialize() a json
|
||||
* @param enabled
|
||||
*/
|
||||
void setPrettySerialize(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief isPrettySerialize check if is enabled pretty-print when serialize() a json
|
||||
* @return
|
||||
*/
|
||||
bool isPrettySerialize();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* QVariant based Json object
|
||||
*/
|
||||
class Object : public QVariant {
|
||||
template<typename T>
|
||||
Object& insertKey(Object* ptr, const QString& key) {
|
||||
T* p = (T*)ptr->data();
|
||||
if (!p->contains(key)) p->insert(key, QVariant());
|
||||
return *reinterpret_cast<Object*>(&p->operator[](key));
|
||||
}
|
||||
template<typename T>
|
||||
void removeKey(Object *ptr, const QString& key) {
|
||||
T* p = (T*)ptr->data();
|
||||
p->remove(key);
|
||||
}
|
||||
public:
|
||||
Object() : QVariant() {}
|
||||
Object(const Object& ref) : QVariant(ref) {}
|
||||
|
||||
Object& operator=(const QVariant& rhs) {
|
||||
/** It maybe more robust when running under Qt versions below 4.7 */
|
||||
QObject * obj = qvariant_cast<QObject *>(rhs);
|
||||
// setValue(rhs);
|
||||
setValue(obj);
|
||||
return *this;
|
||||
}
|
||||
Object& operator[](const QString& key) {
|
||||
if (type() == QVariant::Map)
|
||||
return insertKey<QVariantMap>(this, key);
|
||||
else if (type() == QVariant::Hash)
|
||||
return insertKey<QVariantHash>(this, key);
|
||||
|
||||
setValue(QVariantMap());
|
||||
|
||||
return insertKey<QVariantMap>(this, key);
|
||||
}
|
||||
const Object& operator[](const QString& key) const {
|
||||
return const_cast<Object*>(this)->operator[](key);
|
||||
}
|
||||
void remove(const QString& key) {
|
||||
if (type() == QVariant::Map)
|
||||
removeKey<QVariantMap>(this, key);
|
||||
else if (type() == QVariant::Hash)
|
||||
removeKey<QVariantHash>(this, key);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BuilderJsonArray;
|
||||
|
||||
/**
|
||||
* @brief The BuilderJsonObject class
|
||||
*/
|
||||
class BuilderJsonObject {
|
||||
|
||||
public:
|
||||
BuilderJsonObject();
|
||||
BuilderJsonObject(JsonObject &json);
|
||||
|
||||
BuilderJsonObject *set(const QString &key, const QVariant &value);
|
||||
BuilderJsonObject *set(const QString &key, BuilderJsonObject *builder);
|
||||
BuilderJsonObject *set(const QString &key, BuilderJsonArray *builder);
|
||||
JsonObject create();
|
||||
|
||||
private:
|
||||
static QQueue<BuilderJsonObject *> created_list;
|
||||
|
||||
JsonObject obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The BuilderJsonArray class
|
||||
*/
|
||||
class BuilderJsonArray {
|
||||
|
||||
public:
|
||||
BuilderJsonArray();
|
||||
BuilderJsonArray(JsonArray &json);
|
||||
|
||||
BuilderJsonArray *add(const QVariant &element);
|
||||
BuilderJsonArray *add(BuilderJsonObject *builder);
|
||||
BuilderJsonArray *add(BuilderJsonArray *builder);
|
||||
JsonArray create();
|
||||
|
||||
private:
|
||||
static QQueue<BuilderJsonArray *> created_list;
|
||||
|
||||
JsonArray array;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a BuilderJsonObject
|
||||
* @return
|
||||
*/
|
||||
BuilderJsonObject *objectBuilder();
|
||||
|
||||
/**
|
||||
* @brief Create a BuilderJsonObject starting from copy of another json
|
||||
* @return
|
||||
*/
|
||||
BuilderJsonObject *objectBuilder(JsonObject &json);
|
||||
|
||||
/**
|
||||
* @brief Create a BuilderJsonArray
|
||||
* @return
|
||||
*/
|
||||
BuilderJsonArray *arrayBuilder();
|
||||
|
||||
/**
|
||||
* @brief Create a BuilderJsonArray starting from copy of another json
|
||||
* @return
|
||||
*/
|
||||
BuilderJsonArray *arrayBuilder(JsonArray &json);
|
||||
}
|
||||
|
||||
#endif //JSON_H
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef IOS_VPNPROTOCOL_H
|
||||
#define IOS_VPNPROTOCOL_H
|
||||
|
||||
#include "platforms/ios/json.h"
|
||||
#include "vpnprotocol.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
@@ -50,15 +49,17 @@ private:
|
||||
|
||||
bool m_isChangingState = false;
|
||||
|
||||
void setupWireguardProtocol(const QtJson::JsonObject& result);
|
||||
void setupOpenVPNProtocol(const QtJson::JsonObject& result);
|
||||
void setupShadowSocksProtocol(const QtJson::JsonObject& result);
|
||||
void setupWireguardProtocol(const QJsonObject& rawConfig);
|
||||
void setupOpenVPNProtocol(const QJsonObject& rawConfig);
|
||||
void setupCloakProtocol(const QJsonObject& rawConfig);
|
||||
void setupShadowSocksProtocol(const QJsonObject& rawConfig);
|
||||
|
||||
void launchWireguardTunnel(const QtJson::JsonObject &result);
|
||||
void launchOpenVPNTunnel(const QtJson::JsonObject &result);
|
||||
void launchShadowSocksTunnel(const QtJson::JsonObject &result);
|
||||
void launchWireguardTunnel(const QJsonObject& rawConfig);
|
||||
void launchOpenVPNTunnel(const QJsonObject& rawConfig);
|
||||
void launchCloakTunnel(const QJsonObject& rawConfig);
|
||||
void launchShadowSocksTunnel(const QJsonObject& rawConfig);
|
||||
|
||||
QString serializeSSConfig(const QtJson::JsonObject &ssConfig);
|
||||
QString serializeSSConfig(const QJsonObject &ssConfig);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -35,34 +35,22 @@ IOSVpnProtocol* IOSVpnProtocol::instance() {
|
||||
}
|
||||
|
||||
bool IOSVpnProtocol::initialize()
|
||||
{
|
||||
qDebug() << "Initializing Swift Controller";
|
||||
|
||||
|
||||
qDebug() << "RECEIVED CONFIG FROM SERVER SIDE =>";
|
||||
qDebug() << QJsonDocument(m_rawConfig).toJson();
|
||||
|
||||
if (!m_controller) {
|
||||
bool ok;
|
||||
QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap();
|
||||
|
||||
if(!ok) {
|
||||
qDebug() << QString("An error occurred during parsing");
|
||||
return false;
|
||||
}
|
||||
|
||||
QString protoName = result["protocol"].toString();
|
||||
qDebug() << "PROTOCOL: " << protoName;
|
||||
{
|
||||
if (!m_controller) {
|
||||
QString protoName = m_rawConfig["protocol"].toString();
|
||||
|
||||
if (protoName == "wireguard") {
|
||||
setupWireguardProtocol(result);
|
||||
setupWireguardProtocol(m_rawConfig);
|
||||
currentProto = amnezia::Proto::WireGuard;
|
||||
} else if (protoName == "openvpn") {
|
||||
setupOpenVPNProtocol(result);
|
||||
setupOpenVPNProtocol(m_rawConfig);
|
||||
currentProto = amnezia::Proto::OpenVpn;
|
||||
} else if (protoName == "shadowsocks") {
|
||||
setupShadowSocksProtocol(result);
|
||||
setupShadowSocksProtocol(m_rawConfig);
|
||||
currentProto = amnezia::Proto::ShadowSocks;
|
||||
} else if (protoName == "cloak") {
|
||||
setupCloakProtocol(m_rawConfig);
|
||||
currentProto = amnezia::Proto::Cloak;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -72,39 +60,39 @@ bool IOSVpnProtocol::initialize()
|
||||
|
||||
|
||||
ErrorCode IOSVpnProtocol::start()
|
||||
{
|
||||
bool ok;
|
||||
QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap();
|
||||
qDebug() << "current protocol: " << currentProto;
|
||||
qDebug() << "new protocol: " << m_protocol;
|
||||
qDebug() << "config: " << result;
|
||||
|
||||
if(!ok) {
|
||||
qDebug() << QString("An error occurred during config parsing");
|
||||
return InternalError;
|
||||
}
|
||||
|
||||
{
|
||||
if (m_isChangingState)
|
||||
return NoError;
|
||||
|
||||
QString protocol = result["protocol"].toString();
|
||||
|
||||
|
||||
if (!m_controller)
|
||||
initialize();
|
||||
|
||||
switch (m_protocol) {
|
||||
case amnezia::Proto::Cloak:
|
||||
if (currentProto != m_protocol) {
|
||||
if (m_controller) {
|
||||
stop();
|
||||
initialize();
|
||||
}
|
||||
launchCloakTunnel(m_rawConfig);
|
||||
currentProto = amnezia::Proto::OpenVpn;
|
||||
return NoError;
|
||||
}
|
||||
initialize();
|
||||
launchCloakTunnel(m_rawConfig);
|
||||
break;
|
||||
case amnezia::Proto::OpenVpn:
|
||||
if (currentProto != m_protocol) {
|
||||
if (m_controller) {
|
||||
stop();
|
||||
initialize();
|
||||
}
|
||||
launchOpenVPNTunnel(result);
|
||||
launchOpenVPNTunnel(m_rawConfig);
|
||||
currentProto = amnezia::Proto::OpenVpn;
|
||||
return NoError;
|
||||
}
|
||||
initialize();
|
||||
launchOpenVPNTunnel(result);
|
||||
launchOpenVPNTunnel(m_rawConfig);
|
||||
break;
|
||||
case amnezia::Proto::WireGuard:
|
||||
if (currentProto != m_protocol) {
|
||||
@@ -112,12 +100,12 @@ ErrorCode IOSVpnProtocol::start()
|
||||
stop();
|
||||
initialize();
|
||||
}
|
||||
launchWireguardTunnel(result);
|
||||
launchWireguardTunnel(m_rawConfig);
|
||||
currentProto = amnezia::Proto::WireGuard;
|
||||
return NoError;
|
||||
}
|
||||
initialize();
|
||||
launchWireguardTunnel(result);
|
||||
launchWireguardTunnel(m_rawConfig);
|
||||
break;
|
||||
case amnezia::Proto::ShadowSocks:
|
||||
if (currentProto != m_protocol) {
|
||||
@@ -125,12 +113,12 @@ ErrorCode IOSVpnProtocol::start()
|
||||
stop();
|
||||
initialize();
|
||||
}
|
||||
launchShadowSocksTunnel(result);
|
||||
launchShadowSocksTunnel(m_rawConfig);
|
||||
currentProto = amnezia::Proto::ShadowSocks;
|
||||
return NoError;
|
||||
}
|
||||
initialize();
|
||||
launchShadowSocksTunnel(result);
|
||||
launchShadowSocksTunnel(m_rawConfig);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -143,11 +131,6 @@ void IOSVpnProtocol::stop()
|
||||
{
|
||||
if (!m_controller) {
|
||||
qDebug() << "Not correctly initialized";
|
||||
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// emit connectionStateChanged(Disconnected);
|
||||
// });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,16 +148,12 @@ void IOSVpnProtocol::resume_start()
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::checkStatus()
|
||||
{
|
||||
qDebug() << "Checking status";
|
||||
|
||||
{
|
||||
if (m_checkingStatus) {
|
||||
qDebug() << "We are still waiting for the previous status.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_controller) {
|
||||
qDebug() << "Not correctly initialized";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,9 +185,6 @@ void IOSVpnProtocol::checkStatus()
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "ServerIpv4Gateway:" << QString::fromNSString(serverIpv4Gateway)
|
||||
<< "DeviceIpv4Address:" << QString::fromNSString(deviceIpv4Address)
|
||||
<< "RxBytes:" << rxBytes << "TxBytes:" << txBytes;
|
||||
emit newTransmittedDataCount(rxBytes, txBytes);
|
||||
}];
|
||||
}
|
||||
@@ -255,22 +231,18 @@ void IOSVpnProtocol::cleanupBackendLogs()
|
||||
file.remove();
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||
void IOSVpnProtocol::setupWireguardProtocol(const QJsonObject& rawConfig)
|
||||
{
|
||||
static bool creating = false;
|
||||
// No nested creation!
|
||||
Q_ASSERT(creating == false);
|
||||
creating = true;
|
||||
|
||||
QtJson::JsonObject config = result["wireguard_config_data"].toMap();
|
||||
QJsonObject config = rawConfig["wireguard_config_data"].toObject();
|
||||
|
||||
QString privateKey = config["client_priv_key"].toString();
|
||||
QByteArray key = QByteArray::fromBase64(privateKey.toLocal8Bit());
|
||||
|
||||
qDebug() << " - " << "client_priv_key: " << config["client_priv_key"].toString();
|
||||
qDebug() << " - " << "client_pub_key: " << config["client_pub_key"].toString();
|
||||
qDebug() << " - " << "interface config: " << config["config"].toString();
|
||||
|
||||
QString addr = config["config"].toString().split("\n").takeAt(1).split(" = ").takeLast();
|
||||
QString dns = config["config"].toString().split("\n").takeAt(2).split(" = ").takeLast();
|
||||
QString privkey = config["config"].toString().split("\n").takeAt(3).split(" = ").takeLast();
|
||||
@@ -279,25 +251,12 @@ void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||
QString allowedips = config["config"].toString().split("\n").takeAt(8).split(" = ").takeLast();
|
||||
QString endpoint = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast();
|
||||
QString keepalive = config["config"].toString().split("\n").takeAt(10).split(" = ").takeLast();
|
||||
qDebug() << " - " << "[Interface] address: " << addr;
|
||||
qDebug() << " - " << "[Interface] dns: " << dns;
|
||||
qDebug() << " - " << "[Interface] private key: " << privkey;
|
||||
qDebug() << " - " << "[Peer] public key: " << pubkey;
|
||||
qDebug() << " - " << "[Peer] preshared key: " << presharedkey;
|
||||
qDebug() << " - " << "[Peer] allowed ips: " << allowedips;
|
||||
qDebug() << " - " << "[Peer] endpoint: " << endpoint;
|
||||
qDebug() << " - " << "[Peer] keepalive: " << keepalive;
|
||||
|
||||
qDebug() << " - " << "hostName: " << config["hostName"].toString();
|
||||
qDebug() << " - " << "psk_key: " << config["psk_key"].toString();
|
||||
qDebug() << " - " << "server_pub_key: " << config["server_pub_key"].toString();
|
||||
|
||||
m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||
privateKey:key.toNSData()
|
||||
deviceIpv4Address:addr.toNSString()
|
||||
deviceIpv6Address:@"::/0"
|
||||
closure:^(ConnectionState state, NSDate* date) {
|
||||
qDebug() << "Creation completed with connection state:" << state;
|
||||
creating = false;
|
||||
|
||||
switch (state) {
|
||||
@@ -312,7 +271,6 @@ void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||
}
|
||||
case ConnectionStateConnected: {
|
||||
Q_ASSERT(date);
|
||||
// QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||
m_isChangingState = false;
|
||||
@@ -320,7 +278,6 @@ void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||
return;
|
||||
}
|
||||
case ConnectionStateDisconnected:
|
||||
// Just in case we are connecting, let's call disconnect.
|
||||
[m_controller disconnect];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||
@@ -349,16 +306,76 @@ void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||
void IOSVpnProtocol::setupCloakProtocol(const QJsonObject &rawConfig)
|
||||
{
|
||||
static bool creating = false;
|
||||
// No nested creation!
|
||||
Q_ASSERT(creating == false);
|
||||
creating = true;
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
|
||||
m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||
config:ovpnConfig.toNSString()
|
||||
closure:^(ConnectionState state, NSDate* date) {
|
||||
creating = false;
|
||||
|
||||
switch (state) {
|
||||
case ConnectionStateError: {
|
||||
[m_controller dealloc];
|
||||
m_controller = nullptr;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Error);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
case ConnectionStateConnected: {
|
||||
Q_ASSERT(date);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
case ConnectionStateDisconnected:
|
||||
// Just in case we are connecting, let's call disconnect.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback:^(BOOL a_connected) {
|
||||
if (currentProto != m_protocol) {
|
||||
qDebug() << "Protocols switched: " << a_connected;
|
||||
return;
|
||||
}
|
||||
qDebug() << "VPN State changed: " << a_connected;
|
||||
if (a_connected) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(Connected);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(Disconnected);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::setupOpenVPNProtocol(const QJsonObject &rawConfig)
|
||||
{
|
||||
static bool creating = false;
|
||||
// No nested creation!
|
||||
Q_ASSERT(creating == false);
|
||||
creating = true;
|
||||
|
||||
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
// qDebug() << ovpn;
|
||||
|
||||
m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||
config:ovpnConfig.toNSString()
|
||||
@@ -378,7 +395,6 @@ void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||
}
|
||||
case ConnectionStateConnected: {
|
||||
Q_ASSERT(date);
|
||||
// QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||
m_isChangingState = false;
|
||||
@@ -387,7 +403,6 @@ void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||
}
|
||||
case ConnectionStateDisconnected:
|
||||
// Just in case we are connecting, let's call disconnect.
|
||||
// [m_controller disconnect];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||
m_isChangingState = false;
|
||||
@@ -400,7 +415,7 @@ void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||
qDebug() << "Protocols switched: " << a_connected;
|
||||
return;
|
||||
}
|
||||
qDebug() << "OVPN State changed: " << a_connected;
|
||||
qDebug() << "VPN State changed: " << a_connected;
|
||||
if (a_connected) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(Connected);
|
||||
@@ -415,23 +430,21 @@ void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::setupShadowSocksProtocol(const QtJson::JsonObject &result)
|
||||
void IOSVpnProtocol::setupShadowSocksProtocol(const QJsonObject &rawConfig)
|
||||
{
|
||||
static bool creating = false;
|
||||
// No nested creation!
|
||||
Q_ASSERT(creating == false);
|
||||
creating = true;
|
||||
|
||||
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
qDebug() << "OpenVPN Config:\n" << ovpn;
|
||||
QtJson::JsonObject ssConfig = result["shadowsocks_config_data"].toMap();
|
||||
QJsonObject ssConfig = rawConfig["shadowsocks_config_data"].toObject();
|
||||
|
||||
m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||
tunnelConfig:ovpnConfig.toNSString()
|
||||
ssConfig:serializeSSConfig(ssConfig).toNSString()
|
||||
closure:^(ConnectionState state, NSDate* date) {
|
||||
qDebug() << "ShadowSocks creation completed with connection state:" << state;
|
||||
creating = false;
|
||||
|
||||
switch (state) {
|
||||
@@ -446,7 +459,6 @@ void IOSVpnProtocol::setupShadowSocksProtocol(const QtJson::JsonObject &result)
|
||||
}
|
||||
case ConnectionStateConnected: {
|
||||
Q_ASSERT(date);
|
||||
// QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||
m_isChangingState = false;
|
||||
@@ -455,7 +467,6 @@ void IOSVpnProtocol::setupShadowSocksProtocol(const QtJson::JsonObject &result)
|
||||
}
|
||||
case ConnectionStateDisconnected:
|
||||
// Just in case we are connecting, let's call disconnect.
|
||||
// [m_controller disconnect];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||
m_isChangingState = false;
|
||||
@@ -483,9 +494,9 @@ void IOSVpnProtocol::setupShadowSocksProtocol(const QtJson::JsonObject &result)
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::launchWireguardTunnel(const QtJson::JsonObject &result)
|
||||
void IOSVpnProtocol::launchWireguardTunnel(const QJsonObject &rawConfig)
|
||||
{
|
||||
QtJson::JsonObject config = result["wireguard_config_data"].toMap();
|
||||
QJsonObject config = rawConfig["wireguard_config_data"].toObject();
|
||||
|
||||
QString clientPrivateKey = config["client_priv_key"].toString();
|
||||
QByteArray key = QByteArray::fromBase64(clientPrivateKey.toLocal8Bit());
|
||||
@@ -506,13 +517,6 @@ void IOSVpnProtocol::launchWireguardTunnel(const QtJson::JsonObject &result)
|
||||
QString pskKey = config["psk_key"].toString();
|
||||
QString serverPubKey = config["server_pub_key"].toString();
|
||||
|
||||
qDebug() << "IOSVPNProtocol starts for" << hostname;
|
||||
qDebug() << "DNS:" << dnsServersList.takeFirst().toNSString();
|
||||
qDebug() << "serverPublicKey:" << serverPubKey.toNSString();
|
||||
qDebug() << "serverIpv4AddrIn:" << serverAddr.toNSString();
|
||||
qDebug() << "serverPort:" << (uint32_t)port.toInt();
|
||||
qDebug() << "allowed ip list" << allowedIPList;
|
||||
|
||||
NSMutableArray<VPNIPAddressRange*>* allowedIPAddressRangesNS =
|
||||
[NSMutableArray<VPNIPAddressRange*> arrayWithCapacity:allowedIPList.length()];
|
||||
for (const IPAddressRange item : allowedIPList) {
|
||||
@@ -541,9 +545,56 @@ void IOSVpnProtocol::launchWireguardTunnel(const QtJson::JsonObject &result)
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::launchOpenVPNTunnel(const QtJson::JsonObject &result)
|
||||
|
||||
void IOSVpnProtocol::launchCloakTunnel(const QJsonObject &rawConfig)
|
||||
{
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
|
||||
if(rawConfig["protocol"].toString() == "cloak"){
|
||||
QJsonObject cloak = rawConfig["cloak_config_data"].toObject();
|
||||
cloak["NumConn"] = 1;
|
||||
cloak["RemoteHost"] = cloak["remote"].toString();
|
||||
cloak["RemotePort"] = cloak["port"].toString();
|
||||
|
||||
cloak.remove("remote");
|
||||
cloak.remove("port");
|
||||
|
||||
// Convert JSONObject to JSONDocument
|
||||
QJsonObject jsonObject {};
|
||||
foreach(const QString& key, cloak.keys()) {
|
||||
if(key == "NumConn" or key == "StreamTimeout"){
|
||||
jsonObject.insert(key, cloak.value(key).toInt());
|
||||
}else{
|
||||
jsonObject.insert(key, cloak.value(key).toString());
|
||||
}
|
||||
}
|
||||
QJsonDocument doc(jsonObject);
|
||||
QString strJson(doc.toJson(QJsonDocument::Compact));
|
||||
|
||||
QString cloakBase64 = strJson.toUtf8().toBase64();
|
||||
ovpnConfig.append("\n<cloak>\n");
|
||||
ovpnConfig.append(cloakBase64);
|
||||
ovpnConfig.append("\n</cloak>\n");
|
||||
}
|
||||
|
||||
|
||||
[m_controller connectWithOvpnConfig:ovpnConfig.toNSString()
|
||||
failureCallback:^{
|
||||
qDebug() << "IOSVPNProtocol (OpenVPN Cloak) - connection failed";
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
emit connectionStateChanged(Disconnected);
|
||||
m_isChangingState = false;
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOSVpnProtocol::launchOpenVPNTunnel(const QJsonObject &rawConfig)
|
||||
{
|
||||
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
|
||||
[m_controller connectWithOvpnConfig:ovpnConfig.toNSString()
|
||||
@@ -556,10 +607,10 @@ void IOSVpnProtocol::launchOpenVPNTunnel(const QtJson::JsonObject &result)
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSVpnProtocol::launchShadowSocksTunnel(const QtJson::JsonObject &result) {
|
||||
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||
void IOSVpnProtocol::launchShadowSocksTunnel(const QJsonObject &rawConfig) {
|
||||
QJsonObject ovpn = rawConfig["openvpn_config_data"].toObject();
|
||||
QString ovpnConfig = ovpn["config"].toString();
|
||||
QtJson::JsonObject ssConfig = result["shadowsocks_config_data"].toMap();
|
||||
QJsonObject ssConfig = rawConfig["shadowsocks_config_data"].toObject();
|
||||
QString ss = serializeSSConfig(ssConfig);
|
||||
|
||||
[m_controller connectWithSsConfig:ss.toNSString()
|
||||
@@ -573,26 +624,18 @@ void IOSVpnProtocol::launchShadowSocksTunnel(const QtJson::JsonObject &result) {
|
||||
}];
|
||||
}
|
||||
|
||||
QString IOSVpnProtocol::serializeSSConfig(const QtJson::JsonObject &ssConfig) {
|
||||
QString IOSVpnProtocol::serializeSSConfig(const QJsonObject &ssConfig) {
|
||||
QString ssLocalPort = ssConfig["local_port"].toString();
|
||||
QString ssMethod = ssConfig["method"].toString();
|
||||
QString ssPassword = ssConfig["password"].toString();
|
||||
QString ssServer = ssConfig["server"].toString();
|
||||
QString ssPort = ssConfig["server_port"].toString();
|
||||
QString ssTimeout = ssConfig["timeout"].toString();
|
||||
qDebug() << "\n\nSS CONFIG:";
|
||||
qDebug() << " local port -" << ssLocalPort;
|
||||
qDebug() << " method -" << ssMethod;
|
||||
qDebug() << " password -" << ssPassword;
|
||||
qDebug() << " server -" << ssServer;
|
||||
qDebug() << " port -" << ssPort;
|
||||
qDebug() << " timeout -" << ssTimeout;
|
||||
|
||||
QJsonObject shadowSocksConfig = QJsonObject();
|
||||
shadowSocksConfig.insert("local_addr", "127.0.0.1");
|
||||
shadowSocksConfig.insert("local_port", ssConfig["local_port"].toInt());
|
||||
shadowSocksConfig.insert("method", ssConfig["method"].toString());
|
||||
// shadowSocksConfig.insert("method", "aes-256-gcm");
|
||||
shadowSocksConfig.insert("password", ssConfig["password"].toString());
|
||||
shadowSocksConfig.insert("server", ssConfig["server"].toString());
|
||||
shadowSocksConfig.insert("server_port", ssConfig["server_port"].toInt());
|
||||
|
||||
@@ -80,7 +80,6 @@ void VpnLogic::onUpdatePage()
|
||||
set_labelCurrentDns(dns.first + ", " + dns.second);
|
||||
}
|
||||
|
||||
|
||||
set_isContainerSupportedByCurrentPlatform(ContainerProps::isSupportedByCurrentPlatform(selectedContainer));
|
||||
if (!isContainerSupportedByCurrentPlatform()) {
|
||||
set_labelErrorText(tr("AmneziaVPN not supporting selected protocol on this device. Select another protocol."));
|
||||
|
||||
Reference in New Issue
Block a user