diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 86779f33..3cef327f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -149,10 +149,10 @@ jobs: # ------------------------------------------------------ Build-iOS: - runs-on: macos-13 + runs-on: macos-latest env: - QT_VERSION: 6.6.2 + QT_VERSION: 6.8.0 CC: cc CXX: c++ PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} @@ -167,7 +167,7 @@ jobs: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.2' + xcode-version: '15.4.0' - name: 'Install desktop Qt' uses: jurplel/install-qt-action@v3 @@ -219,6 +219,7 @@ jobs: - name: 'Build project' run: | + set -o pipefail git submodule update --init --recursive export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin" export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos" @@ -338,7 +339,7 @@ jobs: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.4.0' + xcode-version: '15.4' - name: 'Install Qt' uses: jurplel/install-qt-action@v3 @@ -370,9 +371,14 @@ jobs: - name: 'Build project' run: | + set -o pipefail export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" - bash deploy/build_macos.sh + bash deploy/build_macos.sh | \ + sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \ + sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \ + sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \ + sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d' - name: 'Upload installer artifact' uses: actions/upload-artifact@v4 @@ -388,6 +394,87 @@ jobs: path: deploy/build/client/AmneziaVPN.app retention-days: 7 +# ------------------------------------------------------ + Build-MacOS-NE: + runs-on: macos-15 + + env: + QT_VERSION: 6.8.0 + QIF_VERSION: 4.6 + QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} + DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + + steps: + - name: 'Setup Xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.2' + + - name: 'Install desktop Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'desktop' + modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia qtimageformats' + arch: 'clang_64' + dir: ${{ runner.temp }} + set-env: 'true' + extra: '--base ${{ env.QT_MIRROR }}' + - name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}' + run: | + mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework + wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip + unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/ + - name: 'Install Go' + uses: actions/setup-go@v5 + with: + go-version: '1.22.1' + cache: false + + - name: 'Get sources' + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Install dependencies' + run: pip install jsonschema jinja2 + + - name: 'Set execute permissions for deploy script' + run: chmod +x deploy/build_macos_ne.sh + + - name: 'Build and deploy macOS NE' + run: | + set -o pipefail + export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" + export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos" + bash deploy/build_macos_ne.sh + # sed -u -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \ + # sed -u -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \ + # sed -u -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \ + # sed -u -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d' + + env: + MAC_TRUST_CERT_BASE64: ${{ secrets.MAC_TRUST_CERT_BASE64 }} + MAC_SIGNING_CERT_BASE64: ${{ secrets.MAC_SIGNING_CERT_BASE64 }} + MAC_SIGNING_CERT_PASSWORD: ${{ secrets.MAC_SIGNING_CERT_PASSWORD }} + MAC_APP_PROVISIONING_PROFILE: ${{ secrets.APPSTORE_CONNECT_MAC_PROVISIONING }} + MAC_NE_PROVISIONING_PROFILE: ${{ secrets.APPSTORE_CONNECT_MAC_NE_PROVISIONING }} + APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }} + APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }} + APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }} + - name: 'Upload macOS .app and dSYMs to artifacts' + uses: actions/upload-artifact@v4 + with: + name: macos app & dsyms + path: | + ${{ github.workspace }}/AmneziaVPN.app + retention-days: 7 # ------------------------------------------------------ Build-Android: diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e1ca99..a6e50934 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,13 +31,19 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(APPLE AND NOT IOS) - set(CMAKE_OSX_ARCHITECTURES "x86_64") +if(APPLE) + if(IOS) + set(CMAKE_OSX_ARCHITECTURES "arm64") + elseif(MACOS_NE) + set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") + else() + set(CMAKE_OSX_ARCHITECTURES "x86_64") + endif() endif() add_subdirectory(client) -if(NOT IOS AND NOT ANDROID) +if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE) add_subdirectory(service) include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 0f3748ef..f5d8deeb 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259 +Subproject commit f5d8deeb828343e21a72a95df5e428dfd589810a diff --git a/client/3rd/amneziawg-apple b/client/3rd/amneziawg-apple index 76e7db55..25f7657e 160000 --- a/client/3rd/amneziawg-apple +++ b/client/3rd/amneziawg-apple @@ -1 +1 @@ -Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c +Subproject commit 25f7657eb593ae00cb722358f0fb8c777a509424 diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a454142d..96cb0424 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) project(${PROJECT}) - set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen") set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen") @@ -53,6 +52,9 @@ endif() qt_standard_project_setup() qt_add_executable(${PROJECT} MANUAL_FINALIZATION) +target_include_directories(${PROJECT} PUBLIC + $ +) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep) @@ -110,6 +112,15 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) +if(MACOS_NE) + message("MACOS_NE is ON") + add_definitions(-DQ_OS_MAC) + add_definitions(-DMACOS_NE) + message("Add macros for MacOS Network Extension") +else() + message("MACOS_NE is OFF") +endif() + include_directories(mozilla) include_directories(mozilla/shared) include_directories(mozilla/models) @@ -139,7 +150,7 @@ if(WIN32) endif() if(APPLE) - cmake_policy(SET CMP0099 OLD) + cmake_policy(SET CMP0099 NEW) cmake_policy(SET CMP0114 NEW) if(NOT BUILD_OSX_APP_IDENTIFIER) @@ -158,7 +169,6 @@ if(APPLE) set(CMAKE_XCODE_GENERATE_SCHEME FALSE) set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM}) set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER}) - endif() if(LINUX AND NOT ANDROID) @@ -166,8 +176,7 @@ if(LINUX AND NOT ANDROID) link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux) endif() -if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) - message("Client desktop build") +if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID)) add_compile_definitions(AMNEZIA_DESKTOP) endif() @@ -178,7 +187,9 @@ endif() if(IOS) include(cmake/ios.cmake) include(cmake/ios-arch-fixup.cmake) -elseif(APPLE AND NOT IOS) +elseif(APPLE AND MACOS_NE) + include(cmake/macos_ne.cmake) +elseif(APPLE) include(cmake/osxtools.cmake) include(cmake/macos.cmake) endif() @@ -199,7 +210,7 @@ elseif(APPLE AND NOT IOS) set(DEPLOY_PLATFORM_PATH "macos") endif() -if(NOT IOS AND NOT ANDROID) +if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE) add_custom_command( TARGET ${PROJECT} POST_BUILD COMMAND ${CMAKE_COMMAND} -E $,copy_directory,true> @@ -214,7 +225,6 @@ if(NOT IOS AND NOT ANDROID) $ COMMAND_EXPAND_LISTS ) - endif() target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC}) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index f32d525a..4a585ac0 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "logger.h" #include "ui/controllers/pageController.h" @@ -21,6 +22,8 @@ #include "platforms/ios/QRCodeReaderBase.h" #include "protocols/qml_register_protocols.h" +#include // for QQuickWindow +#include // for qobject_cast AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv) { @@ -63,12 +66,19 @@ void AmneziaApplication::init() const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml")); QObject::connect( - m_engine, &QQmlApplicationEngine::objectCreated, this, - [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, - Qt::QueuedConnection); + m_engine, &QQmlApplicationEngine::objectCreated, this, + [this, url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) { + QCoreApplication::exit(-1); + return; + } + // install filter on main window + if (auto win = qobject_cast(obj)) { + win->installEventFilter(this); + win->show(); + } + }, + Qt::QueuedConnection); m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance()); @@ -167,7 +177,7 @@ bool AmneziaApplication::parseCommands() QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" }; m_parser.addOption(c_cleanup); - + m_parser.process(*this); if (m_parser.isSet(c_cleanup)) { @@ -179,9 +189,8 @@ bool AmneziaApplication::parseCommands() return true; } -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) -void AmneziaApplication::startLocalServer() -{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) +void AmneziaApplication::startLocalServer() { const QString serverName("AmneziaVPNInstance"); QLocalServer::removeServer(serverName); @@ -198,6 +207,22 @@ void AmneziaApplication::startLocalServer() } #endif +bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::Close) { +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + quit(); +#else + if (m_coreController && m_coreController->pageController()) { + m_coreController->pageController()->hideMainWindow(); + } +#endif + return true; // eat the close + } + // call base QObject::eventFilter + return QObject::eventFilter(watched, event); +} + QQmlApplicationEngine *AmneziaApplication::qmlEngine() const { return m_engine; diff --git a/client/amnezia_application.h b/client/amnezia_application.h index ea5f6f52..28aefab0 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -7,9 +7,9 @@ #include #include #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - #include + #include #else - #include + #include #endif #include @@ -20,9 +20,9 @@ #define amnApp (static_cast(QCoreApplication::instance())) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - #define AMNEZIA_BASE_CLASS QGuiApplication + #define AMNEZIA_BASE_CLASS QGuiApplication #else - #define AMNEZIA_BASE_CLASS QApplication + #define AMNEZIA_BASE_CLASS QApplication #endif class AmneziaApplication : public AMNEZIA_BASE_CLASS @@ -37,7 +37,7 @@ public: void loadFonts(); bool parseCommands(); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) void startLocalServer(); #endif @@ -60,6 +60,8 @@ private: QThread m_vpnConnectionThread; QNetworkAccessManager *m_nam; +protected: + bool eventFilter(QObject *watched, QEvent *event) override; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/cmake/3rdparty.cmake b/client/cmake/3rdparty.cmake index 2b5036c5..6c372614 100644 --- a/client/cmake/3rdparty.cmake +++ b/client/cmake/3rdparty.cmake @@ -27,9 +27,15 @@ if(WIN32) set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") endif() elseif(APPLE AND NOT IOS) - set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a") - set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a") - set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64") + if(MACOS_NE) + set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a") + set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a") + set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2") + else() + set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a") + set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a") + set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64") + endif() set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include") set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a") set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a") diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index 58192237..cb66924f 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -76,8 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" XCODE_EMBED_APP_EXTENSIONS networkextension - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic ) + +if(DEFINED DEPLOY) + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN" + ) +else() + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" diff --git a/client/cmake/macos.cmake b/client/cmake/macos.cmake index 7b7cd381..3cf605ae 100644 --- a/client/cmake/macos.cmake +++ b/client/cmake/macos.cmake @@ -31,6 +31,8 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm ) + + set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns) set(MACOSX_BUNDLE_ICON_FILE app.icns) set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/client/cmake/macos_ne.cmake b/client/cmake/macos_ne.cmake new file mode 100644 index 00000000..90876a35 --- /dev/null +++ b/client/cmake/macos_ne.cmake @@ -0,0 +1,168 @@ +message("Client ==> MacOS NE build") + +set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE) +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) + +set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) + +enable_language(OBJC) +enable_language(Swift) + +find_package(Qt6 REQUIRED COMPONENTS ShaderTools Widgets) +# Link Qt Widgets for QWidget, QMenu, QAction etc. +set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets) + +find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices) +find_library(FW_AVFOUNDATION AVFoundation) +find_library(FW_FOUNDATION Foundation) +find_library(FW_STOREKIT StoreKit) +find_library(FW_SERVICEMGMT ServiceManagement) +find_library(FW_USERNOTIFICATIONS UserNotifications) +find_library(FW_NETWORKEXTENSION NetworkExtension) + +set(LIBS ${LIBS} + ${FW_AUTHENTICATIONSERVICES} + ${FW_AVFOUNDATION} + ${FW_FOUNDATION} + ${FW_STOREKIT} + ${FW_SERVICEMGMT} + ${FW_USERNOTIFICATIONS} + ${FW_NETWORKEXTENSION} +) + + +set(HEADERS ${HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h +) +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE) + + +set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm +) + +set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns) +set(MACOSX_BUNDLE_ICON_FILE app.icns) +set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) +set(SOURCES ${SOURCES} ${ICON_FILE}) + + +target_include_directories(${PROJECT} PRIVATE + ${Qt6Gui_PRIVATE_INCLUDE_DIRS} + ${Qt6Widgets_PRIVATE_INCLUDE_DIRS} +) + + +set_target_properties(${PROJECT} PROPERTIES + XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in + MACOSX_BUNDLE_ICON_FILE "AppIcon" + MACOSX_BUNDLE_INFO_STRING "AmneziaVPN" + MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN" + 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}" + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements" + 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" + XCODE_GENERATE_SCHEME TRUE + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO" + XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES" + XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0" + + XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks" + XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension +) + +if(DEPLOY) + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN" + ) +else() + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + +set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" + XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" + XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO" + XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h" + XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx" +) +set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" +) +target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_compile_options(${PROJECT} PRIVATE + -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\" + -DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\" +) + +set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources) + +target_sources(${PROJECT} PRIVATE + ${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c + ${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift + ${CLIENT_ROOT_DIR}/platforms/ios/Log.swift + ${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift + ${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift + ${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift +) + +target_sources(${PROJECT} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets + ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy +) + +set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets + ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy +) + +add_subdirectory(macos/networkextension) +add_dependencies(${PROJECT} AmneziaVPNNetworkExtension) + +get_target_property(QtCore_location Qt6::Core LOCATION) +message("QtCore_location") +message(${QtCore_location}) + +get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE) + +set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS + "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework" +) + +set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos) +target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework") + +add_custom_command(TARGET ${PROJECT} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + $/Contents/Frameworks + COMMAND /usr/bin/find "$/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete + COMMAND /usr/bin/codesign --force --sign "Apple Distribution" + "$/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter" + COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $ -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Signing OpenVPNAdapter framework" +) diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index c3af531a..3161be58 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -39,7 +39,7 @@ set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h ) -if(NOT IOS) +if(NOT IOS AND NOT MACOS_NE) set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h ) @@ -89,12 +89,24 @@ set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp ) -if(NOT IOS) +if(NOT IOS AND NOT MACOS_NE) set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp ) endif() +# Include native macOS platform helpers (dock/status-item) +if(APPLE AND NOT IOS) + list(APPEND HEADERS + ${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h + ${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h + ) + list(APPEND SOURCES + ${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm + ${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm + ) +endif() + if(NOT ANDROID) set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 6d6603da..4f7a035c 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -125,7 +125,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairrouteMode() == Settings::VpnAllExceptSites) { -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); // Prevent ipv6 leak #endif diff --git a/client/configurators/ssh_configurator.cpp b/client/configurators/ssh_configurator.cpp index 308f5947..8e190103 100644 --- a/client/configurators/ssh_configurator.cpp +++ b/client/configurators/ssh_configurator.cpp @@ -8,7 +8,7 @@ #include #include #include -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) #include #else #include @@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr settings, const QShar QString SshConfigurator::convertOpenSShKey(const QString &key) { -#ifndef Q_OS_IOS +#if !defined(Q_OS_IOS) && !defined(MACOS_NE) QProcess p; p.setProcessChannelMode(QProcess::MergedChannels); @@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key) #endif } +// DEAD CODE. void SshConfigurator::openSshTerminal(const ServerCredentials &credentials) { -#ifndef Q_OS_IOS +#if !defined(Q_OS_IOS) && !defined(MACOS_NE) QProcess *p = new QProcess(); p->setProcessChannelMode(QProcess::SeparateChannels); @@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv() pathEnvVar.clear(); pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;"); pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;"); -#elif defined(Q_OS_MACX) +#elif defined(Q_OS_MACX) && !defined(MACOS_NE) pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS"); #endif diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 214e2a51..3fff3e20 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -260,7 +260,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c) #ifdef Q_OS_WINDOWS return true; -#elif defined(Q_OS_IOS) +#elif defined(Q_OS_IOS) || defined(MACOS_NE) switch (c) { case DockerContainer::WireGuard: return true; case DockerContainer::OpenVpn: return true; diff --git a/client/core/networkUtilities.cpp b/client/core/networkUtilities.cpp index cf33fa55..9c245948 100644 --- a/client/core/networkUtilities.cpp +++ b/client/core/networkUtilities.cpp @@ -23,7 +23,7 @@ #include #include #endif -#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) +#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE) #include #include #include @@ -390,7 +390,7 @@ QString NetworkUtilities::getGatewayAndIface() close(sock); return gateway_address; #endif -#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) +#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE) QString gateway; int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY}; int afinet_type[] = {AF_INET, AF_INET6}; diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index dde03b3b..329bf3bc 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -26,10 +26,22 @@ set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks" - - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic ) +if(DEPLOY) + set_target_properties(networkextension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension" + ) +else() + set_target_properties(networkextension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/128.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/128.png index 1be4f3f5..e355ae48 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/128.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/128.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/128@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/128@2x.png index 7ebd9732..89743eca 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/128@2x.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/128@2x.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/16.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/16.png index 8eec5064..ac8a9346 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/16.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/16.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/16@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/16@2x.png index d3085f40..7e8391e3 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/16@2x.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/16@2x.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/256.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/256.png index 95708591..89743eca 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/256.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/256.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/256@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/256@2x.png index d3b06078..f02ea1b0 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/256@2x.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/256@2x.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/32.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/32.png index 4f12879d..7e8391e3 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/32.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/32.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/32@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/32@2x.png index d837424b..3f13a4c7 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/32@2x.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/32@2x.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/512.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/512.png index 0c636cfe..f02ea1b0 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/512.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/512.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/512@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/512@2x.png index 018f668e..2f129f12 100644 Binary files a/client/macos/app/Images.xcassets/AppIcon.appiconset/512@2x.png and b/client/macos/app/Images.xcassets/AppIcon.appiconset/512@2x.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/64.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/64.png new file mode 100644 index 00000000..3f13a4c7 Binary files /dev/null and b/client/macos/app/Images.xcassets/AppIcon.appiconset/64.png differ diff --git a/client/macos/app/Images.xcassets/AppIcon.appiconset/64@2x.png b/client/macos/app/Images.xcassets/AppIcon.appiconset/64@2x.png new file mode 100644 index 00000000..e355ae48 Binary files /dev/null and b/client/macos/app/Images.xcassets/AppIcon.appiconset/64@2x.png differ diff --git a/client/macos/app/Images.xcassets/Contents.json b/client/macos/app/Images.xcassets/Contents.json index 73c00596..ed48d1a9 100644 --- a/client/macos/app/Images.xcassets/Contents.json +++ b/client/macos/app/Images.xcassets/Contents.json @@ -1,6 +1,68 @@ { - "info" : { - "author" : "xcode", - "version" : 1 + "images": [ + { + "idiom": "mac", + "size": "16x16", + "scale": "1x", + "filename": "16.png" + }, + { + "idiom": "mac", + "size": "16x16", + "scale": "2x", + "filename": "16@2x.png" + }, + { + "idiom": "mac", + "size": "32x32", + "scale": "1x", + "filename": "32.png" + }, + { + "idiom": "mac", + "size": "32x32", + "scale": "2x", + "filename": "32@2x.png" + }, + { + "idiom": "mac", + "size": "128x128", + "scale": "1x", + "filename": "128.png" + }, + { + "idiom": "mac", + "size": "128x128", + "scale": "2x", + "filename": "128@2x.png" + }, + { + "idiom": "mac", + "size": "256x256", + "scale": "1x", + "filename": "256.png" + }, + { + "idiom": "mac", + "size": "256x256", + "scale": "2x", + "filename": "256@2x.png" + }, + { + "idiom": "mac", + "size": "512x512", + "scale": "1x", + "filename": "512.png" + }, + { + "idiom": "mac", + "size": "512x512", + "scale": "2x", + "filename": "512@2x.png" + } + ], + "info": { + "version": 1, + "author": "xcode" } } diff --git a/client/macos/app/Info.plist.in b/client/macos/app/Info.plist.in new file mode 100644 index 00000000..1c9ad48e --- /dev/null +++ b/client/macos/app/Info.plist.in @@ -0,0 +1,172 @@ + + + + + CFBundleAllowMixedLocalizations + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME} + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + ITSAppUsesNonExemptEncryption + + LSApplicationCategoryType + public.app-category.utilities + + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + LSSupportsOpeningDocumentsInPlace + + com.wireguard.ios.app_group_id + group.org.amnezia.AmneziaVPN + NSCameraUsageDescription + Amnezia VPN needs access to the camera for reading QR-codes. + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + CFBundleIcons + + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + Amnezia VPN config + UTTypeIconFiles + + UTTypeIdentifier + org.amnezia.AmneziaVPN.amnezia-config + UTTypeTagSpecification + + public.filename-extension + + vpn + + public.mime-type + + text/plain + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + WireGuard config + UTTypeIconFiles + + UTTypeIdentifier + org.amnezia.AmneziaVPN.wireguard-config + UTTypeTagSpecification + + public.filename-extension + + conf + cfg + + public.mime-type + + text/plain + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + OpenVPN config + UTTypeIconFiles + + UTTypeIdentifier + org.amnezia.AmneziaVPN.openvpn-config + UTTypeTagSpecification + + public.filename-extension + + ovpn + + public.mime-type + + text/plain + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + AmneziaVPN backup file + UTTypeIconFiles + + UTTypeIdentifier + org.amnezia.AmneziaVPN.backup-config + UTTypeTagSpecification + + public.filename-extension + + backup + + public.mime-type + + text/plain + + + + + CFBundleDocumentTypes + + + CFBundleTypeName + Amnezia VPN config + LSHandlerRank + Alternate + LSItemContentTypes + + org.amnezia.AmneziaVPN.amnezia-config + org.amnezia.AmneziaVPN.wireguard-config + org.amnezia.AmneziaVPN.openvpn-config + org.amnezia.AmneziaVPN.backup-config + + + + NSExtensions + + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + + + diff --git a/client/macos/app/app.entitlements b/client/macos/app/app.entitlements index 1eaae6ec..d4d7195f 100644 --- a/client/macos/app/app.entitlements +++ b/client/macos/app/app.entitlements @@ -2,34 +2,40 @@ - com.apple.application-identifier - $(DEVELOPMENT_TEAM).$(APP_ID_MACOS) - + com.apple.developer.networking.custom-protocol + com.apple.developer.networking.networkextension + app-proxy-provider packet-tunnel-provider + dns-settings + relay + content-filter-provider + dns-proxy - + com.apple.developer.system-extension.install + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.org.amnezia.AmneziaVPN + + com.apple.security.files.user-selected.read-only + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + keychain-access-groups $(DEVELOPMENT_TEAM).* - - com.apple.developer.team-identifier - $(DEVELOPMENT_TEAM) - - com.apple.security.app-sandbox - - - com.apple.security.application-groups - - $(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS) - - - com.apple.security.network.client - - - com.apple.security.network.server - diff --git a/client/macos/networkextension/AmneziaVPNNetworkExtension.entitlements b/client/macos/networkextension/AmneziaVPNNetworkExtension.entitlements index b4f08784..7e2b2072 100644 --- a/client/macos/networkextension/AmneziaVPNNetworkExtension.entitlements +++ b/client/macos/networkextension/AmneziaVPNNetworkExtension.entitlements @@ -2,41 +2,30 @@ - com.apple.application-identifier - $(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS) - + com.apple.developer.networking.custom-protocol + com.apple.developer.networking.networkextension + dns-settings + relay packet-tunnel-provider + content-filter-provider + dns-proxy + app-proxy-provider - - keychain-access-groups + com.apple.developer.networking.vpn.api - $(DEVELOPMENT_TEAM).* + allow-vpn - - com.apple.developer.team-identifier - $(DEVELOPMENT_TEAM) - - com.apple.developer.system-extension.install - - com.apple.security.app-sandbox - com.apple.security.application-groups - $(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS) + group.org.amnezia.AmneziaVPN - com.apple.security.network.client - com.apple.security.network.server - com.apple.security.app-sandbox - - com.apple.private.network.socket-delegate - diff --git a/client/macos/networkextension/CMakeLists.txt b/client/macos/networkextension/CMakeLists.txt new file mode 100644 index 00000000..efe1b835 --- /dev/null +++ b/client/macos/networkextension/CMakeLists.txt @@ -0,0 +1,138 @@ +enable_language(Swift) +message("Client message >> macos build >> AmneziaVPNNetworkExtension") +set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) + +add_executable(AmneziaVPNNetworkExtension) + +message("executable_path is: @executable_path/../../Frameworks") +set_target_properties(AmneziaVPNNetworkExtension PROPERTIES + XCODE_PRODUCT_TYPE com.apple.product-type.app-extension + # MACOSX_BUNDLE YES + BUNDLE_EXTENSION appex + MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}" + MACOSX_BUNDLE_INFO_STRING "AmneziaVPNNetworkExtension" + MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension" + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements + XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}" + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}" + XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension" + + XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES" + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0" + + XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks" +) + +if(DEPLOY) + message("DEPLOY is ON") + set_target_properties(AmneziaVPNNetworkExtension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN.NE" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.amneziaVPN.NE" + ) +else() + set_target_properties(AmneziaVPNNetworkExtension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + +set_target_properties(AmneziaVPNNetworkExtension PROPERTIES + XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" + XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" + XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h" + XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone" + XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO" +) + +set_target_properties("AmneziaVPNNetworkExtension" PROPERTIES + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" +) + +find_library(FW_ASSETS_LIBRARY AssetsLibrary) +find_library(FW_MOBILE_CORE MobileCoreServices) +find_library(FW_UI_KIT UIKit) +find_library(FW_LIBRESOLV libresolv.9.tbd) + + +# Set the root directory +set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) + +target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${FW_LIBRESOLV}) + +target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\") +target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DNETWORK_EXTENSION=1) + +set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources) + +message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}") +message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}") + +target_sources(AmneziaVPNNetworkExtension PRIVATE + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift + ${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift + ${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift + ${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift + ${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift + ${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift + ${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift + ${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift + ${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift + ${CLIENT_ROOT_DIR}/platforms/ios/Log.swift + ${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift + ${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift + ${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm + ${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift +) + +target_sources(AmneziaVPNNetworkExtension PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy +) + +set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy +) + +## Build wireguard-go-version.h +execute_process( + COMMAND go list -m golang.zx2c4.com/wireguard + WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo + OUTPUT_VARIABLE WG_VERSION_FULL +) +string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h) +target_sources(AmneziaVPNNetworkExtension PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h) + +target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}) +target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a) + +message(${CLIENT_ROOT_DIR}) +message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a) +target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a) + +target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers) diff --git a/client/macos/networkextension/Info.plist b/client/macos/networkextension/Info.plist.in similarity index 63% rename from client/macos/networkextension/Info.plist rename to client/macos/networkextension/Info.plist.in index 96d82459..fa307001 100644 --- a/client/macos/networkextension/Info.plist +++ b/client/macos/networkextension/Info.plist.in @@ -3,27 +3,32 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - AmneziaVPNNetworkExtension + en CFBundleExecutable - $(EXECUTABLE_NAME) + AmneziaVPNNetworkExtension + CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + org.amnezia.AmneziaVPN.network-extension CFBundleInfoDictionaryVersion 6.0 CFBundleName - $(PRODUCT_NAME) + AmneziaVPNNetworkExtension CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - $(MARKETING_VERSION) + ${APPLE_PROJECT_VERSION} CFBundleVersion - $(CURRENT_PROJECT_VERSION) + ${CMAKE_PROJECT_VERSION_TWEAK} + ITSAppUsesNonExemptEncryption + LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) + ${CMAKE_OSX_DEPLOYMENT_TARGET} + + CFBundleDisplayName + AmneziaVPNNetworkExtension + NSExtension NSExtensionPointIdentifier @@ -31,5 +36,11 @@ NSExtensionPrincipalClass $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + com.wireguard.ios.app_group_id + group.org.amnezia.AmneziaVPN + + com.wireguard.macos.app_group_id + ${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN diff --git a/client/macos/networkextension/PrivacyInfo.xcprivacy b/client/macos/networkextension/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..380e0b7b --- /dev/null +++ b/client/macos/networkextension/PrivacyInfo.xcprivacy @@ -0,0 +1,25 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + diff --git a/client/macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h b/client/macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h index 4ae7bded..12bf89be 100644 --- a/client/macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h +++ b/client/macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h @@ -1,10 +1,10 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "macos/gobridge/wireguard.h" + #include "wireguard-go-version.h" -#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h" +#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h" +#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h" #include #include @@ -23,3 +23,8 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex); bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]); void write_msg_to_log(const char* tag, const char* msg); + +// init function definition in C +void hev_socks5_tunnel_quit(void); +// Updated function definition in C +int hev_socks5_tunnel_main(const char* configFile, int fd); diff --git a/client/macos/networkextension/wireguard-go-version.h.in b/client/macos/networkextension/wireguard-go-version.h.in new file mode 100644 index 00000000..860bc3c3 --- /dev/null +++ b/client/macos/networkextension/wireguard-go-version.h.in @@ -0,0 +1,3 @@ +#ifndef WIREGUARD_GO_VERSION +#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@" +#endif // WIREGUARD_GO_VERSION \ No newline at end of file diff --git a/client/main.cpp b/client/main.cpp index aca9e62b..40af3f7e 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -15,7 +15,7 @@ #include "platforms/ios/QtAppDelegate-C-Interface.h" #endif -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) bool isAnotherInstanceRunning() { QLocalSocket socket; @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) AmneziaApplication app(argc, argv); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) if (isAnotherInstanceRunning()) { QTimer::singleShot(1000, &app, [&]() { app.quit(); }); return app.exec(); diff --git a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift b/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift index 3e0a4a07..bfd1165f 100644 --- a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift +++ b/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift @@ -73,7 +73,7 @@ extension PacketTunnelProvider { startHandler = completionHandler ovpnAdapter?.connect(using: packetFlow) } - + func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { guard let completionHandler = completionHandler else { return } let bytesin = ovpnAdapter?.transportStatistics.bytesIn diff --git a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift index 18200c7f..5d6e66de 100644 --- a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift +++ b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift @@ -112,9 +112,19 @@ extension PacketTunnelProvider { } } + let lastHandshakeString = settingsDictionary["last_handshake_time_sec"] + let lastHandshake: Int64 + + if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) { + lastHandshake = handshakeValue + } else { + lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec` + } + let response: [String: Any] = [ "rx_bytes": settingsDictionary["rx_bytes"] ?? "0", - "tx_bytes": settingsDictionary["tx_bytes"] ?? "0" + "tx_bytes": settingsDictionary["tx_bytes"] ?? "0", + "last_handshake_time_sec": lastHandshake ] completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) diff --git a/client/platforms/ios/QRCodeReaderBase.mm b/client/platforms/ios/QRCodeReaderBase.mm index af879e2f..963c35a8 100644 --- a/client/platforms/ios/QRCodeReaderBase.mm +++ b/client/platforms/ios/QRCodeReaderBase.mm @@ -1,3 +1,4 @@ +#if !MACOS_NE #include "QRCodeReaderBase.h" #import @@ -108,3 +109,19 @@ void QRCodeReader::startReading() { void QRCodeReader::stopReading() { [m_qrCodeReader stopReading]; } +#else +#include "QRCodeReaderBase.h" + +QRCodeReader::QRCodeReader() +{ + +} + +QRect QRCodeReader::cameraSize() { + return QRect(); +} + +void QRCodeReader::startReading() {} +void QRCodeReader::stopReading() {} +void QRCodeReader::setCameraSize(QRect) {} +#endif diff --git a/client/platforms/ios/QtAppDelegate.h b/client/platforms/ios/QtAppDelegate.h index c2c1d2d3..1668f4c3 100644 --- a/client/platforms/ios/QtAppDelegate.h +++ b/client/platforms/ios/QtAppDelegate.h @@ -1,5 +1,6 @@ +#if !MACOS_NE #import - +#endif @interface QIOSApplicationDelegate @end diff --git a/client/platforms/ios/QtAppDelegate.mm b/client/platforms/ios/QtAppDelegate.mm index bd7ad6b1..64ee9425 100644 --- a/client/platforms/ios/QtAppDelegate.mm +++ b/client/platforms/ios/QtAppDelegate.mm @@ -5,7 +5,7 @@ @implementation QIOSApplicationDelegate (AmneziaVPNDelegate) - +#if !MACOS_NE - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum]; @@ -57,5 +57,5 @@ } return NO; } - +#endif @end diff --git a/client/platforms/ios/ScreenProtection.swift b/client/platforms/ios/ScreenProtection.swift index 200cf0cb..98758f30 100644 --- a/client/platforms/ios/ScreenProtection.swift +++ b/client/platforms/ios/ScreenProtection.swift @@ -1,3 +1,13 @@ +#if MACOS_NE +public func toggleScreenshots(_ isEnabled: Bool) { + +} + +class ScreenProtection { + + +} +#else import UIKit public func toggleScreenshots(_ isEnabled: Bool) { @@ -90,3 +100,4 @@ struct ProtectionPair { textField.removeFromSuperview() } } +#endif diff --git a/client/platforms/ios/ios_controller.h b/client/platforms/ios/ios_controller.h index 85580769..7e815bde 100644 --- a/client/platforms/ios/ios_controller.h +++ b/client/platforms/ios/ios_controller.h @@ -46,6 +46,7 @@ public: void disconnectVpn(); void vpnStatusDidChange(void *pNotification); + void vpnConfigurationDidChange(void *pNotification); void getBackendLogs(std::function &&callback); diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 85fb50b7..9d7525ce 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -27,6 +27,7 @@ const char* MessageKey::isOnDemand = "is-on-demand"; const char* MessageKey::SplitTunnelType = "SplitTunnelType"; const char* MessageKey::SplitTunnelSites = "SplitTunnelSites"; +#if !MACOS_NE static UIViewController* getViewController() { NSArray *windows = [[UIApplication sharedApplication]windows]; for (UIWindow *window in windows) { @@ -36,6 +37,7 @@ static UIViewController* getViewController() { } return nil; } +#endif Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { switch (status) { @@ -249,6 +251,19 @@ void IosController::checkStatus() sendVpnExtensionMessage(message, [&](NSDictionary* response){ uint64_t txBytes = [response[@"tx_bytes"] intValue]; uint64_t rxBytes = [response[@"rx_bytes"] intValue]; + + uint64_t last_handshake_time_sec = 0; + if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) { + last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue]; + } else { + qDebug() << "Key last_handshake_time_sec is missing or null"; + } + + if (last_handshake_time_sec < 0) { + disconnectVpn(); + qDebug() << "Invalid handshake time, disconnecting VPN."; + } + emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes); m_rxBytes = rxBytes; m_txBytes = txBytes; @@ -789,14 +804,14 @@ bool IosController::shareText(const QStringList& filesToSend) { NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()]; [sharingItems addObject:logFileUrl]; } - +#if !MACOS_NE UIViewController *qtController = getViewController(); if (!qtController) return; UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; - +#endif __block bool isAccepted = false; - +#if !MACOS_NE [activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { isAccepted = completed; emit finished(); @@ -808,7 +823,7 @@ bool IosController::shareText(const QStringList& filesToSend) { popController.sourceView = qtController.view; popController.sourceRect = CGRectMake(100, 100, 100, 100); } - +#endif QEventLoop wait; QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); wait.exec(); @@ -817,6 +832,7 @@ bool IosController::shareText(const QStringList& filesToSend) { } QString IosController::openFile() { +#if !MACOS_NE UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen]; DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init]; @@ -826,9 +842,9 @@ QString IosController::openFile() { if (!qtController) return; [qtController presentViewController:documentPicker animated:YES completion:nil]; - +#endif __block QString filePath; - +#if !MACOS_NE documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { if (path) { filePath = QString::fromUtf8(path.UTF8String); @@ -837,7 +853,7 @@ QString IosController::openFile() { } emit finished(); }; - +#endif QEventLoop wait; QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); wait.exec(); diff --git a/client/platforms/ios/ios_controller_wrapper.h b/client/platforms/ios/ios_controller_wrapper.h index f0333d77..ab325154 100644 --- a/client/platforms/ios/ios_controller_wrapper.h +++ b/client/platforms/ios/ios_controller_wrapper.h @@ -1,7 +1,11 @@ #import #import #import + +#if !MACOS_NE #include +#endif + #include class IosController; @@ -17,9 +21,10 @@ class IosController; @end typedef void (^DocumentPickerClosedCallback)(NSString *path); - +#if !MACOS_NE @interface DocumentPickerDelegate : NSObject @property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback; @end +#endif diff --git a/client/platforms/ios/ios_controller_wrapper.mm b/client/platforms/ios/ios_controller_wrapper.mm index 1f8c938f..38eb2d22 100644 --- a/client/platforms/ios/ios_controller_wrapper.mm +++ b/client/platforms/ios/ios_controller_wrapper.mm @@ -26,7 +26,8 @@ @end -@implementation DocumentPickerDelegate +#if !MACOS_NE +@implementation DocumentPickerDelegate - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { for (NSURL *url in urls) { @@ -42,4 +43,5 @@ } } -@end \ No newline at end of file +@end +#endif diff --git a/client/platforms/ios/iosnotificationhandler.mm b/client/platforms/ios/iosnotificationhandler.mm index efa48385..773c6297 100644 --- a/client/platforms/ios/iosnotificationhandler.mm +++ b/client/platforms/ios/iosnotificationhandler.mm @@ -6,6 +6,8 @@ #import #import + +#if !MACOS_NE #import @interface IOSNotificationDelegate @@ -87,3 +89,86 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt } }]; } +#else + +// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS +@interface IOSNotificationDelegate + : NSObject { + IOSNotificationHandler* m_iosNotificationHandler; +} +@end + +@implementation IOSNotificationDelegate + +- (id)initWithObject:(IOSNotificationHandler*)notification { + self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific + if (self) { + m_iosNotificationHandler = notification; + } + return self; +} + +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + willPresentNotification:(UNNotification*)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler { + Q_UNUSED(center) + completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner); +} + +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + didReceiveNotificationResponse:(UNNotificationResponse*)response + withCompletionHandler:(void (^)())completionHandler { + Q_UNUSED(center) + Q_UNUSED(response) + completionHandler(); +} +@end + +IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) { + + UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; + [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | + UNAuthorizationOptionBadge) + completionHandler:^(BOOL granted, NSError* _Nullable error) { + Q_UNUSED(granted); + if (!error) { + m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this]; + } + }]; +} + +IOSNotificationHandler::~IOSNotificationHandler() { } + +void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title, + const QString& message, int timerMsec) { + Q_UNUSED(type); + + if (!m_delegate) { + return; + } + + UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init]; + content.title = title.toNSString(); + content.body = message.toNSString(); + content.sound = [UNNotificationSound defaultSound]; + + int timerSec = timerMsec / 1000; + UNTimeIntervalNotificationTrigger* trigger = + [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO]; + + UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn" + content:content + trigger:trigger]; + + UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; + center.delegate = (id)m_delegate; + + [center addNotificationRequest:request + withCompletionHandler:^(NSError* _Nullable error) { + if (error) { + NSLog(@"Local Notification failed"); + } + }]; +} +#endif diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index feeabb2f..e59c8ac3 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -179,7 +179,7 @@ namespace amnezia constexpr char defaultPort[] = "51820"; -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) constexpr char defaultMtu[] = "1280"; #else constexpr char defaultMtu[] = "1376"; @@ -199,7 +199,7 @@ namespace amnezia namespace awg { constexpr char defaultPort[] = "55424"; -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) constexpr char defaultMtu[] = "1280"; #else constexpr char defaultMtu[] = "1376"; diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index 056089b8..4b3edca5 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -4,7 +4,7 @@ #include "core/errorstrings.h" #include "vpnprotocol.h" -#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) +#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) #include "openvpnovercloakprotocol.h" #include "openvpnprotocol.h" #include "shadowsocksvpnprotocol.h" @@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject & #if defined(Q_OS_WINDOWS) case DockerContainer::Ipsec: return new Ikev2Protocol(configuration); #endif -#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) +#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration); case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration); case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration); diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 9fc60493..c85367da 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -1,6 +1,6 @@ #include "connectionController.h" -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) #include #else #include @@ -32,8 +32,9 @@ ConnectionController::ConnectionController(const QSharedPointer &s void ConnectionController::openConnection() { -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) { +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) + if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) + { emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning); return; } diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index fdc06120..69488eb8 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -18,7 +18,7 @@ #ifdef Q_OS_ANDROID #include "platforms/android/android_controller.h" #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) #include #endif @@ -569,7 +569,7 @@ void ImportController::startDecodingQr() m_totalQrCodeChunksCount = 0; m_receivedQrCodeChunksCount = 0; - #if defined Q_OS_IOS + #if defined(Q_OS_IOS) || defined(MACOS_NE) m_isQrCodeProcessed = true; #endif #if defined Q_OS_ANDROID diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index d515df49..b8e32a0e 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -1,8 +1,11 @@ #include "pageController.h" #include "utils/converter.h" #include "core/errorstrings.h" +#if defined(MACOS_NE) +#include "platforms/ios/ios_controller.h" +#endif -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) #include #else #include @@ -11,7 +14,7 @@ #ifdef Q_OS_ANDROID #include "platforms/android/android_controller.h" #endif -#if defined Q_OS_MAC +#if defined Q_OS_MAC && !defined(MACOS_NE) #include "ui/macos_util.h" #endif @@ -24,7 +27,7 @@ PageController::PageController(const QSharedPointer &serversModel, AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor); #endif -#if defined Q_OS_MACX +#if defined Q_OS_MACX and !defined MACOS_NE connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); }); connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); }); #endif @@ -56,14 +59,11 @@ QString PageController::getPagePath(PageLoader::PageEnum page) void PageController::closeWindow() { -#ifdef Q_OS_ANDROID +// On mobile platforms, quit app on close; on desktop, just hide window +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) qApp->quit(); #else - if (m_serversModel->getServersCount() == 0) { - qApp->quit(); - } else { - emit hideMainWindow(); - } + emit hideMainWindow(); #endif } @@ -114,7 +114,7 @@ void PageController::showOnStartup() } else { #if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) emit hideMainWindow(); -#elif defined Q_OS_MACX +#elif defined(Q_OS_MACX) && !defined(MACOS_NE) setDockIconVisible(false); #endif } diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index c5c569db..bc1d5c40 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -10,7 +10,7 @@ #include "platforms/android/android_controller.h" #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) #include #endif @@ -76,7 +76,7 @@ bool SettingsController::isLoggingEnabled() void SettingsController::toggleLogging(bool enable) { m_settings->setSaveLogs(enable); -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) AmneziaVPN::toggleLogging(enable); #endif if (enable == true) { @@ -131,8 +131,12 @@ void SettingsController::backupAppConfig(const QString &fileName) void SettingsController::restoreAppConfig(const QString &fileName) { - QByteArray data; - SystemController::readFile(fileName, data); + QFile file(fileName); + + file.open(QIODevice::ReadOnly); + + QByteArray data = file.readAll(); + restoreAppConfigFromData(data); } @@ -169,7 +173,7 @@ void SettingsController::clearSettings() emit changeSettingsFinished(tr("All settings have been reset to default values")); -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) AmneziaVPN::clearSettings(); #endif } diff --git a/client/ui/controllers/systemController.cpp b/client/ui/controllers/systemController.cpp index 52ca1294..12b86990 100644 --- a/client/ui/controllers/systemController.cpp +++ b/client/ui/controllers/systemController.cpp @@ -14,7 +14,7 @@ #include "platforms/android/android_controller.h" #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) #include "platforms/ios/ios_controller.h" #include #endif @@ -58,8 +58,10 @@ void SystemController::saveFile(const QString &fileName, const QString &data) const auto url = fi.absoluteDir().absolutePath(); #endif +#ifndef MACOS_NE QDesktopServices::openUrl(url); #endif +#endif } bool SystemController::readFile(const QString &fileName, QByteArray &data) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 0a7b2526..67cc292b 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -4,7 +4,7 @@ #include "core/controllers/serverController.h" #include "core/networkUtilities.h" -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) #include #endif @@ -779,7 +779,7 @@ void ServersModel::removeApiConfig(const int serverIndex) { auto serverConfig = getServerConfig(serverIndex); -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) QString vpncName = QString("%1 (%2) %3") .arg(serverConfig[config_key::description].toString()) .arg(serverConfig[config_key::hostName].toString()) diff --git a/client/ui/ne_notificationhandler.h b/client/ui/ne_notificationhandler.h new file mode 100644 index 00000000..e84d8068 --- /dev/null +++ b/client/ui/ne_notificationhandler.h @@ -0,0 +1,36 @@ +#ifndef NE_NOTIFICATION_HANDLER_H +#define NE_NOTIFICATION_HANDLER_H + +#include "notificationhandler.h" +#include +#include + +class MacOSStatusIcon; + +class NEStatusBarNotificationHandler : public NotificationHandler { + Q_OBJECT +public: + explicit NEStatusBarNotificationHandler(QObject* parent); + ~NEStatusBarNotificationHandler() override; + + void setConnectionState(Vpn::ConnectionState state) override; + void onTranslationsUpdated() override; + +protected: + void notify(Message type, const QString& title, + const QString& message, int timerMsec) override; + +private: + void buildMenu(); + + QMenu m_menu; + MacOSStatusIcon* m_statusIcon; + + QAction* m_actionShow; + QAction* m_actionConnect; + QAction* m_actionDisconnect; + QAction* m_actionVisitWebsite; + QAction* m_actionQuit; +}; + +#endif // NE_NOTIFICATION_HANDLER_H diff --git a/client/ui/notificationhandler.cpp b/client/ui/notificationhandler.cpp index 5efb45c4..4ccdcdf1 100644 --- a/client/ui/notificationhandler.cpp +++ b/client/ui/notificationhandler.cpp @@ -11,18 +11,12 @@ # include "systemtray_notificationhandler.h" #endif + // static NotificationHandler* NotificationHandler::create(QObject* parent) { #if defined(Q_OS_IOS) return new IOSNotificationHandler(parent); #else - -# if defined(Q_OS_LINUX) - //if (LinuxSystemTrayNotificationHandler::requiredCustomImpl()) { - // return new LinuxSystemTrayNotificationHandler(parent); - //} -# endif - return new SystemTrayNotificationHandler(parent); #endif } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index 7cd5790b..69c244d3 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -26,7 +26,8 @@ Window { color: AmneziaStyle.color.midnightBlack - onClosing: function() { + onClosing: function(close) { + close.accepted = false PageController.closeWindow() } diff --git a/client/ui/systemtray_notificationhandler.cpp b/client/ui/systemtray_notificationhandler.cpp index e1361302..34268d14 100644 --- a/client/ui/systemtray_notificationhandler.cpp +++ b/client/ui/systemtray_notificationhandler.cpp @@ -22,6 +22,9 @@ SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) : m_systemTrayIcon(parent) { +#ifdef MACOS_NE + MacOSUtils::hideDockIcon(); +#endif m_systemTrayIcon.show(); connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &SystemTrayNotificationHandler::onTrayActivated); @@ -38,9 +41,11 @@ SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) : QDesktopServices::openUrl(QUrl("https://amnezia.org")); }); - m_trayActionQuit = m_menu.addAction(QIcon(":/images/tray/cancel.png"), tr("Quit") + " " + APPLICATION_NAME, this, [&](){ - qApp->quit(); - }); + // Quit action: disconnect VPN first on macOS NE, else quit directly + m_trayActionQuit = m_menu.addAction(QIcon(":/images/tray/cancel.png"), + tr("Quit") + " " + APPLICATION_NAME, + this, + [&](){ qApp->quit(); }); m_systemTrayIcon.setContextMenu(&m_menu); setTrayState(Vpn::ConnectionState::Disconnected); diff --git a/client/utilities.cpp b/client/utilities.cpp index 61944e51..51a9885a 100755 --- a/client/utilities.cpp +++ b/client/utilities.cpp @@ -190,7 +190,7 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag) CloseHandle(hSnapshot); return false; -#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID) +#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(MACOS_NE) return false; #else QProcess process; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 3de0f035..4f31d99f 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -22,7 +22,7 @@ #include "platforms/android/android_controller.h" #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) #include "platforms/ios/ios_controller.h" #endif @@ -33,7 +33,7 @@ VpnConnection::VpnConnection(std::shared_ptr settings, QObject *parent : QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this)) { m_checkTimer.setInterval(1000); -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged); connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged); @@ -123,7 +123,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) if (state == Vpn::ConnectionState::Connected) { m_checkTimer.start(); } else { @@ -237,10 +237,11 @@ ErrorCode VpnConnection::lastError() const void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration) { - qDebug() << QString("Trying to connect to VPN, server index is %1, container is %2") + qDebug() << QString("ConnectToVpn, Server index is %1, container is %2, route mode is") .arg(serverIndex) - .arg(ContainerProps::containerToString(container)); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + .arg(ContainerProps::containerToString(container)) + << m_settings->routeMode(); +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) if (!m_IpcClient) { m_IpcClient = new IpcClient(this); } @@ -271,7 +272,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede appendSplitTunnelingConfig(); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration)); if (!m_vpnProtocol) { emit connectionStateChanged(Vpn::ConnectionState::Error); @@ -283,7 +284,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede createAndroidConnections(); m_vpnProtocol.reset(androidVpnProtocol); -#elif defined Q_OS_IOS +#elif defined Q_OS_IOS || defined(MACOS_NE) Proto proto = ContainerProps::defaultProtocol(container); IosController::Instance()->connectVpn(proto, m_vpnConfiguration); connect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus); @@ -363,20 +364,20 @@ void VpnConnection::appendSplitTunnelingConfig() } } - Settings::RouteMode sitesRouteMode = Settings::RouteMode::VpnAllSites; + Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites; QJsonArray sitesJsonArray; if (m_settings->isSitesSplitTunnelingEnabled()) { - sitesRouteMode = m_settings->routeMode(); + routeMode = m_settings->routeMode(); if (allowSiteBasedSplitTunneling) { - auto sites = m_settings->getVpnIps(sitesRouteMode); + auto sites = m_settings->getVpnIps(routeMode); for (const auto &site : sites) { sitesJsonArray.append(site); } if (sitesJsonArray.isEmpty()) { - sitesRouteMode = Settings::RouteMode::VpnAllSites; - } else if (sitesRouteMode == Settings::VpnOnlyForwardSites) { + routeMode = Settings::RouteMode::VpnAllSites; + } else if (routeMode == Settings::VpnOnlyForwardSites) { // Allow traffic to Amnezia DNS sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); @@ -384,7 +385,7 @@ void VpnConnection::appendSplitTunnelingConfig() } } - m_vpnConfiguration.insert(config_key::splitTunnelType, sitesRouteMode); + m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray); Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps; @@ -404,13 +405,6 @@ void VpnConnection::appendSplitTunnelingConfig() m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode); m_vpnConfiguration.insert(config_key::splitTunnelApps, appsJsonArray); - - qDebug() << QString("Site split tunneling is %1, route mode is %2") - .arg(m_settings->isSitesSplitTunnelingEnabled() ? "enabled" : "disabled") - .arg(sitesRouteMode); - qDebug() << QString("App split tunneling is %1, route mode is %2") - .arg(m_settings->isAppsSplitTunnelingEnabled() ? "enabled" : "disabled") - .arg(appsRouteMode); } #ifdef Q_OS_ANDROID @@ -472,7 +466,7 @@ void VpnConnection::disconnectFromVpn() } #endif -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(MACOS_NE) IosController::Instance()->disconnectVpn(); disconnect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus); #endif diff --git a/deploy/build_ios.sh b/deploy/build_ios.sh index 5dc11ff1..7619e146 100755 --- a/deploy/build_ios.sh +++ b/deploy/build_ios.sh @@ -32,7 +32,7 @@ cmake --version clang -v # Generate XCodeProj -$QT_BIN_DIR/qt-cmake . -B $BUILD_DIR -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR +$QT_BIN_DIR/qt-cmake . -B $BUILD_DIR -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -DDEPLOY=ON KEYCHAIN=amnezia.build.ios.keychain KEYCHAIN_FILE=$HOME/Library/Keychains/${KEYCHAIN}-db diff --git a/deploy/build_macos_ne.sh b/deploy/build_macos_ne.sh new file mode 100755 index 00000000..fd3e0b74 --- /dev/null +++ b/deploy/build_macos_ne.sh @@ -0,0 +1,122 @@ +#!/bin/bash +echo "Build script for macOS Network Extension started ..." + +set -o errexit -o nounset + +while getopts n flag +do + case "${flag}" in + n) NOTARIZE_APP=1;; + esac +done + +# Hold on to current directory +PROJECT_DIR=$(pwd) +DEPLOY_DIR=$PROJECT_DIR/deploy + +mkdir -p $DEPLOY_DIR/build-macos +BUILD_DIR=$DEPLOY_DIR/build-macos + +echo "Project dir: ${PROJECT_DIR}" +echo "Build dir: ${BUILD_DIR}" + +APP_NAME=AmneziaVPN +APP_FILENAME=$APP_NAME.app +APP_DOMAIN=org.amneziavpn.package +PLIST_NAME=$APP_NAME.plist + +OUT_APP_DIR=$BUILD_DIR/client +BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME + +PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos +DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos + +INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data +INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME +DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg + +echo "Import certificate" + +TRUST_CERT_CER=$BUILD_DIR/trust-cert.cer +SIGNING_CERT_P12=$BUILD_DIR/signing-cert.p12 + +echo $MAC_TRUST_CERT_BASE64 | base64 --decode > $TRUST_CERT_CER +echo $MAC_SIGNING_CERT_BASE64 | base64 --decode > $SIGNING_CERT_P12 + +shasum -a 256 $TRUST_CERT_CER +shasum -a 256 $SIGNING_CERT_P12 +KEYCHAIN_PASS=$MAC_SIGNING_CERT_PASSWORD + +# Keychain setup +KEYCHAIN=amnezia.build.macos.keychain +TEMP_PASS=tmp_pass +KEYCHAIN_FILE=$HOME/Library/Keychains/$KEYCHAIN-db + +security create-keychain -p $TEMP_PASS $KEYCHAIN || true +security default-keychain -s $KEYCHAIN +security unlock-keychain -p $TEMP_PASS $KEYCHAIN + +security default-keychain +security list-keychains + +# Import certificates into keychain +security import $TRUST_CERT_CER -k $KEYCHAIN -P "" -T /usr/bin/codesign || true +security import $SIGNING_CERT_P12 -k $KEYCHAIN -P $MAC_SIGNING_CERT_PASSWORD -T /usr/bin/codesign || true + +# Configure keychain settings +security set-key-partition-list -S apple-tool:,apple: -k $TEMP_PASS $KEYCHAIN +security find-identity -p codesigning + +# Setup provisioning profiles for main app and NE +echo "Setting up provisioning profiles..." + +# Copy provisioning prifiles +mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles/" + +echo $MAC_APP_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision +echo $MAC_NE_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision + +shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision +shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision + +profile_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision | grep -io "[-A-F0-9]\{36\}"` +echo $profile_uuid +profile_ne_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision | grep -io "[-A-F0-9]\{36\}"` +echo $profile_ne_uuid + +mv ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_uuid.mobileprovision +mv ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_ne_uuid.mobileprovision + +# setup environment +QT_MACOS_BIN=$QT_BIN_DIR +export PATH=$PATH:~/go/bin +echo "QT_BIN_DIR: $QT_BIN_DIR" + + +# Build the Network Extension app +echo "Building MAC Network Extension App..." +mkdir -p build-macos + +$QT_MACOS_BIN/qt-cmake . -B build-macos -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -DMACOS_NE=TRUE -DCMAKE_BUILD_TYPE=Release -DDEPLOY=ON + +# Build and run tests here + +echo "____________________________________" +echo "............Deploying..............." +echo "____________________________________" +echo "Deploying MAC Network Extension App..." + +echo "xcode build" +xcodebuild \ +"OTHER_CODE_SIGN_FLAGS=--keychain '$KEYCHAIN_FILE'" \ +-configuration Release \ +-scheme AmneziaVPN \ +-destination "platform=macOS" \ +-project $PROJECT_DIR/build-macos/AmneziaVPN.xcodeproj + + +# Restore keychain to default +echo "Restoring default keychain..." +security default-keychain -s "/Users/runner/Library/Keychains/login.keychain-db" + +echo "Build and signing process completed successfully!" \ No newline at end of file diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt index f05dbb23..02a21631 100644 --- a/service/CMakeLists.txt +++ b/service/CMakeLists.txt @@ -6,6 +6,6 @@ project(${PROJECT}) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(NOT IOS AND NOT ANDROID) +if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE) add_subdirectory(server) endif()