diff options
author | Saturn&Eric <[email protected]> | 2021-12-16 21:20:56 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-12-16 21:20:56 +0000 |
commit | 52ac9979bd8c4820a0034d619cb7d1d3e4105d8b (patch) | |
tree | c7524432467825603d83a17f398249d431c28b18 | |
parent | Merge pull request #32 from saturneric/develop (diff) | |
parent | Fixed bugs & Improve Speed. (diff) | |
download | GpgFrontend-52ac9979bd8c4820a0034d619cb7d1d3e4105d8b.tar.gz GpgFrontend-52ac9979bd8c4820a0034d619cb7d1d3e4105d8b.zip |
Merge pull request #34 from saturneric/develop
v2.0.3
63 files changed, 2876 insertions, 1004 deletions
diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml deleted file mode 100644 index 13e4b733..00000000 --- a/.github/workflows/release-ci.yml +++ /dev/null @@ -1,215 +0,0 @@ -name: Build & Package CI Test - -on: - push: - branches: [ develop-ci ] - paths-ignore: - - "**/README.md" - - "**/README_CN.md" - - "resource/ts/**" - - "docs/**" - - "**.md" - pull_request: - branches: [ develop-ci ] - paths-ignore: - - "**/README.md" - - "**/README_CN.md" - - "resource/ts/**" - - "docs/**" - - "**.md" - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - EXECUTABLE_OUTPUT_PATH: ./ - -jobs: - build: - strategy: - matrix: - os: [ "ubuntu-18.04", "macos-10.15", "windows-latest" ] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Install Dependence (Linux) - run: | - sudo apt-get update - sudo apt-get -y install build-essential binutils git autoconf automake gettext texinfo - sudo apt-get -y install gcc-8 g++-8 libconfig++-dev libboost-all-dev - sudo apt-get -y install gpgsm libxcb-xinerama0 libxcb-icccm4-dev libcups2-dev libdrm-dev libegl1-mesa-dev - sudo apt-get -y install libgcrypt11-dev libnss3-dev libpci-dev libpulse-dev libudev-dev libxtst-dev gyp ninja-build - sudo apt-get -y install libglu1-mesa-dev libfontconfig1-dev libx11-xcb-dev libicu-dev libxcb-image0 - sudo apt-get -y install libglu1-mesa-dev libfontconfig1-dev libx11-xcb-dev libicu-dev libxcb-* - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8 - sudo update-alternatives --set gcc "/usr/bin/gcc-8" - sudo update-alternatives --set g++ "/usr/bin/g++-8" - if: matrix.os == 'ubuntu-18.04' - - - name: Codesign Configuration (macOS) - run: | - echo ${{secrets.MACOS_CERTIFICATE}} | base64 --decode > certificate.p12 - security create-keychain -p gpgfrontend build.keychain - security default-keychain -s build.keychain - security unlock-keychain -p gpgfrontend build.keychain - security import certificate.p12 -k build.keychain -P ${{secrets.MAOS_CERTIFICATE_PWD}} -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k gpgfrontend build.keychain - security set-keychain-settings -lut 3600 - if: matrix.os == 'macos-10.15' - - - name: Install Dependence (macOS) - run: | - brew install cmake git autoconf automake qt@5 gcc texinfo gettext libgpg-error libassuan gpgme openssl - brew install boost libconfig gettext - brew unlink gettext && brew link --force gettext - brew link qt@5 - brew link gcc - brew link openssl --force - if: matrix.os == 'macos-10.15' - - - name: Cache Qt - id: cache-qt - uses: actions/cache@v1 - with: - path: ../Qt - key: ${{ runner.os }}-QtCache - if: matrix.os == 'ubuntu-18.04' - - - name: Install Qt - uses: jurplel/install-qt-action@v2 - with: - cached: ${{ steps.cache-qt.outputs.cache-hit }} - if: matrix.os == 'ubuntu-18.04' - - - name: Set up MinGW (Windows) - uses: msys2/setup-msys2@v2 - with: - install: git msys2-devel base-devel binutils mingw-w64-x86_64-toolchain - release: false - if: matrix.os == 'windows-latest' - - - name: Set up Dependence (Windows) - shell: msys2 {0} - run: | - pacman --noconfirm -S --needed mingw-w64-x86_64-gcc mingw-w64-x86_64-make mingw-w64-x86_64-cmake autoconf automake mingw-w64-x86_64-gpgme - pacman --noconfirm -S --needed make texinfo mingw-w64-x86_64-libconfig mingw-w64-x86_64-boost mingw-w64-x86_64-gnupg gettext-devel libintl msys2-runtime-devel - pacman --noconfirm -S --needed mingw-w64-x86_64-qt5 - if: matrix.os == 'windows-latest' - - - name: Build gpg-error (Linux) - run: | - cd ${{github.workspace}}/third_party/libgpg-error - ./autogen.sh - ./configure --enable-maintainer-mode --enable-static=yes && make -j2 - sudo make install - cd ${{github.workspace}} - if: matrix.os == 'ubuntu-18.04' - - - name: Build assuan (Linux) - run: | - cd ${{github.workspace}}/third_party/libassuan - ./autogen.sh - ./configure --enable-maintainer-mode --enable-static=yes && make -j2 - sudo make install - cd ${{github.workspace}} - if: matrix.os == 'ubuntu-18.04' - - - name: Build GpgME (Linux & macOS) - run: | - cd ${{github.workspace}}/third_party/gpgme - ./autogen.sh - ./configure --enable-maintainer-mode --enable-static=yes --enable-languages=cpp && make -j2 - sudo make install - cd ${{github.workspace}} - if: matrix.os == 'ubuntu-18.04' || matrix.os == 'macos-10.15' - - - name: Build GpgME (Windows) - shell: msys2 {0} - run: | - git clone https://github.com/saturneric/gpgme - cd gpgme - ./autogen.sh - ./configure --enable-maintainer-mode --enable-static=yes --disable-gpg-test --enable-languages=cpp LDFLAGS="-static" && make -j2 - make install - if: matrix.os == 'windows-latest' - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DEXECUTABLE_OUTPUT_PATH=${{env.EXECUTABLE_OUTPUT_PATH}} - if: matrix.os == 'ubuntu-18.04' || matrix.os == 'macos-10.15' - - - name: Build GpgFrontend - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config $env.BUILD_TYPE}} -- -j 2 - if: matrix.os == 'ubuntu-18.04' || matrix.os == 'macos-10.15' - - - name: Build & Sign App Bundle (macOS) - run: | - security -v unlock-keychain -p gpgfrontend - macdeployqt ${{github.workspace}}/build/release/GpgFrontend.app - codesign --deep --force --options=runtime -s "Developer ID Application: Yu Hu (4279AWUL3X)" ${{github.workspace}}/build/release/GpgFrontend.app -v - mkdir ${{github.workspace}}/build/tmp/ - if: matrix.os == 'macos-10.15' - - - name: Package & Sign App Bundle (macOS) - run: | - security -v unlock-keychain -p gpgfrontend - hdiutil create ${{github.workspace}}/build/tmp/tmp.dmg -ov -volname "GpgFrontend" -fs HFS+ -srcfolder ${{github.workspace}}/build/release/ - mkdir ${{github.workspace}}/build/artifactOut - hdiutil convert ${{github.workspace}}/build/tmp/tmp.dmg -format UDZO -o ${{github.workspace}}/build/artifactOut/GpgFrontend.dmg - codesign -s "Developer ID Application: Yu Hu (4279AWUL3X)" ${{github.workspace}}/build/artifactOut/GpgFrontend.dmg - mv ${{github.workspace}}/build/artifactOut/GpgFrontend.dmg ${{github.workspace}}/build/artifactOut/GpgFrontend-${{steps.vars.outputs.sha_short}}-x86_64.dmg - if: matrix.os == 'macos-10.15' - - - name: Notarize Release Build (macOS) - run: | - xcrun altool --notarize-app -f ${{github.workspace}}/build/artifactOut/GpgFrontend-${{steps.vars.outputs.sha_short}}-x86_64.dmg --primary-bundle-id pub.gpgfrontend.gpgfrontend -u ${{secrets.APPLE_DEVELOPER_ID}} -p ${{secrets.APPLE_DEVELOPER_ID_SECRET}} - if: matrix.os == 'macos-10.15' - - - name: Package App Image (Linux) - run: | - mkdir ${{github.workspace}}/build/artifactOut - cd ${{github.workspace}}/build/artifactOut - wget -c -nv https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage - chmod u+x linuxdeployqt-continuous-x86_64.AppImage - ./linuxdeployqt-continuous-x86_64.AppImage ${{github.workspace}}/build/release/gpgfrontend/usr/share/applications/*.desktop -appimage - if: matrix.os == 'ubuntu-18.04' - - - name: Configure CMake & Build Binary(Windows) - shell: msys2 {0} - run: | - cd $(echo "/${{github.workspace}}" | sed 's/\\/\//g' | sed 's/://') - mkdir build && cd build - cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DEXECUTABLE_OUTPUT_PATH=${{env.EXECUTABLE_OUTPUT_PATH}} .. - # Build your program with the given configuration - cmake --build . --config ${{env.BUILD_TYPE}} -- -j 2 - if: matrix.os == 'windows-latest' - - - name: Get Short SHA of Commit - id: vars - run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - - name: Upload Artifact(Linux) - uses: actions/upload-artifact@master - with: - name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE}}-${{steps.vars.outputs.sha_short}} - path: ${{github.workspace}}/build/artifactOut/GpgFrontend*.AppImage* - if: matrix.os == 'ubuntu-18.04' - - - name: Upload Artifact(macOS) - uses: actions/upload-artifact@master - with: - name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE}}-${{steps.vars.outputs.sha_short}} - path: ${{github.workspace}}/build/artifactOut/* - if: matrix.os == 'macos-10.15' - - - name: Upload Artifact(Windows) - uses: actions/upload-artifact@master - with: - name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE}}-${{steps.vars.outputs.sha_short}} - path: ${{github.workspace}}/build/release/* - if: matrix.os == 'windows-latest' diff --git a/.github/workflows/release-linux-package.yml b/.github/workflows/release-linux-package.yml new file mode 100644 index 00000000..43f26e9b --- /dev/null +++ b/.github/workflows/release-linux-package.yml @@ -0,0 +1,112 @@ +name: Build Linux Packages + +on: + push: + branches: [ main, develop ] + paths-ignore: + - '**/README.md' + - '**/README_CN.md' + - 'resource/ts/**' + - 'docs/**' + pull_request: + branches: [ develop ] + paths-ignore: + - '**/README.md' + - '**/README_CN.md' + - 'resource/ts/**' + - 'docs/**' + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + EXECUTABLE_OUTPUT_PATH: ./ + +jobs: + build: + strategy: + matrix: + os: [ 'ubuntu-18.04' ] + runs-on: ${{ matrix.os }} + steps: + + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Get Short SHA of Commit + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + + - name: Install Dependence (Linux) + run: | + sudo apt-get update + sudo apt-get -y install build-essential binutils git autoconf automake gettext texinfo + sudo apt-get -y install gcc-8 g++-8 libconfig++-dev libboost-all-dev + sudo apt-get -y install gpgsm libxcb-xinerama0 libxcb-icccm4-dev libcups2-dev libdrm-dev libegl1-mesa-dev + sudo apt-get -y install libgcrypt11-dev libnss3-dev libpci-dev libpulse-dev libudev-dev libxtst-dev gyp ninja-build + sudo apt-get -y install libglu1-mesa-dev libfontconfig1-dev libx11-xcb-dev libicu-dev libxcb-image0 + sudo apt-get -y install libglu1-mesa-dev libfontconfig1-dev libx11-xcb-dev libicu-dev libxcb-* + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8 + sudo update-alternatives --set gcc "/usr/bin/gcc-8" + sudo update-alternatives --set g++ "/usr/bin/g++-8" + if: matrix.os == 'ubuntu-18.04' + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v1 + with: + path: ../Qt + key: ${{ runner.os }}-QtCache-5.12.8 + if: matrix.os == 'ubuntu-18.04' + + - name: Install Qt + uses: jurplel/install-qt-action@v2 + with: + cached: ${{ steps.cache-qt.outputs.cache-hit }} + version: '5.12.8' + if: matrix.os == 'ubuntu-18.04' + + - name: Build gpg-error + run: | + cd ${{github.workspace}}/third_party/libgpg-error + ./autogen.sh + ./configure --enable-maintainer-mode --enable-static=yes && make -j2 + sudo make install + cd ${{github.workspace}} + if: matrix.os == 'ubuntu-18.04' + + - name: Build assuan + run: | + cd ${{github.workspace}}/third_party/libassuan + ./autogen.sh + ./configure --enable-maintainer-mode --enable-static=yes && make -j2 + sudo make install + cd ${{github.workspace}} + if: matrix.os == 'ubuntu-18.04' + + - name: Build GpgME + run: | + cd ${{github.workspace}}/third_party/gpgme + ./autogen.sh + ./configure --enable-maintainer-mode --enable-static=yes --enable-languages=cpp && make -j2 + sudo make install + cd ${{github.workspace}} + if: matrix.os == 'ubuntu-18.04' + + - name: Build & Package GpgFrontend (Linux DEB Package) + # Build your program with the given configuration + run: | + cmake -B ${{github.workspace}}/build-deb -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DEXECUTABLE_OUTPUT_PATH=${{env.EXECUTABLE_OUTPUT_PATH}} -DAPP_PACKAGE_DEB=ON -DLINUX_INSTALL_SOFTWARE=ON + cmake --build ${{github.workspace}}/build-deb --config {{$env.BUILD_TYPE}} -- -j 2 + cd ${{github.workspace}}/build-deb + make package + cd ${{github.workspace}} + if: matrix.os == 'ubuntu-18.04' + + - name: Upload Artifact(Linux DEB) + uses: actions/upload-artifact@master + with: + name: gpgfrontend-${{matrix.os}}-${{env.BUILD_TYPE}}-${{steps.vars.outputs.sha_short}}-deb + path: ${{github.workspace}}/build-deb/gpgfrontend*.deb* + if: matrix.os == 'ubuntu-18.04' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 187b2399..7bb8120b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,10 @@ jobs: with: submodules: recursive + - name: Get Short SHA of Commit + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + - name: Install Dependence (Linux) run: | sudo apt-get update @@ -143,7 +147,7 @@ jobs: - name: Build GpgFrontend # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config $env.BUILD_TYPE}} -- -j 2 + run: cmake --build ${{github.workspace}}/build --config {{$env.BUILD_TYPE}} -- -j 2 if: matrix.os == 'ubuntu-18.04' || matrix.os == 'macos-10.15' - name: Build & Sign App Bundle (macOS) @@ -188,10 +192,6 @@ jobs: cmake --build . --config ${{env.BUILD_TYPE}} -- -j 2 if: matrix.os == 'windows-latest' - - name: Get Short SHA of Commit - id: vars - run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - name: Upload Artifact(Linux) uses: actions/upload-artifact@master with: @@ -1,6 +1,7 @@ # Project src/GpgFrontend.h src/GpgFrontendBuildInfo.h +src/GpgFrontendBuildInstallInfo.h # gettext *.mo diff --git a/CMakeLists.txt b/CMakeLists.txt index 377f627c..8a524da7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(GpgFrontend VERSION 2.0.2 LANGUAGES CXX) +project(GpgFrontend VERSION 2.0.3 LANGUAGES CXX) message(STATUS "GpgFrontend Build Configuration Started CMAKE Version ${CMAKE_VERSION}") @@ -164,7 +164,6 @@ IF (MINGW) endif () if (APPLE) - message(STATUS "Configuration For OS Platform MacOS") set(OS_PLATFORM 1) @@ -222,6 +221,18 @@ set(BASIC_ENV_CONFIG 1) set(QT_MOC_CONFIG 1) set(ESAY_LOGGING_PP 1) +if (LINUX_INSTALL_SOFTWARE) + include(GNUInstallDirs) + set(INSTALL_GPGFRONTEND_APP 1) + set(APP_INSTALL_FLAG LINUX_INSTALL) + add_compile_definitions(LINUX_INSTALL_BUILD) + set(Boost_USE_STATIC_LIBS ON) +else () + set(APP_INSTALL_FLAG BUNDLE) + add_compile_definitions(BUNDLE_BUILD) +endif () + + if (FULL_APPLICATION_BUILD) message(STATUS "Build Full Application") set(QT5_ENV_SUPPORT 1) @@ -239,7 +250,6 @@ elseif (MINIMUM_APPLICATION_BUILD) set(UI_CORE 1) set(APPLICATION_BUILD 1) set(BASIC_ENV_CONFIG 1) - set(MULTI_LANG_SUPPORT 1) # Disable APP_IMAGE_UPDATE because of too many issues # if (LINUX) # set(APP_IMAGE_UPDATE 1) @@ -250,7 +260,7 @@ elseif (STABLE_APPLICATION_BUILD) set(UI_CORE 1) set(APPLICATION_BUILD 1) set(BASIC_ENV_CONFIG 1) - # MULTI_LANG_SUPPORT now work only on Linux and macOS + set(SMTP_SUPPORT 1) set(MULTI_LANG_SUPPORT 1) elseif (TEST_BUILD) message(STATUS "Build Test Cases") @@ -268,7 +278,11 @@ find_package(Boost COMPONENTS date_time filesystem REQUIRED) if (QT5_ENV_SUPPORT) # Support Qt version Both 5.12.x and 5.15.x - find_package(Qt5 COMPONENTS Core Test Widgets PrintSupport Network REQUIRED) + if (LINUX_INSTALL_SOFTWARE) + find_package(Qt5 5.12 COMPONENTS Core Test Widgets PrintSupport Network REQUIRED) + else () + find_package(Qt5 COMPONENTS Core Test Widgets PrintSupport Network REQUIRED) + endif () # find_package(Qt5 5.15.2 EXACT COMPONENTS Core Test Widgets PrintSupport Network LinguistTools REQUIRED) # Qt configuration set(CMAKE_AUTOMOC ON) diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index c2589b83..c7ecc963 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -20,6 +20,7 @@ <file alias="configure.png">resource/icons/configure.png</file> <file alias="decrypted.png">resource/icons/decrypted.png</file> <file alias="edit.png">resource/icons/edit.png</file> + <file alias="email.png">resource/icons/email.png</file> <file alias="encrypted.png">resource/icons/encrypted.png</file> <file alias="encrypted_signed.png">resource/icons/encrypted_signed.png</file> <file alias="decrypted_verified.png">resource/icons/decrypted_verified.png</file> diff --git a/resource/desktop/gpgfrontend-gpgfrontend.desktop b/resource/desktop/gpgfrontend-gpgfrontend.desktop new file mode 100644 index 00000000..927c8ae0 --- /dev/null +++ b/resource/desktop/gpgfrontend-gpgfrontend.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=GpgFrontend +Exec=/usr/bin/GpgFrontend +Icon=pub.gpgfrontend.gpgfrontend +Comment=An OpenPGP Crypto Tool +Terminal=false +Categories=Utility;
\ No newline at end of file diff --git a/resource/hicolor/128x128/apps/pub.gpgfrontend.gpgfrontend.png b/resource/hicolor/128x128/apps/pub.gpgfrontend.gpgfrontend.png Binary files differnew file mode 100644 index 00000000..5c24791f --- /dev/null +++ b/resource/hicolor/128x128/apps/pub.gpgfrontend.gpgfrontend.png diff --git a/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png b/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png Binary files differnew file mode 100644 index 00000000..b3268b01 --- /dev/null +++ b/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png diff --git a/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png b/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png Binary files differnew file mode 100644 index 00000000..c90e1d1d --- /dev/null +++ b/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png diff --git a/resource/icons/email.png b/resource/icons/email.png Binary files differnew file mode 100644 index 00000000..f0517848 --- /dev/null +++ b/resource/icons/email.png diff --git a/resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml b/resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml new file mode 100644 index 00000000..281215ae --- /dev/null +++ b/resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component> + <id>pub.gpgfrontend.gpgfrontend</id> + <metadata_license>MIT</metadata_license> + <name>gpgfrontend</name> + <summary>An OpenPGP Crypto Tool</summary> + <icon type="stock">gpgfrontend</icon> + <categories> + <category>Utility</category> + </categories> + <url type="homepage">https://gpgfrontend.pub</url> + <launchable type="desktop-id">gpgfrontend.gpgfrontend.desktop</launchable> + <description> + <p> + GpgFrontend is a Free, Open Source, Powerful, Easy-to-Use, Compact, Cross-Platform OpenPGP Crypto Tool. + By using GpgFrontend, you can quickly encrypt and decrypt text or files. Or at the same time as the above + operations, you can add your own signature to let others know that this document or this paragraph of text + was issued by you. + </p> + </description> + <screenshots> + <screenshot type="default"> + <image>https://github.com/saturneric/Blob/blob/master/screenshots/main-ubuntu.png</image> + <caption>Main Dialog</caption> + </screenshot> + </screenshots> + <keywords> + <keyword>gpg</keyword> + <keyword>encrypt</keyword> + <keyword>crypto</keyword> + <keyword>pgp</keyword> + <keyword>gnupg</keyword> + <keyword>openpgp</keyword> + </keywords> + <project_license>GPL-3.0+</project_license> + <developer_name>Saturneric</developer_name> +</component>
\ No newline at end of file diff --git a/resource/pixmaps/pub.gpgfrontend.gpgfrontend.png b/resource/pixmaps/pub.gpgfrontend.gpgfrontend.png Binary files differnew file mode 100644 index 00000000..b3268b01 --- /dev/null +++ b/resource/pixmaps/pub.gpgfrontend.gpgfrontend.png diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c75be69..346b1524 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,7 +42,7 @@ if (APPLICATION_BUILD) if (${CMAKE_BUILD_TYPE} STREQUAL "Release") if (APPLE) set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Resources) - elseif (LINUX) + elseif (LINUX AND NOT LINUX_INSTALL_SOFTWARE) file(COPY ${CMAKE_SOURCE_DIR}/resource/gpgfrontend DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gpgfrontend/usr/share) else () @@ -64,6 +64,10 @@ if (MULTI_LANG_SUPPORT) find_package(Gettext REQUIRED) FIND_PROGRAM(GETTEXT_MSGFMT_EXECUTABLE msgfmt) FIND_PROGRAM(GETTEXT_XGETTEXT_EXECUTABLE xgettext) + + set(LOCALE_OUTPUT_PATH ${RESOURCE_OUTPUT_DIRECTORY}/locales) + message(STATUS "LOCALE_OUTPUT_PATH ${LOCALE_OUTPUT_PATH}") + if (NOT GETTEXT_MSGFMT_EXECUTABLE OR NOT GETTEXT_XGETTEXT_EXECUTABLE) message(ERROR "msgfmt or xgettext not found. Translations will *not* be installed") else (NOT GETTEXT_MSGFMT_EXECUTABLE) @@ -91,7 +95,7 @@ if (MULTI_LANG_SUPPORT) ) add_custom_command( TARGET translations - COMMAND msgfmt --check --verbose --output-file ${RESOURCE_OUTPUT_DIRECTORY}/locales/${_langName}/LC_MESSAGES/GpgFrontend.mo ${_poFile} + COMMAND msgfmt --check --verbose --output-file ${LOCALE_OUTPUT_PATH}/${_langName}/LC_MESSAGES/GpgFrontend.mo ${_poFile} ) endforeach () @@ -102,6 +106,7 @@ if (BASIC_ENV_CONFIG) # Set Build Information configure_file(${CMAKE_SOURCE_DIR}/src/GpgFrontend.h.in ${CMAKE_SOURCE_DIR}/src/GpgFrontend.h @ONLY) configure_file(${CMAKE_SOURCE_DIR}/src/GpgFrontendBuildInfo.h.in ${CMAKE_SOURCE_DIR}/src/GpgFrontendBuildInfo.h @ONLY) + configure_file(${CMAKE_SOURCE_DIR}/src/GpgFrontendBuildInstallInfo.h.in ${CMAKE_SOURCE_DIR}/src/GpgFrontendBuildInstallInfo.h @ONLY) endif () if (APPLICATION_BUILD) @@ -117,7 +122,7 @@ if (APPLICATION_BUILD) file(COPY ${CMAKE_SOURCE_DIR}/gpgfrontend.icns DESTINATION ${RESOURCE_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) # Refresh App Bundle file(REMOVE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${AppName}.app) - elseif (LINUX) + elseif (LINUX AND NOT LINUX_INSTALL_SOFTWARE) file(REMOVE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gpgfrontend/usr/bin/${AppName}) endif () endif () @@ -168,7 +173,7 @@ if (APPLICATION_BUILD) COMMAND /bin/mv -n ./Resources ./${AppName}.app/Contents/ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMENT "Copying Resources into App Bundle Resource") - elseif (LINUX) + elseif (LINUX AND NOT LINUX_INSTALL_SOFTWARE) add_executable(${AppName} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) add_custom_command(TARGET ${AppName} POST_BUILD COMMAND /bin/mkdir -p ./gpgfrontend/usr/bin && /bin/mv -f ./${AppName} ./gpgfrontend/usr/bin/ @@ -199,7 +204,7 @@ if (APPLICATION_BUILD) set(GPGFRONTEND_BEFORE_UI_LIBS ${GPGFRONTEND_BEFORE_UI_LIBS} server) endif () if (SMTP_SUPPORT) - set(GPGFRONTEND_AFTER_UI_LIBS ${GPGFRONTEND_AFTER_UI_LIBS} server) + set(GPGFRONTEND_BEFORE_UI_LIBS ${GPGFRONTEND_BEFORE_UI_LIBS} smtp) endif () set(GPGFRONTEND_LIBS ${GPGFRONTEND_AFTER_UI_LIBS} gpgfrontend-ui gpg_core ${GPGFRONTEND_BEFORE_UI_LIBS} easy_logging_pp) @@ -238,3 +243,43 @@ if (APPLICATION_BUILD) endif () endif () +if (LINUX_INSTALL_SOFTWARE) + if (LINUX) + if (INSTALL_GPGFRONTEND_APP) + install(TARGETS ${AppName} + EXPORT GpgFrontendTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + install(FILES ${CMAKE_SOURCE_DIR}/resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml + DESTINATION /usr/share/metainfo/) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/resource/desktop/ + DESTINATION /usr/share/applications/) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/resource/pixmaps/ + DESTINATION /usr/share/pixmaps/) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/resource/hicolor/ + DESTINATION /usr/share/icons/hicolor/) + endif () + if (MULTI_LANG_SUPPORT) + message(STATUS "Local Output Path ${LOCALE_OUTPUT_PATH}") + install(DIRECTORY ${LOCALE_OUTPUT_PATH}/ + DESTINATION ${CMAKE_INSTALL_FULL_LOCALEDIR}) + endif () + if (APP_PACKAGE_DEB) + SET(CPACK_GENERATOR "DEB") + set(CPACK_INSTALL_PREFIX "/usr/local/") + set(CPACK_PACKAGE_NAME "gpgfrontend") + set(CPACK_DEBIAN_PACKAGE_NAME "gpgfrontend") + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") + set(CPACK_PACKAGE_CONTACT "[email protected]") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Saturneric") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "gpg (>= 2.2), libqt5core5a (>= 5.9), libqt5gui5 (>= 5.9), libqt5widgets5 (>= 5.9), libqt5network5 (>= 5.9), libqt5printsupport5 (>= 5.9)") + set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") + set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") + set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") + include(CPack) + endif () + endif () +endif () + diff --git a/src/GpgFrontendBuildInfo.h.in b/src/GpgFrontendBuildInfo.h.in index eaf0475b..12bef1b8 100644 --- a/src/GpgFrontendBuildInfo.h.in +++ b/src/GpgFrontendBuildInfo.h.in @@ -50,5 +50,9 @@ */ #define BUILD_FLAG @BUILD_FLAG@ #define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@" +#define APP_INSTALL_FLAG "@APP_INSTALL_FLAG@" + + + #endif // GPGFRONTEND_BUILD_INFO_H_IN diff --git a/src/GpgFrontendBuildInstallInfo.h.in b/src/GpgFrontendBuildInstallInfo.h.in new file mode 100644 index 00000000..057f30fe --- /dev/null +++ b/src/GpgFrontendBuildInstallInfo.h.in @@ -0,0 +1,37 @@ +/** + * 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. + * + * Foobar 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 Foobar. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from gpg4usb-team. + * Their source code version also complies with GNU General Public License. + * + * The source code version of this software was modified and released + * by Saturneric<[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_BUILD_INSTALL_INFO_H_IN +#define GPGFRONTEND_BUILD_INSTALL_INFO_H_IN + +/** + * Build & Install Path Information + */ +#define APP_LOCALE_PATH "@CMAKE_INSTALL_FULL_LOCALEDIR@" +#define APP_BIN_PATH "@CMAKE_INSTALL_FULL_BINDIR@" +#define APP_LOCALSTATE_PATH "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@" +#define APP_SYSCONF_PATH "@CMAKE_INSTALL_FULL_SYSCONFDIR@" +#define APP_INFO_PATH "@CMAKE_INSTALL_FULL_INFODIR@" + +#endif // GPGFRONTEND_BUILD_INSTALL_INFO_H_IN
\ No newline at end of file diff --git a/src/gpg/GpgConstants.cpp b/src/gpg/GpgConstants.cpp index 2454daa2..fd3c56b4 100644 --- a/src/gpg/GpgConstants.cpp +++ b/src/gpg/GpgConstants.cpp @@ -47,9 +47,9 @@ const char* GpgFrontend::GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD = gpgme_error_t GpgFrontend::check_gpg_error(gpgme_error_t err) { if (gpg_err_code(err) != GPG_ERR_NO_ERROR) { - LOG(ERROR) << "[Error " << gpg_err_code(err) - << "] Source: " << gpgme_strsource(err) - << " Description: " << gpgme_strerror(err); + LOG(ERROR) << "[" << _("Error") << " " << gpg_err_code(err) << "] " + << _("Source: ") << gpgme_strsource(err) << " " + << _("Description: ") << gpgme_strerror(err); } return err; } @@ -58,9 +58,9 @@ gpg_err_code_t GpgFrontend::check_gpg_error_2_err_code(gpgme_error_t err, gpgme_error_t predict) { auto err_code = gpg_err_code(err); if (err_code != predict) { - LOG(ERROR) << "[Error " << gpg_err_code(err) - << "] Source: " << gpgme_strsource(err) - << " Description: " << gpgme_strerror(err); + LOG(ERROR) << "[" << _("Error") << " " << gpg_err_code(err) << "] " + << _("Source: ") << gpgme_strsource(err) << " " + << _("Description: ") << gpgme_strerror(err); } return err_code; } @@ -69,9 +69,9 @@ gpg_err_code_t GpgFrontend::check_gpg_error_2_err_code(gpgme_error_t err, gpgme_error_t GpgFrontend::check_gpg_error(gpgme_error_t err, const std::string& comment) { if (gpg_err_code(err) != GPG_ERR_NO_ERROR) { - LOG(ERROR) << "[Error " << gpg_err_code(err) - << "] Source: " << gpgme_strsource(err) - << " Description: " << gpgme_strerror(err) << " " << comment; + LOG(ERROR) << "[" << _("Error") << " " << gpg_err_code(err) << "] " + << _("Source: ") << gpgme_strsource(err) << " " + << _("Description: ") << gpgme_strerror(err); } return err; } @@ -190,3 +190,32 @@ int GpgFrontend::text_is_signed(GpgFrontend::BypeArrayRef text) { else return 0; } + +GpgFrontend::GpgEncrResult GpgFrontend::_new_result( + gpgme_encrypt_result_t&& result) { + gpgme_result_ref(result); + return {result, _result_ref_deletor()}; +} + +GpgFrontend::GpgDecrResult GpgFrontend::_new_result( + gpgme_decrypt_result_t&& result) { + gpgme_result_ref(result); + return {result, _result_ref_deletor()}; +} + +GpgFrontend::GpgSignResult GpgFrontend::_new_result( + gpgme_sign_result_t&& result) { + gpgme_result_ref(result); + return {result, _result_ref_deletor()}; +} + +GpgFrontend::GpgVerifyResult GpgFrontend::_new_result( + gpgme_verify_result_t&& result) { + gpgme_result_ref(result); + return {result, _result_ref_deletor()}; +} + +void GpgFrontend::_result_ref_deletor::operator()(void* _result) { + DLOG(INFO) << _("Called") << _result; + if (_result != nullptr) gpgme_result_unref(_result); +} diff --git a/src/gpg/GpgConstants.h b/src/gpg/GpgConstants.h index 1cd0f64d..14895df7 100644 --- a/src/gpg/GpgConstants.h +++ b/src/gpg/GpgConstants.h @@ -25,8 +25,6 @@ #ifndef GPG_CONSTANTS_H #define GPG_CONSTANTS_H -#include "GpgFrontend.h" - #include <gpg-error.h> #include <gpgme.h> @@ -35,6 +33,8 @@ #include <memory> #include <string> +#include "GpgFrontend.h" + const int RESTART_CODE = 1000; namespace GpgFrontend { @@ -49,22 +49,21 @@ using StringArgsRef = std::vector<std::string>&; using GpgError = gpgme_error_t; -// Result Deletor +// Result Deleter struct _result_ref_deletor { - void operator()(void* _result) { - // if (_result != nullptr) - // gpgme_result_unref(_result); - } + void operator()(void* _result); }; -using GpgEncrResult = - std::unique_ptr<struct _gpgme_op_encrypt_result, _result_ref_deletor>; -using GpgDecrResult = - std::unique_ptr<struct _gpgme_op_decrypt_result, _result_ref_deletor>; -using GpgSignResult = - std::unique_ptr<struct _gpgme_op_sign_result, _result_ref_deletor>; -using GpgVerifyResult = - std::unique_ptr<struct _gpgme_op_verify_result, _result_ref_deletor>; +using GpgEncrResult = std::shared_ptr<struct _gpgme_op_encrypt_result>; +using GpgDecrResult = std::shared_ptr<struct _gpgme_op_decrypt_result>; +using GpgSignResult = std::shared_ptr<struct _gpgme_op_sign_result>; +using GpgVerifyResult = std::shared_ptr<struct _gpgme_op_verify_result>; + +// Convert from gpgme_xxx_result to GpgXXXResult +GpgEncrResult _new_result(gpgme_encrypt_result_t&& result); +GpgDecrResult _new_result(gpgme_decrypt_result_t&& result); +GpgSignResult _new_result(gpgme_sign_result_t&& result); +GpgVerifyResult _new_result(gpgme_verify_result_t&& result); // Error Info Printer GpgError check_gpg_error(GpgError err); diff --git a/src/gpg/function/BasicOperator.cpp b/src/gpg/function/BasicOperator.cpp index 5f6ffb85..56b7ca54 100644 --- a/src/gpg/function/BasicOperator.cpp +++ b/src/gpg/function/BasicOperator.cpp @@ -48,7 +48,7 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Encrypt( auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_result = GpgEncrResult(gpgme_op_encrypt_result(ctx)); + auto temp_result = _new_result(gpgme_op_encrypt_result(ctx)); std::swap(result, temp_result); return err; @@ -65,7 +65,7 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Decrypt( auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_result = GpgDecrResult(gpgme_op_decrypt_result(ctx)); + auto temp_result = _new_result(gpgme_op_decrypt_result(ctx)); std::swap(result, temp_result); return err; @@ -86,7 +86,7 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Verify( } else err = check_gpg_error(gpgme_op_verify(ctx, data_in, nullptr, data_out)); - auto temp_result = GpgVerifyResult(gpgme_op_verify_result(ctx)); + auto temp_result = _new_result(gpgme_op_verify_result(ctx)); std::swap(result, temp_result); return err; @@ -122,7 +122,7 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Sign(KeyListPtr keys, auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_result = GpgSignResult(gpgme_op_sign_result(ctx)); + auto temp_result = _new_result(gpgme_op_sign_result(ctx)); std::swap(result, temp_result); @@ -141,10 +141,10 @@ gpgme_error_t GpgFrontend::BasicOperator::DecryptVerify( auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_decr_result = GpgDecrResult(gpgme_op_decrypt_result(ctx)); + auto temp_decr_result = _new_result(gpgme_op_decrypt_result(ctx)); std::swap(decrypt_result, temp_decr_result); - auto temp_verify_result = GpgVerifyResult(gpgme_op_verify_result(ctx)); + auto temp_verify_result = _new_result(gpgme_op_verify_result(ctx)); std::swap(verify_result, temp_verify_result); return err; @@ -176,9 +176,9 @@ gpgme_error_t GpgFrontend::BasicOperator::EncryptSign( auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_encr_result = GpgEncrResult(gpgme_op_encrypt_result(ctx)); + auto temp_encr_result = _new_result(gpgme_op_encrypt_result(ctx)); swap(encr_result, temp_encr_result); - auto temp_sign_result = GpgSignResult(gpgme_op_sign_result(ctx)); + auto temp_sign_result = _new_result(gpgme_op_sign_result(ctx)); swap(sign_result, temp_sign_result); return err; @@ -217,8 +217,11 @@ gpg_error_t GpgFrontend::BasicOperator::EncryptSymmetric( auto temp_data_out = data_out.Read2Buffer(); std::swap(temp_data_out, out_buffer); - auto temp_result = GpgEncrResult(gpgme_op_encrypt_result(ctx)); - std::swap(result, temp_result); + // TODO(Saturneric): maybe a bug of gpgme + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + auto temp_result = _new_result(gpgme_op_encrypt_result(ctx)); + std::swap(result, temp_result); + } return err; } diff --git a/src/gpg/function/GpgKeyGetter.cpp b/src/gpg/function/GpgKeyGetter.cpp index be27d69e..664aff56 100644 --- a/src/gpg/function/GpgKeyGetter.cpp +++ b/src/gpg/function/GpgKeyGetter.cpp @@ -29,7 +29,7 @@ #include "GpgConstants.h" GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetKey(const std::string& fpr) { - gpgme_key_t _p_key; + gpgme_key_t _p_key = nullptr; gpgme_get_key(ctx, fpr.c_str(), &_p_key, 1); if (_p_key == nullptr) { DLOG(WARNING) << "GpgKeyGetter GetKey Private _p_key Null fpr" << fpr; @@ -41,7 +41,7 @@ GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetKey(const std::string& fpr) { GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetPubkey( const std::string& fpr) { - gpgme_key_t _p_key; + gpgme_key_t _p_key = nullptr; gpgme_get_key(ctx, fpr.c_str(), &_p_key, 0); if (_p_key == nullptr) DLOG(WARNING) << "GpgKeyGetter GetKey _p_key Null" << fpr; @@ -67,9 +67,24 @@ GpgFrontend::KeyLinkListPtr GpgFrontend::GpgKeyGetter::FetchKey() { return keys_list; } + GpgFrontend::KeyListPtr GpgFrontend::GpgKeyGetter::GetKeys( const KeyIdArgsListPtr& ids) { auto keys = std::make_unique<KeyArgsList>(); for (const auto& id : *ids) keys->push_back(GetKey(id)); return keys; } + +GpgFrontend::KeyLinkListPtr GpgFrontend::GpgKeyGetter::GetKeysCopy( + const GpgFrontend::KeyLinkListPtr& keys) { + auto keys_copy = std::make_unique<GpgKeyLinkList>(); + for (const auto& key : *keys) keys_copy->push_back(key.copy()); + return keys_copy; +} + +GpgFrontend::KeyListPtr GpgFrontend::GpgKeyGetter::GetKeysCopy( + const GpgFrontend::KeyListPtr& keys) { + auto keys_copy = std::make_unique<KeyArgsList>(); + for (const auto& key : *keys) keys_copy->push_back(key.copy()); + return keys_copy; +} diff --git a/src/gpg/function/GpgKeyGetter.h b/src/gpg/function/GpgKeyGetter.h index c8f5d73a..3af51815 100644 --- a/src/gpg/function/GpgKeyGetter.h +++ b/src/gpg/function/GpgKeyGetter.h @@ -43,6 +43,10 @@ class GpgKeyGetter : public SingletonFunctionObject<GpgKeyGetter> { KeyLinkListPtr FetchKey(); + static KeyListPtr GetKeysCopy(const KeyListPtr& keys); + + static KeyLinkListPtr GetKeysCopy(const KeyLinkListPtr& keys); + private: GpgContext& ctx = GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); diff --git a/src/gpg/function/GpgKeyOpera.cpp b/src/gpg/function/GpgKeyOpera.cpp index cdf5ab24..4bad303d 100644 --- a/src/gpg/function/GpgKeyOpera.cpp +++ b/src/gpg/function/GpgKeyOpera.cpp @@ -66,8 +66,6 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::SetExpire( std::unique_ptr<boost::posix_time::ptime>& expires) { unsigned long expires_time = 0; - LOG(INFO) << "expires" << *expires; - if (expires != nullptr) { using namespace boost::posix_time; using namespace std::chrono; @@ -78,7 +76,7 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::SetExpire( LOG(INFO) << key.id() << subkey_fpr << expires_time; GpgError err; - if (subkey_fpr.empty()) + if (key.fpr() == subkey_fpr || subkey_fpr.empty()) err = gpgme_op_setexpire(ctx, gpgme_key_t(key), expires_time, nullptr, 0); else err = gpgme_op_setexpire(ctx, gpgme_key_t(key), expires_time, diff --git a/src/gpg/model/GpgKey.h b/src/gpg/model/GpgKey.h index 3bebcd41..fb7d5735 100644 --- a/src/gpg/model/GpgKey.h +++ b/src/gpg/model/GpgKey.h @@ -144,6 +144,12 @@ class GpgKey { explicit operator gpgme_key_t() const { return _key_ref.get(); } + [[nodiscard]] GpgKey copy() const { + gpgme_key_ref(_key_ref.get()); + auto* _new_key_ref = _key_ref.get(); + return GpgKey(std::move(_new_key_ref)); + } + private: struct _key_ref_deletor { void operator()(gpgme_key_t _key) { diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 00000000..4f441c29 --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,128 @@ +/** + * 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. + * + * Foobar 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 Foobar. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from gpg4usb-team. + * Their source code version also complies with GNU General Public License. + * + * The source code version of this software was modified and released + * by Saturneric<[email protected]> starting on May 12, 2021. + * + */ + +#include <boost/date_time.hpp> + +#include "ui/settings/GlobalSettingStation.h" + +void init_logging() { + using namespace boost::posix_time; + using namespace boost::gregorian; + + ptime now = second_clock::local_time(); + + el::Loggers::addFlag(el::LoggingFlag::AutoSpacing); + el::Configurations defaultConf; + defaultConf.setToDefault(); + el::Loggers::reconfigureLogger("default", defaultConf); + + defaultConf.setGlobally(el::ConfigurationType::Format, + "%datetime %level %func %msg"); + + auto logfile_path = + (GpgFrontend::UI::GlobalSettingStation::GetInstance().GetLogDir() / + to_iso_string(now)); + logfile_path.replace_extension(".log"); + defaultConf.setGlobally(el::ConfigurationType::Filename, + logfile_path.string()); + + el::Loggers::reconfigureLogger("default", defaultConf); + + LOG(INFO) << _("Logfile Path") << logfile_path; +} + +void init_locale() { + auto& settings = + GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("general") || + settings.lookup("general").getType() != libconfig::Setting::TypeGroup) + settings.add("general", libconfig::Setting::TypeGroup); + + // set system default at first + auto& general = settings["general"]; + if (!general.exists("lang")) + general.add("lang", libconfig::Setting::TypeString) = ""; + + GpgFrontend::UI::GlobalSettingStation::GetInstance().Sync(); + + LOG(INFO) << "current system locale" << setlocale(LC_ALL, nullptr); + + // read from settings file + std::string lang; + if (!general.lookupValue("lang", lang)) { + LOG(ERROR) << _("Could not read properly from configure file"); + }; + + LOG(INFO) << "lang from settings" << lang; + LOG(INFO) << "PROJECT_NAME" << PROJECT_NAME; + LOG(INFO) << "locales path" + << GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetLocaleDir() + .c_str(); + +#ifndef WINDOWS + if (!lang.empty()) { + std::string lc = lang.empty() ? "" : lang + ".UTF-8"; + + // set LC_ALL + auto* locale_name = setlocale(LC_ALL, lc.c_str()); + if (locale_name == nullptr) LOG(WARNING) << "set LC_ALL failed" << lc; + auto language = getenv("LANGUAGE"); + // set LANGUAGE + std::string language_env = language == nullptr ? "en" : language; + language_env.insert(0, lang + ":"); + LOG(INFO) << "language env" << language_env; + if (setenv("LANGUAGE", language_env.c_str(), 1)) { + LOG(WARNING) << "set LANGUAGE failed" << language_env; + }; + } +#else + if (!lang.empty()) { + std::string lc = lang.empty() ? "" : lang; + + // set LC_ALL + auto* locale_name = setlocale(LC_ALL, lc.c_str()); + if (locale_name == nullptr) LOG(WARNING) << "set LC_ALL failed" << lc; + + auto language = getenv("LANGUAGE"); + // set LANGUAGE + std::string language_env = language == nullptr ? "en" : language; + language_env.insert(0, lang + ":"); + language_env.insert(0, "LANGUAGE="); + LOG(INFO) << "language env" << language_env; + if (putenv(language_env.c_str())) { + LOG(WARNING) << "set LANGUAGE failed" << language_env; + }; + } +#endif + + bindtextdomain(PROJECT_NAME, + GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetLocaleDir() + .string() + .c_str()); + bind_textdomain_codeset(PROJECT_NAME, "utf-8"); + textdomain(PROJECT_NAME); +} diff --git a/src/main.cpp b/src/main.cpp index 5faf34a9..10d54958 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,21 +22,32 @@ * */ +#include <csetjmp> +#include <csignal> #include <cstdlib> #include "GpgFrontendBuildInfo.h" #include "gpg/GpgContext.h" +#include "gpg/function/GpgKeyGetter.h" #include "ui/MainWindow.h" -#include "ui/WaitingDialog.h" #include "ui/settings/GlobalSettingStation.h" // Easy Logging Cpp INITIALIZE_EASYLOGGINGPP -void init_logging(); -void init_locale(); +// Recover buff +jmp_buf recover_env; + +extern void init_logging(); +extern void init_locale(); +extern void handle_signal(int sig); int main(int argc, char* argv[]) { + // Register Signals + signal(SIGSEGV, handle_signal); + signal(SIGFPE, handle_signal); + signal(SIGILL, handle_signal); + // Qt Q_INIT_RESOURCE(gpgfrontend); @@ -67,7 +78,8 @@ int main(int argc, char* argv[]) { #if !defined(RELEASE) && defined(WINDOWS) // css - QFile file(RESOURCE_DIR(qApp->applicationDirPath()) + "/css/default.qss"); + boost::filesystem::path css_path = GpgFrontend::UI::GlobalSettingStation::GetInstance().GetResourceDir() / "css" / "default.qss"; + QFile file(css_path.string().c_str()); file.open(QFile::ReadOnly); QString styleSheet = QLatin1String(file.readAll()); qApp->setStyleSheet(styleSheet); @@ -86,13 +98,13 @@ int main(int argc, char* argv[]) { QCoreApplication::quit(); exit(0); } + // Try fetching key + GpgFrontend::GpgKeyGetter::GetInstance().FetchKey(); }); QApplication::connect(init_ctx_thread, &QThread::finished, init_ctx_thread, &QThread::deleteLater); - init_ctx_thread->start(); - // Waiting Dialog auto* waiting_dialog = new QProgressDialog(); waiting_dialog->setMaximum(0); @@ -116,6 +128,8 @@ int main(int argc, char* argv[]) { // Show Waiting Dialog waiting_dialog->show(); waiting_dialog->setFocus(); + + init_ctx_thread->start(); while (init_ctx_thread->isRunning()) { QApplication::processEvents(); } @@ -129,126 +143,47 @@ int main(int argc, char* argv[]) { int return_from_event_loop_code; do { - try { - // i18n - init_locale(); - - QApplication::setQuitOnLastWindowClosed(true); - - auto main_window = std::make_unique<GpgFrontend::UI::MainWindow>(); - main_window->init(); - main_window->show(); - return_from_event_loop_code = QApplication::exec(); - - } catch (...) { - QMessageBox::information(nullptr, _("Unhandled Exception Thrown"), - _("Oops, an unhandled exception was thrown " - "during the running of the " - "program, and now it needs to be restarted.")); +#ifndef WINDOWS + int r = sigsetjmp(recover_env, 1); +#else + int r = setjmp(recover_env); +#endif + if (!r) { + + try { + // i18n + init_locale(); + + QApplication::setQuitOnLastWindowClosed(true); + + auto main_window = std::make_unique<GpgFrontend::UI::MainWindow>(); + main_window->init(); + main_window->show(); + return_from_event_loop_code = QApplication::exec(); + + } catch (...) { + QMessageBox::information( + nullptr, _("Unhandled Exception Thrown"), + _("Oops, an unhandled exception was thrown " + "during the running of the " + "program, and now it needs to be restarted. This is not a " + "serious problem, it may be the negligence of the programmer, " + "please report this problem if you can.")); + continue; + } + + } else { + QMessageBox::information( + nullptr, _("A serious error has occurred"), + _("Oh no! GpgFrontend caught a serious error in the software, so it " + "needs to be restarted. If the problem recurs, please manually " + "terminate the program and report the problem to the developer.")); + return_from_event_loop_code = RESTART_CODE; + LOG(INFO) << "return_from_event_loop_code" << return_from_event_loop_code; continue; } + LOG(INFO) << "loop refresh"; } while (return_from_event_loop_code == RESTART_CODE); return return_from_event_loop_code; } - -void init_logging() { - using namespace boost::posix_time; - using namespace boost::gregorian; - - ptime now = second_clock::local_time(); - - el::Loggers::addFlag(el::LoggingFlag::AutoSpacing); - el::Configurations defaultConf; - defaultConf.setToDefault(); - el::Loggers::reconfigureLogger("default", defaultConf); - - defaultConf.setGlobally(el::ConfigurationType::Format, - "%datetime %level %func %msg"); - - auto logfile_path = - (GpgFrontend::UI::GlobalSettingStation::GetInstance().GetLogDir() / - to_iso_string(now)); - logfile_path.replace_extension(".log"); - defaultConf.setGlobally(el::ConfigurationType::Filename, - logfile_path.string()); - - el::Loggers::reconfigureLogger("default", defaultConf); - - LOG(INFO) << _("Logfile Path") << logfile_path; -} - -void init_locale() { - auto& settings = - GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings(); - - if (!settings.exists("general") || - settings.lookup("general").getType() != libconfig::Setting::TypeGroup) - settings.add("general", libconfig::Setting::TypeGroup); - - // set system default at first - auto& general = settings["general"]; - if (!general.exists("lang")) - general.add("lang", libconfig::Setting::TypeString) = ""; - - GpgFrontend::UI::GlobalSettingStation::GetInstance().Sync(); - - LOG(INFO) << "current system locale" << setlocale(LC_ALL, nullptr); - - // read from settings file - std::string lang; - if (!general.lookupValue("lang", lang)) { - LOG(ERROR) << _("Could not read properly from configure file"); - }; - - LOG(INFO) << "lang from settings" << lang; - LOG(INFO) << "PROJECT_NAME" << PROJECT_NAME; - LOG(INFO) << "locales path" - << GpgFrontend::UI::GlobalSettingStation::GetInstance() - .GetLocaleDir() - .c_str(); - -#ifndef WINDOWS - if (!lang.empty()) { - std::string lc = lang.empty() ? "" : lang + ".UTF-8"; - - // set LC_ALL - auto* locale_name = setlocale(LC_ALL, lc.c_str()); - if (locale_name == nullptr) LOG(WARNING) << "set LC_ALL failed" << lc; - auto language = getenv("LANGUAGE"); - // set LANGUAGE - std::string language_env = language == nullptr ? "en" : language; - language_env.insert(0, lang + ":"); - LOG(INFO) << "language env" << language_env; - if (setenv("LANGUAGE", language_env.c_str(), 1)) { - LOG(WARNING) << "set LANGUAGE failed" << language_env; - }; - } -#else - if (!lang.empty()) { - std::string lc = lang.empty() ? "" : lang; - - // set LC_ALL - auto* locale_name = setlocale(LC_ALL, lc.c_str()); - if (locale_name == nullptr) LOG(WARNING) << "set LC_ALL failed" << lc; - - auto language = getenv("LANGUAGE"); - // set LANGUAGE - std::string language_env = language == nullptr ? "en" : language; - language_env.insert(0, lang + ":"); - language_env.insert(0, "LANGUAGE="); - LOG(INFO) << "language env" << language_env; - if (putenv(language_env.c_str())) { - LOG(WARNING) << "set LANGUAGE failed" << language_env; - }; - } -#endif - - bindtextdomain(PROJECT_NAME, - GpgFrontend::UI::GlobalSettingStation::GetInstance() - .GetLocaleDir() - .string() - .c_str()); - bind_textdomain_codeset(PROJECT_NAME, "utf-8"); - textdomain(PROJECT_NAME); -} diff --git a/src/signal.cpp b/src/signal.cpp new file mode 100644 index 00000000..efcd8146 --- /dev/null +++ b/src/signal.cpp @@ -0,0 +1,38 @@ +/** + * 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. + * + * Foobar 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 Foobar. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from gpg4usb-team. + * Their source code version also complies with GNU General Public License. + * + * The source code version of this software was modified and released + * by Saturneric<[email protected]> starting on May 12, 2021. + * + */ + +#include <easyloggingpp/easylogging++.h> + +#include <csetjmp> + +extern jmp_buf recover_env; + +void handle_signal(int sig) { + LOG(INFO) << "signal caught"; +#ifndef WINDOWS + siglongjmp(recover_env, 1); +#else + longjmp(recover_env, 1); +#endif +} diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 7c6bf732..529921e0 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -9,7 +9,6 @@ aux_source_directory(./function UI_SOURCE) aux_source_directory(./details UI_SOURCE) if (SMTP_SUPPORT) - message(STATUS "Build SMTP Support") aux_source_directory(./smtp UI_SOURCE) endif () @@ -18,6 +17,6 @@ set(GPGFRONTEND_UI_LIB_NAME gpgfrontend-ui) target_link_libraries(${GPGFRONTEND_UI_LIB_NAME} Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core) target_include_directories(gpgfrontend-ui PUBLIC - ${CMAKE_CURRENT_BINARY_DIR}/${GPGFRONTEND_UI_LIB_NAME}_autogen/include) + ${CMAKE_CURRENT_BINARY_DIR}/${GPGFRONTEND_UI_LIB_NAME}_autogen/include) target_compile_features(gpgfrontend-ui PUBLIC cxx_std_17)
\ No newline at end of file diff --git a/src/ui/FileEncryptionDialog.cpp b/src/ui/FileEncryptionDialog.cpp index aeba6497..6930c3c9 100755 --- a/src/ui/FileEncryptionDialog.cpp +++ b/src/ui/FileEncryptionDialog.cpp @@ -86,42 +86,44 @@ FileEncryptionDialog::FileEncryptionDialog(KeyIdArgsListPtr keyList, groupBox1->setLayout(gLayout); /*Setup KeyList*/ - + mKeyList = new KeyList(false, this); if (mAction == Verify) { - mKeyList = - new KeyList(KeyListRow::ONLY_SECRET_KEY, - KeyListColumn::NAME | KeyListColumn::EmailAddress | - KeyListColumn::Usage, - [](const GpgKey& key) -> bool { - if (key.disabled() || key.expired() || key.revoked()) - return false; - else - return true; - }); + mKeyList->addListGroupTab( + _("Default"), KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | KeyListColumn::EmailAddress | + KeyListColumn::Usage, + [](const GpgKey& key) -> bool { + if (key.disabled() || key.expired() || key.revoked()) + return false; + else + return true; + }); } if (mAction == Encrypt) { - mKeyList = new KeyList(KeyListRow::ONLY_SECRET_KEY, - KeyListColumn::NAME | KeyListColumn::EmailAddress | - KeyListColumn::Usage, - [](const GpgKey& key) -> bool { - if (!key.CanEncrActual()) - return false; - else - return true; - }); + mKeyList->addListGroupTab(_("Default"), KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | + KeyListColumn::EmailAddress | + KeyListColumn::Usage, + [](const GpgKey& key) -> bool { + if (!key.CanEncrActual()) + return false; + else + return true; + }); } if (mAction == Encrypt) { - mKeyList = new KeyList(KeyListRow::ONLY_SECRET_KEY, - KeyListColumn::NAME | KeyListColumn::EmailAddress | - KeyListColumn::Usage, - [](const GpgKey& key) -> bool { - if (!key.CanSignActual()) - return false; - else - return true; - }); + mKeyList->addListGroupTab(_("Default"), KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | + KeyListColumn::EmailAddress | + KeyListColumn::Usage, + [](const GpgKey& key) -> bool { + if (!key.CanSignActual()) + return false; + else + return true; + }); } if (mAction == Decrypt) mKeyList->setDisabled(true); diff --git a/src/ui/KeyImportDetailDialog.cpp b/src/ui/KeyImportDetailDialog.cpp index 48d1e9ee..c4a7f6e8 100644 --- a/src/ui/KeyImportDetailDialog.cpp +++ b/src/ui/KeyImportDetailDialog.cpp @@ -46,10 +46,8 @@ KeyImportDetailDialog::KeyImportDetailDialog(GpgImportInformation result, this->createGeneralInfoBox(); mv_box->addWidget(generalInfoBox); - this->createKeysTable(); mv_box->addWidget(keysTable); - this->createButtonBox(); mv_box->addWidget(buttonBox); @@ -60,10 +58,12 @@ KeyImportDetailDialog::KeyImportDetailDialog(GpgImportInformation result, this->setWindowTitle(_("Key Import Details")); auto pos = QPoint(100, 100); - LOG(INFO) << "parent" << parent; if (parent) pos += parent->pos(); this->move(pos); - this->resize(QSize(600, 300)); + + this->setMinimumSize(QSize(600, 300)); + this->adjustSize(); + this->setModal(true); this->show(); } diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index f28e6587..c03b8e6b 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -36,7 +36,7 @@ namespace GpgFrontend::UI { KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { /* the list of Keys available*/ - mKeyList = new KeyList(this); + mKeyList = new KeyList(true, this); mKeyList->addListGroupTab(_("All"), KeyListRow::SECRET_OR_PUBLIC_KEY); @@ -84,6 +84,8 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { new KeyDetailsDialog(key, parent); }); + mKeyList->slotRefresh(); + createActions(); createMenus(); createToolBars(); @@ -139,6 +141,8 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { this->resize(size); this->move(pos); + this->setWindowModality(Qt::ApplicationModal); + this->statusBar()->show(); setWindowTitle(_("KeyPair Management")); mKeyList->addMenuAction(deleteSelectedKeysAct); @@ -146,6 +150,11 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { connect(this, SIGNAL(signalKeyStatusUpdated()), SignalStation::GetInstance(), SIGNAL(KeyDatabaseRefresh())); + connect(SignalStation::GetInstance(), + &SignalStation::signalRefreshStatusBar, this, + [=](const QString& message, int timeout) { + statusBar()->showMessage(message, timeout); + }); } void KeyMgmt::createActions() { @@ -245,6 +254,7 @@ void KeyMgmt::createMenus() { importKeyMenu->addAction(importKeyFromFileAct); importKeyMenu->addAction(importKeyFromClipboardAct); importKeyMenu->addAction(importKeyFromKeyServerAct); + keyMenu->addAction(exportKeyToFileAct); keyMenu->addAction(exportKeyToClipboardAct); keyMenu->addAction(exportKeyAsOpenSSHFormat); diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 6d55aeb5..8e6b6f81 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -28,6 +28,7 @@ #ifdef RELEASE #include "ui/function/VersionCheckThread.h" #endif +#include "ui/SignalStation.h" #include "ui/settings/GlobalSettingStation.h" namespace GpgFrontend::UI { @@ -49,9 +50,7 @@ void MainWindow::init() noexcept { setCentralWidget(edit); /* the list of Keys available*/ - mKeyList = new KeyList(this); - - mKeyList->slotRefresh(); + mKeyList = new KeyList(true, this); infoBoard = new InfoBoardWidget(this); @@ -69,13 +68,16 @@ void MainWindow::init() noexcept { connect(edit->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotDisableTabActions(int))); + connect(SignalStation::GetInstance(), + &SignalStation::signalRefreshStatusBar, this, + [=](const QString& message, int timeout) { + statusBar()->showMessage(message, timeout); + }); mKeyList->addMenuAction(appendSelectedKeysAct); mKeyList->addMenuAction(copyMailAddressToClipboardAct); - mKeyList->addMenuAction(showKeyDetailsAct); mKeyList->addSeparator(); - mKeyList->addMenuAction(refreshKeysFromKeyserverAct); - mKeyList->addMenuAction(uploadKeyToServerAct); + mKeyList->addMenuAction(showKeyDetailsAct); restoreSettings(); diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 1a36bf8a..68b062ce 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -314,14 +314,18 @@ class MainWindow : public QMainWindow { */ [[nodiscard]] bool getRestartNeeded() const; - TextEdit* edit{}; /** Tabwidget holding the edit-windows */ - QMenu* fileMenu{}; /** Submenu for file-operations*/ - QMenu* editMenu{}; /** Submenu for text-operations*/ - QMenu* cryptMenu{}; /** Submenu for crypt-operations */ - QMenu* helpMenu{}; /** Submenu for help-operations */ - QMenu* keyMenu{}; /** Submenu for key-operations */ - QMenu* viewMenu{}; /** Submenu for view operations */ - QMenu* importKeyMenu{}; /** Sumenu for import operations */ + TextEdit* edit{}; /** Tabwidget holding the edit-windows */ + QMenu* fileMenu{}; /** Submenu for file-operations*/ + QMenu* editMenu{}; /** Submenu for text-operations*/ + QMenu* cryptMenu{}; /** Submenu for crypt-operations */ + QMenu* helpMenu{}; /** Submenu for help-operations */ + QMenu* keyMenu{}; /** Submenu for key-operations */ + QMenu* viewMenu{}; /** Submenu for view operations */ + QMenu* importKeyMenu{}; /** Sumenu for import operations */ +#ifdef SMTP_SUPPORT + QMenu* emailMenu{}; /** Sumenu for email operations */ +#endif + QMenu* steganoMenu{}; /** Submenu for steganographic operations*/ QToolBar* cryptToolBar{}; /** Toolbar holding crypt actions */ QToolBar* fileToolBar{}; /** Toolbar holding file actions */ @@ -374,12 +378,13 @@ class MainWindow : public QMainWindow { QAction* translateAct{}; /** Action to open about dialog */ QAction* openSettingsAct{}; /** Action to open settings dialog */ QAction* showKeyDetailsAct{}; /** Action to open key-details dialog */ - QAction* refreshKeysFromKeyserverAct{}; /** Action to refresh a key from - keyserver */ - QAction* uploadKeyToServerAct{}; /** Action to append selected keys to edit */ - QAction* startWizardAct{}; /** Action to open the wizard */ - QAction* cutPgpHeaderAct{}; /** Action for cutting the PGP header */ - QAction* addPgpHeaderAct{}; /** Action for adding the PGP header */ + QAction* startWizardAct{}; /** Action to open the wizard */ + QAction* cutPgpHeaderAct{}; /** Action for cutting the PGP header */ + QAction* addPgpHeaderAct{}; /** Action for adding the PGP header */ + +#ifdef SMTP_SUPPORT + QAction* sendMailAct{}; /** Action for sending a email */ +#endif QAction* importKeyFromFileAct{}; QAction* importKeyFromClipboardAct{}; diff --git a/src/ui/SignalStation.h b/src/ui/SignalStation.h index bfd5e197..394a7323 100644 --- a/src/ui/SignalStation.h +++ b/src/ui/SignalStation.h @@ -42,6 +42,8 @@ class SignalStation : public QObject { void signalRefreshInfoBoard(const QString& text, InfoBoardStatus verify_label_status); + + void signalRefreshStatusBar(const QString& message, int timeout); }; } // namespace GpgFrontend::UI diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index 3c10009a..0931a179 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -24,9 +24,13 @@ #include "UserInterfaceUtils.h" +#include <utility> + #include "gpg/result_analyse/ResultAnalyse.h" #include "ui/SignalStation.h" #include "ui/WaitingDialog.h" +#include "ui/settings/GlobalSettingStation.h" +#include "ui/smtp/SendMailDialog.h" #include "ui/widgets/InfoBoardWidget.h" #include "ui/widgets/TextEdit.h" @@ -35,16 +39,36 @@ namespace GpgFrontend::UI { std::unique_ptr<GpgFrontend::UI::CommonUtils> GpgFrontend::UI::CommonUtils::_instance = nullptr; +#ifdef SMTP_SUPPORT +void send_an_email(QWidget* parent, InfoBoardWidget* info_board, + const QString& text) { + info_board->addOptionalAction(_("Send Encrypted Mail"), [=]() { + bool smtp_enabled = false; + try { + smtp_enabled = GlobalSettingStation::GetInstance().GetUISettings().lookup( + "smtp.enable"); + } catch (...) { + LOG(INFO) << "Reading smtp settings error"; + } + if (smtp_enabled) { + auto dialog = new SendMailDialog(text, parent); + dialog->show(); + } else { + QMessageBox::warning(nullptr, _("Function Disabled"), + _("Please go to the settings interface to " + "enable and configure this function.")); + } + }); +} +#endif + void show_verify_details(QWidget* parent, InfoBoardWidget* info_board, - GpgError error, VerifyResultAnalyse& verify_res) { + GpgError error, const GpgVerifyResult& verify_result) { // take out result - auto _result = verify_res.TakeChargeOfResult(); info_board->resetOptionActionsMenu(); - info_board->addOptionalAction( - "Show Verify Details", [parent, error, _result_ptr = _result.get()]() { - VerifyDetailsDialog(parent, error, GpgVerifyResult(_result_ptr)); - }); - _result.reset(nullptr); + info_board->addOptionalAction("Show Verify Details", [=]() { + VerifyDetailsDialog(parent, error, verify_result); + }); } void import_unknown_key_from_keyserver(QWidget* parent, @@ -164,26 +188,29 @@ void CommonUtils::slotExecuteGpgCommand( QEventLoop looper; auto dialog = new WaitingDialog(_("Processing"), nullptr); dialog->show(); - auto* gpgProcess = new QProcess(&looper); - gpgProcess->setProcessChannelMode(QProcess::MergedChannels); - - connect(gpgProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), - &looper, &QEventLoop::quit); - connect(gpgProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), - dialog, &WaitingDialog::deleteLater); - connect(gpgProcess, &QProcess::errorOccurred, &looper, &QEventLoop::quit); - connect(gpgProcess, &QProcess::started, + auto* gpg_process = new QProcess(&looper); + gpg_process->setProcessChannelMode(QProcess::MergedChannels); + + connect(gpg_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), &looper, + &QEventLoop::quit); + connect(gpg_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), dialog, + &WaitingDialog::deleteLater); + connect(gpg_process, &QProcess::errorOccurred, &looper, &QEventLoop::quit); + connect(gpg_process, &QProcess::started, []() -> void { LOG(ERROR) << "Gpg Process Started Success"; }); - connect(gpgProcess, &QProcess::readyReadStandardOutput, - [interact_func, gpgProcess]() { interact_func(gpgProcess); }); - connect(gpgProcess, &QProcess::errorOccurred, this, [=]() -> void { + connect(gpg_process, &QProcess::readyReadStandardOutput, + [interact_func, gpg_process]() { interact_func(gpg_process); }); + connect(gpg_process, &QProcess::errorOccurred, this, [=]() -> void { LOG(ERROR) << "Error in Process"; dialog->close(); QMessageBox::critical(nullptr, _("Failure"), _("Failed to execute command.")); }); - connect(gpgProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), - this, [=](int, QProcess::ExitStatus status) { + connect(gpg_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, + [=](int, QProcess::ExitStatus status) { dialog->close(); if (status == QProcess::NormalExit) QMessageBox::information(nullptr, _("Success"), @@ -193,12 +220,101 @@ void CommonUtils::slotExecuteGpgCommand( _("Finished executing command.")); }); - gpgProcess->setProgram(GpgContext::GetInstance().GetInfo().AppPath.c_str()); - gpgProcess->setArguments(arguments); - gpgProcess->start(); + gpg_process->setProgram(GpgContext::GetInstance().GetInfo().AppPath.c_str()); + gpg_process->setArguments(arguments); + gpg_process->start(); looper.exec(); dialog->close(); dialog->deleteLater(); } +void CommonUtils::slotImportKeyFromKeyServer( + int ctx_channel, const KeyIdArgsList& key_ids, + const ImportCallbackFunctiopn& callback) { + std::string target_keyserver; + if (target_keyserver.empty()) { + try { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + target_keyserver = settings.lookup("keyserver.default_server").c_str(); + + LOG(INFO) << _("Set target Key Server to default Key Server") + << target_keyserver; + } catch (...) { + LOG(ERROR) << _("Cannot read default_keyserver From Settings"); + QMessageBox::critical( + nullptr, _("Default Keyserver Not Found"), + _("Cannot read default keyserver from your settings, " + "please set a default keyserver first")); + return; + } + } + + auto thread = + QThread::create([target_keyserver, key_ids, callback, ctx_channel]() { + QUrl target_keyserver_url(target_keyserver.c_str()); + + auto network_manager = std::make_unique<QNetworkAccessManager>(); + // LOOP + decltype(key_ids.size()) current_index = 1, all_index = key_ids.size(); + for (const auto& key_id : key_ids) { + // New Req Url + QUrl req_url(target_keyserver_url.scheme() + "://" + + target_keyserver_url.host() + + "/pks/lookup?op=get&search=0x" + key_id.c_str() + + "&options=mr"); + + LOG(INFO) << "request url" << req_url.toString().toStdString(); + + // Waiting for reply + QNetworkReply* reply = network_manager->get(QNetworkRequest(req_url)); + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + // Get Data + auto key_data = reply->readAll(); + auto key_data_ptr = + std::make_unique<ByteArray>(key_data.data(), key_data.size()); + + // Detect status + std::string status; + auto error = reply->error(); + if (error != QNetworkReply::NoError) { + switch (error) { + case QNetworkReply::ContentNotFoundError: + status = _("Key Not Found"); + break; + case QNetworkReply::TimeoutError: + status = _("Timeout"); + break; + case QNetworkReply::HostNotFoundError: + status = _("Key Server Not Found"); + break; + default: + status = _("Connection Error"); + } + } + + reply->deleteLater(); + + // Try importing + GpgImportInformation result = + GpgKeyImportExportor::GetInstance(ctx_channel) + .ImportKey(std::move(key_data_ptr)); + + if (result.imported == 1) { + status = _("The key has been updated"); + } else { + status = _("No need to update the key"); + } + callback(key_id, status, current_index, all_index); + current_index++; + } + }); + + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + thread->start(); +} + } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index 7b05658f..be114b0a 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -25,6 +25,7 @@ #ifndef GPGFRONTEND_USER_INTERFACE_UTILS_H #define GPGFRONTEND_USER_INTERFACE_UTILS_H +#include "gpg/GpgModel.h" #include "gpg/result_analyse/VerifyResultAnalyse.h" #include "ui/GpgFrontendUI.h" @@ -37,8 +38,13 @@ namespace GpgFrontend::UI { class InfoBoardWidget; class TextEdit; +#ifdef SMTP_SUPPORT +void send_an_email(QWidget* parent, InfoBoardWidget* info_board, + const QString& text); +#endif + void show_verify_details(QWidget* parent, InfoBoardWidget* info_board, - GpgError error, VerifyResultAnalyse& verify_res); + GpgError error, const GpgVerifyResult& verify_result); void import_unknown_key_from_keyserver(QWidget* parent, const VerifyResultAnalyse& verify_res); @@ -63,6 +69,9 @@ class CommonUtils : public QWidget { CommonUtils(); + using ImportCallbackFunctiopn = std::function<void( + const std::string&, const std::string&, size_t, size_t)>; + signals: void signalKeyStatusUpdated(); @@ -76,10 +85,16 @@ class CommonUtils : public QWidget { void slotImportKeyFromClipboard(QWidget* parent); + static void slotImportKeyFromKeyServer( + int ctx_channel, const GpgFrontend::KeyIdArgsList& key_ids, + const GpgFrontend::UI::CommonUtils::ImportCallbackFunctiopn& callback); + void slotExecuteGpgCommand( const QStringList& arguments, const std::function<void(QProcess*)>& interact_func); + private slots: + private: static std::unique_ptr<CommonUtils> _instance; }; diff --git a/src/ui/function/FileReadThread.cpp b/src/ui/function/FileReadThread.cpp index a5a861ea..270f50e7 100644 --- a/src/ui/function/FileReadThread.cpp +++ b/src/ui/function/FileReadThread.cpp @@ -54,9 +54,9 @@ void FileReadThread::run() { emit sendReadBlock(QString::fromStdString(buffer_str)); #ifdef RELEASE - QThread::msleep(16); + QThread::msleep(32); #else - QThread::msleep(24); + QThread::msleep(48); #endif } fclose(fp); diff --git a/src/ui/function/VersionCheckThread.cpp b/src/ui/function/VersionCheckThread.cpp index 765ccd2f..bad60ef4 100644 --- a/src/ui/function/VersionCheckThread.cpp +++ b/src/ui/function/VersionCheckThread.cpp @@ -58,13 +58,10 @@ void VersionCheckThread::run() { LOG(INFO) << "latest version from Github" << latest_version; - std::regex re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); - auto version_begin = - std::sregex_iterator(latest_version.begin(), latest_version.end(), re); - auto version_end = std::sregex_iterator(); - if (std::distance(version_begin, version_end)) { - std::smatch match = *version_begin; - latest_version = match.str(); + QRegularExpression re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); + auto version_match = re.match(latest_version.c_str()); + if (version_match.hasMatch()) { + latest_version = version_match.captured(0).toStdString(); LOG(INFO) << "latest version matched" << latest_version; } else { latest_version = current_version; diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index c0a96488..e4b9206a 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -225,18 +225,20 @@ void KeyPairDetailTab::slotExportPublicKey() { _("An error occurred during the export operation.")); return; } - auto fileString = + auto file_string = mKey.name() + " " + mKey.email() + "(" + mKey.id() + ")_pub.asc"; - auto fileName = + auto file_name = QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(fileString), + this, _("Export Key To File"), QString::fromStdString(file_string), QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") .toStdString(); - if (!write_buffer_to_file(fileName, *keyArray)) { + if (file_name.empty()) return; + + if (!write_buffer_to_file(file_name, *keyArray)) { QMessageBox::critical( this, _("Export Error"), - QString(_("Couldn't open %1 for writing")).arg(fileName.c_str())); + QString(_("Couldn't open %1 for writing")).arg(file_name.c_str())); return; } } @@ -266,18 +268,20 @@ void KeyPairDetailTab::slotExportShortPrivateKey() { _("An error occurred during the export operation.")); return; } - auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + - ")_short_secret.asc"; - auto fileName = + auto file_string = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_short_secret.asc"; + auto file_name = QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(fileString), + this, _("Export Key To File"), QString::fromStdString(file_string), QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") .toStdString(); - if (!write_buffer_to_file(fileName, *keyArray)) { + if (file_name.empty()) return; + + if (!write_buffer_to_file(file_name, *keyArray)) { QMessageBox::critical( this, _("Export Error"), - QString(_("Couldn't open %1 for writing")).arg(fileName.c_str())); + QString(_("Couldn't open %1 for writing")).arg(file_name.c_str())); return; } } @@ -303,18 +307,20 @@ void KeyPairDetailTab::slotExportPrivateKey() { _("An error occurred during the export operation.")); return; } - auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + - ")_full_secret.asc"; - auto fileName = + auto file_string = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_full_secret.asc"; + auto file_name = QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(fileString), + this, _("Export Key To File"), QString::fromStdString(file_string), QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") .toStdString(); - if (!write_buffer_to_file(fileName, *keyArray)) { + if (file_name.empty()) return; + + if (!write_buffer_to_file(file_name, *keyArray)) { QMessageBox::critical( this, _("Export Error"), - QString(_("Couldn't open %1 for writing")).arg(fileName.c_str())); + QString(_("Couldn't open %1 for writing")).arg(file_name.c_str())); return; } } @@ -429,12 +435,18 @@ void KeyPairDetailTab::createOperaMenu() { auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this); connect(uploadKeyPair, SIGNAL(triggered()), this, SLOT(slotUploadKeyToServer())); - if (!mKey.is_private_key()) uploadKeyPair->setDisabled(true); + if (!(mKey.is_private_key() && mKey.has_master_key())) + uploadKeyPair->setDisabled(true); - auto* updateKeyPair = new QAction(_("Update Key Pair"), this); + auto* updateKeyPair = new QAction(_("Sync Key Pair From Key Server"), this); connect(updateKeyPair, SIGNAL(triggered()), this, SLOT(slotUpdateKeyFromServer())); + // when a key has master key, it should always upload to keyserver. + if (mKey.has_master_key()) { + updateKeyPair->setDisabled(true); + } + keyServerOperaMenu->addAction(uploadKeyPair); keyServerOperaMenu->addAction(updateKeyPair); diff --git a/src/ui/keypair_details/KeySetExpireDateDialog.cpp b/src/ui/keypair_details/KeySetExpireDateDialog.cpp index a90a4e4b..5b592cf6 100644 --- a/src/ui/keypair_details/KeySetExpireDateDialog.cpp +++ b/src/ui/keypair_details/KeySetExpireDateDialog.cpp @@ -29,12 +29,16 @@ #include "gpg/function/GpgKeyGetter.h" #include "gpg/function/GpgKeyOpera.h" #include "ui/SignalStation.h" +#include "ui/settings/GlobalSettingStation.h" +#include "ui_ModifiedExpirationDateTime.h" namespace GpgFrontend::UI { KeySetExpireDateDialog::KeySetExpireDateDialog(const KeyId& key_id, QWidget* parent) - : QDialog(parent), mKey(GpgKeyGetter::GetInstance().GetKey(key_id)) { + : QDialog(parent), + ui(std::make_shared<Ui_ModifiedExpirationDateTime>()), + mKey(GpgKeyGetter::GetInstance().GetKey(key_id)) { init(); } @@ -42,19 +46,20 @@ KeySetExpireDateDialog::KeySetExpireDateDialog(const KeyId& key_id, std::string subkey_fpr, QWidget* parent) : QDialog(parent), + ui(std::make_shared<Ui_ModifiedExpirationDateTime>()), mKey(GpgKeyGetter::GetInstance().GetKey(key_id)), mSubkey(std::move(subkey_fpr)) { init(); } void KeySetExpireDateDialog::slotConfirm() { - LOG(INFO) << "Called" - << this->dateTimeEdit->dateTime().toLocalTime().toTime_t(); + LOG(INFO) << "Called" << ui->dateEdit->date().toString().toStdString() + << ui->timeEdit->time().toString().toStdString(); + auto datetime = QDateTime(ui->dateEdit->date(), ui->timeEdit->time()); std::unique_ptr<boost::posix_time::ptime> expires = nullptr; - if (this->nonExpiredCheck->checkState() == Qt::Unchecked) { + if (ui->noExpirationCheckBox->checkState() == Qt::Unchecked) { expires = std::make_unique<boost::posix_time::ptime>( - boost::posix_time::from_time_t( - this->dateTimeEdit->dateTime().toLocalTime().toTime_t())); + boost::posix_time::from_time_t(datetime.toLocalTime().toTime_t())); LOG(INFO) << "keyid" << mKey.id() << mSubkey << *expires; } else { LOG(INFO) << "keyid" << mKey.id() << mSubkey << "Non Expired"; @@ -81,44 +86,51 @@ void KeySetExpireDateDialog::slotConfirm() { } void KeySetExpireDateDialog::init() { - QDateTime maxDateTime = - QDateTime::currentDateTime().toLocalTime().addYears(2); - dateTimeEdit = new QDateTimeEdit(maxDateTime); - dateTimeEdit->setTimeSpec(Qt::TimeSpec::TimeZone); - LOG(INFO) << "timespec" << Qt::TimeSpec::TimeZone; - dateTimeEdit->setCalendarPopup(true); - dateTimeEdit->setMinimumDateTime(QDateTime::currentDateTime().addSecs(1)); - dateTimeEdit->setMaximumDateTime(maxDateTime); - - nonExpiredCheck = new QCheckBox(); - nonExpiredCheck->setTristate(false); - confirmButton = new QPushButton(_("Confirm")); - - auto* gridLayout = new QGridLayout(); - gridLayout->addWidget(dateTimeEdit, 0, 0, 1, 2); - gridLayout->addWidget(nonExpiredCheck, 0, 2, 1, 1, Qt::AlignRight); - gridLayout->addWidget(new QLabel(_("Never Expire")), 0, 3); - gridLayout->addWidget(confirmButton, 1, 3); - - connect(nonExpiredCheck, SIGNAL(stateChanged(int)), this, - SLOT(slotNonExpiredChecked(int))); - connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(slotConfirm())); + ui->setupUi(this); + + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + bool longer_expiration_date = false; + try { + longer_expiration_date = settings.lookup("general.longer_expiration_date"); + LOG(INFO) << "longer_expiration_date" << longer_expiration_date; + + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("longer_expiration_date"); + } + + auto max_date_time = + longer_expiration_date + ? QDateTime::currentDateTime().toLocalTime().addYears(30) + : QDateTime::currentDateTime().toLocalTime().addYears(2); - this->setLayout(gridLayout); - this->setWindowTitle(_("Edit Expire Datetime")); - this->setModal(true); - this->setAttribute(Qt::WA_DeleteOnClose, true); + auto min_date_time = QDateTime::currentDateTime().addDays(7); + ui->dateEdit->setMaximumDateTime(max_date_time); + ui->dateEdit->setMinimumDateTime(min_date_time); + + ui->dateEdit->setDateTime(max_date_time); + ui->timeEdit->setDateTime(max_date_time); + + connect(ui->noExpirationCheckBox, SIGNAL(stateChanged(int)), this, + SLOT(slotNonExpiredChecked(int))); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, + &KeySetExpireDateDialog::slotConfirm); connect(this, SIGNAL(signalKeyExpireDateUpdated()), SignalStation::GetInstance(), SIGNAL(KeyDatabaseRefresh())); + + ui->titleLabel->setText(_("Modified Expiration Date (Local Time)")); + ui->label->setText( + _("Tips: For the sake of security, the key is valid for up to two years. " + "If you are an expert user, please unlock it for a longer time in the " + "settings.")); + ui->noExpirationCheckBox->setText(_("No Expiration")); + this->setWindowTitle(_("Modified Expiration Date")); } void KeySetExpireDateDialog::slotNonExpiredChecked(int state) { - if (state == 0) { - this->dateTimeEdit->setDisabled(false); - } else { - this->dateTimeEdit->setDisabled(true); - } + ui->dateEdit->setDisabled(state == Qt::Checked); + ui->timeEdit->setDisabled(state == Qt::Checked); } } // namespace GpgFrontend::UI diff --git a/src/ui/keypair_details/KeySetExpireDateDialog.h b/src/ui/keypair_details/KeySetExpireDateDialog.h index 37a7f7e4..d4386e43 100644 --- a/src/ui/keypair_details/KeySetExpireDateDialog.h +++ b/src/ui/keypair_details/KeySetExpireDateDialog.h @@ -30,6 +30,8 @@ #include "gpg/model/GpgSubKey.h" #include "ui/GpgFrontendUI.h" +class Ui_ModifiedExpirationDateTime; + namespace GpgFrontend::UI { class KeySetExpireDateDialog : public QDialog { @@ -47,13 +49,10 @@ class KeySetExpireDateDialog : public QDialog { private: void init(); + std::shared_ptr<Ui_ModifiedExpirationDateTime> ui; const GpgKey mKey; const SubkeyId mSubkey; - QDateTimeEdit* dateTimeEdit{}; - QPushButton* confirmButton{}; - QCheckBox* nonExpiredCheck{}; - private slots: void slotConfirm(); void slotNonExpiredChecked(int state); diff --git a/src/ui/keypair_details/KeyUIDSignDialog.cpp b/src/ui/keypair_details/KeyUIDSignDialog.cpp index d9592c56..eb459701 100644 --- a/src/ui/keypair_details/KeyUIDSignDialog.cpp +++ b/src/ui/keypair_details/KeyUIDSignDialog.cpp @@ -34,17 +34,17 @@ KeyUIDSignDialog::KeyUIDSignDialog(const GpgKey& key, UIDArgsListPtr uid, QWidget* parent) : QDialog(parent), mUids(std::move(uid)), mKey(key) { const auto key_id = mKey.id(); - mKeyList = new KeyList( - KeyListRow::ONLY_SECRET_KEY, - KeyListColumn::NAME | KeyListColumn::EmailAddress, - [key_id](const GpgKey& key) -> bool { - if (key.disabled() || !key.can_certify() || !key.has_master_key() || - key.expired() || key.revoked() || key_id == key.id()) - return false; - else - return true; - }, - this); + mKeyList = new KeyList(false, this); + mKeyList->addListGroupTab(_("Signers"), KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | KeyListColumn::EmailAddress, + [key_id](const GpgKey& key) -> bool { + if (key.disabled() || !key.can_certify() || + !key.has_master_key() || key.expired() || + key.revoked() || key_id == key.id()) + return false; + else + return true; + }); mKeyList->slotRefresh(); signKeyButton = new QPushButton("Sign"); diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp index 535f47af..2a2c9be9 100644 --- a/src/ui/main_window/MainWindowFileSlotFunction.cpp +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -289,7 +289,7 @@ void MainWindow::slotFileVerify() { }); if (!if_error) { - auto result_analyse = VerifyResultAnalyse(error, std::move(result)); + auto result_analyse = VerifyResultAnalyse(error, result); result_analyse.analyse(); process_result_analyse(edit, infoBoard, result_analyse); @@ -297,7 +297,7 @@ void MainWindow::slotFileVerify() { import_unknown_key_from_keyserver(this, result_analyse); if (result_analyse.getStatus() >= 0) - show_verify_details(this, infoBoard, error, result_analyse); + show_verify_details(this, infoBoard, error, result); fileTreeView->update(); } else { @@ -431,7 +431,7 @@ void MainWindow::slotFileDecryptVerify() { if (!if_error) { auto decrypt_res = DecryptResultAnalyse(error, std::move(d_result)); - auto verify_res = VerifyResultAnalyse(error, std::move(v_result)); + auto verify_res = VerifyResultAnalyse(error, v_result); decrypt_res.analyse(); verify_res.analyse(); process_result_analyse(edit, infoBoard, decrypt_res, verify_res); @@ -440,7 +440,7 @@ void MainWindow::slotFileDecryptVerify() { import_unknown_key_from_keyserver(this, verify_res); if (verify_res.getStatus() >= 0) - show_verify_details(this, infoBoard, error, verify_res); + show_verify_details(this, infoBoard, error, v_result); fileTreeView->update(); } else { diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 41549182..2dd3f842 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -40,6 +40,7 @@ #include "gpg/function/GpgKeyImportExportor.h" #include "ui/UserInterfaceUtils.h" #include "ui/help/AboutDialog.h" +#include "ui/settings/GlobalSettingStation.h" #include "ui/widgets/SignersPicker.h" namespace GpgFrontend::UI { @@ -103,28 +104,18 @@ void MainWindow::slotEncrypt() { } if (!if_error) { + LOG(INFO) << "result" << result.get(); auto resultAnalyse = EncryptResultAnalyse(error, std::move(result)); resultAnalyse.analyse(); process_result_analyse(edit, infoBoard, resultAnalyse); if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) edit->slotFillTextEditWithText(QString::fromStdString(*tmp)); + infoBoard->resetOptionActionsMenu(); #ifdef SMTP_SUPPORT - // set optional actions - if (resultAnalyse.getStatus() >= 0) { - infoBoard->resetOptionActionsMenu(); - infoBoard->addOptionalAction("Send Mail", [this]() { - if (settings.value("sendMail/enable", false).toBool()) - new SendMailDialog(edit->curTextPage()->toPlainText(), this); - else { - QMessageBox::warning(nullptr, _("Function Disabled"), - _("Please go to the settings interface to " - "enable and configure this function.")); - } - }); - } + if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) + send_an_email(this, infoBoard, edit->curTextPage()->toPlainText()); #endif - } else { QMessageBox::critical(this, _("Error"), _("An error occurred during operation.")); @@ -261,7 +252,7 @@ void MainWindow::slotVerify() { }); if (!if_error) { - auto result_analyse = VerifyResultAnalyse(error, std::move(result)); + auto result_analyse = VerifyResultAnalyse(error, result); result_analyse.analyse(); process_result_analyse(edit, infoBoard, result_analyse); @@ -269,7 +260,7 @@ void MainWindow::slotVerify() { import_unknown_key_from_keyserver(this, result_analyse); if (result_analyse.getStatus() >= 0) - show_verify_details(this, infoBoard, error, result_analyse); + show_verify_details(this, infoBoard, error, result); } } @@ -354,17 +345,10 @@ void MainWindow::slotEncryptSign() { if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) edit->slotFillTextEditWithText(QString::fromStdString(*tmp)); -#ifdef SMTP_SUPPORT infoBoard->resetOptionActionsMenu(); - infoBoard->addOptionalAction("Send Mail", [this]() { - if (settings.value("sendMail/enable", false).toBool()) - new SendMailDialog(edit->curTextPage()->toPlainText(), this); - else { - QMessageBox::warning(nullptr, _("Function Disabled"), - _("Please go to the settings interface to " - "enable and configure this function.")); - } - }); +#ifdef SMTP_SUPPORT + if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) + send_an_email(this, infoBoard, edit->curTextPage()->toPlainText()); #endif #ifdef ADVANCE_SUPPORT @@ -378,7 +362,6 @@ void MainWindow::slotEncryptSign() { } }); #endif - } else { QMessageBox::critical(this, _("Error"), _("An error occurred during operation.")); @@ -434,7 +417,7 @@ void MainWindow::slotDecryptVerify() { if (!if_error) { auto decrypt_res = DecryptResultAnalyse(error, std::move(d_result)); - auto verify_res = VerifyResultAnalyse(error, std::move(v_result)); + auto verify_res = VerifyResultAnalyse(error, v_result); decrypt_res.analyse(); verify_res.analyse(); process_result_analyse(edit, infoBoard, decrypt_res, verify_res); @@ -445,7 +428,7 @@ void MainWindow::slotDecryptVerify() { import_unknown_key_from_keyserver(this, verify_res); if (verify_res.getStatus() >= 0) - show_verify_details(this, infoBoard, error, verify_res); + show_verify_details(this, infoBoard, error, v_result); } else { QMessageBox::critical(this, _("Error"), diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index a629057e..0c557d16 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -24,6 +24,7 @@ #include "MainWindow.h" #include "ui/UserInterfaceUtils.h" +#include "ui/smtp/SendMailDialog.h" namespace GpgFrontend::UI { @@ -276,19 +277,6 @@ void MainWindow::createActions() { connect(showKeyDetailsAct, SIGNAL(triggered()), this, SLOT(slotShowKeyDetails())); - refreshKeysFromKeyserverAct = - new QAction(_("Refresh Key From Key Server"), this); - refreshKeysFromKeyserverAct->setToolTip( - _("Refresh key from default key server")); - connect(refreshKeysFromKeyserverAct, SIGNAL(triggered()), this, - SLOT(refreshKeysFromKeyserver())); - - uploadKeyToServerAct = new QAction(_("Upload Public Key(s) To Server"), this); - uploadKeyToServerAct->setToolTip( - _("Upload The Selected Public Keys To Server")); - connect(uploadKeyToServerAct, SIGNAL(triggered()), this, - SLOT(uploadKeyToServer())); - /* Key-Shortcuts for Tab-Switchung-Action */ switchTabUpAct = new QAction(this); @@ -307,6 +295,15 @@ void MainWindow::createActions() { addPgpHeaderAct = new QAction(_("Add PGP Header"), this); connect(addPgpHeaderAct, SIGNAL(triggered()), this, SLOT(slotAddPgpHeader())); + +#ifdef SMTP_SUPPORT + sendMailAct = new QAction(_("Send An Email"), this); + sendMailAct->setIcon(QIcon(":email.png")); + connect(sendMailAct, &QAction::triggered, this, [=]() { + auto* dialog = new SendMailDialog({}, this); + dialog->show(); + }); +#endif } void MainWindow::createMenus() { @@ -363,6 +360,10 @@ void MainWindow::createMenus() { steganoMenu = menuBar()->addMenu(_("Steganography")); steganoMenu->addAction(cutPgpHeaderAct); steganoMenu->addAction(addPgpHeaderAct); +#ifdef SMTP_SUPPORT + emailMenu = menuBar()->addMenu(_("Email")); + emailMenu->addAction(sendMailAct); +#endif #ifdef ADVANCED_SUPPORT // Hide menu, when steganography menu is disabled in settings @@ -482,6 +483,8 @@ void MainWindow::createDockWindows() { !(key.revoked() || key.disabled() || key.expired()); }); + mKeyList->slotRefresh(); + keyListDock->setWidget(mKeyList); viewMenu->addAction(keyListDock->toggleViewAction()); diff --git a/src/ui/settings/GlobalSettingStation.h b/src/ui/settings/GlobalSettingStation.h index 07b904a8..a7cbe569 100644 --- a/src/ui/settings/GlobalSettingStation.h +++ b/src/ui/settings/GlobalSettingStation.h @@ -28,6 +28,7 @@ #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> +#include "GpgFrontendBuildInstallInfo.h" #include "ui/GpgFrontendUI.h" namespace GpgFrontend::UI { @@ -69,11 +70,22 @@ class GlobalSettingStation : public QObject { // Program Data Location boost::filesystem::path app_log_path = app_data_path / "logs"; +#ifdef LINUX_INSTALL_BUILD + // Program Data Location + boost::filesystem::path app_resource_path = + boost::filesystem::path(APP_LOCALSTATE_PATH) / "gpgfrontend"; +#else // Program Data Location boost::filesystem::path app_resource_path = RESOURCE_DIR_BOOST_PATH(app_path); +#endif +#ifdef LINUX_INSTALL_BUILD + // Program Data Location + boost::filesystem::path app_locale_path = std::string(APP_LOCALE_PATH); +#else // Program Data Location boost::filesystem::path app_locale_path = app_resource_path / "locales"; +#endif // Program Configure Location boost::filesystem::path app_configure_path = diff --git a/src/ui/settings/SettingsDialog.cpp b/src/ui/settings/SettingsDialog.cpp index 1fbfb8db..7b8a8624 100644 --- a/src/ui/settings/SettingsDialog.cpp +++ b/src/ui/settings/SettingsDialog.cpp @@ -94,7 +94,8 @@ SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { connect(this, SIGNAL(signalRestartNeeded(bool)), parent, SLOT(slotSetRestartNeeded(bool))); - this->resize(480, 640); + this->setMinimumSize(480, 680); + this->adjustSize(); this->show(); } @@ -105,7 +106,7 @@ void SettingsDialog::slotSetRestartNeeded(bool needed) { } void SettingsDialog::slotAccept() { - LOG(INFO) << "called"; + LOG(INFO) << "Called"; generalTab->applySettings(); #ifdef SMTP_SUPPORT diff --git a/src/ui/settings/SettingsGeneral.cpp b/src/ui/settings/SettingsGeneral.cpp index 978a7b5f..6b6568a4 100644 --- a/src/ui/settings/SettingsGeneral.cpp +++ b/src/ui/settings/SettingsGeneral.cpp @@ -65,6 +65,17 @@ GeneralTab::GeneralTab(QWidget* parent) : QWidget(parent) { saveCheckedKeysBox->setLayout(saveCheckedKeysBoxLayout); /***************************************** + * Longer-Expire-Date-Box + *****************************************/ + auto* longerKeyExpirationDateBox = + new QGroupBox(_("Longer Key Expiration Date")); + auto* longerKeyExpirationDateBoxLayout = new QHBoxLayout(); + longerKeyExpirationDateCheckBox = new QCheckBox( + _("Unlock key expiration date setting up to 30 years."), this); + longerKeyExpirationDateBoxLayout->addWidget(longerKeyExpirationDateCheckBox); + longerKeyExpirationDateBox->setLayout(longerKeyExpirationDateBoxLayout); + + /***************************************** * Key-Impport-Confirmation Box *****************************************/ auto* importConfirmationBox = @@ -89,7 +100,7 @@ GeneralTab::GeneralTab(QWidget* parent) : QWidget(parent) { for (const auto& l : lang) { langSelectBox->addItem(l); } - langSelectBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + langSelectBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); langBoxLayout->addWidget(langSelectBox); langBoxLayout->addWidget(new QLabel( @@ -151,6 +162,7 @@ GeneralTab::GeneralTab(QWidget* parent) : QWidget(parent) { #ifdef SERVER_SUPPORT mainLayout->addWidget(serverBox); #endif + mainLayout->addWidget(longerKeyExpirationDateBox); mainLayout->addWidget(saveCheckedKeysBox); mainLayout->addWidget(importConfirmationBox); #ifdef MULTI_LANG_SUPPORT @@ -179,6 +191,16 @@ void GeneralTab::setSettings() { LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); } + try { + bool longer_expiration_date = + settings.lookup("general.longer_expiration_date"); + LOG(INFO) << "longer_expiration_date" << longer_expiration_date; + if (longer_expiration_date) + longerKeyExpirationDateCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("longer_expiration_date"); + } + #ifdef SERVER_SUPPORT auto serverList = settings.value("general/gpgfrontendServerList").toStringList(); @@ -257,6 +279,14 @@ void GeneralTab::applySettings() { auto& general = settings["general"]; + if (!general.exists("longer_expiration_date")) + general.add("longer_expiration_date", libconfig::Setting::TypeBoolean) = + longerKeyExpirationDateCheckBox->isChecked(); + else { + general["longer_expiration_date"] = + longerKeyExpirationDateCheckBox->isChecked(); + } + if (!general.exists("save_key_checked")) general.add("save_key_checked", libconfig::Setting::TypeBoolean) = saveCheckedKeysCheckBox->isChecked(); diff --git a/src/ui/settings/SettingsGeneral.h b/src/ui/settings/SettingsGeneral.h index 7e118f98..bebe7609 100644 --- a/src/ui/settings/SettingsGeneral.h +++ b/src/ui/settings/SettingsGeneral.h @@ -43,6 +43,7 @@ class GeneralTab : public QWidget { private: QCheckBox* saveCheckedKeysCheckBox; QCheckBox* importConfirmationCheckBox; + QCheckBox* longerKeyExpirationDateCheckBox; #ifdef MULTI_LANG_SUPPORT QComboBox* langSelectBox; diff --git a/src/ui/settings/SettingsKeyServer.cpp b/src/ui/settings/SettingsKeyServer.cpp index f05ad7fb..1d520e35 100644 --- a/src/ui/settings/SettingsKeyServer.cpp +++ b/src/ui/settings/SettingsKeyServer.cpp @@ -25,64 +25,81 @@ #include "SettingsKeyServer.h" #include "GlobalSettingStation.h" +#include "ui_KeyServerSettings.h" namespace GpgFrontend::UI { -KeyserverTab::KeyserverTab(QWidget* parent) : QWidget(parent) { - auto generalGroupBox = new QGroupBox(_("General")); - auto generalLayout = new QVBoxLayout(); - - keyServerTable = new QTableWidget(); - keyServerTable->setColumnCount(3); - keyServerTable->horizontalHeader()->setSectionResizeMode( - QHeaderView::ResizeToContents); - keyServerTable->horizontalHeader()->setStretchLastSection(false); - keyServerTable->verticalHeader()->hide(); - keyServerTable->setShowGrid(false); - keyServerTable->sortByColumn(0, Qt::AscendingOrder); - keyServerTable->setSelectionBehavior(QAbstractItemView::SelectRows); - keyServerTable->setSelectionMode(QAbstractItemView::SingleSelection); - - // tableitems not editable - keyServerTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - // no focus (rectangle around tableitems) - // may be it should focus on whole row - keyServerTable->setFocusPolicy(Qt::NoFocus); - keyServerTable->setAlternatingRowColors(true); - - QStringList labels; - labels << _("No.") << _("Address") << _("Available"); - keyServerTable->setHorizontalHeaderLabels(labels); - - auto* mainLayout = new QVBoxLayout(this); - auto* label = new QLabel(QString(_("Default Key Server for Import")) + ": "); - - comboBox = new QComboBox; - comboBox->setEditable(false); - comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - - auto* addKeyServerBox = new QWidget(this); - auto* addKeyServerLayout = new QHBoxLayout(addKeyServerBox); - auto* http = new QLabel("URL: "); - newKeyServerEdit = new QLineEdit(this); - auto* newKeyServerButton = new QPushButton(_("Add"), this); - connect(newKeyServerButton, SIGNAL(clicked()), this, SLOT(addKeyServer())); - addKeyServerLayout->addWidget(http); - addKeyServerLayout->addWidget(newKeyServerEdit); - addKeyServerLayout->addWidget(newKeyServerButton); - - generalLayout->addWidget(label); - generalLayout->addWidget(comboBox); - generalLayout->addWidget(keyServerTable); - generalLayout->addWidget(addKeyServerBox); - generalLayout->addStretch(0); - - generalGroupBox->setLayout(generalLayout); - mainLayout->addWidget(generalGroupBox); - mainLayout->addStretch(0); - - setLayout(mainLayout); - // Read keylist from ini-file and fill it into combobox +KeyserverTab::KeyserverTab(QWidget* parent) + : QWidget(parent), ui(std::make_shared<Ui_KeyServerSettings>()) { + ui->setupUi(this); + ui->keyServerListTable->setSizeAdjustPolicy( + QAbstractScrollArea::AdjustToContents); + + connect(ui->addKeyServerPushButton, &QPushButton::clicked, this, + &KeyserverTab::addKeyServer); + connect(ui->testKeyServerButton, &QPushButton::clicked, this, + &KeyserverTab::slotTestListedKeyServer); + + ui->keyServerListGroupBox->setTitle(_("Keyserver List")); + ui->operationsGroupBox->setTitle(_("Operations")); + + ui->keyServerListTable->horizontalHeaderItem(0)->setText(_("Default")); + ui->keyServerListTable->horizontalHeaderItem(1)->setText( + _("Keyserver Address")); + ui->keyServerListTable->horizontalHeaderItem(2)->setText(_("Security")); + ui->keyServerListTable->horizontalHeaderItem(3)->setText(_("Available")); + + ui->addKeyServerPushButton->setText(_("Add")); + ui->testKeyServerButton->setText(_("Test Listed Keyserver")); + + ui->tipsLabel->setText(_("Tips: Please Double-click table item to edit it.")); + ui->actionDelete_Selected_Key_Server->setText(_("Delete Selected")); + ui->actionDelete_Selected_Key_Server->setToolTip( + _("Delete Selected Key Server")); + ui->actionSet_As_Default->setText(_("Set As Default")); + ui->actionSet_As_Default->setToolTip(_("Set As Default")); + + popupMenu = new QMenu(this); + popupMenu->addAction(ui->actionSet_As_Default); + popupMenu->addAction(ui->actionDelete_Selected_Key_Server); + + connect(ui->keyServerListTable, &QTableWidget::itemChanged, + [=](QTableWidgetItem* item) { + LOG(INFO) << "item edited" << item->column(); + if (item->column() != 1) return; + const auto row_size = ui->keyServerListTable->rowCount(); + // Update Actions + if (row_size > 0) { + keyServerStrList.clear(); + for (int i = 0; i < row_size; i++) { + const auto key_server = + ui->keyServerListTable->item(i, 1)->text(); + keyServerStrList.append(key_server); + } + } + }); + + connect(ui->actionSet_As_Default, &QAction::triggered, [=]() { + const auto row_size = ui->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto item = ui->keyServerListTable->item(i, 1); + if (!item->isSelected()) continue; + this->defaultKeyServer = item->text(); + } + this->refreshTable(); + }); + + connect(ui->actionDelete_Selected_Key_Server, &QAction::triggered, [=]() { + const auto row_size = ui->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto item = ui->keyServerListTable->item(i, 1); + if (!item->isSelected()) continue; + this->keyServerStrList.removeAt(i); + } + this->refreshTable(); + }); + + // Read key-list from ini-file and fill it into combobox setSettings(); refreshTable(); } @@ -100,7 +117,6 @@ void KeyserverTab::setSettings() { const auto server_list_size = server_list.getLength(); for (int i = 0; i < server_list_size; i++) { std::string server_url = server_list[i]; - comboBox->addItem(server_url.c_str()); keyServerStrList.append(server_url.c_str()); } } catch (...) { @@ -109,28 +125,46 @@ void KeyserverTab::setSettings() { try { std::string default_server = settings.lookup("keyserver.default_server"); - comboBox->setCurrentText(default_server.c_str()); + if (!keyServerStrList.contains(default_server.c_str())) + keyServerStrList.append(default_server.c_str()); + defaultKeyServer = QString::fromStdString(default_server); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("default_server"); } } void KeyserverTab::addKeyServer() { - QString targetUrl; - if (newKeyServerEdit->text().startsWith("http://") || - newKeyServerEdit->text().startsWith("https://")) - targetUrl = newKeyServerEdit->text(); - else - targetUrl = "http://" + newKeyServerEdit->text(); - keyServerStrList.append(targetUrl); - comboBox->addItem(targetUrl); + auto target_url = ui->addKeyServerEdit->text(); + if (url_reg.match(target_url).hasMatch()) { + if (target_url.startsWith("https://")) { + ; + } else if (target_url.startsWith("http://")) { + QMessageBox::warning( + this, _("Insecure keyserver address"), + _("For security reasons, using HTTP as the communication protocol " + "with " + "the key server is not recommended. It is recommended to use " + "HTTPS.")); + } + keyServerStrList.append(ui->addKeyServerEdit->text()); + } else { + auto ret = QMessageBox::warning( + this, _("Warning"), + _("You may not use HTTPS or HTTP as the protocol for communicating " + "with the key server, which may not be wrong. But please check the " + "address you entered again to make sure it is correct. Are you " + "sure " + "that want to add it into the keyserver list?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + else + keyServerStrList.append(ui->addKeyServerEdit->text()); + } refreshTable(); } -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ void KeyserverTab::applySettings() { auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); @@ -140,43 +174,97 @@ void KeyserverTab::applySettings() { auto& keyserver = settings["keyserver"]; - if (keyserver.exists("server_list")) - keyserver.remove("server_list"); + if (keyserver.exists("server_list")) keyserver.remove("server_list"); keyserver.add("server_list", libconfig::Setting::TypeList); + const auto row_size = ui->keyServerListTable->rowCount(); auto& server_list = keyserver["server_list"]; - for (const auto& key_server_url : keyServerStrList) { - server_list.add(libconfig::Setting::TypeString) = - key_server_url.toStdString(); + for (int i = 0; i < row_size; i++) { + const auto key_server = ui->keyServerListTable->item(i, 1)->text(); + server_list.add(libconfig::Setting::TypeString) = key_server.toStdString(); } if (!keyserver.exists("default_server")) { keyserver.add("default_server", libconfig::Setting::TypeString) = - comboBox->currentText().toStdString(); + defaultKeyServer.toStdString(); } else { - keyserver["default_server"] = comboBox->currentText().toStdString(); + keyserver["default_server"] = defaultKeyServer.toStdString(); } } void KeyserverTab::refreshTable() { LOG(INFO) << "Start Refreshing Key Server Table"; - keyServerTable->setRowCount(keyServerStrList.size()); + ui->keyServerListTable->blockSignals(true); + ui->keyServerListTable->setRowCount(keyServerStrList.size()); int index = 0; for (const auto& server : keyServerStrList) { - auto* tmp1 = new QTableWidgetItem(QString::number(index)); + auto* tmp1 = + new QTableWidgetItem(server == defaultKeyServer ? "*" : QString{}); tmp1->setTextAlignment(Qt::AlignCenter); - keyServerTable->setItem(index, 0, tmp1); + ui->keyServerListTable->setItem(index, 0, tmp1); + tmp1->setFlags(tmp1->flags() ^ Qt::ItemIsEditable); + auto* tmp2 = new QTableWidgetItem(server); tmp2->setTextAlignment(Qt::AlignCenter); - keyServerTable->setItem(index, 1, tmp2); - auto* tmp3 = new QTableWidgetItem(); + ui->keyServerListTable->setItem(index, 1, tmp2); + + auto* tmp3 = new QTableWidgetItem(server.startsWith("https") ? _("true") + : _("false")); tmp3->setTextAlignment(Qt::AlignCenter); - keyServerTable->setItem(index, 2, tmp3); + ui->keyServerListTable->setItem(index, 2, tmp3); + tmp3->setFlags(tmp3->flags() ^ Qt::ItemIsEditable); + + auto* tmp4 = new QTableWidgetItem(_("unknown")); + tmp4->setTextAlignment(Qt::AlignCenter); + ui->keyServerListTable->setItem(index, 3, tmp4); + tmp4->setFlags(tmp3->flags() ^ Qt::ItemIsEditable); index++; } + const auto column_count = ui->keyServerListTable->columnCount(); + for (int i = 0; i < column_count; i++) { + ui->keyServerListTable->resizeColumnToContents(i); + } + ui->keyServerListTable->blockSignals(false); +} + +void KeyserverTab::slotTestListedKeyServer() { + ui->keyServerListTable->blockSignals(true); + auto timeout = + QInputDialog::getInt(this, _("Set TCP Timeout"), tr("timeout(ms): "), + QLineEdit::Normal, 500, 2000); + + const auto row_size = ui->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto keyserver = ui->keyServerListTable->item(i, 1)->text(); + const auto status = ui->keyServerListTable->item(i, 3); + if (keyserver == nullptr || status == nullptr) continue; + + auto key_url = QUrl{keyserver}; + + LOG(INFO) << "key domain" << key_url.host().toStdString(); + + QTcpSocket socket(this); + socket.abort(); + socket.connectToHost(key_url.host(), 80); + if (socket.waitForConnected(timeout)) { + status->setText(_("Reachable")); + status->setForeground(QBrush(QColor::fromRgb(0, 255, 0))); + } else { + status->setText(_("Not Reachable")); + status->setForeground(QBrush(QColor::fromRgb(255, 0, 0))); + } + socket.close(); + } + ui->keyServerListTable->blockSignals(false); +} +void KeyserverTab::contextMenuEvent(QContextMenuEvent* event) { + QWidget::contextMenuEvent(event); + if (ui->keyServerListTable->selectedItems().length() > 0) { + popupMenu->exec(event->globalPos()); + } } } // namespace GpgFrontend::UI diff --git a/src/ui/settings/SettingsKeyServer.h b/src/ui/settings/SettingsKeyServer.h index 5042aaa5..735ace98 100644 --- a/src/ui/settings/SettingsKeyServer.h +++ b/src/ui/settings/SettingsKeyServer.h @@ -27,6 +27,8 @@ #include "ui/GpgFrontendUI.h" +class Ui_KeyServerSettings; + namespace GpgFrontend::UI { class KeyserverTab : public QWidget { Q_OBJECT @@ -39,10 +41,13 @@ class KeyserverTab : public QWidget { void applySettings(); private: - QComboBox* comboBox; - QLineEdit* newKeyServerEdit; - QTableWidget* keyServerTable; + std::shared_ptr<Ui_KeyServerSettings> ui; + QString defaultKeyServer; QStringList keyServerStrList; + QMenu* popupMenu{}; + + QRegularExpression url_reg{ + R"(^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$)"}; private slots: @@ -50,9 +55,14 @@ class KeyserverTab : public QWidget { void refreshTable(); + void slotTestListedKeyServer(); + signals: void signalRestartNeeded(bool needed); + + protected: + void contextMenuEvent(QContextMenuEvent* event) override; }; } // namespace GpgFrontend::UI diff --git a/src/ui/settings/SettingsSendMail.cpp b/src/ui/settings/SettingsSendMail.cpp index 2518991d..733ed82f 100644 --- a/src/ui/settings/SettingsSendMail.cpp +++ b/src/ui/settings/SettingsSendMail.cpp @@ -28,126 +28,211 @@ #include "smtp/SmtpMime" #endif +#include "ui/settings/GlobalSettingStation.h" +#include "ui_SendMailSettings.h" + namespace GpgFrontend::UI { SendMailTab::SendMailTab(QWidget* parent) - : QWidget(parent), - appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - enableCheckBox = new QCheckBox(_("Enable")); - enableCheckBox->setTristate(false); - - smtpAddress = new QLineEdit(); - username = new QLineEdit(); - password = new QLineEdit(); - password->setEchoMode(QLineEdit::Password); - - portSpin = new QSpinBox(); - portSpin->setMinimum(1); - portSpin->setMaximum(65535); - connectionTypeComboBox = new QComboBox(); - connectionTypeComboBox->addItem("None"); - connectionTypeComboBox->addItem("SSL"); - connectionTypeComboBox->addItem("TLS"); - connectionTypeComboBox->addItem("STARTTLS"); - - defaultSender = new QLineEdit(); - ; - checkConnectionButton = new QPushButton(_("Check Connection")); - - auto generalGroupBox = new QGroupBox(_("General")); - auto connectGroupBox = new QGroupBox(_("Connection")); - auto preferenceGroupBox = new QGroupBox(_("Preference")); - - auto generalLayout = new QGridLayout(); - generalLayout->addWidget(enableCheckBox); - - auto connectLayout = new QGridLayout(); - connectLayout->addWidget(new QLabel(_("SMTP Address")), 1, 0); - connectLayout->addWidget(smtpAddress, 1, 1, 1, 4); - connectLayout->addWidget(new QLabel(_("Username")), 2, 0); - connectLayout->addWidget(username, 2, 1, 1, 4); - connectLayout->addWidget(new QLabel(_("Password")), 3, 0); - connectLayout->addWidget(password, 3, 1, 1, 4); - connectLayout->addWidget(new QLabel(_("Port")), 4, 0); - connectLayout->addWidget(portSpin, 4, 1, 1, 1); - connectLayout->addWidget(new QLabel(_("Connection Security")), 5, 0); - connectLayout->addWidget(connectionTypeComboBox, 5, 1, 1, 1); - connectLayout->addWidget(checkConnectionButton, 6, 0); - - auto preferenceLayout = new QGridLayout(); - - preferenceLayout->addWidget(new QLabel(_("Default Sender")), 0, 0); - preferenceLayout->addWidget(defaultSender, 0, 1, 1, 4); - - generalGroupBox->setLayout(generalLayout); - connectGroupBox->setLayout(connectLayout); - preferenceGroupBox->setLayout(preferenceLayout); - - auto vBox = new QVBoxLayout(); - vBox->addWidget(generalGroupBox); - vBox->addWidget(connectGroupBox); - vBox->addWidget(preferenceGroupBox); - vBox->addStretch(0); - - connect(enableCheckBox, SIGNAL(stateChanged(int)), this, - SLOT(slotCheckBoxSetEnableDisable(int))); - - this->setLayout(vBox); + : QWidget(parent), ui(std::make_shared<Ui_SendMailSettings>()) { + ui->setupUi(this); + + connect(ui->enableCheckBox, &QCheckBox::stateChanged, this, [=](int state) { + ui->smtpServerAddressEdit->setDisabled(state != Qt::Checked); + ui->portSpin->setDisabled(state != Qt::Checked); + ui->connextionSecurityComboBox->setDisabled(state != Qt::Checked); + + ui->identityCheckBox->setDisabled(state != Qt::Checked); + ui->usernameEdit->setDisabled(state != Qt::Checked); + ui->passwordEdit->setDisabled(state != Qt::Checked); + + ui->defaultSenderEmailEdit->setDisabled(state != Qt::Checked); + ui->checkConnectionButton->setDisabled(state != Qt::Checked); + }); + +#ifdef SMTP_SUPPORT + connect(ui->checkConnectionButton, &QPushButton::clicked, this, + &SendMailTab::slotCheckConnection); + connect(ui->senTestMailButton, &QPushButton::clicked, this, + &SendMailTab::slotSendTestMail); +#endif + + connect(ui->identityCheckBox, &QCheckBox::stateChanged, this, [=](int state) { + ui->usernameEdit->setDisabled(state != Qt::Checked); + ui->passwordEdit->setDisabled(state != Qt::Checked); + }); + + ui->generalGroupBox->setTitle(_("General")); + ui->identityGroupBox->setTitle(_("Identity Information")); + ui->preferenceGroupBox->setTitle(_("Preference")); + ui->operationsGroupBox->setTitle(_("Operations")); + + ui->enableCheckBox->setText(_("Enable Send Mail Ability")); + ui->identityCheckBox->setText(_("Need Auth")); + + ui->smtpServerAddressLabel->setText(_("SMTP Server Address")); + ui->smtpServerPortLabel->setText(_("SMTP Server Port")); + ui->connectionSecurityLabel->setText(_("SMTP Connection Security")); + ui->usernameLabel->setText(_("Username")); + ui->passwordLabel->setText(_("Password")); + + ui->senderLabel->setText(_("Default Sender Email")); + ui->checkConnectionButton->setText(_("Check Connection")); + ui->senTestMailButton->setText(_("Send Test Email")); + ui->tipsLabel->setText( + _("Tips: It is recommended that you build your own mail server or use " + "a trusted mail server. If you don't know the detailed configuration " + "information, you can get it from the mail service provider.")); + + ui->senTestMailButton->setDisabled(true); + + auto* email_validator = + new QRegularExpressionValidator(re_email, ui->defaultSenderEmailEdit); + ui->defaultSenderEmailEdit->setValidator(email_validator); + setSettings(); } -/********************************** - * Read the settings from config - * and set the buttons and checkboxes - * appropriately - **********************************/ void SendMailTab::setSettings() { - if (settings.value("sendMail/enable", false).toBool()) - enableCheckBox->setCheckState(Qt::Checked); - else { - enableCheckBox->setCheckState(Qt::Unchecked); - smtpAddress->setDisabled(true); - username->setDisabled(true); - password->setDisabled(true); - portSpin->setDisabled(true); - connectionTypeComboBox->setDisabled(true); - defaultSender->setDisabled(true); - checkConnectionButton->setDisabled(true); - } - - smtpAddress->setText( - settings.value("sendMail/smtpAddress", QString()).toString()); - username->setText(settings.value("sendMail/username", QString()).toString()); - password->setText(settings.value("sendMail/password", QString()).toString()); - portSpin->setValue(settings.value("sendMail/port", 25).toInt()); - connectionTypeComboBox->setCurrentText( - settings.value("sendMail/connectionType", "None").toString()); - defaultSender->setText( - settings.value("sendMail/defaultSender", QString()).toString()); + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + try { + std::string mail_address = settings.lookup("smtp.mail_address"); + ui->smtpServerAddressEdit->setText(mail_address.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("mail_address"); + } + + try { + std::string std_username = settings.lookup("smtp.username"); + ui->usernameEdit->setText(std_username.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("username"); + } + + try { + std::string std_password = settings.lookup("smtp.password"); + ui->passwordEdit->setText(std_password.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("password"); + } + + try { + int port = settings.lookup("smtp.port"); + ui->portSpin->setValue(port); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("port"); + } + + ui->connextionSecurityComboBox->setCurrentText("None"); + try { + std::string connection_type = settings.lookup("smtp.connection_type"); + ui->connextionSecurityComboBox->setCurrentText(connection_type.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("connection_type"); + } + + try { + std::string default_sender = settings.lookup("smtp.default_sender"); + ui->defaultSenderEmailEdit->setText(default_sender.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("default_sender"); + } + + ui->identityCheckBox->setCheckState(Qt::Unchecked); + try { + bool identity_enable = settings.lookup("smtp.identity_enable"); + if (identity_enable) + ui->identityCheckBox->setCheckState(Qt::Checked); + else + ui->identityCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("identity_enable"); + } + + ui->enableCheckBox->setCheckState(Qt::Unchecked); + try { + bool smtp_enable = settings.lookup("smtp.enable"); + if (smtp_enable) + ui->enableCheckBox->setCheckState(Qt::Checked); + else + ui->enableCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); + } } -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ void SendMailTab::applySettings() { - settings.setValue("sendMail/smtpAddress", smtpAddress->text()); - settings.setValue("sendMail/username", username->text()); - settings.setValue("sendMail/password", password->text()); - settings.setValue("sendMail/port", portSpin->value()); - settings.setValue("sendMail/connectionType", - connectionTypeComboBox->currentText()); - settings.setValue("sendMail/defaultSender", defaultSender->text()); - - settings.setValue("sendMail/enable", enableCheckBox->isChecked()); + auto& settings = + GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("smtp") || + settings.lookup("smtp").getType() != libconfig::Setting::TypeGroup) + settings.add("smtp", libconfig::Setting::TypeGroup); + + auto& smtp = settings["smtp"]; + + if (!smtp.exists("mail_address")) + smtp.add("mail_address", libconfig::Setting::TypeString) = + ui->smtpServerAddressEdit->text().toStdString(); + else { + smtp["mail_address"] = ui->smtpServerAddressEdit->text().toStdString(); + } + + if (!smtp.exists("username")) + smtp.add("username", libconfig::Setting::TypeString) = + ui->usernameEdit->text().toStdString(); + else { + smtp["username"] = ui->usernameEdit->text().toStdString(); + } + + if (!smtp.exists("password")) + smtp.add("password", libconfig::Setting::TypeString) = + ui->passwordEdit->text().toStdString(); + else { + smtp["password"] = ui->passwordEdit->text().toStdString(); + } + + if (!smtp.exists("port")) + smtp.add("port", libconfig::Setting::TypeInt) = ui->portSpin->value(); + else { + smtp["port"] = ui->portSpin->value(); + } + + if (!smtp.exists("connection_type")) + smtp.add("connection_type", libconfig::Setting::TypeString) = + ui->connextionSecurityComboBox->currentText().toStdString(); + else { + smtp["connection_type"] = + ui->connextionSecurityComboBox->currentText().toStdString(); + } + + if (!smtp.exists("default_sender")) + smtp.add("default_sender", libconfig::Setting::TypeString) = + ui->defaultSenderEmailEdit->text().toStdString(); + else { + smtp["default_sender"] = ui->defaultSenderEmailEdit->text().toStdString(); + } + + if (!smtp.exists("identity_enable")) + smtp.add("identity_enable", libconfig::Setting::TypeBoolean) = + ui->identityCheckBox->isChecked(); + else { + smtp["identity_enable"] = ui->identityCheckBox->isChecked(); + } + + if (!smtp.exists("enable")) + smtp.add("enable", libconfig::Setting::TypeBoolean) = + ui->enableCheckBox->isChecked(); + else { + smtp["enable"] = ui->enableCheckBox->isChecked(); + } } #ifdef SMTP_SUPPORT void SendMailTab::slotCheckConnection() { SmtpClient::ConnectionType connectionType; - const auto selectedConnType = connectionTypeComboBox->currentText(); + const auto selectedConnType = ui->connextionSecurityComboBox->currentText(); if (selectedConnType == "SSL") { connectionType = SmtpClient::ConnectionType::SslConnection; } else if (selectedConnType == "TLS" || selectedConnType == "STARTTLS") { @@ -156,46 +241,96 @@ void SendMailTab::slotCheckConnection() { connectionType = SmtpClient::ConnectionType::TcpConnection; } - SmtpClient smtp(smtpAddress->text(), portSpin->value(), connectionType); - - smtp.setUser(username->text()); - smtp.setPassword(password->text()); + SmtpClient smtp(ui->smtpServerAddressEdit->text(), ui->portSpin->value(), + connectionType); - bool if_success = true; + if (ui->identityCheckBox->isChecked()) { + smtp.setUser(ui->usernameEdit->text()); + smtp.setPassword(ui->passwordEdit->text()); + } if (!smtp.connectToHost()) { QMessageBox::critical(this, _("Fail"), _("Fail to Connect SMTP Server")); - if_success = false; + ui->senTestMailButton->setDisabled(true); + return; } - if (if_success && !smtp.login()) { + if (!smtp.login()) { QMessageBox::critical(this, _("Fail"), _("Fail to Login")); - if_success = false; + ui->senTestMailButton->setDisabled(true); + return; } - if (if_success) - QMessageBox::information(this, _("Success"), - _("Succeed in connecting and login")); + QMessageBox::information(this, _("Success"), + _("Succeed in connecting and login")); + ui->senTestMailButton->setDisabled(false); } #endif -void SendMailTab::slotCheckBoxSetEnableDisable(int state) { - if (state == Qt::Checked) { - smtpAddress->setEnabled(true); - username->setEnabled(true); - password->setEnabled(true); - portSpin->setEnabled(true); - connectionTypeComboBox->setEnabled(true); - defaultSender->setEnabled(true); - checkConnectionButton->setEnabled(true); +#ifdef SMTP_SUPPORT +void SendMailTab::slotSendTestMail() { + if (ui->defaultSenderEmailEdit->text().isEmpty()) { + QMessageBox::critical(this, _("Fail"), _("Given a default sender first")); + return; + } + + SmtpClient::ConnectionType connectionType; + const auto selectedConnType = ui->connextionSecurityComboBox->currentText(); + if (selectedConnType == "SSL") { + connectionType = SmtpClient::ConnectionType::SslConnection; + } else if (selectedConnType == "TLS" || selectedConnType == "STARTTLS") { + connectionType = SmtpClient::ConnectionType::TlsConnection; } else { - smtpAddress->setDisabled(true); - username->setDisabled(true); - password->setDisabled(true); - portSpin->setDisabled(true); - connectionTypeComboBox->setDisabled(true); - defaultSender->setDisabled(true); - checkConnectionButton->setDisabled(true); + connectionType = SmtpClient::ConnectionType::TcpConnection; + } + + SmtpClient smtp(ui->smtpServerAddressEdit->text(), ui->portSpin->value(), + connectionType); + + if (ui->identityCheckBox->isChecked()) { + smtp.setUser(ui->usernameEdit->text()); + smtp.setPassword(ui->passwordEdit->text()); } + + MimeMessage message; + + auto sender_address = ui->defaultSenderEmailEdit->text(); + message.setSender(new EmailAddress(sender_address)); + message.addRecipient(new EmailAddress(sender_address)); + message.setSubject(_("Test Email from GpgFrontend")); + + MimeText text; + text.setText(_("Hello, this is a test email from GpgFrontend.")); + // Now add it to the mail + message.addPart(&text); + // Now we can send the mail + if (!smtp.connectToHost()) { + LOG(INFO) << "Connect to SMTP Server Failed"; + QMessageBox::critical(this, _("Fail"), _("Fail to Connect SMTP Server")); + ui->senTestMailButton->setDisabled(true); + return; + } + if (!smtp.login()) { + LOG(INFO) << "Login to SMTP Server Failed"; + QMessageBox::critical(this, _("Fail"), _("Fail to Login into SMTP Server")); + ui->senTestMailButton->setDisabled(true); + return; + } + if (!smtp.sendMail(message)) { + LOG(INFO) << "Send Mail to SMTP Server Failed"; + QMessageBox::critical( + this, _("Fail"), + _("Fail to send a test email to the SMTP Server, please " + "recheck your configuration.")); + ui->senTestMailButton->setDisabled(true); + return; + } + smtp.quit(); + + // Close after sending email + QMessageBox::information( + this, _("Success"), + _("Succeed in sending a test email to the SMTP Server")); } +#endif } // namespace GpgFrontend::UI diff --git a/src/ui/settings/SettingsSendMail.h b/src/ui/settings/SettingsSendMail.h index ec8f83c9..cc733f28 100644 --- a/src/ui/settings/SettingsSendMail.h +++ b/src/ui/settings/SettingsSendMail.h @@ -7,6 +7,8 @@ #include "ui/GpgFrontendUI.h" +class Ui_SendMailSettings; + namespace GpgFrontend::UI { class SendMailTab : public QWidget { Q_OBJECT @@ -20,22 +22,16 @@ class SendMailTab : public QWidget { private slots: - void slotCheckBoxSetEnableDisable(int state); - - private: - QString appPath; - QSettings settings; +#ifdef SMTP_SUPPORT + void slotCheckConnection(); - QCheckBox* enableCheckBox; + void slotSendTestMail(); +#endif - QLineEdit* smtpAddress; - QLineEdit* username; - QLineEdit* password; - QSpinBox* portSpin; - QComboBox* connectionTypeComboBox; - QLineEdit* defaultSender; - - QPushButton* checkConnectionButton; + private: + std::shared_ptr<Ui_SendMailSettings> ui; + QRegularExpression re_email{ + R"((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))"}; signals: diff --git a/src/ui/smtp/SendMailDialog.cpp b/src/ui/smtp/SendMailDialog.cpp index 7c8933a0..374b3875 100644 --- a/src/ui/smtp/SendMailDialog.cpp +++ b/src/ui/smtp/SendMailDialog.cpp @@ -26,18 +26,20 @@ #include <utility> -#ifdef STMP_ENABLED +#include "ui_SendMailDialog.h" + +#ifdef SMTP_SUPPORT #include "smtp/SmtpMime" +#include "ui/settings/GlobalSettingStation.h" #endif namespace GpgFrontend::UI { -SendMailDialog::SendMailDialog(QString text, QWidget* parent) - : QDialog(parent), - appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat), - mText(std::move(text)) { +SendMailDialog::SendMailDialog(const QString& text, QWidget* parent) + : QDialog(parent), ui(std::make_shared<Ui_SendMailDialog>()) { + // read from settings + initSettings(); + if (smtpAddress.isEmpty()) { QMessageBox::critical( this, _("Incomplete configuration"), @@ -48,79 +50,108 @@ SendMailDialog::SendMailDialog(QString text, QWidget* parent) return; } - senderEdit = new QLineEdit(); - senderEdit->setText(defaultSender); - recipientEdit = new QTextEdit(); - recipientEdit->setPlaceholderText( - "One or more email addresses. Please use ; to separate."); - subjectEdit = new QLineEdit(); - - errorLabel = new QLabel(); - - qDebug() << "Send Mail Settings" << smtpAddress << username << password - << defaultSender << connectionTypeSettings; - - confirmButton = new QPushButton("Confirm"); - - auto layout = new QGridLayout(); - layout->addWidget(new QLabel("Sender"), 0, 0); - layout->addWidget(senderEdit, 0, 1); - layout->addWidget(new QLabel("Recipient"), 1, 0); - layout->addWidget(recipientEdit, 1, 1); - layout->addWidget(new QLabel("Subject"), 2, 0); - layout->addWidget(subjectEdit, 2, 1); - layout->addWidget(confirmButton, 3, 1); - layout->addWidget(errorLabel, 4, 0, 1, 2); - -#ifdef STMP_ENABLED - connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(slotConfirm())); + ui->setupUi(this); + + ui->ccInputWidget->setHidden(true); + ui->bccInputWidget->setHidden(true); + ui->textEdit->setText(text); + ui->errorLabel->setHidden(true); + + ui->senderEdit->setText(defaultSender); + connect(ui->ccButton, &QPushButton::clicked, [=]() { + ui->ccInputWidget->setHidden(!ui->ccInputWidget->isHidden()); + ui->ccEdit->clear(); + }); + connect(ui->bccButton, &QPushButton::clicked, [=]() { + ui->bccInputWidget->setHidden(!ui->bccInputWidget->isHidden()); + ui->bccEdit->clear(); + }); + +#ifdef SMTP_SUPPORT + connect(ui->sendMailButton, &QPushButton::clicked, this, + &SendMailDialog::slotConfirm); #endif - this->setLayout(layout); - this->setWindowTitle("Send Mail"); - this->setModal(true); - this->setFixedWidth(320); - this->show(); + ui->ccButton->setText(_("CC")); + ui->bccButton->setText(_("BCC")); + ui->senderLabel->setText(_("Sender")); + ui->recipientLabel->setText(_("Recipient")); + ui->subjectLabel->setText(_("Mail Subject")); + ui->bccLabel->setText(_("BCC")); + ui->ccLabel->setText(_("CC")); + ui->tipsLabel->setText( + _("Tips: You can fill in multiple email addresses, please separate them " + "with \";\".")); + ui->sendMailButton->setText(_("Send Mail")); + + this->setWindowTitle(_("Send Mail")); + this->setAttribute(Qt::WA_DeleteOnClose); } bool SendMailDialog::check_email_address(const QString& str) { return re_email.match(str).hasMatch(); } -#ifdef STMP_ENABLED +#ifdef SMTP_SUPPORT void SendMailDialog::slotConfirm() { QString errString; - errorLabel->clear(); - - QStringList rcptStringList = recipientEdit->toPlainText().split(';'); + ui->errorLabel->clear(); + ui->errorLabel->setHidden(true); + QStringList rcpt_string_list = ui->recipientEdit->text().split(';'); + QStringList cc_string_list = ui->ccEdit->text().split(';'); + QStringList bcc_string_list = ui->bccEdit->text().split(';'); - if (rcptStringList.isEmpty()) { + if (rcpt_string_list.isEmpty()) { errString.append(QString(" ") + _("Recipient cannot be empty") + " \n"); } else { - for (const auto& reci : rcptStringList) { - qDebug() << "Receiver" << reci.trimmed(); + for (const auto& reci : rcpt_string_list) { + LOG(INFO) << "Receiver" << reci.trimmed().toStdString(); if (!check_email_address(reci.trimmed())) { errString.append(QString(" ") + - _("One or more Recipient's Email Address is invalid") + + _("One or more recipient's email is invalid") + " \n"); break; } } } - if (senderEdit->text().isEmpty()) { + if (ui->senderEdit->text().isEmpty()) { errString.append(QString(" ") + _("Sender cannot be empty") + " \n"); - } else if (!check_email_address(senderEdit->text())) { - errString.append(QString(" ") + _("Sender's Email Address is invalid") + - " \n"); + } else if (!check_email_address(ui->senderEdit->text())) { + errString.append(QString(" ") + _("Sender's email is invalid") + " \n"); } + if (ui->subjectEdit->text().isEmpty()) { + errString.append(QString(" ") + _("Subject cannot be empty") + " \n"); + } + + if (!ui->ccEdit->text().isEmpty()) + for (const auto& cc : cc_string_list) { + LOG(INFO) << "cc" << cc.trimmed().toStdString(); + if (!check_email_address(cc.trimmed())) { + errString.append(QString(" ") + _("One or more cc email is invalid") + + " \n"); + break; + } + } + + if (!ui->bccEdit->text().isEmpty()) + for (const auto& bcc : bcc_string_list) { + LOG(INFO) << "bcc" << bcc.trimmed().toStdString(); + if (!check_email_address(bcc.trimmed())) { + errString.append(QString(" ") + _("One or more bcc email is invalid") + + " \n"); + break; + } + } + if (!errString.isEmpty()) { - errorLabel->setAutoFillBackground(true); - QPalette error = errorLabel->palette(); + ui->errorLabel->setAutoFillBackground(true); + QPalette error = ui->errorLabel->palette(); error.setColor(QPalette::Window, "#ff8080"); - errorLabel->setPalette(error); - errorLabel->setText(errString); + ui->errorLabel->setPalette(error); + ui->errorLabel->setText(errString); + ui->errorLabel->setHidden(false); return; } @@ -149,18 +180,23 @@ void SendMailDialog::slotConfirm() { MimeMessage message; - message.setSender(new EmailAddress(senderEdit->text())); - for (const auto& reci : rcptStringList) { + message.setSender(new EmailAddress(ui->senderEdit->text())); + for (const auto& reci : rcpt_string_list) { if (!reci.isEmpty()) message.addRecipient(new EmailAddress(reci.trimmed())); } - message.setSubject(subjectEdit->text()); + for (const auto& cc : cc_string_list) { + if (!cc.isEmpty()) message.addCc(new EmailAddress(cc.trimmed())); + } + for (const auto& bcc : cc_string_list) { + if (!bcc.isEmpty()) message.addBcc(new EmailAddress(bcc.trimmed())); + } + message.setSubject(ui->subjectEdit->text()); // Now add some text to the email. // First we create a MimeText object. MimeText text; - - text.setText(mText); + text.setText(ui->textEdit->toPlainText()); // Now add it to the mail message.addPart(&text); @@ -190,6 +226,58 @@ void SendMailDialog::slotConfirm() { deleteLater(); } +void SendMailDialog::initSettings() { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + try { + ability_enable = settings.lookup("smtp.enable"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); + } + + try { + identity_enable = settings.lookup("smtp.identity_enable"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("identity_enable"); + } + + try { + smtpAddress = settings.lookup("smtp.mail_address").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("mail_address"); + } + + try { + username = settings.lookup("smtp.username").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("username"); + } + + try { + password = settings.lookup("smtp.password").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("password"); + } + + try { + port = settings.lookup("smtp.port"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("port"); + } + + try { + connectionTypeSettings = settings.lookup("smtp.connection_type").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("connection_type"); + } + + try { + defaultSender = settings.lookup("smtp.default_sender").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("default_sender"); + } +} + #endif } // namespace GpgFrontend::UI diff --git a/src/ui/smtp/SendMailDialog.h b/src/ui/smtp/SendMailDialog.h index 87dfd81f..979d4f88 100644 --- a/src/ui/smtp/SendMailDialog.h +++ b/src/ui/smtp/SendMailDialog.h @@ -27,38 +27,32 @@ #include "ui/GpgFrontendUI.h" +class Ui_SendMailDialog; + namespace GpgFrontend::UI { class SendMailDialog : public QDialog { Q_OBJECT public: - explicit SendMailDialog(QString text, QWidget* parent = nullptr); + explicit SendMailDialog(const QString& text, QWidget* parent = nullptr); private slots: void slotConfirm(); private: - QString appPath; - QSettings settings; - - QLineEdit* senderEdit; - QTextEdit* recipientEdit; - QLineEdit* subjectEdit; - QPushButton* confirmButton; + void initSettings(); - QLabel* errorLabel; - QString mText; + std::shared_ptr<Ui_SendMailDialog> ui; - QString smtpAddress = - settings.value("sendMail/smtpAddress", QString()).toString(); - QString username = settings.value("sendMail/username", QString()).toString(); - QString password = settings.value("sendMail/password", QString()).toString(); - QString defaultSender = - settings.value("sendMail/defaultSender", QString()).toString(); - QString connectionTypeSettings = - settings.value("sendMail/connectionType", QString()).toString(); - int port = settings.value("sendMail/port", QString()).toInt(); + bool ability_enable = false; + bool identity_enable = false; + QString smtpAddress; + QString username; + QString password; + QString defaultSender; + QString connectionTypeSettings = "None"; + int port = 25; QRegularExpression re_email{ R"((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))"}; diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index 1b7dbda0..d46d5f92 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -37,7 +37,7 @@ InfoBoardWidget::InfoBoardWidget(QWidget* parent) ui->actionButtonLayout->addStretch(); ui->actionLabel->setText(_("InfoBoard's Actions Menu")); ui->copyButton->setText(_("Copy")); - ui->saveButton->setText(_("Save")); + ui->saveButton->setText(_("Save File")); ui->clearButton->setText(_("Clear")); connect(ui->copyButton, &QPushButton::clicked, this, @@ -102,18 +102,12 @@ void InfoBoardWidget::associateTextEdit(QTextEdit* edit) { } void InfoBoardWidget::associateTabWidget(QTabWidget* tab) { - if (mTextPage != nullptr) - disconnect(mTextPage, SIGNAL(textChanged()), this, SLOT(slotReset())); - if (mTabWidget != nullptr) { - disconnect(mTabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); - connect(mTabWidget, SIGNAL(tabCloseRequested(int)), this, - SLOT(slotReset())); - } - mTextPage = nullptr; mTabWidget = tab; connect(tab, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); connect(tab, SIGNAL(tabCloseRequested(int)), this, SLOT(slotReset())); + // reset + this->slotReset(); } void InfoBoardWidget::addOptionalAction(const QString& name, @@ -169,6 +163,8 @@ void InfoBoardWidget::slotSave() { auto file_path = QFileDialog::getSaveFileName( this, _("Save Information Board's Content"), {}, tr("Text (*.txt)")); LOG(INFO) << "file path" << file_path.toStdString(); + if(file_path.isEmpty()) return; + QFile file(file_path); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { file.write(ui->infoBoard->toPlainText().toUtf8()); diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 02a12e80..0e4c5bba 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -24,54 +24,65 @@ #include "ui/widgets/KeyList.h" +#include <boost/format.hpp> #include <utility> #include "gpg/function/GpgKeyGetter.h" #include "ui/SignalStation.h" +#include "ui/UserInterfaceUtils.h" +#include "ui/settings/GlobalSettingStation.h" +#include "ui_KeyList.h" namespace GpgFrontend::UI { -KeyList::KeyList(QWidget* parent) : QWidget(parent) { init(); } +int KeyList::key_list_id = 2048; -KeyList::KeyList(KeyListRow::KeyType selectType, - KeyListColumn::InfoType infoType, - const std::function<bool(const GpgKey&)>& filter, - QWidget* parent) - : QWidget(parent) { +KeyList::KeyList(bool menu, QWidget* parent) + : QWidget(parent), + _m_key_list_id(key_list_id++), + ui(std::make_shared<Ui_KeyList>()), + menu_status(menu) { init(); - addListGroupTab(_("Default"), selectType, infoType, filter); } void KeyList::init() { - mGroupTab = new QTabWidget(); - mGroupTab->setMovable(true); - mGroupTab->setTabsClosable(false); - mGroupTab->setDocumentMode(true); - - auto* layout = new QVBoxLayout; - layout->addWidget(mGroupTab); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(3); - setLayout(layout); + ui->setupUi(this); + ui->menuWidget->setHidden(!menu_status); + ui->keyGroupTab->clear(); popupMenu = new QMenu(this); // register key database refresh signal + connect(this, &KeyList::signalRefreshDatabase, SignalStation::GetInstance(), + &SignalStation::KeyDatabaseRefresh); connect(SignalStation::GetInstance(), SIGNAL(KeyDatabaseRefresh()), this, SLOT(slotRefresh())); + connect(ui->refreshKeyListButton, &QPushButton::clicked, this, + &KeyList::slotRefresh); + connect(ui->syncButton, &QPushButton::clicked, this, + &KeyList::slotSyncWithKeyServer); + connect(this, &KeyList::signalRefreshStatusBar, SignalStation::GetInstance(), + &SignalStation::signalRefreshStatusBar); setAcceptDrops(true); + + ui->keyListOperationsLabel->setText(_("Key List Menu: ")); + ui->refreshKeyListButton->setText(_("Refresh")); + ui->syncButton->setText(_("Sync Public Key")); + ui->syncButton->setToolTip(_("Sync public key with your default keyserver")); } void KeyList::addListGroupTab( const QString& name, KeyListRow::KeyType selectType, KeyListColumn::InfoType infoType, const std::function<bool(const GpgKey&)>& filter) { + LOG(INFO) << _("Called") << name.toStdString(); + auto key_list = new QTableWidget(this); if (mKeyList == nullptr) { mKeyList = key_list; } - mGroupTab->addTab(key_list, name); + ui->keyGroupTab->addTab(key_list, name); mKeyTables.emplace_back(key_list, selectType, infoType, filter); key_list->setColumnCount(7); @@ -120,17 +131,22 @@ void KeyList::addListGroupTab( connect(key_list, &QTableWidget::doubleClicked, this, &KeyList::slotDoubleClicked); - - // refresh - mKeyTables.back().Refresh(); } void KeyList::slotRefresh() { - LOG(INFO) << _("called"); - - for (auto& key_table : mKeyTables) { - key_table.Refresh(); - } + LOG(INFO) << _("Called") << "_m_key_list_id" << _m_key_list_id; + emit signalRefreshStatusBar(_("Refreshing Key List..."), 3000); + auto thread = QThread::create([this, _id = _m_key_list_id]() { + std::lock_guard<std::mutex> guard(buffered_key_list_mutex); + _buffered_keys_list = nullptr; + // buffered keys list + _buffered_keys_list = GpgKeyGetter::GetInstance(_id).FetchKey(); + }); + connect(thread, &QThread::finished, this, &KeyList::slotRefreshUI); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + ui->refreshKeyListButton->setDisabled(true); + ui->syncButton->setDisabled(true); + thread->start(); } KeyIdArgsListPtr KeyList::getChecked(const KeyTable& key_table) { @@ -144,9 +160,9 @@ KeyIdArgsListPtr KeyList::getChecked(const KeyTable& key_table) { } KeyIdArgsListPtr KeyList::getChecked() { - auto key_list = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; auto ret = std::make_unique<KeyIdArgsList>(); for (int i = 0; i < key_list->rowCount(); i++) { if (key_list->item(i, 0)->checkState() == Qt::Checked) { @@ -157,9 +173,9 @@ KeyIdArgsListPtr KeyList::getChecked() { } KeyIdArgsListPtr KeyList::getAllPrivateKeys() { - auto key_list = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; auto ret = std::make_unique<KeyIdArgsList>(); for (int i = 0; i < key_list->rowCount(); i++) { if (key_list->item(i, 1) && buffered_keys[i].is_private_key()) { @@ -171,11 +187,11 @@ KeyIdArgsListPtr KeyList::getAllPrivateKeys() { KeyIdArgsListPtr KeyList::getPrivateChecked() { auto ret = std::make_unique<KeyIdArgsList>(); - if (mGroupTab->size().isEmpty()) return ret; + if (ui->keyGroupTab->size().isEmpty()) return ret; - auto key_list = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; for (int i = 0; i < key_list->rowCount(); i++) { if ((key_list->item(i, 0)->checkState() == Qt::Checked) && @@ -199,10 +215,10 @@ void KeyList::setChecked(const KeyIdArgsListPtr& keyIds, } void KeyList::setChecked(const KeyIdArgsListPtr& keyIds) { - if (mGroupTab->size().isEmpty()) return; - auto key_list = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + if (ui->keyGroupTab->size().isEmpty()) return; + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; if (!keyIds->empty()) { for (int i = 0; i < key_list->rowCount(); i++) { @@ -216,11 +232,11 @@ void KeyList::setChecked(const KeyIdArgsListPtr& keyIds) { KeyIdArgsListPtr KeyList::getSelected() { auto ret = std::make_unique<KeyIdArgsList>(); - if (mGroupTab->size().isEmpty()) return ret; + if (ui->keyGroupTab->size().isEmpty()) return ret; - auto key_list = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; for (int i = 0; i < key_list->rowCount(); i++) { if (key_list->item(i, 0)->isSelected() == 1) { @@ -231,8 +247,8 @@ KeyIdArgsListPtr KeyList::getSelected() { } [[maybe_unused]] bool KeyList::containsPrivateKeys() { - if (mGroupTab->size().isEmpty()) return false; - mKeyList = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + if (ui->keyGroupTab->size().isEmpty()) return false; + mKeyList = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); for (int i = 0; i < mKeyList->rowCount(); i++) { if (mKeyList->item(i, 1)) { @@ -243,15 +259,15 @@ KeyIdArgsListPtr KeyList::getSelected() { } void KeyList::setColumnWidth(int row, int size) { - if (mGroupTab->size().isEmpty()) return; - mKeyList = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + if (ui->keyGroupTab->size().isEmpty()) return; + mKeyList = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); mKeyList->setColumnWidth(row, size); } void KeyList::contextMenuEvent(QContextMenuEvent* event) { - if (mGroupTab->size().isEmpty()) return; - mKeyList = qobject_cast<QTableWidget*>(mGroupTab->currentWidget()); + if (ui->keyGroupTab->size().isEmpty()) return; + mKeyList = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); if (mKeyList->selectedItems().length() > 0) { popupMenu->exec(event->globalPos()); @@ -275,8 +291,16 @@ void KeyList::dropEvent(QDropEvent* event) { // "always import keys"-CheckBox auto* checkBox = new QCheckBox(_("Always import without bothering.")); - if (settings.value("general/confirmImportKeys").toBool()) - checkBox->setCheckState(Qt::Unchecked); + + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + bool confirm_import_keys = true; + try { + confirm_import_keys = settings.lookup("general.confirm_import_keys"); + LOG(INFO) << "confirm_import_keys" << confirm_import_keys; + if (confirm_import_keys) checkBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("confirm_import_keys"); + } // Buttons for ok and cancel auto* buttonBox = @@ -291,16 +315,21 @@ void KeyList::dropEvent(QDropEvent* event) { dialog->setLayout(vbox); - if (settings.value("general/confirmImportKeys", Qt::Checked).toBool()) { + if (confirm_import_keys) { dialog->exec(); - if (dialog->result() == QDialog::Rejected) { - return; - } - if (checkBox->isChecked()) { - settings.setValue("general/confirmImportKeys", false); - } else { - settings.setValue("general/confirmImportKeys", true); + if (dialog->result() == QDialog::Rejected) return; + + if (!settings.exists("general") || + settings.lookup("general").getType() != libconfig::Setting::TypeGroup) + settings.add("general", libconfig::Setting::TypeGroup); + auto& general = settings["general"]; + if (!general.exists("confirm_import_keys")) + general.add("confirm_import_keys", libconfig::Setting::TypeBoolean) = + checkBox->isChecked(); + else { + general["confirm_import_keys"] = checkBox->isChecked(); } + GlobalSettingStation::GetInstance().Sync(); } if (event->mimeData()->hasUrls()) { @@ -335,17 +364,18 @@ void KeyList::dragEnterEvent(QDragEnterEvent* event) { void KeyList::importKeys(const QByteArray& inBuffer) { auto std_buffer = std::make_unique<ByteArray>(inBuffer.toStdString()); GpgImportInformation result = - GpgKeyImportExportor::GetInstance().ImportKey(std::move(std_buffer)); + GpgKeyImportExportor::GetInstance(_m_key_list_id) + .ImportKey(std::move(std_buffer)); new KeyImportDetailDialog(result, false, this); } void KeyList::slotDoubleClicked(const QModelIndex& index) { - if (mGroupTab->size().isEmpty()) return; + if (ui->keyGroupTab->size().isEmpty()) return; const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; if (mAction != nullptr) { - const auto key = - GpgKeyGetter::GetInstance().GetKey(buffered_keys[index.row()].id()); + const auto key = GpgKeyGetter::GetInstance(_m_key_list_id) + .GetKey(buffered_keys[index.row()].id()); mAction(key, this); } } @@ -356,9 +386,9 @@ void KeyList::setDoubleClickedAction( } std::string KeyList::getSelectedKey() { - if (mGroupTab->size().isEmpty()) return {}; + if (ui->keyGroupTab->size().isEmpty()) return {}; const auto& buffered_keys = - mKeyTables[mGroupTab->currentIndex()].buffered_keys; + mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; for (int i = 0; i < mKeyList->rowCount(); i++) { if (mKeyList->item(i, 0)->isSelected() == 1) { @@ -368,6 +398,55 @@ std::string KeyList::getSelectedKey() { return {}; } +void KeyList::slotRefreshUI() { + LOG(INFO) << _("Called") << _buffered_keys_list.get(); + if (_buffered_keys_list != nullptr) { + std::lock_guard<std::mutex> guard(buffered_key_list_mutex); + for (auto& key_table : mKeyTables) { + key_table.Refresh(GpgKeyGetter::GetKeysCopy(_buffered_keys_list)); + } + } + emit signalRefreshStatusBar(_("Key List Refreshed."), 1000); + ui->refreshKeyListButton->setDisabled(false); + ui->syncButton->setDisabled(false); +} + +void KeyList::slotSyncWithKeyServer() { + KeyIdArgsList key_ids; + { + std::lock_guard<std::mutex> guard(buffered_key_list_mutex); + for (const auto& key : *_buffered_keys_list) { + if (!(key.is_private_key() && key.has_master_key())) + key_ids.push_back(key.id()); + } + } + + ui->refreshKeyListButton->setDisabled(true); + ui->syncButton->setDisabled(true); + + emit signalRefreshStatusBar(_("Syncing Key List..."), 3000); + CommonUtils::slotImportKeyFromKeyServer( + _m_key_list_id, key_ids, + [=](const std::string& key_id, const std::string& status, + size_t current_index, size_t all_index) { + LOG(INFO) << _("Called") << key_id << status << current_index + << all_index; + auto key = GpgKeyGetter::GetInstance(_m_key_list_id).GetKey(key_id); + + boost::format status_str = boost::format(_("Sync [%1%/%2%] %3% %4%")) % + current_index % all_index % + key.uids()->front().uid() % status; + emit signalRefreshStatusBar(status_str.str().c_str(), 1500); + + if (current_index == all_index) { + ui->syncButton->setDisabled(false); + ui->refreshKeyListButton->setDisabled(false); + emit signalRefreshStatusBar(_("Key List Sync Done."), 3000); + emit signalRefreshDatabase(); + } + }); +} + KeyIdArgsListPtr KeyTable::GetChecked() { auto ret = std::make_unique<KeyIdArgsList>(); for (int i = 0; i < key_list->rowCount(); i++) { @@ -389,14 +468,22 @@ void KeyTable::SetChecked(const KeyIdArgsListPtr& key_ids) { } } -void KeyTable::Refresh() { +void KeyTable::Refresh(KeyLinkListPtr m_keys) { + LOG(INFO) << "Called"; + auto checked_key_list = GetChecked(); // while filling the table, sort enabled causes errors key_list->setSortingEnabled(false); key_list->clearContents(); - auto keys = GpgKeyGetter::GetInstance().FetchKey(); + // Optimization for copy + KeyLinkListPtr keys = nullptr; + if (m_keys == nullptr) + keys = GpgKeyGetter::GetInstance().FetchKey(); + else + keys = std::move(m_keys); + auto it = keys->begin(); int row_count = 0; @@ -425,7 +512,7 @@ void KeyTable::Refresh() { table_buffered_keys.clear(); while (it != keys->end()) { - table_buffered_keys.push_back(GpgKeyGetter::GetInstance().GetKey(it->id())); + table_buffered_keys.push_back(it->copy()); auto* tmp0 = new QTableWidgetItem(QString::number(row_index)); tmp0->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | @@ -491,5 +578,7 @@ void KeyTable::Refresh() { } SetChecked(checked_key_list); + + LOG(INFO) << "End"; } } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyList.h b/src/ui/widgets/KeyList.h index f09ae2ac..617bb274 100644 --- a/src/ui/widgets/KeyList.h +++ b/src/ui/widgets/KeyList.h @@ -29,6 +29,9 @@ #include "gpg/GpgContext.h" #include "ui/KeyImportDetailDialog.h" + +class Ui_KeyList; + namespace GpgFrontend::UI { struct KeyListRow { @@ -68,7 +71,7 @@ struct KeyTable { info_type(_info_type), filter(std::move(_filter)) {} - void Refresh(); + void Refresh(KeyLinkListPtr m_keys = nullptr); KeyIdArgsListPtr GetChecked(); @@ -79,14 +82,7 @@ class KeyList : public QWidget { Q_OBJECT public: - explicit KeyList( - KeyListRow::KeyType selectType = KeyListRow::SECRET_OR_PUBLIC_KEY, - KeyListColumn::InfoType infoType = KeyListColumn::ALL, - const std::function<bool(const GpgKey&)>& filter = - [](const GpgKey&) -> bool { return true; }, - QWidget* parent = nullptr); - - explicit KeyList(QWidget* parent); + explicit KeyList(bool menu, QWidget* parent = nullptr); void addListGroupTab( const QString& name, @@ -116,6 +112,10 @@ class KeyList : public QWidget { [[maybe_unused]] bool containsPrivateKeys(); + signals: + void signalRefreshStatusBar(const QString& message, int timeout); + void signalRefreshDatabase(); + public slots: void slotRefresh(); @@ -124,22 +124,26 @@ class KeyList : public QWidget { void init(); void importKeys(const QByteArray& inBuffer); - QString appPath; - QSettings settings; + static int key_list_id; + int _m_key_list_id; + std::mutex buffered_key_list_mutex; - QTabWidget* mGroupTab{}; + std::shared_ptr<Ui_KeyList> ui; QTableWidget* mKeyList{}; - std::vector<KeyTable> mKeyTables; - QMenu* popupMenu{}; - + GpgFrontend::KeyLinkListPtr _buffered_keys_list; std::function<void(const GpgKey&, QWidget*)> mAction = nullptr; + bool menu_status = false; private slots: void slotDoubleClicked(const QModelIndex& index); + void slotRefreshUI(); + + void slotSyncWithKeyServer(); + protected: void contextMenuEvent(QContextMenuEvent* event) override; diff --git a/src/ui/widgets/SignersPicker.cpp b/src/ui/widgets/SignersPicker.cpp index 997ee27a..e769d05c 100644 --- a/src/ui/widgets/SignersPicker.cpp +++ b/src/ui/widgets/SignersPicker.cpp @@ -31,8 +31,9 @@ SignersPicker::SignersPicker(QWidget* parent) : QDialog(parent) { connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(accept())); /*Setup KeyList*/ - mKeyList = new KeyList( - KeyListRow::ONLY_SECRET_KEY, + mKeyList = new KeyList(false, this); + mKeyList->addListGroupTab( + _("Signers"), KeyListRow::ONLY_SECRET_KEY, KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage, [](const GpgKey& key) -> bool { if (!key.CanSignActual()) @@ -40,14 +41,13 @@ SignersPicker::SignersPicker(QWidget* parent) : QDialog(parent) { else return true; }); - mKeyList->slotRefresh(); auto* vbox2 = new QVBoxLayout(); vbox2->addWidget(new QLabel(QString(_("Select Signer(s)")) + ": ")); vbox2->addWidget(mKeyList); vbox2->addWidget(new QLabel( - _("Selecting Nothing will eventually use default key to sign."))); + _("If any key is selected, the default key will be used for signing."))); vbox2->addWidget(confirmButton); vbox2->addStretch(0); setLayout(vbox2); diff --git a/ui/KeyList.ui b/ui/KeyList.ui new file mode 100644 index 00000000..99932453 --- /dev/null +++ b/ui/KeyList.ui @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KeyList</class> + <widget class="QWidget" name="KeyList"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1016</width> + <height>856</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="menuWidget" native="true"> + <layout class="QHBoxLayout" name="menu"> + <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="keyListOperationsLabel"> + <property name="text"> + <string>Key List Menu: </string> + </property> + </widget> + </item> + <item alignment="Qt::AlignLeft"> + <widget class="QPushButton" name="refreshKeyListButton"> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignLeft"> + <widget class="QPushButton" name="syncButton"> + <property name="text"> + <string>Sync With Key Server</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> + </layout> + </widget> + </item> + <item> + <widget class="QTabWidget" name="keyGroupTab"> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <property name="movable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Tab 1</string> + </attribute> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Tab 2</string> + </attribute> + </widget> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/KeyServerSettings.ui b/ui/KeyServerSettings.ui new file mode 100644 index 00000000..4a0490b9 --- /dev/null +++ b/ui/KeyServerSettings.ui @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KeyServerSettings</class> + <widget class="QWidget" name="KeyServerSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>548</width> + <height>555</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="keyServerListGroupBox"> + <property name="title"> + <string>Keyserver List</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <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> + <property name="spacing"> + <number>0</number> + </property> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="addKeyServerLabel"> + <property name="text"> + <string>URL: </string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="addKeyServerEdit"/> + </item> + <item> + <widget class="QPushButton" name="addKeyServerPushButton"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QTableWidget" name="keyServerListTable"> + <property name="selectionMode"> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderCascadingSectionResizes"> + <bool>true</bool> + </attribute> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string>default</string> + </property> + </column> + <column> + <property name="text"> + <string>Keyserver Address</string> + </property> + </column> + <column> + <property name="text"> + <string>Security</string> + </property> + </column> + <column> + <property name="text"> + <string>Avaliable</string> + </property> + </column> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="tipsLabel"> + <property name="text"> + <string>Tips: Please Double-click table item to edit it.</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="operationsGroupBox"> + <property name="title"> + <string>Operations</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QPushButton" name="testKeyServerButton"> + <property name="text"> + <string>Test Listed Key Server</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + <action name="actionDelete_Selected_Key_Server"> + <property name="text"> + <string>Delete Selected Key Server</string> + </property> + </action> + <action name="actionSet_As_Default"> + <property name="text"> + <string>Set As Default</string> + </property> + </action> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/ModifiedExpirationDateTime.ui b/ui/ModifiedExpirationDateTime.ui new file mode 100644 index 00000000..c6341745 --- /dev/null +++ b/ui/ModifiedExpirationDateTime.ui @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ModifiedExpirationDateTime</class> + <widget class="QDialog" name="ModifiedExpirationDateTime"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Modified Expiration Date</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item alignment="Qt::AlignTop"> + <widget class="QLabel" name="titleLabel"> + <property name="text"> + <string>Modified Expiration Date (Local Time)</string> + </property> + </widget> + </item> + <item> + <widget class="QDateEdit" name="dateEdit"> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTimeEdit" name="timeEdit"/> + </item> + <item> + <widget class="QCheckBox" name="noExpirationCheckBox"> + <property name="text"> + <string>No Expiration</string> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Tips: For the sake of security, the key is valid for up to two years. If you are an expert user, please unlock it for a longer time in the settings.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ModifiedExpirationDateTime</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ModifiedExpirationDateTime</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/SendMailDialog.ui b/ui/SendMailDialog.ui new file mode 100644 index 00000000..8eb004f7 --- /dev/null +++ b/ui/SendMailDialog.ui @@ -0,0 +1,286 @@ +<?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>958</width> + <height>607</height> + </rect> + </property> + <property name="cursor"> + <cursorShape>ArrowCursor</cursorShape> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <property name="windowTitle"> + <string>Send Mail</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> + </widget> + </item> + <item> + <widget class="QPushButton" name="bccButton"> + <property name="text"> + <string>BCC</string> + </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>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="recipientLabel"> + <property name="text"> + <string>Recipient</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="recipientEdit"/> + </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>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="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>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="ccLabel"> + <property name="text"> + <string>CC</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="ccEdit"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="bccInputWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <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="bccLabel"> + <property name="text"> + <string>BCC</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="bccEdit"/> + </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> + <widget class="QLabel" name="errorLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item alignment="Qt::AlignRight"> + <widget class="QPushButton" name="sendMailButton"> + <property name="text"> + <string>Send</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/SendMailSettings.ui b/ui/SendMailSettings.ui new file mode 100644 index 00000000..96703f7f --- /dev/null +++ b/ui/SendMailSettings.ui @@ -0,0 +1,390 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SendMailSettings</class> + <widget class="QWidget" name="SendMailSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>553</width> + <height>708</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="generalGroupBox"> + <property name="title"> + <string>General</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="enableCheckBox"> + <property name="text"> + <string>Enable Send Mail Ability</string> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <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="smtpServerAddressLabel"> + <property name="text"> + <string>SMTP Server Address</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="smtpServerAddressEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget_2" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <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="smtpServerPortLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>SMTP Server Port</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="portSpin"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>65535</number> + </property> + <property name="value"> + <number>25</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <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="connectionSecurityLabel"> + <property name="text"> + <string>SMTP Connection Security</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="connextionSecurityComboBox"> + <property name="currentText"> + <string>None</string> + </property> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>SSL</string> + </property> + </item> + <item> + <property name="text"> + <string>TLS</string> + </property> + </item> + <item> + <property name="text"> + <string>STARTTLS</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="identityGroupBox"> + <property name="title"> + <string>Identity</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="2" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="identityCheckBox"> + <property name="text"> + <string>Need Auth</string> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget_3" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <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="usernameLabel"> + <property name="text"> + <string>Username</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="usernameEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </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>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="passwordLabel"> + <property name="text"> + <string>Password</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="passwordEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="preferenceGroupBox"> + <property name="title"> + <string>Preference</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <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="senderLabel"> + <property name="text"> + <string>Default Sender Email</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="defaultSenderEmailEdit"/> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="tipsLabel"> + <property name="text"> + <string>Tips: It is recommended that you build your own mail server or use a trusted mail server. If you don't know the detailed configuration information, you can get it from the mail service provider.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="operationsGroupBox"> + <property name="title"> + <string>Operations</string> + </property> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QPushButton" name="checkConnectionButton"> + <property name="text"> + <string>Check Connection</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="senTestMailButton"> + <property name="text"> + <string>Send Test Email</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |