aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSaturn&Eric <[email protected]>2021-12-16 21:20:56 +0000
committerGitHub <[email protected]>2021-12-16 21:20:56 +0000
commit52ac9979bd8c4820a0034d619cb7d1d3e4105d8b (patch)
treec7524432467825603d83a17f398249d431c28b18
parentMerge pull request #32 from saturneric/develop (diff)
parentFixed bugs & Improve Speed. (diff)
downloadGpgFrontend-52ac9979bd8c4820a0034d619cb7d1d3e4105d8b.tar.gz
GpgFrontend-52ac9979bd8c4820a0034d619cb7d1d3e4105d8b.zip
Merge pull request #34 from saturneric/develop
v2.0.3
-rw-r--r--.github/workflows/release-ci.yml215
-rw-r--r--.github/workflows/release-linux-package.yml112
-rw-r--r--.github/workflows/release.yml10
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt24
-rw-r--r--gpgfrontend.qrc1
-rw-r--r--resource/desktop/gpgfrontend-gpgfrontend.desktop8
-rw-r--r--resource/hicolor/128x128/apps/pub.gpgfrontend.gpgfrontend.pngbin0 -> 3819 bytes
-rw-r--r--resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.pngbin0 -> 9155 bytes
-rw-r--r--resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.pngbin0 -> 3533 bytes
-rw-r--r--resource/icons/email.pngbin0 -> 4582 bytes
-rw-r--r--resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml37
-rw-r--r--resource/pixmaps/pub.gpgfrontend.gpgfrontend.pngbin0 -> 9155 bytes
-rw-r--r--src/CMakeLists.txt55
-rw-r--r--src/GpgFrontendBuildInfo.h.in4
-rw-r--r--src/GpgFrontendBuildInstallInfo.h.in37
-rw-r--r--src/gpg/GpgConstants.cpp47
-rw-r--r--src/gpg/GpgConstants.h29
-rw-r--r--src/gpg/function/BasicOperator.cpp23
-rw-r--r--src/gpg/function/GpgKeyGetter.cpp19
-rw-r--r--src/gpg/function/GpgKeyGetter.h4
-rw-r--r--src/gpg/function/GpgKeyOpera.cpp4
-rw-r--r--src/gpg/model/GpgKey.h6
-rw-r--r--src/init.cpp128
-rw-r--r--src/main.cpp181
-rw-r--r--src/signal.cpp38
-rw-r--r--src/ui/CMakeLists.txt3
-rwxr-xr-xsrc/ui/FileEncryptionDialog.cpp60
-rw-r--r--src/ui/KeyImportDetailDialog.cpp8
-rwxr-xr-xsrc/ui/KeyMgmt.cpp12
-rw-r--r--src/ui/MainWindow.cpp14
-rw-r--r--src/ui/MainWindow.h33
-rw-r--r--src/ui/SignalStation.h2
-rw-r--r--src/ui/UserInterfaceUtils.cpp164
-rw-r--r--src/ui/UserInterfaceUtils.h17
-rw-r--r--src/ui/function/FileReadThread.cpp4
-rw-r--r--src/ui/function/VersionCheckThread.cpp11
-rw-r--r--src/ui/keypair_details/KeyPairDetailTab.cpp50
-rw-r--r--src/ui/keypair_details/KeySetExpireDateDialog.cpp86
-rw-r--r--src/ui/keypair_details/KeySetExpireDateDialog.h7
-rw-r--r--src/ui/keypair_details/KeyUIDSignDialog.cpp22
-rw-r--r--src/ui/main_window/MainWindowFileSlotFunction.cpp8
-rw-r--r--src/ui/main_window/MainWindowSlotFunction.cpp41
-rw-r--r--src/ui/main_window/MainWindowUI.cpp29
-rw-r--r--src/ui/settings/GlobalSettingStation.h12
-rw-r--r--src/ui/settings/SettingsDialog.cpp5
-rw-r--r--src/ui/settings/SettingsGeneral.cpp32
-rw-r--r--src/ui/settings/SettingsGeneral.h1
-rw-r--r--src/ui/settings/SettingsKeyServer.cpp252
-rw-r--r--src/ui/settings/SettingsKeyServer.h16
-rw-r--r--src/ui/settings/SettingsSendMail.cpp399
-rw-r--r--src/ui/settings/SettingsSendMail.h24
-rw-r--r--src/ui/smtp/SendMailDialog.cpp206
-rw-r--r--src/ui/smtp/SendMailDialog.h32
-rw-r--r--src/ui/widgets/InfoBoardWidget.cpp14
-rw-r--r--src/ui/widgets/KeyList.cpp219
-rw-r--r--src/ui/widgets/KeyList.h34
-rw-r--r--src/ui/widgets/SignersPicker.cpp8
-rw-r--r--ui/KeyList.ui106
-rw-r--r--ui/KeyServerSettings.ui168
-rw-r--r--ui/ModifiedExpirationDateTime.ui122
-rw-r--r--ui/SendMailDialog.ui286
-rw-r--r--ui/SendMailSettings.ui390
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:
diff --git a/.gitignore b/.gitignore
index 25a5372f..243ab625 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
new file mode 100644
index 00000000..5c24791f
--- /dev/null
+++ b/resource/hicolor/128x128/apps/pub.gpgfrontend.gpgfrontend.png
Binary files differ
diff --git a/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png b/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png
new file mode 100644
index 00000000..b3268b01
--- /dev/null
+++ b/resource/hicolor/256x256/apps/pub.gpgfrontend.gpgfrontend.png
Binary files differ
diff --git a/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png b/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png
new file mode 100644
index 00000000..c90e1d1d
--- /dev/null
+++ b/resource/hicolor/32x32/apps/pub.gpgfrontend.gpgfrontend.png
Binary files differ
diff --git a/resource/icons/email.png b/resource/icons/email.png
new file mode 100644
index 00000000..f0517848
--- /dev/null
+++ b/resource/icons/email.png
Binary files differ
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
new file mode 100644
index 00000000..b3268b01
--- /dev/null
+++ b/resource/pixmaps/pub.gpgfrontend.gpgfrontend.png
Binary files differ
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 &quot;;&quot;.</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>