diff options
author | saturneric <[email protected]> | 2024-12-01 00:21:46 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-12-01 00:21:46 +0000 |
commit | eeec34f66fac8ecb350cd8d683a5dd167c91e706 (patch) | |
tree | 61da717af5370e37963da1e21f9daae0dc426b00 | |
parent | fix: solve link errors on windows (diff) | |
parent | feat: improve windows release structure at nightly build (diff) | |
download | GpgFrontend-eeec34f66fac8ecb350cd8d683a5dd167c91e706.tar.gz GpgFrontend-eeec34f66fac8ecb350cd8d683a5dd167c91e706.zip |
Merge branch 'dev/2.1.5/mingw' into develop
74 files changed, 1648 insertions, 1048 deletions
diff --git a/.github/workflows/testing-nightly.yml b/.github/workflows/testing-nightly.yml index 5417c466..ed763293 100644 --- a/.github/workflows/testing-nightly.yml +++ b/.github/workflows/testing-nightly.yml @@ -31,6 +31,7 @@ on: env: BUILD_TYPE: Release + GNUPG_VERSION: "2.4.7" jobs: build: @@ -164,16 +165,6 @@ jobs: cd ${{github.workspace}} if: matrix.os == 'ubuntu-20.04' - - name: Build GpgME (Windows) - shell: msys2 {0} - run: | - git clone --depth 1 --branch fix/1.18.0 https://git.bktus.com/GpgFrontend/gpgme.git ${{github.workspace}}/third_party/gpgme - cd ${{github.workspace}}/third_party/gpgme - ./autogen.sh - ./configure --enable-maintainer-mode --enable-languages=cpp --disable-gpg-test && make -j4 - make install - if: matrix.os == 'windows-2019' - - name: Build googletest (Linux) run: | git clone --depth 1 --branch v1.15.0 https://github.com/google/googletest.git ${{github.workspace}}/third_party/googletest @@ -194,7 +185,7 @@ jobs: - name: Build & Install Full SDK (Windows) shell: msys2 {0} run: | - cygpath -u ${{github.workspace}} + cd $(cygpath -u "${{github.workspace}}") mkdir build-full-sdk && cd build-full-sdk cmake -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DGPGFRONTEND_BUILD_TYPE_FULL_SDK=ON -DCMAKE_INSTALL_PREFIX=$MSYSTEM_PREFIX .. cmake --build . --config ${{env.BUILD_TYPE}} -- -j 4 @@ -211,7 +202,7 @@ jobs: - name: Build Integrated Modules (Windows) shell: msys2 {0} run: | - cygpath -u ${{github.workspace}} + cd $(cygpath -u "${{github.workspace}}") cd modules mkdir build && cd build cmake -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DGPGFRONTEND_BUILD_TYPE_STABLE=ON -DCMAKE_INSTALL_PREFIX=./artifacts .. @@ -252,10 +243,10 @@ jobs: ditto -c -k --keepParent ${{github.workspace}}/build/package/GpgFrontend.app ${{github.workspace}}/build/GpgFrontend.app.zip hdiutil create ${{github.workspace}}/build/tmp.dmg -ov \ -volname "GpgFrontend" -fs HFS+ -srcfolder ${{github.workspace}}/build/package/ - mkdir ${{github.workspace}}/build/final-artifact - create-dmg --codesign "${{secrets.GPGFRONTEND_XOCDE_CODE_SIGN_IDENTITY}}" --volicon "${{github.workspace}}/resource/lfs/icns/GpgFrontend.icns" --volname GpgFrontend --app-drop-link 600 185 --window-size 800 400 ${{github.workspace}}/build/final-artifact/GpgFrontend.dmg ${{github.workspace}}/build/package/GpgFrontend.app - mv ${{github.workspace}}/build/final-artifact/GpgFrontend.dmg \ - ${{github.workspace}}/build/final-artifact/GpgFrontend-${{env.sha_short}}.dmg + mkdir ${{github.workspace}}/build/upload-artifact + create-dmg --codesign "${{secrets.GPGFRONTEND_XOCDE_CODE_SIGN_IDENTITY}}" --volicon "${{github.workspace}}/resource/lfs/icns/GpgFrontend.icns" --volname GpgFrontend --app-drop-link 600 185 --window-size 800 400 ${{github.workspace}}/build/upload-artifact/GpgFrontend.dmg ${{github.workspace}}/build/package/GpgFrontend.app + mv ${{github.workspace}}/build/upload-artifact/GpgFrontend.dmg \ + ${{github.workspace}}/build/upload-artifact/GpgFrontend-${{env.sha_short}}.dmg mv ${{github.workspace}}/build/GpgFrontend.app.zip \ ${{github.workspace}}/build/GpgFrontend-${{env.sha_short}}.zip if: matrix.os == 'macos-12' @@ -281,8 +272,8 @@ jobs: - name: Copy Modules & Package App Image (Linux) run: | cmake -E copy_directory ${{github.workspace}}/modules/build/artifacts/modules ${{github.workspace}}/build/artifacts/AppDir/usr/modules - mkdir ${{github.workspace}}/build/final-artifact - cd ${{github.workspace}}/build/final-artifact + mkdir ${{github.workspace}}/build/upload-artifact + cd ${{github.workspace}}/build/upload-artifact wget -c -nv https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage chmod u+x linuxdeployqt-continuous-x86_64.AppImage export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib @@ -294,7 +285,7 @@ jobs: - name: Configure CMake & Build Application (Windows) shell: msys2 {0} run: | - cygpath -u ${{github.workspace}} + cd $(cygpath -u "${{github.workspace}}") mkdir build && cd build cmake -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DGPGFRONTEND_BUILD_TYPE_ONLY_APPLICATION=ON .. cmake --build . --config ${{env.BUILD_TYPE}} -- -j 4 @@ -306,38 +297,74 @@ jobs: echo "BUILD_TYPE_LOWER=$("${{env.BUILD_TYPE}}".ToLower())" >> $env:GITHUB_ENV if: matrix.os == 'windows-2019' + - name: Download GnuPG Binary Release (Windows) + shell: msys2 {0} + run: | + export URL="https://ftp.bktus.com/GnuPG/${{env.GNUPG_VERSION}}" + export FILE="gnupg.zip" + export CHECKSUM_FILE="SHA256SUMS.txt" + + cd $(cygpath -u "${{github.workspace}}") + + mkdir -p build/downloads + curl -o build/downloads/$FILE $URL/$FILE + curl -o build/downloads/$CHECKSUM_FILE $URL/$CHECKSUM_FILE + + CHECKSUM=$(grep "$FILE" build/downloads/$CHECKSUM_FILE | awk '{print $1}') + ACTUAL_CHECKSUM=$(sha256sum build/downloads/$FILE | awk '{print $1}') + echo "Expected Checksum: $CHECKSUM" + echo "Actual Checksum: $ACTUAL_CHECKSUM" + if [ "$CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then + echo "Checksum verification failed!" >&2 + exit 1 + fi + + mkdir -p build/artifacts + unzip build/downloads/$FILE -d build/artifacts/ + ls -l build/artifacts/ + if: matrix.os == 'windows-2019' + - name: Copy Modules & Package (Windows) shell: msys2 {0} run: | - cygpath -u ${{github.workspace}} + cd $(cygpath -u "${{github.workspace}}") + cp PrivacyPolicy.md build/artifacts/ + cp README.md build/artifacts/ + cp SECURITY.md build/artifacts/ + cp TRANSLATORS build/artifacts/ + cp COPYING build/artifacts/ + cp gpgfrontend.ico build/artifacts/bin/GpgFrontend.ico + touch build/artifacts/bin/PORTABLE.txt mkdir -p build/artifacts/modules cp -r modules/build/artifacts/bin/* build/artifacts/modules cd build - windeployqt-qt6 --no-translations --force ./artifacts/GpgFrontend.exe - mkdir final-artifact + windeployqt-qt6 --no-translations --force ./artifacts/bin/libgpgfrontend_core.dll + windeployqt-qt6 --no-translations --force ./artifacts/bin/libgpgfrontend_ui.dll + windeployqt-qt6 --no-translations --force ./artifacts/bin/GpgFrontend.exe + mkdir upload-artifact cd artifacts - zip -r ../final-artifact/GpgFrontend-${{env.SHORT_SHA}}-x86_64.zip * + zip -r ../upload-artifact/GpgFrontend-${{env.SHORT_SHA}}-x86_64.zip * if: matrix.os == 'windows-2019' - name: Upload Artifact (Linux) uses: actions/upload-artifact@master with: name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE_LOWER}}-${{env.SHORT_SHA}} - path: ${{github.workspace}}/build/final-artifact/Gpg_Frontend*.AppImage* + path: ${{github.workspace}}/build/upload-artifact/Gpg_Frontend*.AppImage* if: matrix.os == 'ubuntu-20.04' - name: Upload Artifact (macOS) uses: actions/upload-artifact@master with: name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE_LOWER}}-${{env.SHORT_SHA}} - path: ${{github.workspace}}/build/final-artifact/* + path: ${{github.workspace}}/build/upload-artifact/* if: matrix.os == 'macos-12' - name: Upload Artifact (Windows) uses: actions/upload-artifact@master with: name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE_LOWER}}-${{env.SHORT_SHA}} - path: ${{github.workspace}}/build/final-artifact/* + path: ${{github.workspace}}/build/upload-artifact/* if: matrix.os == 'windows-2019' release: needs: build diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index 4e904366..dea3c3f6 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -18,18 +18,18 @@ <file alias="button_paste.png">resource/lfs/icons/button_paste.png</file> <file alias="button_delete.png">resource/lfs/icons/button_delete.png</file> <file alias="configure.png">resource/lfs/icons/configure.png</file> - <file alias="decrypted.png">resource/lfs/icons/decrypted.png</file> <file alias="drafts.png">resource/lfs/icons/drafts.png</file> <file alias="edit.png">resource/lfs/icons/edit.png</file> <file alias="email.png">resource/lfs/icons/email.png</file> - <file alias="encrypted.png">resource/lfs/icons/encrypted.png</file> - <file alias="encrypted_signed.png">resource/lfs/icons/encrypted_signed.png</file> - <file alias="decrypted_verified.png">resource/lfs/icons/decrypted_verified.png</file> + <file alias="lock.png">resource/lfs/icons/lock.png</file> + <file alias="unlock.png">resource/lfs/icons/unlock.png</file> + <file alias="expand.png">resource/lfs/icons/expand.png</file> + <file alias="compress.png">resource/lfs/icons/compress.png</file> <file alias="exit.png">resource/lfs/icons/exit.png</file> <file alias="export_key_to_file.png">resource/lfs/icons/export_key_to_file.png</file> <file alias="file.png">resource/lfs/icons/file.png</file> <file alias="fileencryption.png">resource/lfs/icons/fileencrytion.png</file> - <file alias="file-browser.png">resource/lfs/icons/file-browser.png</file> + <file alias="file-operator.png">resource/lfs/icons/file-operator.png</file> <file alias="fileopen.png">resource/lfs/icons/fileopen.png</file> <file alias="fileprint.png">resource/lfs/icons/fileprint.png</file> <file alias="filesave.png">resource/lfs/icons/filesave.png</file> @@ -105,6 +105,7 @@ <file alias="publish.png">resource/lfs/icons/publish.png</file> <file alias="email-check.png">resource/lfs/icons/email-check.png</file> <file alias="email-open.png">resource/lfs/icons/email-open.png</file> + <file alias="export-email.png">resource/lfs/icons/export-email.png</file> </qresource> <qresource prefix="/test/key"> <file alias="pv1.key">resource/lfs/test/data/pv1.key</file> diff --git a/modules b/modules -Subproject d52d7d683f058f116c832624b2ec8a977017aae +Subproject 419ab0972fff50187956e3b4139f786be877995 diff --git a/resource/lfs/icons/compress.png b/resource/lfs/icons/compress.png Binary files differnew file mode 100644 index 00000000..38066f29 --- /dev/null +++ b/resource/lfs/icons/compress.png diff --git a/resource/lfs/icons/decrypted.png b/resource/lfs/icons/decrypted.png Binary files differdeleted file mode 100644 index 9fe6970a..00000000 --- a/resource/lfs/icons/decrypted.png +++ /dev/null diff --git a/resource/lfs/icons/decrypted_verified.png b/resource/lfs/icons/decrypted_verified.png Binary files differdeleted file mode 100644 index 5e45f625..00000000 --- a/resource/lfs/icons/decrypted_verified.png +++ /dev/null diff --git a/resource/lfs/icons/email.png b/resource/lfs/icons/email.png Binary files differindex f0517848..58c55528 100644 --- a/resource/lfs/icons/email.png +++ b/resource/lfs/icons/email.png diff --git a/resource/lfs/icons/encrypted.png b/resource/lfs/icons/encrypted.png Binary files differdeleted file mode 100644 index 45bbf696..00000000 --- a/resource/lfs/icons/encrypted.png +++ /dev/null diff --git a/resource/lfs/icons/encrypted_signed.png b/resource/lfs/icons/encrypted_signed.png Binary files differdeleted file mode 100644 index ffb57196..00000000 --- a/resource/lfs/icons/encrypted_signed.png +++ /dev/null diff --git a/resource/lfs/icons/expand.png b/resource/lfs/icons/expand.png Binary files differnew file mode 100644 index 00000000..04e8b33c --- /dev/null +++ b/resource/lfs/icons/expand.png diff --git a/resource/lfs/icons/export-email.png b/resource/lfs/icons/export-email.png Binary files differnew file mode 100644 index 00000000..fcf87bbe --- /dev/null +++ b/resource/lfs/icons/export-email.png diff --git a/resource/lfs/icons/file-browser.png b/resource/lfs/icons/file-browser.png Binary files differdeleted file mode 100644 index 29dba7e1..00000000 --- a/resource/lfs/icons/file-browser.png +++ /dev/null diff --git a/resource/lfs/icons/file-operator.png b/resource/lfs/icons/file-operator.png Binary files differnew file mode 100644 index 00000000..cab57a98 --- /dev/null +++ b/resource/lfs/icons/file-operator.png diff --git a/resource/lfs/icons/file.png b/resource/lfs/icons/file.png Binary files differindex d8562029..818f87af 100644 --- a/resource/lfs/icons/file.png +++ b/resource/lfs/icons/file.png diff --git a/resource/lfs/icons/lock.png b/resource/lfs/icons/lock.png Binary files differnew file mode 100644 index 00000000..76dd66c4 --- /dev/null +++ b/resource/lfs/icons/lock.png diff --git a/resource/lfs/icons/misc_doc.png b/resource/lfs/icons/misc_doc.png Binary files differindex 57878ba3..25d2eb55 100644 --- a/resource/lfs/icons/misc_doc.png +++ b/resource/lfs/icons/misc_doc.png diff --git a/resource/lfs/icons/unlock.png b/resource/lfs/icons/unlock.png Binary files differnew file mode 100644 index 00000000..228c8f7c --- /dev/null +++ b/resource/lfs/icons/unlock.png diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba92ef27..0c9171e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,13 +65,13 @@ if(APPLE) configure_file(${CMAKE_SOURCE_DIR}/resource/plist/ExportOptions.plist.in ${CMAKE_BINARY_DIR}/ExportOptions.plist @ONLY) endif() -# Set Runtime Output Directory -if(NOT XCODE_BUILD) - # Set Binary Output Path - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts) -else() - # Set Binary Output Path +# Set Binary Output Path +if(XCODE_BUILD) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +elseif(MINGW) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts/bin) +else() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts) endif() # Print modules @@ -105,11 +105,14 @@ if(BUILD_APPLICATION) set_property(SOURCE gpgfrontend.rc APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/gpgfrontend.ico) endif() +# Set Resource Output Path if(BUILD_APPLICATION) - # Set Resource Output Path if(${CMAKE_BUILD_TYPE} STREQUAL "Release") if(APPLE) set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Resources) + set(GPGFRONTEND_MACOS_ICON ${CMAKE_SOURCE_DIR}/gpgfrontend.icns) + set_source_files_properties(${GPGFRONTEND_MACOS_ICON} PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources") elseif(LINUX AND NOT LINUX_INSTALL_SOFTWARE) set(BUILD_APP_IMAGE 1) else() @@ -120,16 +123,6 @@ if(BUILD_APPLICATION) endif() endif() -if(BUILD_APPLICATION) - if(${CMAKE_BUILD_TYPE} STREQUAL "Release") - if(APPLE) - set(GPGFRONTEND_MACOS_ICON ${CMAKE_SOURCE_DIR}/gpgfrontend.icns) - set_source_files_properties(${GPGFRONTEND_MACOS_ICON} PROPERTIES - MACOSX_PACKAGE_LOCATION "Resources") - endif() - endif() -endif() - if(BUILD_APPLICATION AND MINGW) message(STATUS "Copying Dependent DLL For Windows Runtime Env") @@ -249,35 +242,19 @@ if(BUILD_APPLICATION AND MINGW) file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libxml2-*.dll") list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) - # /mingw64/libexec - execute_process( - COMMAND cygpath -m /mingw64/libexec - OUTPUT_VARIABLE MSYS64_LIBEXEC_PATH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - # gpgme-w32spawn.exe - unset(_libExEPath) - file(GLOB _libExEPath "${MSYS64_LIBEXEC_PATH}/gpgme-*.exe") - list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libExEPath}) + unset(_libDllPath) + file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libarchive*.dll") + list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) - unset(_libExEPath) - file(GLOB _libExEPath "${MSYS64_BIN_PATH}/gpgme-*.exe") - list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libExEPath}) + unset(_libDllPath) + file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libgtest*.dll") + list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) # add gpgfrontend libraries if(STABLE_BUILD_ONLY_APPLICATION) unset(_libDllPath) file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libgpgfrontend_*.dll") list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) - - unset(_libDllPath) - file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libarchive*.dll") - list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) - - unset(_libDllPath) - file(GLOB _libDllPath "${MSYS64_BIN_PATH}/libgtest*.dll") - list(APPEND ALL_RUNTIME_DEP_PATH_LIST ${_libDllPath}) endif() # dll with only name @@ -403,17 +380,6 @@ if(BUILD_APPLICATION) else() # if the status is debug add_executable(${AppName} ${BASE_SOURCE} ${RESOURCE_FILES}) - - if(MINGW) - # include qt dependencies - if(NOT Qt6_DIR) - add_custom_command(TARGET ${AppName} POST_BUILD - COMMAND windeployqt --force ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${AppName}.exe) - else() - add_custom_command(TARGET ${AppName} POST_BUILD - COMMAND windeployqt-qt6.exe --force ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${AppName}.exe) - endif() - endif() endif() # Make app build with resources diff --git a/src/cmd.cpp b/src/cmd.cpp index 2f972329..d56f1c8a 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -72,7 +72,7 @@ auto PrintVersion() -> int { auto PrintEnvInfo() -> int { QTextStream stream(stdout); stream << GetProjectName() << " " << GetProjectVersion() << " " - << "Environemnt Informations:" << '\n'; + << "Environemnt Information:" << '\n'; stream << '\n'; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 90409e22..3ce199e5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -92,13 +92,6 @@ target_precompile_headers(gpgfrontend_core # using std c++ 17 target_compile_features(gpgfrontend_core PUBLIC cxx_std_17) -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - # lib output path - set_target_properties(gpgfrontend_core PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) -endif() - # link for different platforms if(MINGW) message(STATUS "Link GPG Static Library For MINGW") diff --git a/src/core/GpgCoreInit.cpp b/src/core/GpgCoreInit.cpp index e761b857..34ac866b 100644 --- a/src/core/GpgCoreInit.cpp +++ b/src/core/GpgCoreInit.cpp @@ -124,6 +124,17 @@ auto InitGpgME() -> bool { return false; } +#if defined(_WIN32) || defined(WIN32) + auto w32spawn_dir = + GlobalSettingStation::GetInstance().GetAppDir() + "/../gnupg/bin"; + if (gpgme_set_global_flag("w32-inst-dir", + w32spawn_dir.toUtf8().constData()) != 0) { + LOG_E() << "gpgme_set_global_flag() with argument 'w32spawn_dir' failed, " + "abort..."; + return false; + } +#endif + if (CheckGpgError( gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr))) != GPG_ERR_NO_ERROR) { diff --git a/src/core/function/CacheManager.cpp b/src/core/function/CacheManager.cpp index d54d0fac..5670819b 100644 --- a/src/core/function/CacheManager.cpp +++ b/src/core/function/CacheManager.cpp @@ -110,6 +110,7 @@ class CacheManager::Impl : public QObject { key_storage_.push_back(key); } + durable_cache_modified_ = true; if (flush) slot_flush_cache_storage(); } @@ -118,6 +119,7 @@ class CacheManager::Impl : public QObject { if (!durable_cache_storage_.exists(key)) { durable_cache_storage_.insert(key, load_cache_storage(key, {})); + durable_cache_modified_ = true; } auto cache = durable_cache_storage_.get(key); @@ -131,6 +133,7 @@ class CacheManager::Impl : public QObject { if (!durable_cache_storage_.exists(key)) { durable_cache_storage_.insert( key, load_cache_storage(key, std::move(default_value))); + durable_cache_modified_ = true; } auto cache = durable_cache_storage_.get(key); @@ -140,6 +143,7 @@ class CacheManager::Impl : public QObject { auto ResetDurableCache(const QString& key) -> bool { auto data_object_key = get_data_object_key(key); + durable_cache_modified_ = true; return durable_cache_storage_.remove(key); } @@ -188,7 +192,9 @@ class CacheManager::Impl : public QObject { * */ void slot_flush_cache_storage() { - FLOG_D("write cache to file system..."); + if (!durable_cache_modified_) return; + + FLOG_D("update durable cache to disk..."); for (const auto& cache : durable_cache_storage_.mirror()) { auto key = get_data_object_key(cache.first); @@ -197,23 +203,11 @@ class CacheManager::Impl : public QObject { } GpgFrontend::DataObjectOperator::GetInstance().SaveDataObj( drk_key_, QJsonDocument(key_storage_)); + + durable_cache_modified_ = false; } private: - struct CacheObject { - QString value; - qint64 ttl; - - CacheObject(QString value, qint64 ttl) - : value(std::move(value)), ttl(ttl) {} - }; - - QCache<QString, CacheObject> runtime_cache_storage_; - ThreadSafeMap<QString, QJsonDocument> durable_cache_storage_; - QJsonArray key_storage_; - QTimer* flush_timer_; - const QString drk_key_ = "__cache_manage_data_register_key_list"; - /** * @brief Get the data object key object * @@ -273,6 +267,21 @@ class CacheManager::Impl : public QObject { * @param key */ void register_cache_key(const QString& key) {} + + struct CacheObject { + QString value; + qint64 ttl; + + CacheObject(QString value, qint64 ttl) + : value(std::move(value)), ttl(ttl) {} + }; + + QCache<QString, CacheObject> runtime_cache_storage_; + ThreadSafeMap<QString, QJsonDocument> durable_cache_storage_; + QJsonArray key_storage_; + QTimer* flush_timer_; + const QString drk_key_ = "__cache_manage_data_register_key_list"; + std::atomic<bool> durable_cache_modified_{}; }; CacheManager::CacheManager(int channel) diff --git a/src/core/function/GlobalSettingStation.cpp b/src/core/function/GlobalSettingStation.cpp index 42aced01..367437da 100644 --- a/src/core/function/GlobalSettingStation.cpp +++ b/src/core/function/GlobalSettingStation.cpp @@ -40,22 +40,23 @@ class GlobalSettingStation::Impl { * */ explicit Impl() noexcept { - LOG_I() << "app path: " << GetAppDir(); + LOG_I() << "app path: " << app_path_; LOG_I() << "app working path: " << working_path_; auto portable_file_path = working_path_ + "/PORTABLE.txt"; if (QFileInfo(portable_file_path).exists()) { Module::UpsertRTValue("core", "env.state.portable", 1); + LOG_I() << "GpgFrontend runs in the portable mode now"; - app_data_path_ = working_path_; - app_log_path_ = app_data_path_ + "/logs"; - app_data_objs_path_ = app_data_path_ + "/data_objs"; + app_data_path_ = app_path_ + "/../"; + app_config_path_ = app_data_path_ + "/config"; portable_mode_ = true; } LOG_I() << "app data path: " << app_data_path_; - LOG_I() << "app log path: " << app_log_path_; + LOG_I() << "app log path: " << app_log_path(); + LOG_I() << "app modules path: " << app_modules_path(); #if defined(_WIN32) || defined(WIN32) LOG_I() << "app config path: " << app_config_path_; @@ -63,36 +64,35 @@ class GlobalSettingStation::Impl { #endif if (!QDir(app_data_path_).exists()) QDir(app_data_path_).mkpath("."); - if (!QDir(app_log_path_).exists()) QDir(app_log_path_).mkpath("."); - if (!QDir(GetModulesDir()).exists()) QDir(GetModulesDir()).mkpath("."); + if (!QDir(app_log_path()).exists()) QDir(app_log_path()).mkpath("."); + if (!QDir(app_modules_path()).exists()) { + QDir(app_modules_path()).mkpath("."); + } } [[nodiscard]] auto GetSettings() -> QSettings { - if (!portable_mode_) { #if defined(_WIN32) || defined(WIN32) - return QSettings(app_config_target_path_, QSettings::IniFormat); + return QSettings(app_config_file_path(), QSettings::IniFormat); #else - return QSettings(); + return QSettings(); #endif - } - return {app_portable_config_path_, QSettings::IniFormat}; } [[nodiscard]] auto GetLogFilesSize() const -> QString { - return GetHumanFriendlyFileSize(GetFileSizeByPath(app_log_path_, "*.log")); + return GetHumanFriendlyFileSize(GetFileSizeByPath(app_log_path(), "*.log")); } [[nodiscard]] auto GetDataObjectsFilesSize() const -> QString { return GetHumanFriendlyFileSize( - GetFileSizeByPath(app_data_objs_path_, "*")); + GetFileSizeByPath(app_data_objs_path(), "*")); } void ClearAllLogFiles() const { - DeleteAllFilesByPattern(app_log_path_, "*.log"); + DeleteAllFilesByPattern(app_log_path(), "*.log"); } void ClearAllDataObjects() const { - DeleteAllFilesByPattern(app_data_objs_path_, "*"); + DeleteAllFilesByPattern(app_data_objs_path(), "*"); } /** @@ -100,9 +100,7 @@ class GlobalSettingStation::Impl { * * @return QString */ - [[nodiscard]] auto GetAppDir() const -> QString { - return QCoreApplication::applicationDirPath(); - } + [[nodiscard]] auto GetAppDir() const -> QString { return app_path_; } /** * @brief Get the App Data Path object @@ -118,7 +116,7 @@ class GlobalSettingStation::Impl { * * @return QString */ - [[nodiscard]] auto GetLogDir() const -> QString { return app_log_path_; } + [[nodiscard]] auto GetLogDir() const -> QString { return app_log_path(); } /** * @brief Get the Modules Dir object @@ -126,35 +124,33 @@ class GlobalSettingStation::Impl { * @return QString */ [[nodiscard]] auto GetModulesDir() const -> QString { - return GetAppDataPath() + "/mods"; + return app_modules_path(); } private: - QString working_path_ = QDir::currentPath(); - - QString app_data_path_ = QString{QStandardPaths::writableLocation( - QStandardPaths::AppLocalDataLocation)}; ///< Program Data Location - - QString app_config_path_ = QString{ - QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)}; - - QString app_log_path_ = app_data_path_ + "/logs"; ///< Program Data Location + [[nodiscard]] auto app_config_file_path() const -> QString { + return app_config_path_ + "/config.ini"; + } - QString app_data_objs_path_ = - app_data_path_ + "/data_objs"; ///< Object storage path + [[nodiscard]] auto app_data_objs_path() const -> QString { + return app_data_path_ + "/data_objs"; + } - QString app_config_target_path_ = - app_config_path_ + "/config.ini"; ///< take effect only in portable mode + [[nodiscard]] auto app_log_path() const -> QString { + return app_data_path_ + "/logs"; + } - bool portable_mode_ = false; ///< - QString app_portable_config_path_ = - working_path_ + "/config.ini"; ///< take effect only in portable mode + [[nodiscard]] auto app_modules_path() const -> QString { + return app_data_path_ + "/mods"; + } - /** - * @brief - * - */ - void init_app_secure_key() {} + bool portable_mode_ = false; + const QString app_path_ = QCoreApplication::applicationDirPath(); + QString working_path_ = QDir::currentPath(); + QString app_data_path_ = QString{ + QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)}; + QString app_config_path_ = QString{ + QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)}; }; GlobalSettingStation::GlobalSettingStation(int channel) noexcept diff --git a/src/core/function/gpg/GpgAdvancedOperator.cpp b/src/core/function/gpg/GpgAdvancedOperator.cpp index a742b60a..4a373f0a 100644 --- a/src/core/function/gpg/GpgAdvancedOperator.cpp +++ b/src/core/function/gpg/GpgAdvancedOperator.cpp @@ -120,20 +120,23 @@ void GpgFrontend::GpgAdvancedOperator::RestartGpgComponents() { FLOG_D("gpgconf --kill --all execute result: %d", success); if (!success) { - FLOG_W("restart all component after core initilized failed"); + FLOG_W("restart all component after core initalized failed"); Module::UpsertRTValue( "core", "gpg_advanced_operator.restart_gpg_components", false); return; } - // StartGpgAgent([](int err, DataObjectPtr) { - // if (err >= 0) { - // Module::UpsertRTValue( - // "core", "gpg_advanced_operator.restart_gpg_components", - // true); - // return; - // } - // }); +#if defined(__APPLE__) && defined(__MACH__) + FLOG_I("getting gpg-agent to start automatically on macOS"); +#else + StartGpgAgent([](int err, DataObjectPtr) { + if (err >= 0) { + Module::UpsertRTValue( + "core", "gpg_advanced_operator.restart_gpg_components", true); + return; + } + }); +#endif }}); } @@ -176,7 +179,7 @@ void GpgFrontend::GpgAdvancedOperator::StartGpgAgent(OperationCallback cb) { GpgFrontend::GpgCommandExecutor::ExecuteSync( {gpg_agent_path, QStringList{"--homedir", home_path, "--daemon"}, [=](int exit_code, const QString &, const QString &) { - FLOG_D("gpgconf daemon exit code: %d", exit_code); + FLOG_D("gpg-agent daemon exit code: %d", exit_code); cb(exit_code >= 0 ? 0 : -1, TransferParams()); }}); } diff --git a/src/core/function/result_analyse/GpgDecryptResultAnalyse.cpp b/src/core/function/result_analyse/GpgDecryptResultAnalyse.cpp index e569dc3f..6e948705 100644 --- a/src/core/function/result_analyse/GpgDecryptResultAnalyse.cpp +++ b/src/core/function/result_analyse/GpgDecryptResultAnalyse.cpp @@ -80,9 +80,6 @@ void GpgFrontend::GpgDecryptResultAnalyse::doAnalyse() { << Qt::endl; } - stream_ << "- " << tr("German Encryption Standards") << ": " - << (result->is_de_vs == 0 ? tr("false") : tr("true")) << Qt::endl; - stream_ << Qt::endl << Qt::endl; auto *recipient = result->recipients; diff --git a/src/core/model/GpgGenKeyInfo.cpp b/src/core/model/GpgGenKeyInfo.cpp index 1514c3bc..1aaa4cea 100644 --- a/src/core/model/GpgGenKeyInfo.cpp +++ b/src/core/model/GpgGenKeyInfo.cpp @@ -232,7 +232,7 @@ auto GenKeyInfo::GetSupportedKeyAlgo() k_support_key_algo = { {"RSA", "RSA", ""}, {"DSA", "DSA", ""}, - {"ECDSA (ED25519)", "ED25519", ""}, + {"EdDSA (ED25519)", "ED25519", ""}, {"ECDSA (NIST P-256)", "NISTP256", ""}, {"ECDSA (NIST P-384)", "NISTP384", ""}, {"ECDSA (NIST P-521)", "NISTP521", ""}, @@ -242,7 +242,7 @@ auto GenKeyInfo::GetSupportedKeyAlgo() {"RSA", "RSA", ""}, {"DSA", "DSA", ""}, {"EdDSA (ED448)", "ED448", ""}, - {"ECDSA (ED25519)", "ED25519", ""}, + {"EdDSA (ED25519)", "ED25519", ""}, {"ECDSA (SECP256K1)", "SECP256K1", ""}, {"ECDSA (NIST P-256)", "NISTP256", ""}, {"ECDSA (NIST P-384)", "NISTP384", ""}, @@ -278,7 +278,7 @@ auto GenKeyInfo::GetSupportedSubkeyAlgo() {"RSA", "", "RSA"}, {"DSA", "", "DSA"}, {"ELG-E", "", "ELG"}, - {"ECDSA (ED25519)", "", "ED25519"}, + {"EdDSA (ED25519)", "", "ED25519"}, {"ECDH (CV25519)", "", "CV25519"}, {"ECDH (NIST P-256)", "", "NISTP256"}, {"ECDH (NIST P-384)", "", "NISTP384"}, @@ -289,7 +289,7 @@ auto GenKeyInfo::GetSupportedSubkeyAlgo() {"RSA", "", "RSA"}, {"DSA", "", "DSA"}, {"ELG-E", "", "ELG"}, - {"ECDSA (ED25519)", "", "ED25519"}, + {"EdDSA (ED25519)", "", "ED25519"}, {"ECDH (CV25519)", "", "CV25519"}, {"ECDH (SECP256K1)", "", "SECP256K1"}, {"EdDSA (ED448)", "", "ED448"}, diff --git a/src/core/model/GpgSignResult.cpp b/src/core/model/GpgSignResult.cpp index 1819c22b..59c402dd 100644 --- a/src/core/model/GpgSignResult.cpp +++ b/src/core/model/GpgSignResult.cpp @@ -27,7 +27,6 @@ */ #include "GpgSignResult.h" - namespace GpgFrontend { GpgSignResult::GpgSignResult(gpgme_sign_result_t r) : result_ref_(std::shared_ptr<struct _gpgme_op_sign_result>( @@ -62,4 +61,9 @@ auto GpgSignResult::InvalidSigners() } return result; } + +auto GpgSignResult::HashAlgo() -> QString { + if (result_ref_->signatures == nullptr) return {}; + return gpgme_hash_algo_name(result_ref_->signatures->hash_algo); +} } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgSignResult.h b/src/core/model/GpgSignResult.h index a7356c65..3881c86f 100644 --- a/src/core/model/GpgSignResult.h +++ b/src/core/model/GpgSignResult.h @@ -39,6 +39,8 @@ class GPGFRONTEND_CORE_EXPORT GpgSignResult { auto GetRaw() -> gpgme_sign_result_t; + auto HashAlgo() -> QString; + auto InvalidSigners() -> std::vector<std::tuple<QString, GpgError>>; explicit GpgSignResult(gpgme_sign_result_t); diff --git a/src/core/module/ModuleInit.cpp b/src/core/module/ModuleInit.cpp index 972e7025..175d35ad 100644 --- a/src/core/module/ModuleInit.cpp +++ b/src/core/module/ModuleInit.cpp @@ -49,8 +49,8 @@ auto SearchModuleFromPath(const QString& mods_path, } auto LoadIntegratedMods() -> QMap<QString, bool> { - const auto exec_binary_path = QCoreApplication::applicationDirPath(); - QString mods_path = exec_binary_path + "/modules"; + const auto exec_binary_path = GlobalSettingStation::GetInstance().GetAppDir(); + QString mods_path = exec_binary_path + "/../modules"; #ifdef NDEBUG diff --git a/src/sdk/CMakeLists.txt b/src/sdk/CMakeLists.txt index 2e754584..de6d24a7 100644 --- a/src/sdk/CMakeLists.txt +++ b/src/sdk/CMakeLists.txt @@ -45,13 +45,6 @@ target_link_libraries(gpgfrontend_module_sdk PRIVATE gpgfrontend_core gpgfronten file(GLOB _headerPath "${CMAKE_CURRENT_SOURCE_DIR}/*.h") set_target_properties(gpgfrontend_module_sdk PROPERTIES PUBLIC_HEADER "${_headerPath}") -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - # lib output path - set_target_properties(gpgfrontend_module_sdk PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) -endif() - if(XCODE_BUILD) set_target_properties(gpgfrontend_module_sdk PROPERTIES diff --git a/src/sdk/GFSDKBasic.cpp b/src/sdk/GFSDKBasic.cpp index ed734bbe..1c7816d2 100644 --- a/src/sdk/GFSDKBasic.cpp +++ b/src/sdk/GFSDKBasic.cpp @@ -32,7 +32,7 @@ #include "core/function/SecureMemoryAllocator.h" #include "core/function/gpg/GpgCommandExecutor.h" #include "core/utils/BuildInfoUtils.h" -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" #include "ui/UIModuleManager.h" auto GFAllocateMemory(uint32_t size) -> void* { diff --git a/src/sdk/GFSDKExtra.cpp b/src/sdk/GFSDKExtra.cpp index 6a95a8f9..1c755389 100644 --- a/src/sdk/GFSDKExtra.cpp +++ b/src/sdk/GFSDKExtra.cpp @@ -31,7 +31,7 @@ #include <core/utils/BuildInfoUtils.h> #include <core/utils/CommonUtils.h> -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" auto GFCompareSoftwareVersion(const char *current_version, const char *latest_version) -> int { diff --git a/src/sdk/GFSDKGpg.cpp b/src/sdk/GFSDKGpg.cpp index 88162ffa..52ea0520 100644 --- a/src/sdk/GFSDKGpg.cpp +++ b/src/sdk/GFSDKGpg.cpp @@ -24,4 +24,210 @@ * * SPDX-License-Identifier: GPL-3.0-or-later * - */
\ No newline at end of file + */ + +#include "GFSDKGpg.h" + +#include "GFSDKBasic.h" +#include "core/function/gpg/GpgBasicOperator.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/function/gpg/GpgKeyImportExporter.h" +#include "core/model/DataObject.h" +#include "core/model/GpgDecryptResult.h" +#include "core/model/GpgEncryptResult.h" +#include "core/model/GpgSignResult.h" +#include "core/model/GpgVerifyResult.h" +#include "core/typedef/GpgTypedef.h" +#include "core/utils/GpgUtils.h" +#include "ui/UIModuleManager.h" + +// +#include "private/GFSDKPrivat.h" + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgSignData(int channel, char** key_ids, + int key_ids_size, char* data, + int sign_mode, int ascii, + GFGpgSignResult** ps) -> int { + auto singer_ids = CharArrayToQList(key_ids, key_ids_size); + + GpgFrontend::KeyArgsList signer_keys; + for (const auto& signer_id : singer_ids) { + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey(signer_id); + if (key.IsGood()) signer_keys.push_back(key); + } + + if (signer_keys.empty()) return -1; + + auto in_buffer = GpgFrontend::GFBuffer(GFUnStrDup(data).toUtf8()); + + auto gpg_sign_mode = + sign_mode == 0 ? GPGME_SIG_MODE_NORMAL : GPGME_SIG_MODE_DETACH; + + auto [err, data_object] = + GpgFrontend::GpgBasicOperator::GetInstance(channel).SignSync( + signer_keys, in_buffer, gpg_sign_mode, ascii != 0); + + *ps = + static_cast<GFGpgSignResult*>(GFAllocateMemory(sizeof(GFGpgSignResult))); + auto* s = *ps; + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) { + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return -1; + } + + auto result = + GpgFrontend::ExtractParams<GpgFrontend::GpgSignResult>(data_object, 0); + auto out_buffer = + GpgFrontend::ExtractParams<GpgFrontend::GFBuffer>(data_object, 1); + + auto capsule_id = + GpgFrontend::UI::UIModuleManager::GetInstance().MakeCapsule(result); + + s->signature = GFStrDup(out_buffer.ConvertToQByteArray()); + s->hash_algo = GFStrDup(result.HashAlgo()); + s->capsule_id = GFStrDup(capsule_id); + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return 0; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgPublicKey(int channel, char* key_id, + int ascii) -> char* { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey( + GFUnStrDup(key_id)); + + if (!key.IsGood()) return nullptr; + + auto [err, buffer] = + GpgFrontend::GpgKeyImportExporter::GetInstance(channel).ExportKey( + key, false, ascii != 0, true); + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) return nullptr; + + return GFStrDup(buffer.ConvertToQByteArray()); +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgKeyPrimaryUID(int channel, char* key_id, + GFGpgKeyUID** ps) -> int { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey( + GFUnStrDup(key_id)); + + if (!key.IsGood()) return -1; + + auto uids = key.GetUIDs(); + auto& primary_uid = uids->front(); + + *ps = static_cast<GFGpgKeyUID*>(GFAllocateMemory(sizeof(GFGpgKeyUID))); + + auto* s = *ps; + s->name = GFStrDup(primary_uid.GetName()); + s->email = GFStrDup(primary_uid.GetEmail()); + s->comment = GFStrDup(primary_uid.GetComment()); + return 0; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFGpgEncryptData(int channel, char** key_ids, int key_ids_size, char* data, + int ascii, GFGpgEncryptionResult** ps) -> int { + auto encrypt_key_ids = CharArrayToQList(key_ids, key_ids_size); + + GpgFrontend::KeyArgsList encrypt_keys; + for (const auto& encrypt_key_id : encrypt_key_ids) { + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey(encrypt_key_id); + if (key.IsGood()) encrypt_keys.push_back(key); + } + + if (encrypt_keys.empty()) return -1; + + auto in_buffer = GpgFrontend::GFBuffer(GFUnStrDup(data).toUtf8()); + + auto [err, data_object] = + GpgFrontend::GpgBasicOperator::GetInstance(channel).EncryptSync( + encrypt_keys, in_buffer, ascii != 0); + + *ps = static_cast<GFGpgEncryptionResult*>( + GFAllocateMemory(sizeof(GFGpgEncryptionResult))); + auto* s = *ps; + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) { + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return -1; + } + + auto result = + GpgFrontend::ExtractParams<GpgFrontend::GpgEncryptResult>(data_object, 0); + auto out_buffer = + GpgFrontend::ExtractParams<GpgFrontend::GFBuffer>(data_object, 1); + + auto capsule_id = + GpgFrontend::UI::UIModuleManager::GetInstance().MakeCapsule(result); + + s->encrypted_data = GFStrDup(out_buffer.ConvertToQByteArray()); + s->capsule_id = GFStrDup(capsule_id); + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return 0; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFGpgDecryptData(int channel, char* data, GFGpgDecryptResult** ps) -> int { + auto in_buffer = GpgFrontend::GFBuffer(GFUnStrDup(data).toUtf8()); + + auto [err, data_object] = + GpgFrontend::GpgBasicOperator::GetInstance(channel).DecryptSync( + in_buffer); + + *ps = static_cast<GFGpgDecryptResult*>( + GFAllocateMemory(sizeof(GFGpgDecryptResult))); + auto* s = *ps; + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) { + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return -1; + } + + auto result = + GpgFrontend::ExtractParams<GpgFrontend::GpgDecryptResult>(data_object, 0); + auto out_buffer = + GpgFrontend::ExtractParams<GpgFrontend::GFBuffer>(data_object, 1); + + auto capsule_id = + GpgFrontend::UI::UIModuleManager::GetInstance().MakeCapsule(result); + + s->decrypted_data = GFStrDup(out_buffer.ConvertToQByteArray()); + s->capsule_id = GFStrDup(capsule_id); + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return 0; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgVerifyData( + int channel, char* data, char* signature, GFGpgVerifyResult** ps) -> int { + auto in_buffer = GpgFrontend::GFBuffer(GFUnStrDup(data).toUtf8()); + auto sig_buffer = GpgFrontend::GFBuffer(GFUnStrDup(signature).toUtf8()); + + auto [err, data_object] = + GpgFrontend::GpgBasicOperator::GetInstance(channel).VerifySync( + in_buffer, sig_buffer); + + *ps = static_cast<GFGpgVerifyResult*>( + GFAllocateMemory(sizeof(GFGpgVerifyResult))); + auto* s = *ps; + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) { + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return -1; + } + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) return -1; + + auto result = + GpgFrontend::ExtractParams<GpgFrontend::GpgVerifyResult>(data_object, 0); + + auto capsule_id = + GpgFrontend::UI::UIModuleManager::GetInstance().MakeCapsule(result); + + s->capsule_id = GFStrDup(capsule_id); + s->error_string = GFStrDup(GpgFrontend::DescribeGpgErrCode(err).second); + return 0; +} diff --git a/src/sdk/GFSDKGpg.h b/src/sdk/GFSDKGpg.h index 1bbf5914..8977a975 100644 --- a/src/sdk/GFSDKGpg.h +++ b/src/sdk/GFSDKGpg.h @@ -28,4 +28,108 @@ #pragma once -extern "C" {}
\ No newline at end of file +#include "GFSDKExport.h" + +extern "C" { + +struct GFGpgSignResult { + char* signature; + char* hash_algo; + char* capsule_id; + char* error_string; +}; + +struct GFGpgEncryptionResult { + char* encrypted_data; + char* capsule_id; + char* error_string; +}; + +struct GFGpgDecryptResult { + char* decrypted_data; + char* capsule_id; + char* error_string; +}; + +struct GFGpgVerifyResult { + char* capsule_id; + char* error_string; +}; + +struct GFGpgKeyUID { + char* name; + char* email; + char* comment; +}; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgSignData(int channel, char** key_ids, + int key_ids_size, char* data, + int sign_mode, int ascii, + GFGpgSignResult**) -> int; + +/** + * @brief + * + * @param channel + * @param key_ids + * @param key_ids_size + * @param data + * @param ascii + * @return int + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFGpgEncryptData(int channel, char** key_ids, int key_ids_size, char* data, + int ascii, GFGpgEncryptionResult**) -> int; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFGpgDecryptData(int channel, char* data, GFGpgDecryptResult**) -> int; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgVerifyData(int channel, char* data, + char* signature, + GFGpgVerifyResult**) -> int; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgPublicKey(int channel, char* key_id, + int ascii) -> char*; + +/** + * @brief + * + * @param channel + * @param key_id + * @return GpgKeyUID + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgKeyPrimaryUID(int channel, char* key_id, + GFGpgKeyUID**) -> int; +}
\ No newline at end of file diff --git a/src/sdk/GFSDKModule.cpp b/src/sdk/GFSDKModule.cpp index 5435b0a1..e76847d2 100644 --- a/src/sdk/GFSDKModule.cpp +++ b/src/sdk/GFSDKModule.cpp @@ -29,9 +29,9 @@ #include "GFSDKModule.h" #include <core/module/ModuleManager.h> -#include <sdk/private/CommonUtils.h> #include "GFSDKBasic.h" +#include "private/GFSDKPrivat.h" void GFModuleListenEvent(const char *module_id, const char *event_id) { return GpgFrontend::Module::ModuleManager::GetInstance().ListenEvent( diff --git a/src/sdk/GFSDKUI.cpp b/src/sdk/GFSDKUI.cpp index 54716209..cc08705c 100644 --- a/src/sdk/GFSDKUI.cpp +++ b/src/sdk/GFSDKUI.cpp @@ -31,9 +31,10 @@ #include <core/utils/CommonUtils.h> #include <QMap> +#include <QObject> #include <QString> -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" #include "ui/UIModuleManager.h" auto MetaDataArrayToQMap(MetaData** meta_data_array, @@ -53,7 +54,7 @@ auto MetaDataArrayToQMap(MetaData** meta_data_array, } auto GFUIMountEntry(const char* id, MetaData** meta_data_array, - int meta_data_array_size, EntryFactory factory) -> int { + int meta_data_array_size, QObjectFactory factory) -> int { if (id == nullptr || factory == nullptr) return -1; auto meta_data = MetaDataArrayToQMap(meta_data_array, meta_data_array_size); @@ -69,3 +70,76 @@ auto GFUIMountEntry(const char* id, MetaData** meta_data_array, return 0; } + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMainWindowPtr() -> void* { + return GpgFrontend::UI::UIModuleManager::GetInstance().GetQObject( + "main_window"); +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFUIShowDialog(void* dialog_raw_ptr, void* parent_raw_ptr) -> bool { + if (dialog_raw_ptr == nullptr) { + LOG_E() << "dialog raw ptr is nullptr"; + return false; + } + + auto* q_obj = static_cast<QObject*>(dialog_raw_ptr); + QPointer<QDialog> dialog = qobject_cast<QDialog*>(q_obj); + + if (dialog == nullptr) { + LOG_E() << "convert dialog raw ptr to qdialog failed"; + return false; + } + + QPointer<QWidget> parent = nullptr; + if (parent_raw_ptr != nullptr) { + auto* qp_obj = static_cast<QObject*>(parent_raw_ptr); + parent = qobject_cast<QWidget*>(qp_obj); + + if (parent == nullptr) { + LOG_E() << "convert parent raw ptr to qwidget failed"; + return false; + } + } + + auto* main_thread = QApplication::instance()->thread(); + + LOG_D() << "before entering into main thread, current thread id:" + << QThread::currentThreadId() + << ", dialog thread: " << dialog->thread() + << "main thread: " << main_thread; + + if (dialog->thread() != main_thread) { + LOG_E() << "dialog must be created on main thread"; + return false; + } + + QMetaObject::invokeMethod( + parent == nullptr ? QPointer<QObject>(QApplication::instance()) : parent, + [dialog, parent]() -> int { + LOG_D() << "show qdialog, current thread id:" + << QThread::currentThreadId(); + dialog->setParent(parent); + dialog->show(); + return 0; + }); + + return true; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUICreateGUIObject(QObjectFactory factory, + void* data) -> void* { + QEventLoop loop; + void* object = nullptr; + + QMetaObject::invokeMethod(QApplication::instance(), [&]() -> int { + LOG_D() << "create gui object, current thread id:" + << QThread::currentThreadId(); + object = factory(data); + loop.quit(); + return 0; + }); + + loop.exec(); + return object; +} diff --git a/src/sdk/GFSDKUI.h b/src/sdk/GFSDKUI.h index bf4c3721..f9307cee 100644 --- a/src/sdk/GFSDKUI.h +++ b/src/sdk/GFSDKUI.h @@ -32,15 +32,24 @@ extern "C" { -using EntryFactory = void* (*)(const char*); +using QObjectFactory = void* (*)(void*); struct MetaData { const char* key; const char* value; }; -auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMountEntry(const char* id, - MetaData** meta_data_array, - int meta_data_array_size, - EntryFactory factory) -> int; +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFUIMountEntry(const char* id, MetaData** meta_data_array, + int meta_data_array_size, QObjectFactory factory) -> int; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUICreateGUIObject(QObjectFactory factory, + void* data) -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMainWindowPtr() -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIActiveWindowPtr() -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIShowDialog(void* dialog, + void* parent) -> bool; }
\ No newline at end of file diff --git a/src/sdk/private/CommonUtils.cpp b/src/sdk/private/GFSDKPrivat.cpp index 68afcb5f..71136535 100644 --- a/src/sdk/private/CommonUtils.cpp +++ b/src/sdk/private/GFSDKPrivat.cpp @@ -26,7 +26,7 @@ * */ -#include "CommonUtils.h" +#include "GFSDKPrivat.h" #include <core/utils/MemoryUtils.h> @@ -34,6 +34,8 @@ #include "GFSDKModule.h" +Q_LOGGING_CATEGORY(sdk, "sdk") + auto GFStrDup(const QString& str) -> char* { auto utf8_str = str.toUtf8(); auto* c_str = static_cast<char*>( @@ -58,9 +60,10 @@ auto CharArrayToQMap(char** char_array, int size) -> QMap<QString, QString> { QMap<QString, QString> map; for (int i = 0; i < size; i += 2) { QString const key = GFUnStrDup(char_array[i]); - QString const value = QString::fromUtf8(char_array[i + 1]); + QString const value = GFUnStrDup(char_array[i + 1]); map.insert(key, value); } + return map; } @@ -100,4 +103,32 @@ auto ConvertEventParamsToMap(GFModuleEventParam* params) } return param_map; -}
\ No newline at end of file +} + +auto CharArrayToQList(char** char_array, int size) -> QStringList { + QStringList list; + for (int i = 0; i < size; ++i) { + if (char_array[i] != nullptr) { + QString value = GFUnStrDup(char_array[i]); + list.append(value); + } + } + GpgFrontend::SecureFree(char_array); + return list; +} + +auto QListToCharArray(const QStringList& list) -> char** { + char** char_array = static_cast<char**>( + GpgFrontend::SecureMalloc(list.size() * sizeof(char*))); + + int index = 0; + for (const QString& item : list) { + QByteArray value = item.toUtf8(); + char_array[index] = + static_cast<char*>(GpgFrontend::SecureMalloc(value.size() + 1)); + std::strcpy(char_array[index], value.constData()); + index++; + } + + return char_array; +} diff --git a/src/sdk/private/CommonUtils.h b/src/sdk/private/GFSDKPrivat.h index 9539befe..7cfa2c76 100644 --- a/src/sdk/private/CommonUtils.h +++ b/src/sdk/private/GFSDKPrivat.h @@ -28,6 +28,15 @@ #pragma once +// declare logging category +Q_DECLARE_LOGGING_CATEGORY(sdk) + +#define LOG_D() qCDebug(sdk) +#define LOG_I() qCInfo(sdk) +#define LOG_W() qCWarning(sdk) +#define LOG_E() qCCritical(sdk) +#define LOG_F() qCFatal(sdk) + struct GFModuleEventParam; /** @@ -77,4 +86,22 @@ auto QMapToCharArray(const QMap<QString, QString> &map, int &size) -> char **; * @return QMap<QString, QString> */ auto ConvertEventParamsToMap(GFModuleEventParam *params) - -> QMap<QString, QString>;
\ No newline at end of file + -> QMap<QString, QString>; + +/** + * @brief + * + * @param char_array + * @param size + * @return QStringList + */ +auto CharArrayToQList(char **char_array, int size) -> QStringList; + +/** + * @brief + * + * @param list + * @param size + * @return char** + */ +auto QListToCharArray(const QStringList &list) -> char **;
\ No newline at end of file diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index c9690c2a..7a8b9450 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -52,11 +52,4 @@ if(XCODE_BUILD) XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${GPGFRONTEND_XOCDE_CODE_SIGN_IDENTITY}") endif() -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - # lib output path - set_target_properties(gpgfrontend_test PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) -endif() - add_test(AllTestsInGpgFrontend gpgfrontend_test) diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index a253d0ee..644053be 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -69,13 +69,6 @@ target_include_directories(gpgfrontend_ui PUBLIC # using std c++ 17 target_compile_features(gpgfrontend_ui PUBLIC cxx_std_17) -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - # lib output path - set_target_properties(gpgfrontend_ui PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) -endif() - # for xcode archive build if(XCODE_BUILD) set_target_properties(gpgfrontend_ui diff --git a/src/ui/GpgFrontendUIInit.cpp b/src/ui/GpgFrontendUIInit.cpp index 0baa1fbb..bc41f5f7 100644 --- a/src/ui/GpgFrontendUIInit.cpp +++ b/src/ui/GpgFrontendUIInit.cpp @@ -220,7 +220,7 @@ auto RunGpgFrontendUI(QApplication* app) -> int { auto main_window = SecureCreateUniqueObject<GpgFrontend::UI::MainWindow>(); // pre-check, if application need to restart - if (CommonUtils::GetInstance()->isApplicationNeedRestart()) { + if (CommonUtils::GetInstance()->IsApplicationNeedRestart()) { FLOG_D("application need to restart, before main window init."); return kDeepRestartCode; } diff --git a/src/ui/UIModuleManager.cpp b/src/ui/UIModuleManager.cpp index ed18f473..89621f9c 100644 --- a/src/ui/UIModuleManager.cpp +++ b/src/ui/UIModuleManager.cpp @@ -61,7 +61,7 @@ auto UIModuleManager::DeclareMountPoint( auto UIModuleManager::MountEntry(const QString& id, QMap<QString, QString> meta_data, - EntryFactory factory) -> bool { + QObjectFactory factory) -> bool { if (id.isEmpty() || !mount_points_.contains(id)) return false; if (factory == nullptr) return false; @@ -82,7 +82,7 @@ auto UIModuleManager::QueryMountedEntries(QString id) -> QList<MountedUIEntry> { } auto MountedUIEntry::GetWidget() const -> QWidget* { - return qobject_cast<QWidget*>(static_cast<QObject*>(factory_(id_.toUtf8()))); + return qobject_cast<QWidget*>(static_cast<QObject*>(factory_(nullptr))); } auto MountedUIEntry::GetMetaDataByDefault( @@ -176,4 +176,25 @@ void UIModuleManager::TranslateAllModulesParams() { #endif } +auto UIModuleManager::RegisterQObject(const QString& id, QObject* p) -> bool { + if (id.isEmpty() || registered_qobjects_.contains(id)) return false; + + registered_qobjects_[id] = p; + return true; +} + +auto UIModuleManager::GetQObject(const QString& id) -> QObject* { + return registered_qobjects_.value(id, nullptr); +} + +auto UIModuleManager::GetCapsule(const QString& uuid) -> std::any { + return capsule_.take(uuid); +} + +auto UIModuleManager::MakeCapsule(std::any v) -> QString { + auto uuid = QUuid::createUuid().toString(); + capsule_[uuid] = std::move(v); + return uuid; +} + } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/UIModuleManager.h b/src/ui/UIModuleManager.h index 15f80030..2388dc71 100644 --- a/src/ui/UIModuleManager.h +++ b/src/ui/UIModuleManager.h @@ -41,7 +41,7 @@ struct MountedUIEntry { QString id_; QMap<QString, QString> meta_data_; QMap<QString, QString> meta_data_translated_; - EntryFactory factory_; + QObjectFactory factory_; MountedUIEntry() = default; @@ -92,7 +92,7 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager * @return false */ auto MountEntry(const QString& id, QMap<QString, QString> meta_data, - EntryFactory factory) -> bool; + QObjectFactory factory) -> bool; /** * @brief @@ -113,6 +113,38 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager /** * @brief * + * @param id + * @return auto + */ + auto RegisterQObject(const QString& id, QObject*) -> bool; + + /** + * @brief + * + * @param id + * @return auto + */ + auto GetQObject(const QString& id) -> QObject*; + + /** + * @brief + * + * @param id + * @return auto + */ + auto MakeCapsule(std::any) -> QString; + + /** + * @brief + * + * @param id + * @return auto + */ + auto GetCapsule(const QString& uuid) -> std::any; + + /** + * @brief + * */ void RegisterAllModuleTranslators(); @@ -128,6 +160,8 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager QMap<QString, ModuleTranslatorInfo> translator_data_readers_; QList<QTranslator*> registered_translators_; QList<QByteArray> read_translator_data_list_; + QMap<QString, QPointer<QObject>> registered_qobjects_; + QMap<QString, std::any> capsule_; }; } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index b8fc9961..d8c0059b 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -55,8 +55,8 @@ namespace GpgFrontend::UI { -std::unique_ptr<GpgFrontend::UI::CommonUtils> - GpgFrontend::UI::CommonUtils::instance_ = nullptr; +QScopedPointer<CommonUtils> CommonUtils::instance = + QScopedPointer<CommonUtils>(nullptr); void show_verify_details(QWidget *parent, int channel, InfoBoardWidget *info_board, GpgError error, @@ -121,10 +121,10 @@ void process_operation(QWidget *parent, const QString &waiting_title, } auto CommonUtils::GetInstance() -> CommonUtils * { - if (instance_ == nullptr) { - instance_ = std::make_unique<CommonUtils>(); + if (!instance) { + instance.reset(new CommonUtils()); } - return instance_.get(); + return instance.get(); } CommonUtils::CommonUtils() : QWidget(nullptr) { @@ -373,7 +373,7 @@ void CommonUtils::SlotExecuteGpgCommand( void CommonUtils::SlotImportKeyFromKeyServer( int channel, const KeyIdArgsList &key_ids, - const ImportCallbackFunctiopn &callback) { + const ImportCallbackFunction &callback) { auto target_keyserver = KeyServerSO(SettingsObject("key_server")).GetTargetServer(); if (target_keyserver.isEmpty()) { @@ -554,7 +554,7 @@ void CommonUtils::SlotRestartApplication(int code) { } } -auto CommonUtils::isApplicationNeedRestart() -> bool { +auto CommonUtils::IsApplicationNeedRestart() -> bool { return application_need_to_restart_at_once_; } diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index dab442dd..6aae75ba 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -89,7 +89,7 @@ class CommonUtils : public QWidget { * @brief * */ - using ImportCallbackFunctiopn = + using ImportCallbackFunction = std::function<void(const QString&, const QString&, size_t, size_t)>; /** @@ -131,7 +131,7 @@ class CommonUtils : public QWidget { * @brief * */ - auto isApplicationNeedRestart() -> bool; + auto IsApplicationNeedRestart() -> bool; /** * @brief @@ -238,7 +238,7 @@ class CommonUtils : public QWidget { */ static void SlotImportKeyFromKeyServer( int channel, const GpgFrontend::KeyIdArgsList& key_ids, - const GpgFrontend::UI::CommonUtils::ImportCallbackFunctiopn& callback); + const CommonUtils::ImportCallbackFunction& callback); /** * @brief @@ -282,7 +282,8 @@ class CommonUtils : public QWidget { std::shared_ptr<GpgImportInformation>); private: - static std::unique_ptr<CommonUtils> instance_; ///< + static QScopedPointer<CommonUtils> instance; ///< + bool application_need_to_restart_at_once_ = false; }; diff --git a/src/ui/dialog/SignersPicker.cpp b/src/ui/dialog/SignersPicker.cpp index 507ee467..dcd23a6b 100644 --- a/src/ui/dialog/SignersPicker.cpp +++ b/src/ui/dialog/SignersPicker.cpp @@ -82,6 +82,16 @@ auto SignersPicker::GetCheckedSigners() -> GpgFrontend::KeyIdArgsListPtr { return key_list_->GetCheckedPrivateKey(); } +auto SignersPicker::GetCheckedSignerKeyIds() -> QStringList { + auto priv_keys = key_list_->GetCheckedPrivateKey(); + + QStringList r; + for (const auto& priv_key : *priv_keys) { + r.append(priv_key); + } + return r; +} + auto SignersPicker::GetStatus() const -> bool { return this->accepted_; } } // namespace GpgFrontend::UI diff --git a/src/ui/dialog/SignersPicker.h b/src/ui/dialog/SignersPicker.h index 0b1b7cb9..accf6952 100644 --- a/src/ui/dialog/SignersPicker.h +++ b/src/ui/dialog/SignersPicker.h @@ -59,6 +59,12 @@ class SignersPicker : public GeneralDialog { auto GetCheckedSigners() -> KeyIdArgsListPtr; /** + * @brief Get the Checked Signer Key Ids object + * + * @return QStringList + */ + auto GetCheckedSignerKeyIds() -> QStringList; + /** * * @return */ diff --git a/src/ui/dialog/controller/GnuPGControllerDialog.cpp b/src/ui/dialog/controller/GnuPGControllerDialog.cpp index fe334e45..72e9c276 100644 --- a/src/ui/dialog/controller/GnuPGControllerDialog.cpp +++ b/src/ui/dialog/controller/GnuPGControllerDialog.cpp @@ -272,7 +272,9 @@ void GnuPGControllerDialog::set_settings() { buffered_key_db_so_ = GetGpgKeyDatabaseInfos(); editable_key_db_so_ = buffered_key_db_so_; - editable_key_db_so_.pop_front(); + if (!editable_key_db_so_.isEmpty()) { + editable_key_db_so_.pop_front(); + } this->slot_refresh_key_database_table(); } @@ -380,7 +382,9 @@ void GnuPGControllerDialog::slot_add_new_key_database() { key_databases.append(key_database); editable_key_db_so_ = buffered_key_db_so_; - editable_key_db_so_.pop_front(); + if (!editable_key_db_so_.isEmpty()) { + editable_key_db_so_.pop_front(); + } // refresh ui slot_refresh_key_database_table(); @@ -429,6 +433,16 @@ void GnuPGControllerDialog::slot_remove_existing_key_database() { for (int i = 0; i < row_size; i++) { auto* const item = ui_->keyDatabaseTable->item(i, 1); if (!item->isSelected()) continue; + + QMessageBox::StandardButton reply = QMessageBox::question( + this, tr("Confirm Deletion"), + tr("Are you sure you want to delete the selected key database?"), + QMessageBox::Yes | QMessageBox::No); + + if (reply != QMessageBox::Yes) { + return; + } + key_databases.remove(i); break; } diff --git a/src/ui/dialog/controller/ModuleControllerDialog.cpp b/src/ui/dialog/controller/ModuleControllerDialog.cpp index 8bae2ccc..77949a61 100644 --- a/src/ui/dialog/controller/ModuleControllerDialog.cpp +++ b/src/ui/dialog/controller/ModuleControllerDialog.cpp @@ -46,7 +46,7 @@ ModuleControllerDialog::ModuleControllerDialog(QWidget* parent) ui_->setupUi(this); ui_->actionsGroupBox->hide(); - ui_->moduleInfoLabel->setText(tr("Module Informations")); + ui_->moduleInfoLabel->setText(tr("Module Information")); ui_->actionsGroupBox->setTitle(tr("Actions")); ui_->showModsDirButton->setText(tr("Show Mods Directory")); diff --git a/src/ui/main_window/GeneralMainWindow.cpp b/src/ui/main_window/GeneralMainWindow.cpp index 1cdb44e0..76eda3fc 100644 --- a/src/ui/main_window/GeneralMainWindow.cpp +++ b/src/ui/main_window/GeneralMainWindow.cpp @@ -29,6 +29,7 @@ #include "GeneralMainWindow.h" #include "core/model/SettingsObject.h" +#include "ui/UIModuleManager.h" #include "ui/struct/settings_object/AppearanceSO.h" #include "ui/struct/settings_object/WindowStateSO.h" @@ -36,9 +37,10 @@ namespace GpgFrontend::UI { class GeneralWindowState {}; -GpgFrontend::UI::GeneralMainWindow::GeneralMainWindow(QString name, +GpgFrontend::UI::GeneralMainWindow::GeneralMainWindow(QString id, QWidget *parent) - : QMainWindow(parent), name_(std::move(name)) { + : QMainWindow(parent), id_(std::move(id)) { + UIModuleManager::GetInstance().RegisterQObject(id_, this); slot_restore_settings(); } @@ -51,7 +53,7 @@ void GpgFrontend::UI::GeneralMainWindow::closeEvent(QCloseEvent *event) { void GpgFrontend::UI::GeneralMainWindow::slot_restore_settings() noexcept { try { - WindowStateSO window_state(SettingsObject(name_ + "_state")); + WindowStateSO window_state(SettingsObject(id_ + "_state")); if (!window_state.window_state_data.isEmpty()) { // state sets pos & size of dock-widgets @@ -112,13 +114,13 @@ void GpgFrontend::UI::GeneralMainWindow::slot_restore_settings() noexcept { icon_style_ = toolButtonStyle(); } catch (...) { - LOG_W() << "general main window: " << name_ << ", caught exception"; + LOG_W() << "general main window: " << id_ << ", caught exception"; } } void GpgFrontend::UI::GeneralMainWindow::slot_save_settings() noexcept { try { - SettingsObject general_windows_state(name_ + "_state"); + SettingsObject general_windows_state(id_ + "_state"); // update geo of current dialog size_ = this->size(); @@ -134,7 +136,7 @@ void GpgFrontend::UI::GeneralMainWindow::slot_save_settings() noexcept { general_windows_state.Store(window_state.Json()); } catch (...) { - LOG_W() << "general main window: " << name_ << ", caught exception"; + LOG_W() << "general main window: " << id_ << ", caught exception"; } } @@ -197,4 +199,6 @@ void GeneralMainWindow::update_rect_cache() { this->parent_rect_ = QRect{0, 0, 0, 0}; } } + +auto GeneralMainWindow::GetId() const -> QString { return id_; } } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/main_window/GeneralMainWindow.h b/src/ui/main_window/GeneralMainWindow.h index 7ef1234f..d7d28930 100644 --- a/src/ui/main_window/GeneralMainWindow.h +++ b/src/ui/main_window/GeneralMainWindow.h @@ -41,13 +41,20 @@ class GeneralMainWindow : public QMainWindow { * * @param name */ - explicit GeneralMainWindow(QString name, QWidget* parent = nullptr); + explicit GeneralMainWindow(QString id, QWidget* parent = nullptr); /** * */ ~GeneralMainWindow() override; + /** + * @brief Get the Id object + * + * @return QString + */ + [[nodiscard]] auto GetId() const -> QString; + protected: /** * @@ -66,7 +73,7 @@ class GeneralMainWindow : public QMainWindow { */ void movePosition2CenterOfParent(); - QSize icon_size_{}; ///< + QSize icon_size_; ///< int font_size_{}; ///< Qt::ToolButtonStyle icon_style_; ///< @@ -88,9 +95,9 @@ class GeneralMainWindow : public QMainWindow { void update_rect_cache(); private: - QString name_; ///< - QPoint pos_; ///< - QSize size_; ///< + QString id_; ///< + QPoint pos_; ///< + QSize size_; ///< QRect rect_; QRect screen_rect_; QRect parent_rect_; diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index 47e7487b..1fcb14df 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -29,9 +29,7 @@ #include "MainWindow.h" #include "core/function/CacheManager.h" -#include "core/function/CoreSignalStation.h" #include "core/function/GlobalSettingStation.h" -#include "core/model/GpgPassphraseContext.h" #include "core/model/SettingsObject.h" #include "core/module/ModuleManager.h" #include "ui/UISignalStation.h" @@ -74,7 +72,7 @@ void MainWindow::Init() noexcept { attachment_dock_created_ = false; /* Variable containing if restart is needed */ - this->SlotSetRestartNeeded(false); + this->SlotSetRestartNeeded(0); // init menu bar this->setMenuBar(new QMenuBar()); diff --git a/src/ui/main_window/MainWindow.h b/src/ui/main_window/MainWindow.h index ef3fda13..40dd5862 100644 --- a/src/ui/main_window/MainWindow.h +++ b/src/ui/main_window/MainWindow.h @@ -63,7 +63,6 @@ class MainWindow : public GeneralMainWindow { static constexpr OperationType kVerify = 1 << 3; static constexpr OperationType kEncryptAndSign = 1 << 4; static constexpr OperationType kDecryptAndVerify = 1 << 5; - static constexpr OperationType kVerifyEMail = 1 << 6; }; /** @@ -178,6 +177,30 @@ class MainWindow : public GeneralMainWindow { void SlotDecryptEML(); /** + * @brief + * + */ + void SlotSignEML(); + + /** + * @brief + * + */ + void SlotEncryptEML(); + + /** + * @brief + * + */ + void SlotEncryptSignEML(); + + /** + * @brief + * + */ + void SlotDecryptVerifyEML(); + + /** * @details decrypt and verify the text of currently active textedit-page * with the currently checked keys */ @@ -299,18 +322,6 @@ class MainWindow : public GeneralMainWindow { */ void SlotGeneralDecryptVerify(bool); - /** - * @brief - * - */ - void SlotGeneralDecryptEMail(bool); - - /** - * @brief - * - */ - void SlotGeneralVerifyEMail(bool); - private slots: /** @@ -532,6 +543,15 @@ class MainWindow : public GeneralMainWindow { void slot_decrypt_email_by_eml_data_result_helper( const QMap<QString, QString>& p); + /** + * @brief + * + * @param err_code + * @param error_string + * @return QString + */ + auto slot_handle_module_error(const QMap<QString, QString>& p) -> bool; + private: /** * @details Create actions for the main-menu and the context-menu of the @@ -601,7 +621,6 @@ class MainWindow : public GeneralMainWindow { QMenu* key_menu_{}; ///< Submenu for key-operations QMenu* view_menu_{}; ///< Submenu for view operations QMenu* import_key_menu_{}; ///< Submenu for import operations - QMenu* email_menu_{}; ///< Submenu for email operations QToolBar* crypt_tool_bar_{}; ///< Toolbar holding crypt actions QToolBar* file_tool_bar_{}; ///< Toolbar holding file actions @@ -609,7 +628,6 @@ class MainWindow : public GeneralMainWindow { QToolBar* special_edit_tool_bar_{}; ///< Toolbar holding special edit actions QToolBar* key_tool_bar_{}; ///< Toolbar holding key operations - QToolBar* email_tool_bar_{}; QToolButton* import_button_{}; ///< Tool button for import dropdown menu in toolbar QDockWidget* key_list_dock_{}; ///< Encrypt Dock @@ -617,6 +635,7 @@ class MainWindow : public GeneralMainWindow { QDockWidget* info_board_dock_{}; QAction* new_tab_act_{}; ///< Action to create new tab + QAction* new_email_tab_act_{}; ///< Action to create email tab QAction* switch_tab_up_act_{}; ///< Action to switch tab up QAction* switch_tab_down_act_{}; ///< Action to switch tab down QAction* open_act_{}; ///< Action to open file @@ -681,9 +700,6 @@ class MainWindow : public GeneralMainWindow { QAction* import_key_from_clipboard_act_{}; ///< QAction* import_key_from_key_server_act_{}; ///< - QAction* verify_email_by_eml_data_act_{}; ///< - QAction* decrypt_email_by_eml_data_act_{}; - QLabel* status_bar_icon_{}; ///< KeyList* m_key_list_{}; ///< diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index c7579ff9..ea772b3e 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -31,12 +31,18 @@ #include "core/function/gpg/GpgKeyGetter.h" #include "core/function/gpg/GpgKeyImportExporter.h" #include "core/function/result_analyse/GpgDecryptResultAnalyse.h" +#include "core/function/result_analyse/GpgEncryptResultAnalyse.h" +#include "core/function/result_analyse/GpgSignResultAnalyse.h" #include "core/model/GpgDecryptResult.h" +#include "core/model/GpgEncryptResult.h" +#include "core/model/GpgSignResult.h" #include "core/module/ModuleManager.h" #include "core/typedef/GpgTypedef.h" #include "core/utils/CommonUtils.h" #include "core/utils/GpgUtils.h" +#include "ui/UIModuleManager.h" #include "ui/UserInterfaceUtils.h" +#include "ui/dialog/SignersPicker.h" #include "ui/dialog/help/AboutDialog.h" #include "ui/dialog/import_export/KeyUploadDialog.h" #include "ui/dialog/keypair_details/KeyDetailsDialog.h" @@ -350,53 +356,31 @@ void MainWindow::slot_import_key_from_edit() { } void MainWindow::slot_verify_email_by_eml_data(const QByteArray& buffer) { - Module::TriggerEvent( - "EMAIL_VERIFY_EML_DATA", - { - {"eml_data", QString::fromLatin1(buffer.toBase64())}, - }, - [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, - Module::Event::Params p) { - LOG_D() << "EMAIL_VERIFY_EML_DATA callback: " << i << ei; - if (p["ret"] != "0" || !p["err"].isEmpty()) { - LOG_E() << "An error occurred trying to verify email, " - << "error message: " << p["err"]; - - if (p["ret"] == "-2") { - QString detailed_error = p["err"]; - - QString info = - tr("# EML Data Error\n\n" - "The provided EML data does not conform to the " - "structure described in RFC 3156 and cannot be " - "validated.\n\n" - "Details: %1\n\n" - "What is EML Data?\n" - "EML is a file format used to represent email messages. " - "It typically contains the entire contents of an email, " - "including headers, " - "body text, attachments, and metadata. In order to validate " - "the email properly, it is necessary to provide the " - "complete, original EML " - "data.\n\n" - "For more information about the expected EML structure, " - "please refer to the RFC 3156 standard:\n" - "%2\n\n" - "Please ensure the EML data follows the standard and try " - "again.") - .arg(detailed_error) - .arg("https://www.rfc-editor.org/rfc/rfc3156.txt"); - slot_refresh_info_board(-2, info); - } - - return; - } + CommonUtils::WaitForOpera( + this, tr("Verifying"), [this, buffer](const OperaWaitingHd& hd) { + Module::TriggerEvent( + "EMAIL_VERIFY_EML_DATA", + { + {"eml_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + }, + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_VERIFY_EML_DATA callback: " << i << ei; + + // end waiting dialog + hd(); - if (p.contains("signature") && p.contains("mime")) { - slot_verify_email_by_eml_data_result_helper(p); - } + // check if error occurred + if (slot_handle_module_error(p)) return -1; - LOG_E() << "mime or signature data is missing"; + if (p.contains("signature") && p.contains("mime")) { + slot_verify_email_by_eml_data_result_helper(p); + } + + return 0; + }); }); } @@ -405,49 +389,21 @@ void MainWindow::slot_decrypt_email_by_eml_data(const QByteArray& buffer) { "EMAIL_DECRYPT_EML_DATA", { {"eml_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, }, [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, Module::Event::Params p) { LOG_D() << "EMAIL_DECRYPT_EML_DATA callback: " << i << ei; - if (p["ret"] != "0" || !p["err"].isEmpty()) { - LOG_E() << "An error occurred trying to decrypt email, " - << "error message: " << p["err"]; - - if (p["ret"] == "-2") { - QString detailed_error = p["err"]; - - QString info = - tr("# EML Data Error\n\n" - "The provided EML data does not conform to the " - "structure described in RFC 3156 and cannot be " - "validated.\n\n" - "Details: %1\n\n" - "What is EML Data?\n" - "EML is a file format used to represent email messages. " - "It typically contains the entire contents of an email, " - "including headers, " - "body text, attachments, and metadata. In order to validate " - "the email properly, it is necessary to provide the " - "complete, original EML " - "data.\n\n" - "For more information about the expected EML structure, " - "please refer to the RFC 3156 standard:\n" - "%2\n\n" - "Please ensure the EML data follows the standard and try " - "again.") - .arg(detailed_error) - .arg("https://www.rfc-editor.org/rfc/rfc3156.txt"); - slot_refresh_info_board(-2, info); - } - - return; - } - if (p.contains("encrypted")) { + // check if error occurred + if (slot_handle_module_error(p)) return -1; + + if (p.contains("eml_data")) { slot_decrypt_email_by_eml_data_result_helper(p); } - LOG_E() << "mime or signature data is missing"; + return 0; }); } @@ -540,50 +496,25 @@ void MainWindow::slot_verify_email_by_eml_data_result_helper( .arg(prm_micalg_value)); email_info.append("\n"); - // set input buffer - auto raw_data_buffer = GFBuffer(mime); - auto signature_buffer = GFBuffer(signature); + if (!p["capsule_id"].isEmpty()) { + auto v = + UIModuleManager::GetInstance().GetCapsule(p.value("capsule_id", "")); - CommonUtils::WaitForOpera( - this, tr("Verifying"), - [this, email_info, raw_data_buffer, - signature_buffer](const OperaWaitingHd& hd) { - GpgFrontend::GpgBasicOperator::GetInstance( - m_key_list_->GetCurrentGpgContextChannel()) - .Verify( - raw_data_buffer, signature_buffer, - [this, email_info, hd](GpgError err, - const DataObjectPtr& data_obj) { - // stop waiting - hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgVerifyResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - auto verify_result = - ExtractParams<GpgVerifyResult>(data_obj, 0); - - // analyse result - auto result_analyse = GpgVerifyResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - verify_result); - result_analyse.Analyse(); - auto verify_result_report = result_analyse.GetResultReport(); + try { + auto sign_result = std::any_cast<GpgVerifyResult>(v); + auto result_analyse = + GpgVerifyResultAnalyse(m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, sign_result); + result_analyse.Analyse(); - slot_refresh_info_board(result_analyse.GetStatus(), - email_info + verify_result_report); + slot_refresh_info_board(result_analyse.GetStatus(), + email_info + result_analyse.GetResultReport()); - if (!result_analyse.GetUnknownSignatures().isEmpty() && - Module::IsModuleActivate(kKeyServerSyncModuleID)) { - slot_verifying_unknown_signature_helper(result_analyse); - return; - } - }); - }); + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] << "convert to real type failed" + << e.what(); + } + } } void MainWindow::slot_eml_verify_show_helper(const QString& email_info, @@ -628,14 +559,15 @@ void MainWindow::SlotDecryptEML() { void MainWindow::slot_decrypt_email_by_eml_data_result_helper( const QMap<QString, QString>& p) { - const auto encrypted = QByteArray::fromBase64(p["encrypted"].toLatin1()); - auto timestamp = p.value("datetime", "-1").toLongLong(); auto datetime = tr("None"); if (timestamp > 0) { datetime = QLocale().toString(QDateTime::fromMSecsSinceEpoch(timestamp)); } + const auto eml_data = QByteArray::fromBase64(p["eml_data"].toLatin1()); + edit_->SlotFillTextEditWithText(eml_data); + QString email_info; email_info.append("# E-Mail Information\n\n"); email_info.append(QString("- %1: %2\n") @@ -654,39 +586,414 @@ void MainWindow::slot_decrypt_email_by_eml_data_result_helper( email_info.append("\n"); - // data to transfer into task - auto buffer = GFBuffer(encrypted); + if (!p["capsule_id"].isEmpty()) { + auto v = + UIModuleManager::GetInstance().GetCapsule(p.value("capsule_id", "")); + + try { + auto sign_result = std::any_cast<GpgDecryptResult>(v); + auto result_analyse = + GpgDecryptResultAnalyse(m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, sign_result); + result_analyse.Analyse(); + + slot_refresh_info_board(result_analyse.GetStatus(), + email_info + result_analyse.GetResultReport()); + + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] << "convert to real type failed" + << e.what(); + } + } +} + +void MainWindow::SlotEncryptEML() { + if (edit_->TabCount() == 0 || edit_->CurEMailPage() == nullptr) return; + auto checked_keys = m_key_list_->GetCheckedKeys(); + + if (checked_keys.isEmpty()) { + QMessageBox::warning(this, tr("No Key Selected"), + tr("Please select a key for encrypt the EML.")); + return; + } + auto buffer = edit_->CurPlainText().toUtf8(); CommonUtils::WaitForOpera( - this, tr("Decrypting"), [this, buffer](const OperaWaitingHd& hd) { - GpgFrontend::GpgBasicOperator::GetInstance( - m_key_list_->GetCurrentGpgContextChannel()) - .Decrypt(buffer, [this, hd](GpgError err, - const DataObjectPtr& data_obj) { - // stop waiting + this, tr("Encrypting"), + [this, buffer, checked_keys](const OperaWaitingHd& hd) { + Module::TriggerEvent( + "EMAIL_ENCRYPT_EML_DATA", + { + {"body_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + {"encrypt_keys", checked_keys.join(';')}, + }, + + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_ENCRYPT_EML_DATA callback: " << i << ei; + + // close waiting dialog hd(); - if (CheckGpgError(err) == GPG_ERR_USER_1 || data_obj == nullptr || - !data_obj->Check<GpgDecryptResult, GFBuffer>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); + if (p["ret"] != "0" || !p["err"].isEmpty()) { + LOG_E() << "An error occurred trying to decrypt email, " + << "error message: " << p["err"]; + return; } - auto decrypt_result = - ExtractParams<GpgDecryptResult>(data_obj, 0); - auto out_buffer = ExtractParams<GFBuffer>(data_obj, 1); - auto result_analyse = GpgDecryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - decrypt_result); - result_analyse.Analyse(); - slot_result_analyse_show_helper(result_analyse); - - if (CheckGpgError(err) == GPG_ERR_NO_ERROR) { - edit_->SlotFillTextEditWithText( - out_buffer.ConvertToQByteArray()); + + if (!p["eml_data"].isEmpty()) { + edit_->SlotSetText2CurEMailPage(p.value("eml_data", "")); + } + + if (!p["capsule_id"].isEmpty()) { + auto v = UIModuleManager::GetInstance().GetCapsule( + p.value("capsule_id", "")); + + try { + auto encr_result = std::any_cast<GpgEncryptResult>(v); + auto result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, encr_result); + result_analyse.Analyse(); + slot_result_analyse_show_helper(result_analyse); + + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] + << "convert to real type failed" << e.what(); + } + } + + LOG_E() << "mime or signature data is missing"; + }); + }); +} + +void MainWindow::SlotSignEML() { + if (edit_->TabCount() == 0 || edit_->CurEMailPage() == nullptr) return; + auto checked_keys = m_key_list_->GetCheckedKeys(); + + if (checked_keys.isEmpty()) { + QMessageBox::warning(this, tr("No Key Selected"), + tr("Please select a key for signing the EML.")); + return; + } + + if (checked_keys.size() > 1) { + QMessageBox::warning(this, tr("Multiple Keys Selected"), + tr("Please select only one key to sign the EML.")); + return; + } + + auto buffer = edit_->CurPlainText().toUtf8(); + + CommonUtils::WaitForOpera( + this, tr("Signing"), + [this, buffer, checked_keys](const OperaWaitingHd& hd) { + Module::TriggerEvent( + "EMAIL_SIGN_EML_DATA", + { + {"body_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + {"sign_key", checked_keys.front()}, + }, + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_SIGN_EML_DATA callback: " << i << ei; + + // close waiting dialog + hd(); + + // check if error occurred + if (slot_handle_module_error(p)) return -1; + + if (!p["eml_data"].isEmpty()) { + edit_->SlotSetText2CurEMailPage(p.value("eml_data", "")); + } + + if (!p["capsule_id"].isEmpty()) { + auto v = UIModuleManager::GetInstance().GetCapsule( + p.value("capsule_id", "")); + + try { + auto sign_result = std::any_cast<GpgSignResult>(v); + auto result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, sign_result); + result_analyse.Analyse(); + slot_result_analyse_show_helper(result_analyse); + + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] + << "convert to real type failed" << e.what(); + } } + + return 0; }); }); } +void MainWindow::SlotEncryptSignEML() { + if (edit_->TabCount() == 0 || edit_->CurEMailPage() == nullptr) return; + auto checked_keys = m_key_list_->GetCheckedKeys(); + + if (checked_keys.isEmpty()) { + QMessageBox::warning(this, tr("No Key Selected"), + tr("Please select a key for encrypt the EML.")); + return; + } + + auto* signers_picker = + new SignersPicker(m_key_list_->GetCurrentGpgContextChannel(), this); + QEventLoop loop; + connect(signers_picker, &SignersPicker::finished, &loop, &QEventLoop::quit); + loop.exec(); + + // return when canceled + if (!signers_picker->GetStatus()) return; + + auto signer_keys = signers_picker->GetCheckedSignerKeyIds(); + + if (signer_keys.isEmpty()) { + QMessageBox::warning(this, tr("No Key Selected"), + tr("Please select a key for signing the EML.")); + return; + } + + if (signer_keys.size() > 1) { + QMessageBox::warning(this, tr("Multiple Keys Selected"), + tr("Please select only one key to sign the EML.")); + return; + } + + auto buffer = edit_->CurPlainText().toUtf8(); + + CommonUtils::WaitForOpera( + this, tr("Encrypting and Signing"), + [this, buffer, checked_keys, signer_keys](const OperaWaitingHd& hd) { + Module::TriggerEvent( + "EMAIL_ENCRYPT_SIGN_EML_DATA", + { + {"body_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + {"sign_key", signer_keys.front()}, + {"encrypt_keys", checked_keys.front()}, + }, + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_ENCRYPT_SIGN_EML_DATA callback: " << i << ei; + + // close waiting dialog + hd(); + + // check if error occurred + if (slot_handle_module_error(p)) return -1; + + if (!p["sign_capsule_id"].isEmpty() && + !p["encr_capsule_id"].isEmpty()) { + auto v1 = UIModuleManager::GetInstance().GetCapsule( + p.value("sign_capsule_id", "")); + auto v2 = UIModuleManager::GetInstance().GetCapsule( + p.value("encr_capsule_id", "")); + + try { + auto sign_result = std::any_cast<GpgSignResult>(v1); + auto encr_result = std::any_cast<GpgSignResult>(v1); + auto sign_result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, sign_result); + auto encr_result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, sign_result); + + sign_result_analyse.Analyse(); + encr_result_analyse.Analyse(); + slot_result_analyse_show_helper(sign_result_analyse, + encr_result_analyse); + + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] + << "convert to real type failed" << e.what(); + } + } + + return 0; + }); + }); +} + +void MainWindow::SlotDecryptVerifyEML() { + if (edit_->TabCount() == 0 || edit_->CurEMailPage() == nullptr) return; + + auto buffer = edit_->CurPlainText().toUtf8(); + + CommonUtils::WaitForOpera( + this, tr("Decrypting and Verifying"), + [this, buffer](const OperaWaitingHd& hd) { + Module::TriggerEvent( + "EMAIL_DECRYPT_VERIFY_EML_DATA", + { + {"eml_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + }, + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_DECRYPT_VERIFY_EML_DATA callback: " << i << ei; + + // close waiting dialog + hd(); + + // check if error occurred + if (slot_handle_module_error(p)) return -1; + + edit_->SlotSetText2CurEMailPage(p.value("eml_data", "")); + + const auto mime = QByteArray::fromBase64(p["mime"].toLatin1()); + const auto signature = + QByteArray::fromBase64(p["signature"].toLatin1()); + const auto part_mime_content_hash = p["mime_hash"]; + const auto prm_micalg_value = p["micalg"]; + + auto timestamp = p.value("datetime", "-1").toLongLong(); + auto datetime = tr("None"); + if (timestamp > 0) { + datetime = QLocale().toString( + QDateTime::fromMSecsSinceEpoch(timestamp)); + } + + QString email_info; + email_info.append("# E-Mail Information\n\n"); + email_info.append(QString("- %1: %2\n") + .arg(tr("From")) + .arg(p.value("from", tr("Unknown")))); + email_info.append(QString("- %1: %2\n") + .arg(tr("To")) + .arg(p.value("to", tr("Unknown")))); + email_info.append(QString("- %1: %2\n") + .arg(tr("Subject")) + .arg(p.value("subject", tr("None")))); + email_info.append(QString("- %1: %2\n") + .arg(tr("CC")) + .arg(p.value("cc", tr("None")))); + email_info.append(QString("- %1: %2\n") + .arg(tr("BCC")) + .arg(p.value("bcc", tr("None")))); + email_info.append( + QString("- %1: %2\n").arg(tr("Date")).arg(datetime)); + + email_info.append("\n"); + email_info.append("# OpenPGP Information\n\n"); + email_info.append(QString("- %1: %2\n") + .arg(tr("Signed EML Data Hash (SHA1)")) + .arg(part_mime_content_hash)); + email_info.append( + QString("- %1: %2\n") + .arg(tr("Message Integrity Check Algorithm")) + .arg(prm_micalg_value)); + email_info.append("\n"); + + if (!p["decr_capsule_id"].isEmpty() && + !p["verify_capsule_id"].isEmpty()) { + auto v1 = UIModuleManager::GetInstance().GetCapsule( + p.value("decr_capsule_id", "")); + auto v2 = UIModuleManager::GetInstance().GetCapsule( + p.value("verify_capsule_id", "")); + + try { + auto decr_result = std::any_cast<GpgDecryptResult>(v1); + auto verify_result = std::any_cast<GpgVerifyResult>(v2); + auto decr_result_analyse = GpgDecryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, decr_result); + auto verify_result_analyse = GpgVerifyResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), + GPG_ERR_NO_ERROR, verify_result); + + decr_result_analyse.Analyse(); + verify_result_analyse.Analyse(); + slot_refresh_info_board( + std::min(decr_result_analyse.GetStatus(), + verify_result_analyse.GetStatus()), + email_info + decr_result_analyse.GetResultReport() + + verify_result_analyse.GetResultReport()); + + } catch (const std::bad_any_cast& e) { + LOG_E() << "capsule" << p["capsule_id"] + << "convert to real type failed" << e.what(); + } + } + + return 0; + }); + }); +} + +auto MainWindow::slot_handle_module_error(const QMap<QString, QString>& p) + -> bool { + if (p["ret"] == "-2") { + QString detailed_error = p["err"]; + + QString info = + tr("# EML Data Error\n\n" + "The provided EML data does not conform to RFC 3156 standards and " + "cannot be processed.\n\n" + "**Details:** %1\n\n" + "### What is EML Data?\n" + "EML is a file format for representing email messages, typically " + "including headers, body text, attachments, and metadata. " + "Complete and properly structured EML data is required for " + "validation.\n\n" + "### Suggested Solutions\n" + "1. Verify the EML data is complete and matches the structure " + "outlined in RFC 3156.\n" + "2. Refer to the official documentation for the EML structure: " + "%2\n\n" + "After correcting the EML data, try the operation again.") + .arg(detailed_error) + .arg("https://www.rfc-editor.org/rfc/rfc3156.txt"); + slot_refresh_info_board(-2, info); + return true; + } + + if (p["ret"] != "0" || !p["err"].isEmpty()) { + LOG_E() << "An error occurred trying to operate email, " + << "error message: " << p["err"]; + + QString error_message = + tr("# Email Operation Error\n\n" + "An error occurred during the email operation. The process " + "could not be completed.\n\n" + "**Details:**\n" + "- **Error Code:** %1\n" + "- **Error Message:** %2\n\n" + "### Possible Causes\n" + "1. The email data may be incomplete or corrupted.\n" + "2. The selected GPG key does not have the necessary " + "permissions.\n" + "3. Issues in the GPG environment or configuration.\n\n" + "### Suggested Solutions\n" + "1. Ensure the email data is complete and follows the expected " + "format.\n" + "2. Verify the GPG key has the required access permissions.\n" + "3. Check your GPG environment and configuration settings.\n" + "4. Review the error details above or application logs for " + "further troubleshooting.\n\n" + "If the issue persists, consider seeking technical support or " + "consulting the documentation.") + .arg(p["ret"]) + .arg(p["err"]); + + slot_refresh_info_board(-1, error_message); + return true; + } + + return false; +} + } // namespace GpgFrontend::UI diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index 105a2374..53fa29a4 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -80,11 +80,6 @@ void MainWindow::slot_switch_menu_control_mode(int index) { decrypt_act_->setDisabled(disable); decrypt_verify_act_->setDisabled(disable); - if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_->setDisabled(disable); - decrypt_email_by_eml_data_act_->setDisabled(disable); - } - redo_act_->setDisabled(disable); undo_act_->setDisabled(disable); zoom_out_act_->setDisabled(disable); @@ -189,11 +184,6 @@ void MainWindow::SlotUpdateCryptoMenuStatus(unsigned int type) { decrypt_act_->setDisabled(true); decrypt_verify_act_->setDisabled(true); - if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_->setDisabled(true); - decrypt_email_by_eml_data_act_->setDisabled(true); - } - // gnupg operations if ((opera_type & MainWindow::OperationMenu::kVerify) != 0U) { verify_act_->setDisabled(false); @@ -213,12 +203,6 @@ void MainWindow::SlotUpdateCryptoMenuStatus(unsigned int type) { if ((opera_type & MainWindow::OperationMenu::kDecryptAndVerify) != 0U) { decrypt_verify_act_->setDisabled(false); } - - // email operations - if (Module::IsModuleActivate(kEmailModuleID) && - (opera_type & MainWindow::OperationMenu::kVerifyEMail) != 0U) { - verify_email_by_eml_data_act_->setDisabled(false); - } } void MainWindow::SlotGeneralEncrypt(bool) { @@ -233,6 +217,12 @@ void MainWindow::SlotGeneralEncrypt(bool) { this->SlotDirectoryEncrypt(path); } } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotEncryptEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) { this->SlotEncrypt(); } @@ -254,6 +244,12 @@ void MainWindow::SlotGeneralDecrypt(bool) { } } } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotDecryptEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) { this->SlotDecrypt(); } @@ -267,6 +263,12 @@ void MainWindow::SlotGeneralSign(bool) { const auto file_info = QFileInfo(path); if (file_info.isFile()) this->SlotFileSign(path); } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotSignEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotSign(); } @@ -278,6 +280,12 @@ void MainWindow::SlotGeneralVerify(bool) { const auto file_info = QFileInfo(path); if (file_info.isFile()) this->SlotFileVerify(path); } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotVerifyEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotVerify(); } @@ -293,6 +301,12 @@ void MainWindow::SlotGeneralEncryptSign(bool) { this->SlotDirectoryEncryptSign(path); } } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotEncryptSignEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) { this->SlotEncryptSign(); } @@ -314,20 +328,15 @@ void MainWindow::SlotGeneralDecryptVerify(bool) { } } } - if (edit_->SlotCurPageTextEdit() != nullptr) { - this->SlotDecryptVerify(); - } -} -void MainWindow::SlotGeneralVerifyEMail(bool) { - if (edit_->SlotCurPageFileTreeView() != nullptr) { - const auto* file_tree_view = edit_->SlotCurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); + if (edit_->CurEMailPage() != nullptr) { + this->SlotDecryptVerifyEML(); + return; + } - const auto file_info = QFileInfo(path); - if (file_info.isFile()) this->SlotFileVerifyEML(path); + if (edit_->SlotCurPageTextEdit() != nullptr) { + this->SlotDecryptVerify(); } - if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotVerifyEML(); } void MainWindow::slot_clean_gpg_password_cache(bool) { @@ -376,15 +385,4 @@ void MainWindow::slot_restart_gpg_components(bool) { }); } -void MainWindow::SlotGeneralDecryptEMail(bool) { - // if (edit_->SlotCurPageFileTreeView() != nullptr) { - // const auto* file_tree_view = edit_->SlotCurPageFileTreeView(); - // const auto path = file_tree_view->GetSelected(); - - // const auto file_info = QFileInfo(path); - // if (file_info.isFile()) this->SlotFileVerify(path); - // } - if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotDecryptEML(); -} - } // namespace GpgFrontend::UI diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 9c2a5003..59ad8401 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -49,8 +49,8 @@ void MainWindow::create_actions() { connect(open_act_, &QAction::triggered, edit_, &TextEdit::SlotOpen); browser_act_ = create_action( - "file_browser", tr("File Browser"), ":/icons/file-browser.png", - tr("Open a file browser"), {QKeySequence(Qt::CTRL | Qt::Key_B)}); + "file_browser", tr("File Panel"), ":/icons/file-operator.png", + tr("Open a file panel"), {QKeySequence(Qt::CTRL | Qt::Key_B)}); connect(browser_act_, &QAction::triggered, this, &MainWindow::slot_open_file_tab); @@ -141,29 +141,29 @@ void MainWindow::create_actions() { /* * Crypt Menu */ - encrypt_act_ = create_action("encrypt", tr("Encrypt"), - ":/icons/encrypted.png", tr("Encrypt Message"), + encrypt_act_ = create_action("encrypt", tr("Encrypt"), ":/icons/lock.png", + tr("Encrypt Message"), {QKeySequence(Qt::CTRL | Qt::Key_E)}); connect(encrypt_act_, &QAction::triggered, this, &MainWindow::SlotGeneralEncrypt); - encrypt_sign_act_ = create_action( - "encrypt_sign", tr("Encrypt Sign"), ":/icons/encrypted_signed.png", - tr("Encrypt and Sign Message"), - {QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_E)}); + encrypt_sign_act_ = + create_action("encrypt_sign", tr("Encrypt Sign"), ":/icons/compress.png", + tr("Encrypt and Sign Message"), + {QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_E)}); connect(encrypt_sign_act_, &QAction::triggered, this, &MainWindow::SlotGeneralEncryptSign); - decrypt_act_ = create_action("decrypt", tr("Decrypt"), - ":/icons/decrypted.png", tr("Decrypt Message"), + decrypt_act_ = create_action("decrypt", tr("Decrypt"), ":/icons/unlock.png", + tr("Decrypt Message"), {QKeySequence(Qt::CTRL | Qt::Key_D)}); connect(decrypt_act_, &QAction::triggered, this, &MainWindow::SlotGeneralDecrypt); - decrypt_verify_act_ = create_action( - "decrypt_verify", tr("Decrypt Verify"), ":/icons/decrypted_verified.png", - tr("Decrypt and Verify Message"), - {QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D)}); + decrypt_verify_act_ = + create_action("decrypt_verify", tr("Decrypt Verify"), + ":/icons/expand.png", tr("Decrypt and Verify Message"), + {QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D)}); connect(decrypt_verify_act_, &QAction::triggered, this, &MainWindow::SlotGeneralDecryptVerify); @@ -260,17 +260,11 @@ void MainWindow::create_actions() { * E-Mail Menu */ if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_ = create_action( - "verify_email_by_eml_data", tr("Verify E-Mail"), - ":/icons/email-check.png", tr("Verify RAW E-Mail Data (EML)")); - connect(verify_email_by_eml_data_act_, &QAction::triggered, this, - &MainWindow::SlotGeneralVerifyEMail); - - decrypt_email_by_eml_data_act_ = create_action( - "decrypt_email_by_eml_data", tr("Decrypt E-Mail"), - ":/icons/email-open.png", tr("Decrypt RAW E-Mail Data (EML)")); - connect(decrypt_email_by_eml_data_act_, &QAction::triggered, this, - &MainWindow::SlotGeneralDecryptEMail); + new_email_tab_act_ = + create_action("new_email_tab", tr("New E-Mail"), ":/icons/email.png", + tr("Create A New E-Mail Tab")); + connect(new_email_tab_act_, &QAction::triggered, edit_, + &TextEdit::SlotNewEMailTab); } /* @@ -407,6 +401,11 @@ void MainWindow::create_actions() { void MainWindow::create_menus() { file_menu_ = menuBar()->addMenu(tr("File")); file_menu_->addAction(new_tab_act_); + + if (Module::IsModuleActivate(kEmailModuleID)) { + file_menu_->addAction(new_email_tab_act_); + } + file_menu_->addAction(browser_act_); file_menu_->addAction(open_act_); file_menu_->addSeparator(); @@ -465,12 +464,6 @@ void MainWindow::create_menus() { advance_menu_->addAction(gnupg_controller_open_act_); advance_menu_->addAction(module_controller_open_act_); - if (Module::IsModuleActivate(kEmailModuleID)) { - email_menu_ = menuBar()->addMenu(tr("E-Mail")); - email_menu_->addAction(verify_email_by_eml_data_act_); - email_menu_->addAction(decrypt_email_by_eml_data_act_); - } - view_menu_ = menuBar()->addMenu(tr("View")); help_menu_ = menuBar()->addMenu(tr("Help")); @@ -494,6 +487,11 @@ void MainWindow::create_tool_bars() { file_tool_bar_ = addToolBar(tr("File")); file_tool_bar_->setObjectName("fileToolBar"); file_tool_bar_->addAction(new_tab_act_); + + if (Module::IsModuleActivate(kEmailModuleID)) { + file_tool_bar_->addAction(new_email_tab_act_); + } + file_tool_bar_->addAction(open_act_); file_tool_bar_->addAction(browser_act_); view_menu_->addAction(file_tool_bar_->toggleViewAction()); @@ -501,9 +499,9 @@ void MainWindow::create_tool_bars() { crypt_tool_bar_ = addToolBar(tr("Operations")); crypt_tool_bar_->setObjectName("cryptToolBar"); crypt_tool_bar_->addAction(encrypt_act_); - crypt_tool_bar_->addAction(encrypt_sign_act_); + // crypt_tool_bar_->addAction(encrypt_sign_act_); crypt_tool_bar_->addAction(decrypt_act_); - crypt_tool_bar_->addAction(decrypt_verify_act_); + // crypt_tool_bar_->addAction(decrypt_verify_act_); crypt_tool_bar_->addAction(sign_act_); crypt_tool_bar_->addAction(verify_act_); view_menu_->addAction(crypt_tool_bar_->toggleViewAction()); @@ -530,14 +528,6 @@ void MainWindow::create_tool_bars() { special_edit_tool_bar_->hide(); view_menu_->addAction(special_edit_tool_bar_->toggleViewAction()); - if (Module::IsModuleActivate(kEmailModuleID)) { - email_tool_bar_ = addToolBar(tr("E-Mail")); - email_tool_bar_->setObjectName("emailToolBar"); - email_tool_bar_->addAction(verify_email_by_eml_data_act_); - email_tool_bar_->addAction(decrypt_email_by_eml_data_act_); - view_menu_->addAction(email_tool_bar_->toggleViewAction()); - } - // Add dropdown menu for key import to keytoolbar import_button_ = new QToolButton(); import_button_->setMenu(import_key_menu_); diff --git a/src/ui/widgets/EMailEditorPage.cpp b/src/ui/widgets/EMailEditorPage.cpp new file mode 100644 index 00000000..e37695be --- /dev/null +++ b/src/ui/widgets/EMailEditorPage.cpp @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "EMailEditorPage.h" + +#include "ui_PlainTextEditor.h" + +namespace GpgFrontend::UI { + +EMailEditorPage::EMailEditorPage() { + this->ui_->encodingLabel->setText("E-Mail"); +} + +EMailEditorPage::EMailEditorPage(const QString& file_path, QWidget* parent) + : PlainTextEditorPage(file_path, parent) { + this->ui_->encodingLabel->setText("E-Mail"); +} +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/EMailEditorPage.h b/src/ui/widgets/EMailEditorPage.h new file mode 100644 index 00000000..a5eb6f50 --- /dev/null +++ b/src/ui/widgets/EMailEditorPage.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <utility> + +#include "ui/GpgFrontendUI.h" +#include "ui/widgets/PlainTextEditorPage.h" + +class Ui_FilePage; + +namespace GpgFrontend::UI { +class EMailEditorPage : public PlainTextEditorPage { + Q_OBJECT + public: + EMailEditorPage(); + + EMailEditorPage(const QString& file_path, QWidget* parent); +}; +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index 4d9ce26a..998b4245 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -160,10 +160,6 @@ void FilePage::update_main_basical_opera_menu(const QString& selected_path) { operation_type |= MainWindow::OperationMenu::kVerify; } - if (info.isFile() && (info.suffix() == "eml")) { - operation_type |= MainWindow::OperationMenu::kVerifyEMail; - } - emit SignalMainWindowlUpdateBasicalOperaMenu(operation_type); } } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 73328885..9498480e 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -328,6 +328,17 @@ auto KeyList::GetChecked() -> KeyIdArgsListPtr { return ret; } +auto KeyList::GetCheckedKeys() -> QStringList { + auto* key_table = qobject_cast<KeyTable*>(ui_->keyGroupTab->currentWidget()); + QStringList key_id_list; + for (int i = 0; i < key_table->GetRowCount(); i++) { + if (key_table->IsRowChecked(i)) { + key_id_list.append(key_table->GetKeyIdByRow(i)); + } + } + return key_id_list; +} + auto KeyList::GetAllPrivateKeys() -> KeyIdArgsListPtr { auto* key_table = qobject_cast<KeyTable*>(ui_->keyGroupTab->currentWidget()); auto ret = std::make_unique<KeyIdArgsList>(); diff --git a/src/ui/widgets/KeyList.h b/src/ui/widgets/KeyList.h index 18c9576e..4216eba8 100644 --- a/src/ui/widgets/KeyList.h +++ b/src/ui/widgets/KeyList.h @@ -145,6 +145,13 @@ class KeyList : public QWidget { auto GetChecked() -> KeyIdArgsListPtr; /** + * @brief Get the Checked Keys object + * + * @return QStringList + */ + auto GetCheckedKeys() -> QStringList; + + /** * @brief Get the Checked object * * @param key_table diff --git a/src/ui/widgets/ModuleListView.cpp b/src/ui/widgets/ModuleListView.cpp index 75396dde..835668b5 100644 --- a/src/ui/widgets/ModuleListView.cpp +++ b/src/ui/widgets/ModuleListView.cpp @@ -37,7 +37,7 @@ ModuleListView::ModuleListView(QWidget *parent) setModel(model_); setEditTriggers(QAbstractItemView::NoEditTriggers); - load_module_informations(); + load_module_information(); } void ModuleListView::currentChanged(const QModelIndex ¤t, @@ -49,7 +49,7 @@ void ModuleListView::currentChanged(const QModelIndex ¤t, } } -void ModuleListView::load_module_informations() { +void ModuleListView::load_module_information() { auto &module_manager = Module::ModuleManager::GetInstance(); auto module_ids = module_manager.ListAllRegisteredModuleID(); diff --git a/src/ui/widgets/ModuleListView.h b/src/ui/widgets/ModuleListView.h index 30922585..04a59797 100644 --- a/src/ui/widgets/ModuleListView.h +++ b/src/ui/widgets/ModuleListView.h @@ -48,6 +48,6 @@ class ModuleListView : public QListView { private: QStandardItemModel *model_; - void load_module_informations(); + void load_module_information(); }; }; // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/PlainTextEditorPage.h b/src/ui/widgets/PlainTextEditorPage.h index c4423378..86ec0a56 100644 --- a/src/ui/widgets/PlainTextEditorPage.h +++ b/src/ui/widgets/PlainTextEditorPage.h @@ -115,8 +115,10 @@ class PlainTextEditorPage : public QWidget { */ void SignalUIBytesDisplayed(); - private: + protected: std::shared_ptr<Ui_PlainTextEditor> ui_; ///< + + private: QString full_file_path_; ///< The path to the file handled in the tab bool sign_marked_{}; ///< true, if the signed header is marked, false if not bool read_done_ = false; ///< diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 2b1f4766..1ea96fde 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -126,18 +126,31 @@ void TextEdit::SlotOpen() { } void TextEdit::SlotSave() { - if (tab_widget_->count() == 0 || SlotCurPageTextEdit() == 0) { + if (tab_widget_->count() == 0) { return; } - QString file_name = SlotCurPageTextEdit()->GetFilePath(); + if (CurEMailPage() != nullptr) { + QString file_name = CurEMailPage()->GetFilePath(); - if (file_name.isEmpty()) { - // QString docname = tabWidget->tabText(tabWidget->currentIndex()); - // docname.remove(0,2); - SlotSaveAs(); - } else { - saveFile(file_name); + if (file_name.isEmpty()) { + SlotSaveAsEML(); + } else { + saveEMLFile(file_name); + } + return; + } + + if (CurTextPage() != nullptr) { + QString file_name = SlotCurPageTextEdit()->GetFilePath(); + + if (file_name.isEmpty()) { + // QString docname = tabWidget->tabText(tabWidget->currentIndex()); + // docname.remove(0,2); + SlotSaveAs(); + } else { + saveFile(file_name); + } } } @@ -171,6 +184,38 @@ auto TextEdit::saveFile(const QString& file_name) -> bool { return false; } +auto TextEdit::saveEMLFile(const QString& file_name) -> bool { + if (file_name.isEmpty()) return false; + + PlainTextEditorPage* page = CurEMailPage(); + if (page == nullptr) return false; + + QFile file(file_name); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::warning( + this, tr("Warning"), + tr("Cannot read file %1:\n%2.").arg(file_name).arg(file.errorString())); + return false; + } + + QTextStream output_stream(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + output_stream + << page->GetTextPage()->toPlainText().replace("\n", "\r\n").toLatin1(); + QApplication::restoreOverrideCursor(); + QTextDocument* document = page->GetTextPage()->document(); + + document->setModified(false); + + int cur_index = tab_widget_->currentIndex(); + tab_widget_->setTabText(cur_index, stripped_name(file_name)); + page->SetFilePath(file_name); + page->NotifyFileSaved(); + + file.close(); + return true; +} + auto TextEdit::SlotSaveAs() -> bool { if (tab_widget_->count() == 0 || SlotCurPageTextEdit() == nullptr) { return true; @@ -187,6 +232,22 @@ auto TextEdit::SlotSaveAs() -> bool { return saveFile(QFileDialog::getSaveFileName(this, tr("Save file"), path)); } +auto TextEdit::SlotSaveAsEML() -> bool { + if (tab_widget_->count() == 0 || CurEMailPage() == nullptr) { + return true; + } + + PlainTextEditorPage* page = CurEMailPage(); + QString path; + if (!page->GetFilePath().isEmpty()) { + path = page->GetFilePath(); + } else { + path = tab_widget_->tabText(tab_widget_->currentIndex()).remove(0, 2); + } + + return saveEMLFile(QFileDialog::getSaveFileName(this, tr("Save file"), path)); +} + void TextEdit::SlotCloseTab() { slot_remove_tab(tab_widget_->currentIndex()); if (tab_widget_->count() != 0) { @@ -321,6 +382,13 @@ auto TextEdit::MaybeSaveAnyTab() -> bool { return false; } +void TextEdit::SlotSetText2CurEMailPage(const QString& text) { + if (CurTextPage() == nullptr) SlotNewEMailTab(); + auto* edit = CurTextPage()->GetTextPage(); + edit->clear(); + edit->appendPlainText(text); +} + void TextEdit::SlotAppendText2CurTextPage(const QString& text) { if (CurTextPage() == nullptr) SlotNewTab(); CurTextPage()->GetTextPage()->appendPlainText(text); @@ -554,4 +622,11 @@ auto TextEdit::CurPlainText() const -> QString { } auto TextEdit::TabWidget() const -> QTabWidget* { return tab_widget_; } + +auto TextEdit::CurEMailPage() const -> EMailEditorPage* { + return tab_widget_->CurEMailPage(); +} + +void TextEdit::SlotNewEMailTab() { tab_widget_->SlotNewEMailTab(); } + } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.h b/src/ui/widgets/TextEdit.h index 30540569..68b648e5 100644 --- a/src/ui/widgets/TextEdit.h +++ b/src/ui/widgets/TextEdit.h @@ -29,6 +29,7 @@ #pragma once #include "ui/dialog/QuitDialog.h" +#include "ui/widgets/EMailEditorPage.h" #include "ui/widgets/FilePage.h" #include "ui/widgets/PlainTextEditorPage.h" @@ -78,6 +79,13 @@ class TextEdit : public QWidget { /** * @brief * + * @return EMailEditorPage* + */ + [[nodiscard]] auto CurEMailPage() const -> EMailEditorPage*; + + /** + * @brief + * * @return FilePage* */ [[nodiscard]] auto CurFilePage() const -> FilePage*; @@ -161,6 +169,12 @@ class TextEdit : public QWidget { void SlotNewTab(); /** + * @details Adds a new tab with the title "untitled"+countpage+".eml" + * Sets the focus to the new tab. Increase Tab-Count by one + */ + void SlotNewEMailTab(); + + /** * @details * */ @@ -260,6 +274,21 @@ class TextEdit : public QWidget { */ void SlotAppendText2CurTextPage(const QString& text); + /** + * @brief + * + * @param text + */ + void SlotSetText2CurEMailPage(const QString& text); + + /** + * @brief + * + * @return true + * @return false + */ + auto SlotSaveAsEML() -> bool; + protected: /** * @brief Saves the content of currentTab to the file filename @@ -268,6 +297,13 @@ class TextEdit : public QWidget { */ auto saveFile(const QString& file_name) -> bool; + /** + * @brief + * + * @return auto + */ + auto saveEMLFile(const QString& file_name) -> bool; + private slots: /** diff --git a/src/ui/widgets/TextEditTabWidget.cpp b/src/ui/widgets/TextEditTabWidget.cpp index 97826f03..8007eb85 100644 --- a/src/ui/widgets/TextEditTabWidget.cpp +++ b/src/ui/widgets/TextEditTabWidget.cpp @@ -31,8 +31,9 @@ #include "core/function/GlobalSettingStation.h" #include "core/model/CacheObject.h" #include "ui/UISignalStation.h" +#include "ui/widgets/EMailEditorPage.h" +#include "ui/widgets/FilePage.h" #include "ui/widgets/PlainTextEditorPage.h" -#include "widgets/FilePage.h" namespace GpgFrontend::UI { @@ -80,6 +81,11 @@ void TextEditTabWidget::dropEvent(QDropEvent* event) { continue; } + if (file_info.suffix() == "eml") { + SlotOpenEMLFile(local_file); + return; + } + SlotOpenFile(local_file); } @@ -125,29 +131,58 @@ void TextEditTabWidget::SlotOpenFile(const QString& path) { file.close(); } -void TextEditTabWidget::SlotShowModified(bool changed) { + +void TextEditTabWidget::SlotOpenEMLFile(const QString& path) { + QFile file(path); + auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); + if (result) { + auto* page = new EMailEditorPage(path, this); + + connect(page->GetTextPage(), &QPlainTextEdit::textChanged, this, + &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage(), &QPlainTextEdit::selectionChanged, this, + &TextEditTabWidget::slot_save_status_to_cache_for_recovery); + + QApplication::setOverrideCursor(Qt::WaitCursor); + auto index = this->addTab(page, stripped_name(path)); + this->setTabIcon(index, QIcon(":/icons/email.png")); + this->setCurrentIndex(this->count() - 1); + QApplication::restoreOverrideCursor(); + page->GetTextPage()->setFocus(); + page->ReadFile(); + } else { + QMessageBox::warning( + this, tr("Warning"), + tr("Cannot read file %1:\n%2.").arg(path).arg(file.errorString())); + } + + file.close(); +} + +void TextEditTabWidget::SlotShowModified() { // get current tab int index = this->currentIndex(); - QString title = this->tabText(index); + QString title = this->tabText(index).trimmed(); - // if changed - if (!changed) { - this->setTabText(index, title.remove(0, 2)); - return; - } + if (title.startsWith("*")) return; // if doc is modified now, add leading * to title, // otherwise remove the leading * from the title if (CurTextPage()->GetTextPage()->document()->isModified()) { - this->setTabText(index, title.trimmed().prepend("* ")); + this->setTabText(index, title.prepend("* ")); } else { this->setTabText(index, title.remove(0, 2)); } } + auto TextEditTabWidget::CurTextPage() const -> PlainTextEditorPage* { return qobject_cast<PlainTextEditorPage*>(this->currentWidget()); } +auto TextEditTabWidget::CurEMailPage() const -> EMailEditorPage* { + return qobject_cast<EMailEditorPage*>(this->currentWidget()); +} + auto TextEditTabWidget::SlotCurPageTextEdit() -> PlainTextEditorPage* { auto* cur_page = qobject_cast<PlainTextEditorPage*>(this->currentWidget()); return cur_page; @@ -228,6 +263,22 @@ void TextEditTabWidget::SlotNewTab() { connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, this, &TextEditTabWidget::slot_save_status_to_cache_for_recovery); } + +void TextEditTabWidget::SlotNewEMailTab() { + QString header = tr("untitled") + QString::number(++count_page_) + ".eml"; + + auto* page = new EMailEditorPage(); + auto index = this->addTab(page, header); + this->setTabIcon(index, QIcon(":/icons/email.png")); + this->setCurrentIndex(this->count() - 1); + page->GetTextPage()->setFocus(); + + connect(page->GetTextPage(), &QPlainTextEdit::textChanged, this, + &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage(), &QPlainTextEdit::selectionChanged, this, + &TextEditTabWidget::slot_save_status_to_cache_for_recovery); +} + void TextEditTabWidget::SlotNewTabWithContent(QString title, const QString& content) { QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; diff --git a/src/ui/widgets/TextEditTabWidget.h b/src/ui/widgets/TextEditTabWidget.h index b0cafae2..13ed00f0 100644 --- a/src/ui/widgets/TextEditTabWidget.h +++ b/src/ui/widgets/TextEditTabWidget.h @@ -28,10 +28,11 @@ #pragma once -#include "widgets/FilePage.h" namespace GpgFrontend::UI { class PlainTextEditorPage; +class EMailEditorPage; +class FilePage; class TextEditTabWidget : public QTabWidget { Q_OBJECT @@ -49,6 +50,12 @@ class TextEditTabWidget : public QTabWidget { /** * @brief * + */ + void SlotNewEMailTab(); + + /** + * @brief + * * @param title * @param content */ @@ -62,6 +69,13 @@ class TextEditTabWidget : public QTabWidget { /** * @brief * + * @param path + */ + void SlotOpenEMLFile(const QString& path); + + /** + * @brief + * */ void SlotOpenDirectory(const QString& target_directory); @@ -69,7 +83,7 @@ class TextEditTabWidget : public QTabWidget { * @details put a * in front of current tabs title, if current textedit is * modified */ - void SlotShowModified(bool); + void SlotShowModified(); /** * @brief @@ -83,6 +97,13 @@ class TextEditTabWidget : public QTabWidget { * * @return PlainTextEditorPage* */ + [[nodiscard]] auto CurEMailPage() const -> EMailEditorPage*; + + /** + * @brief + * + * @return PlainTextEditorPage* + */ auto SlotCurPageTextEdit() -> PlainTextEditorPage*; /** diff --git a/third_party/qttranslations b/third_party/qttranslations -Subproject c4da2d3ed4511af796f88d0718aa9258908cc82 +Subproject 62d8170d01b861e58a6fa91c3f9ece4b9f9cc3a diff --git a/ui/EmailListEditor.ui b/ui/EmailListEditor.ui deleted file mode 100644 index 5cc0ddef..00000000 --- a/ui/EmailListEditor.ui +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>EmailListEditorDialog</class> - <widget class="QDialog" name="EmailListEditorDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>618</width> - <height>498</height> - </rect> - </property> - <property name="windowTitle"> - <string>Email List Editor</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QLabel" name="titleLabel"> - <property name="text"> - <string>Email List:</string> - </property> - </widget> - </item> - <item> - <widget class="QListWidget" name="emaillistWidget"> - <property name="editTriggers"> - <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> - </property> - <property name="dragEnabled"> - <bool>true</bool> - </property> - <property name="isWrapping" stdset="0"> - <bool>false</bool> - </property> - <property name="spacing"> - <number>6</number> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="addEmailAddressButton"> - <property name="text"> - <string>Add An Email Address</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="tipsLabel"> - <property name="text"> - <string>Tips: You can double-click the email address in the edit list, or click the email to pop up the option menu.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - </layout> - <action name="actionDelete_Selected_Email_Address"> - <property name="text"> - <string>Delete Selected Email Address</string> - </property> - </action> - </widget> - <resources/> - <connections/> -</ui> diff --git a/ui/ModuleControllerDialog.ui b/ui/ModuleControllerDialog.ui index 910a3de8..ac61c45a 100644 --- a/ui/ModuleControllerDialog.ui +++ b/ui/ModuleControllerDialog.ui @@ -86,7 +86,7 @@ <item> <widget class="QLabel" name="moduleInfoLabel"> <property name="text"> - <string>Module Informations</string> + <string>Module Information</string> </property> </widget> </item> diff --git a/ui/PlainTextEditor.ui b/ui/PlainTextEditor.ui index 266eba56..241c6f50 100644 --- a/ui/PlainTextEditor.ui +++ b/ui/PlainTextEditor.ui @@ -39,6 +39,12 @@ <property name="spacing"> <number>12</number> </property> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> <item> <widget class="QLabel" name="loadingLabel"> <property name="text"> @@ -49,7 +55,7 @@ <item> <spacer name="horizontalSpacer"> <property name="orientation"> - <enum>Qt::Horizontal</enum> + <enum>Qt::Orientation::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> diff --git a/ui/SendMailDialog.ui b/ui/SendMailDialog.ui deleted file mode 100644 index f68d2f45..00000000 --- a/ui/SendMailDialog.ui +++ /dev/null @@ -1,469 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SendMailDialog</class> - <widget class="QDialog" name="SendMailDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>995</width> - <height>760</height> - </rect> - </property> - <property name="cursor"> - <cursorShape>ArrowCursor</cursorShape> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::NoContextMenu</enum> - </property> - <property name="windowTitle"> - <string>New Message</string> - </property> - <property name="sizeGripEnabled"> - <bool>false</bool> - </property> - <property name="modal"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QWidget" name="horizontalWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="senderLabel"> - <property name="text"> - <string>Sender</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="senderEdit"/> - </item> - <item> - <widget class="Line" name="line"> - <property name="toolTipDuration"> - <number>0</number> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="lineWidth"> - <number>2</number> - </property> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="ccButton"> - <property name="text"> - <string>CC</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="bccButton"> - <property name="text"> - <string>BCC</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_4" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="recipientLabel"> - <property name="text"> - <string>Recipient(s)</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="recipientEdit"/> - </item> - <item> - <widget class="QPushButton" name="recipientsEditButton"> - <property name="text"> - <string>Edit Recipients(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="Line" name="line_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_5" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="subjectLabel"> - <property name="text"> - <string>Mail Subject</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="subjectEdit"/> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_6" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="gpgOperaLabel"> - <property name="text"> - <string>GPG Operations</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="senderKeySelectButton"> - <property name="text"> - <string>Select Sender GPG Key</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="recipientKeySelectButton"> - <property name="text"> - <string>Select Recipient(s) GPG Key</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="Line" name="line_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="ccInputWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="ccLabel"> - <property name="text"> - <string>CC</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="ccEdit"/> - </item> - <item> - <widget class="QPushButton" name="ccEditButton"> - <property name="text"> - <string>Edit CC(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="bccInputWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="bccLabel"> - <property name="text"> - <string>BCC</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="bccEdit"/> - </item> - <item> - <widget class="QPushButton" name="bccEditButton"> - <property name="text"> - <string>Edit BCC(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QLabel" name="tipsLabel"> - <property name="text"> - <string>Tips: You can fill in multiple email addresses, please separate them with ";".</string> - </property> - </widget> - </item> - <item> - <widget class="Line" name="line_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QTextEdit" name="textEdit"/> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> - <widget class="QLabel" name="senderKeyLabel"> - <property name="text"> - <string>Sender GPG Key:</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="senderKeyValueLabel"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> - <widget class="QLabel" name="recipientKeysLabel"> - <property name="text"> - <string>Recipient(s) GPG Key:</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="recipientsKeyValueLabel"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QLabel" name="errorLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="Line" name="line_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <item> - <widget class="QCheckBox" name="contentEncryptCheckBox"> - <property name="text"> - <string>Encrypt content</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="attacSignatureCheckBox"> - <property name="text"> - <string>Attach signature</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="attachSenderPublickeyCheckBox"> - <property name="text"> - <string>Attach sender's public key</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item alignment="Qt::AlignRight"> - <widget class="QPushButton" name="sendMailButton"> - <property name="text"> - <string>Send Mail</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> |