From 33d1518fd22037600272963a75e8b1a088532c6e Mon Sep 17 00:00:00 2001 From: pokamest Date: Thu, 16 May 2024 06:19:56 -0700 Subject: [PATCH] Request internet permission before connect for iOS (#794) * Attempt to fix API error 1100 * NSURLSession fake call to exec iOS network settings dialog * use http://captive.apple.com/generate_204 for requesting internet permission * moved MobileUtils to IosController * replaced callbacks with signal-slots in apiController --- client/CMakeLists.txt | 2 - client/amnezia_application.cpp | 3 +- client/amnezia_application.h | 4 + client/cmake/ios.cmake | 1 - client/core/controllers/apiController.cpp | 105 +++++++++-------- client/core/controllers/apiController.h | 13 ++- client/core/defs.h | 3 + client/ios/app/Info.plist.in | 7 ++ client/main.cpp | 1 + client/platforms/ios/MobileUtils.cpp | 15 --- client/platforms/ios/MobileUtils.h | 22 ---- client/platforms/ios/MobileUtils.mm | 109 ----------------- client/platforms/ios/ios_controller.h | 7 ++ client/platforms/ios/ios_controller.mm | 93 +++++++++++++++ client/platforms/ios/ios_controller_wrapper.h | 10 ++ .../platforms/ios/ios_controller_wrapper.mm | 19 ++- client/secure_qsettings.cpp | 1 - .../ui/controllers/connectionController.cpp | 110 +++++++++--------- client/ui/controllers/connectionController.h | 5 + client/ui/controllers/systemController.cpp | 8 +- 20 files changed, 274 insertions(+), 264 deletions(-) delete mode 100644 client/platforms/ios/MobileUtils.cpp delete mode 100644 client/platforms/ios/MobileUtils.h delete mode 100644 client/platforms/ios/MobileUtils.mm diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 19351aef..92bf3dff 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -151,7 +151,6 @@ include_directories(mozilla/models) if(NOT IOS) set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.h ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h ) endif() @@ -193,7 +192,6 @@ endif() if(NOT IOS) set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.cpp ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp ) endif() diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 910ae214..06d2f9ac 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -55,6 +55,7 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecond #endif m_settings = std::shared_ptr(new Settings); + m_nam = new QNetworkAccessManager(this); } AmneziaApplication::~AmneziaApplication() @@ -150,7 +151,7 @@ void AmneziaApplication::init() connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow); connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(), - &ConnectionController::openConnection); + static_cast(&ConnectionController::openConnection)); connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), &ConnectionController::closeConnection); connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated); diff --git a/client/amnezia_application.h b/client/amnezia_application.h index d260cd47..5561d7c7 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -2,6 +2,7 @@ #define AMNEZIA_APPLICATION_H #include +#include #include #include #include @@ -75,6 +76,7 @@ public: bool parseCommands(); QQmlApplicationEngine *qmlEngine() const; + QNetworkAccessManager *manager() { return m_nam; } signals: void translationsUpdated(); @@ -128,6 +130,8 @@ private: QScopedPointer m_sitesController; QScopedPointer m_systemController; QScopedPointer m_appSplitTunnelingController; + + QNetworkAccessManager *m_nam; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index 97678bf7..5fda3506 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -46,7 +46,6 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm - ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/MobileUtils.mm ) diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp index 9e1cfa2f..0b6b7d36 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -5,7 +5,9 @@ #include #include +#include "amnezia_application.h" #include "configurators/wireguard_configurator.h" +#include "core/errorstrings.h" namespace { @@ -27,8 +29,7 @@ ApiController::ApiController(QObject *parent) : QObject(parent) { } -void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, - QString &config) +void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config) { if (protocol == configKey::cloak) { config.replace("", "\n"); @@ -64,50 +65,45 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont return obj; } -ErrorCode ApiController::updateServerConfigFromApi(const QString &installationUuid, QJsonObject &serverConfig) +void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig) { - QFutureWatcher watcher; +#ifdef Q_OS_IOS + IosController::Instance()->requestInetAccess(); + QThread::msleep(10); +#endif - QFuture future = QtConcurrent::run([this, &serverConfig, &installationUuid]() { - auto containerConfig = serverConfig.value(config_key::containers).toArray(); + auto containerConfig = serverConfig.value(config_key::containers).toArray(); - if (serverConfig.value(config_key::configVersion).toInt()) { - QNetworkAccessManager manager; + if (serverConfig.value(config_key::configVersion).toInt()) { + QNetworkRequest request; + request.setTransferTimeout(7000); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); + QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); + request.setUrl(endpoint); - QNetworkRequest request; - request.setTransferTimeout(7000); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Authorization", - "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); - QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); - request.setUrl(endpoint); + QString protocol = serverConfig.value(configKey::protocol).toString(); - QString protocol = serverConfig.value(configKey::protocol).toString(); + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); - auto apiPayloadData = generateApiPayloadData(protocol); + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::uuid] = installationUuid; - auto apiPayload = fillApiPayload(protocol, apiPayloadData); - apiPayload[configKey::uuid] = installationUuid; + QByteArray requestBody = QJsonDocument(apiPayload).toJson(); - QByteArray requestBody = QJsonDocument(apiPayload).toJson(); - - QScopedPointer reply; - reply.reset(manager.post(request, requestBody)); - - QEventLoop wait; - QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit); - wait.exec(); + QNetworkReply *reply = amnApp->manager()->post(request, requestBody); // ?? + QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable { if (reply->error() == QNetworkReply::NoError) { QString contents = QString::fromUtf8(reply->readAll()); - auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); + QString data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); data.replace("vpn://", ""); - QByteArray ba = QByteArray::fromBase64(data.toUtf8(), - QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); if (ba.isEmpty()) { - return ErrorCode::ApiConfigDownloadError; + emit errorOccurred(errorString(ErrorCode::ApiConfigEmptyError)); + return; } QByteArray ba_uncompressed = qUncompress(ba); @@ -119,30 +115,37 @@ ErrorCode ApiController::updateServerConfigFromApi(const QString &installationUu processApiConfig(protocol, apiPayloadData, configStr); QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - - serverConfig.insert(config_key::dns1, apiConfig.value(config_key::dns1)); - serverConfig.insert(config_key::dns2, apiConfig.value(config_key::dns2)); - serverConfig.insert(config_key::containers, apiConfig.value(config_key::containers)); - serverConfig.insert(config_key::hostName, apiConfig.value(config_key::hostName)); + serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1); + serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); + serverConfig[config_key::containers] = apiConfig.value(config_key::containers); + serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString(); - serverConfig.insert(config_key::defaultContainer, defaultContainer); + serverConfig[config_key::defaultContainer] = defaultContainer; + + emit configUpdated(true, serverConfig, serverIndex); } else { - QString err = reply->errorString(); - qDebug() << QString::fromUtf8(reply->readAll()); - qDebug() << reply->error(); - qDebug() << err; - qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - return ErrorCode::ApiConfigDownloadError; + if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError + || reply->error() == QNetworkReply::NetworkError::TimeoutError) { + emit errorOccurred(errorString(ErrorCode::ApiConfigTimeoutError)); + } else { + QString err = reply->errorString(); + qDebug() << QString::fromUtf8(reply->readAll()); + qDebug() << reply->error(); + qDebug() << err; + qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + emit errorOccurred(errorString(ErrorCode::ApiConfigDownloadError)); + } } - } - return ErrorCode::NoError; - }); - QEventLoop wait; - connect(&watcher, &QFutureWatcher::finished, &wait, &QEventLoop::quit); - watcher.setFuture(future); - wait.exec(); + reply->deleteLater(); + }); - return watcher.result(); + QObject::connect(reply, &QNetworkReply::errorOccurred, + [this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; }); + connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList &errors) { + qDebug().noquote() << errors; + emit errorOccurred(errorString(ErrorCode::ApiConfigSslError)); + }); + } } diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h index 554e8849..fad00c99 100644 --- a/client/core/controllers/apiController.h +++ b/client/core/controllers/apiController.h @@ -5,6 +5,10 @@ #include "configurators/openvpn_configurator.h" +#ifdef Q_OS_IOS + #include "platforms/ios/ios_controller.h" +#endif + class ApiController : public QObject { Q_OBJECT @@ -13,10 +17,15 @@ public: explicit ApiController(QObject *parent = nullptr); public slots: - ErrorCode updateServerConfigFromApi(const QString &installationUuid, QJsonObject &serverConfig); + void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); + +signals: + void errorOccurred(const QString &errorMessage); + void configUpdated(const bool updateConfig, const QJsonObject &config, const int serverIndex); private: - struct ApiPayloadData { + struct ApiPayloadData + { OpenVpnConfigurator::ConnectionData certRequest; QString wireGuardClientPrivKey; diff --git a/client/core/defs.h b/client/core/defs.h index e35c2f38..87b03824 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -99,6 +99,9 @@ namespace amnezia // Api errors ApiConfigDownloadError = 1100, ApiConfigAlreadyAdded = 1101, + ApiConfigEmptyError = 1102, + ApiConfigTimeoutError = 1103, + ApiConfigSslError = 1104, // QFile errors OpenError = 1200, diff --git a/client/ios/app/Info.plist.in b/client/ios/app/Info.plist.in index 19272c89..45b08cc9 100644 --- a/client/ios/app/Info.plist.in +++ b/client/ios/app/Info.plist.in @@ -51,6 +51,13 @@ NSCameraUsageDescription Amnezia VPN needs access to the camera for reading QR-codes. + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + CFBundleIcons CFBundleIcons~ipad diff --git a/client/main.cpp b/client/main.cpp index 77c91fbc..3a719096 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -64,6 +64,7 @@ int main(int argc, char *argv[]) qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()); + qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString()); return app.exec(); } diff --git a/client/platforms/ios/MobileUtils.cpp b/client/platforms/ios/MobileUtils.cpp deleted file mode 100644 index 14c0854a..00000000 --- a/client/platforms/ios/MobileUtils.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "MobileUtils.h" - -MobileUtils::MobileUtils(QObject *parent) : QObject(parent) -{ -} - -bool MobileUtils::shareText(const QStringList &) -{ - return false; -} - -QString MobileUtils::openFile() -{ - return QString(); -} diff --git a/client/platforms/ios/MobileUtils.h b/client/platforms/ios/MobileUtils.h deleted file mode 100644 index 04a12c51..00000000 --- a/client/platforms/ios/MobileUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MOBILEUTILS_H -#define MOBILEUTILS_H - -#include -#include - -class MobileUtils : public QObject -{ - Q_OBJECT - -public: - explicit MobileUtils(QObject *parent = nullptr); - -public slots: - bool shareText(const QStringList &filesToSend); - QString openFile(); - -signals: - void finished(); -}; - -#endif // MOBILEUTILS_H diff --git a/client/platforms/ios/MobileUtils.mm b/client/platforms/ios/MobileUtils.mm deleted file mode 100644 index 58faad88..00000000 --- a/client/platforms/ios/MobileUtils.mm +++ /dev/null @@ -1,109 +0,0 @@ -#include "MobileUtils.h" - -#include -#include - -#include - -static UIViewController* getViewController() { - NSArray *windows = [[UIApplication sharedApplication]windows]; - for (UIWindow *window in windows) { - if (window.isKeyWindow) { - return window.rootViewController; - } - } - return nil; -} - -MobileUtils::MobileUtils(QObject *parent) : QObject(parent) { - -} - -bool MobileUtils::shareText(const QStringList& filesToSend) { - NSMutableArray *sharingItems = [NSMutableArray new]; - - for (int i = 0; i < filesToSend.size(); i++) { - NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()]; - [sharingItems addObject:logFileUrl]; - } - - UIViewController *qtController = getViewController(); - if (!qtController) return; - - UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; - - __block bool isAccepted = false; - - [activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - isAccepted = completed; - emit finished(); - }]; - - [qtController presentViewController:activityController animated:YES completion:nil]; - UIPopoverPresentationController *popController = activityController.popoverPresentationController; - if (popController) { - popController.sourceView = qtController.view; - popController.sourceRect = CGRectMake(100, 100, 100, 100); - } - - QEventLoop wait; - QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit); - wait.exec(); - - return isAccepted; -} - -typedef void (^DocumentPickerClosedCallback)(NSString *path); - -@interface DocumentPickerDelegate : NSObject - -@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback; - -@end - -@implementation DocumentPickerDelegate - -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - for (NSURL *url in urls) { - if (self.documentPickerClosedCallback) { - self.documentPickerClosedCallback([url path]); - } - } -} - -- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { - if (self.documentPickerClosedCallback) { - self.documentPickerClosedCallback(nil); - } -} - -@end - -QString MobileUtils::openFile() { - UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen]; - - DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init]; - documentPicker.delegate = documentPickerDelegate; - - UIViewController *qtController = getViewController(); - if (!qtController) return; - - [qtController presentViewController:documentPicker animated:YES completion:nil]; - - __block QString filePath; - - documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { - if (path) { - filePath = QString::fromUtf8(path.UTF8String); - } else { - filePath = QString(); - } - emit finished(); - }; - - QEventLoop wait; - QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit); - wait.exec(); - - return filePath; -} diff --git a/client/platforms/ios/ios_controller.h b/client/platforms/ios/ios_controller.h index 2597e15d..a36bbef5 100644 --- a/client/platforms/ios/ios_controller.h +++ b/client/platforms/ios/ios_controller.h @@ -50,12 +50,19 @@ public: void getBackendLogs(std::function &&callback); void checkStatus(); + + bool shareText(const QStringList &filesToSend); + QString openFile(); + + void requestInetAccess(); signals: void connectionStateChanged(Vpn::ConnectionState state); void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void importConfigFromOutside(const QString); void importBackupFromOutside(const QString); + void finished(); + protected slots: private: diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index c10182ba..44924452 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -6,6 +6,7 @@ #include #include #include +#include #include "../protocols/vpnprotocol.h" #import "ios_controller_wrapper.h" @@ -26,6 +27,15 @@ const char* MessageKey::isOnDemand = "is-on-demand"; const char* MessageKey::SplitTunnelType = "SplitTunnelType"; const char* MessageKey::SplitTunnelSites = "SplitTunnelSites"; +static UIViewController* getViewController() { + NSArray *windows = [[UIApplication sharedApplication]windows]; + for (UIWindow *window in windows) { + if (window.isKeyWindow) { + return window.rootViewController; + } + } + return nil; +} Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { switch (status) { @@ -703,3 +713,86 @@ void IosController::sendVpnExtensionMessage(NSDictionary* message, std::function } } + +bool IosController::shareText(const QStringList& filesToSend) { + NSMutableArray *sharingItems = [NSMutableArray new]; + + for (int i = 0; i < filesToSend.size(); i++) { + NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()]; + [sharingItems addObject:logFileUrl]; + } + + UIViewController *qtController = getViewController(); + if (!qtController) return; + + UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; + + __block bool isAccepted = false; + + [activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { + isAccepted = completed; + emit finished(); + }]; + + [qtController presentViewController:activityController animated:YES completion:nil]; + UIPopoverPresentationController *popController = activityController.popoverPresentationController; + if (popController) { + popController.sourceView = qtController.view; + popController.sourceRect = CGRectMake(100, 100, 100, 100); + } + + QEventLoop wait; + QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); + wait.exec(); + + return isAccepted; +} + +QString IosController::openFile() { + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen]; + + DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init]; + documentPicker.delegate = documentPickerDelegate; + + UIViewController *qtController = getViewController(); + if (!qtController) return; + + [qtController presentViewController:documentPicker animated:YES completion:nil]; + + __block QString filePath; + + documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { + if (path) { + filePath = QString::fromUtf8(path.UTF8String); + } else { + filePath = QString(); + } + emit finished(); + }; + + QEventLoop wait; + QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); + wait.exec(); + + return filePath; +} + +void IosController::requestInetAccess() { + NSURL *url = [NSURL URLWithString:@"http://captive.apple.com/generate_204"]; + if (url) { + qDebug() << "IosController::requestInetAccess URL error"; + return; + } + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + qDebug() << "IosController::requestInetAccess error:" << error.localizedDescription; + } else { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + QString responseBody = QString::fromUtf8((const char*)data.bytes, data.length); + qDebug() << "IosController::requestInetAccess server response:" << httpResponse.statusCode << "\n\n" < #import #import +#include +#include class IosController; @@ -13,3 +15,11 @@ class IosController; - (void)vpnConfigurationDidChange:(NSNotification *)notification; @end + +typedef void (^DocumentPickerClosedCallback)(NSString *path); + +@interface DocumentPickerDelegate : NSObject + +@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback; + +@end diff --git a/client/platforms/ios/ios_controller_wrapper.mm b/client/platforms/ios/ios_controller_wrapper.mm index 79f3bcf9..1f8c938f 100644 --- a/client/platforms/ios/ios_controller_wrapper.mm +++ b/client/platforms/ios/ios_controller_wrapper.mm @@ -24,5 +24,22 @@ // cppController->vpnStatusDidChange(notification); } - @end + +@implementation DocumentPickerDelegate + +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { + for (NSURL *url in urls) { + if (self.documentPickerClosedCallback) { + self.documentPickerClosedCallback([url path]); + } + } +} + +- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + if (self.documentPickerClosedCallback) { + self.documentPickerClosedCallback(nil); + } +} + +@end \ No newline at end of file diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index b478b38d..59bf817b 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -1,5 +1,4 @@ #include "secure_qsettings.h" -#include "platforms/ios/MobileUtils.h" #include "QAead.h" #include "QBlockCipher.h" diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index a72ff06b..2308f7ad 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -7,8 +7,6 @@ #endif #include -#include "utilities.h" -#include "core/controllers/apiController.h" #include "core/controllers/vpnConfigurationController.h" #include "core/errorstrings.h" #include "version.h" @@ -19,6 +17,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s const QSharedPointer &vpnConnection, const std::shared_ptr &settings, QObject *parent) : QObject(parent), + m_apiController(this), m_serversModel(serversModel), m_containersModel(containersModel), m_clientManagementModel(clientManagementModel), @@ -29,6 +28,10 @@ ConnectionController::ConnectionController(const QSharedPointer &s connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection); connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection); + connect(&m_apiController, &ApiController::configUpdated, this, + static_cast(&ConnectionController::openConnection)); + connect(&m_apiController, &ApiController::errorOccurred, this, &ConnectionController::connectionErrorOccurred); + m_state = Vpn::ConnectionState::Disconnected; } @@ -43,64 +46,16 @@ void ConnectionController::openConnection() #endif int serverIndex = m_serversModel->getDefaultServerIndex(); - auto serverConfig = m_serversModel->getServerConfig(serverIndex); - - ErrorCode errorCode = ErrorCode::NoError; + QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex); emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing); if (serverConfig.value(config_key::configVersion).toInt() && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { - ApiController apiController; - errorCode = apiController.updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverConfig); - if (errorCode != ErrorCode::NoError) { - emit connectionErrorOccurred(errorString(errorCode)); - return; - } - m_serversModel->editServer(serverConfig, serverIndex); + m_apiController.updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig); + } else { + openConnection(false, serverConfig, serverIndex); } - - if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { - emit noInstalledContainers(); - emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); - return; - } - - DockerContainer container = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole)); - - if (!m_containersModel->isSupportedByCurrentPlatform(container)) { - emit connectionErrorOccurred(tr("The selected protocol is not supported on the current platform")); - return; - } - - if (container == DockerContainer::None) { - emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first")); - return; - } - - qApp->processEvents(); - - QSharedPointer serverController(new ServerController(m_settings)); - VpnConfigurationsController vpnConfigurationController(m_settings, serverController); - - QJsonObject containerConfig = m_containersModel->getContainerConfig(container); - ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); - errorCode = updateProtocolConfig(container, credentials, containerConfig, serverController); - if (errorCode != ErrorCode::NoError) { - emit connectionErrorOccurred(errorString(errorCode)); - return; - } - - auto dns = m_serversModel->getDnsPair(serverIndex); - serverConfig = m_serversModel->getServerConfig(serverIndex); - - auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container, errorCode); - if (errorCode != ErrorCode::NoError) { - emit connectionErrorOccurred(tr("unable to create configuration")); - return; - } - - emit connectToVpn(serverIndex, credentials, container, vpnConfiguration); } void ConnectionController::closeConnection() @@ -231,6 +186,53 @@ bool ConnectionController::isProtocolConfigExists(const QJsonObject &containerCo return true; } +void ConnectionController::openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex) +{ + // Update config for this server as it was received from API + if (updateConfig) { + m_serversModel->editServer(config, serverIndex); + } + + if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { + emit noInstalledContainers(); + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); + return; + } + + DockerContainer container = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole)); + + if (!m_containersModel->isSupportedByCurrentPlatform(container)) { + emit connectionErrorOccurred(tr("The selected protocol is not supported on the current platform")); + return; + } + + if (container == DockerContainer::None) { + emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first")); + return; + } + + QSharedPointer serverController(new ServerController(m_settings)); + VpnConfigurationsController vpnConfigurationController(m_settings, serverController); + + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); + ErrorCode errorCode = updateProtocolConfig(container, credentials, containerConfig, serverController); + if (errorCode != ErrorCode::NoError) { + emit connectionErrorOccurred(errorString(errorCode)); + return; + } + + auto dns = m_serversModel->getDnsPair(serverIndex); + + auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, config, containerConfig, container, errorCode); + if (errorCode != ErrorCode::NoError) { + emit connectionErrorOccurred(tr("unable to create configuration")); + return; + } + + emit connectToVpn(serverIndex, credentials, container, vpnConfiguration); +} + ErrorCode ConnectionController::updateProtocolConfig(const DockerContainer container, const ServerCredentials &credentials, QJsonObject &containerConfig, QSharedPointer serverController) { diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index 7c6dd969..a35d45b5 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -1,6 +1,7 @@ #ifndef CONNECTIONCONTROLLER_H #define CONNECTIONCONTROLLER_H +#include "core/controllers/apiController.h" #include "protocols/vpnprotocol.h" #include "ui/models/clientManagementModel.h" #include "ui/models/containers_model.h" @@ -60,6 +61,10 @@ private: Vpn::ConnectionState getCurrentConnectionState(); bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container); + void openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex); + + ApiController m_apiController; + QSharedPointer m_serversModel; QSharedPointer m_containersModel; QSharedPointer m_clientManagementModel; diff --git a/client/ui/controllers/systemController.cpp b/client/ui/controllers/systemController.cpp index ae523090..1ca4017d 100644 --- a/client/ui/controllers/systemController.cpp +++ b/client/ui/controllers/systemController.cpp @@ -15,7 +15,7 @@ #endif #ifdef Q_OS_IOS - #include "platforms/ios/MobileUtils.h" + #include "platforms/ios/ios_controller.h" #include #endif @@ -46,9 +46,8 @@ void SystemController::saveFile(QString fileName, const QString &data) #ifdef Q_OS_IOS QStringList filesToSend; filesToSend.append(fileUrl.toString()); - MobileUtils mobileUtils; // todo check if save successful - mobileUtils.shareText(filesToSend); + IosController::Instance()->shareText(filesToSend); return; #else QFileInfo fi(fileName); @@ -67,8 +66,7 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString #ifdef Q_OS_IOS - MobileUtils mobileUtils; - fileName = mobileUtils.openFile(); + fileName = IosController::Instance()->openFile(); if (fileName.isEmpty()) { return fileName; }