diff options
author | Saturn&Eric <[email protected]> | 2022-01-05 20:02:19 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2022-01-05 20:02:19 +0000 |
commit | a3ca1bf1d4404327d68d3d49127085638c272152 (patch) | |
tree | deb10c33977e5002fa6e9b434f2b07d2719a4e31 | |
parent | Merge pull request #37 from saturneric/document (diff) | |
parent | <doc>(navbar): take repository and GitHub apart. (diff) | |
download | GpgFrontend-a3ca1bf1d4404327d68d3d49127085638c272152.tar.gz GpgFrontend-a3ca1bf1d4404327d68d3d49127085638c272152.zip |
Merge pull request #36 from saturneric/developv2.0.4
v2.0.4
141 files changed, 7485 insertions, 2439 deletions
@@ -2,6 +2,7 @@ src/GpgFrontend.h src/GpgFrontendBuildInfo.h src/GpgFrontendBuildInstallInfo.h +third_party/AppImageUpdate # gettext *.mo diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a524da7..4a96a11d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(GpgFrontend VERSION 2.0.3 LANGUAGES CXX) +project(GpgFrontend VERSION 2.0.4 LANGUAGES CXX) message(STATUS "GpgFrontend Build Configuration Started CMAKE Version ${CMAKE_VERSION}") @@ -42,16 +42,16 @@ else () endif () # Specify different compilation modes -if (BUILD_CONFIG) +if (GPGFRONTEND_BUILD_CONFIG) # Test Build - if (${BUILD_CONFIG} STREQUAL "test") - message(STATUS "Switch TEST_BUILD") - set(TEST_BUILD 1) - set(AppName GpgFrontendTest) + if (${GPGFRONTEND_BUILD_CONFIG} STREQUAL "test_core") + message(STATUS "Switch TEST_CORE_BUILD") + set(TEST_CORE_BUILD 1) + set(AppName GpgFrontendCoreTest) # Test Build With Coverage Test - elseif (${BUILD_CONFIG} STREQUAL "test_coverage") + elseif (${GPGFRONTEND_BUILD_CONFIG} STREQUAL "test_core_coverage") message(STATUS "Switch TEST_COVERAGE_BUILD") - set(TEST_BUILD 1) + set(TEST_CORE_BUILD 1) if (USING_COMPILER_CLANG OR USING_COMPILER_GCC) set(TEST_COVERAGE_BUILD 1) set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) @@ -61,11 +61,11 @@ if (BUILD_CONFIG) endif () set(AppName GpgFrontendTest) # Test Build with minimum UI - elseif (${BUILD_CONFIG} STREQUAL "test_ui") + elseif (${GPGFRONTEND_BUILD_CONFIG} STREQUAL "test_ui") message(STATUS "Switch TEST_MINIMUM_UI_BUILD") set(MINIMUM_APPLICATION_BUILD 1) set(AppName GpgFrontend) - elseif (${BUILD_CONFIG} STREQUAL "test_all") + elseif (${GPGFRONTEND_BUILD_CONFIG} STREQUAL "test_all") message(STATUS "Switch FULL_APPLICATION_BUILD") set(FULL_APPLICATION_BUILD 1) set(AppName GpgFrontend) @@ -171,6 +171,14 @@ if (APPLE) set(ENV{Qt5_DIR} /usr/local/opt/qt5/lib/cmake) + if (XCODE_BUILD) + set(XCODE_CODE_SIGN_IDENTITY "\"${XCODE_CODE_SIGN_IDENTITY}\"") + message(STATUS "XCODE_CODE_SIGN_IDENTITY ${XCODE_CODE_SIGN_IDENTITY}") + if (APPLE_SANDBOX) + add_compile_definitions(APPLE_SANDBOX) + endif () + endif () + include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/third_party @@ -216,10 +224,14 @@ endif () message(STATUS "OS_PLATFORM ${OS_PLATFORM}") +if (GPG_STANDALONE_MODE) + add_compile_definitions(GPG_STANDALONE_MODE) +endif () + # Basic Envirnoment Configure set(BASIC_ENV_CONFIG 1) set(QT_MOC_CONFIG 1) -set(ESAY_LOGGING_PP 1) +set(EASY_LOGGING_PP 1) if (LINUX_INSTALL_SOFTWARE) include(GNUInstallDirs) @@ -262,7 +274,7 @@ elseif (STABLE_APPLICATION_BUILD) set(BASIC_ENV_CONFIG 1) set(SMTP_SUPPORT 1) set(MULTI_LANG_SUPPORT 1) -elseif (TEST_BUILD) +elseif (TEST_CORE_BUILD) message(STATUS "Build Test Cases") if (MODULES) @@ -317,7 +329,7 @@ endif () add_subdirectory(third_party) add_subdirectory(src) -if (TEST_BUILD) +if (TEST_CORE_BUILD) include(CTest) enable_testing() add_subdirectory(test) diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index c7ecc963..461c488d 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -45,6 +45,7 @@ <file alias="key_generate.png">resource/icons/key_generate.png</file> <file alias="key_import.png">resource/icons/key_import.png</file> <file alias="kgpg_key2.png">resource/icons/kgpg_key2.png</file> + <file alias="key_package.png">resource/icons/key_package.png</file> <file alias="misc_doc.png">resource/icons/misc_doc.png</file> <file alias="quote.png">resource/icons/quote.png</file> <file alias="signature.png">resource/icons/signature.png</file> diff --git a/resource/icons/key_package.png b/resource/icons/key_package.png Binary files differnew file mode 100644 index 00000000..6464cb9d --- /dev/null +++ b/resource/icons/key_package.png diff --git a/resource/plist/MacOSXBundleInfo.plist.in b/resource/plist/MacOSXBundleInfo.plist.in new file mode 100644 index 00000000..55c42e1a --- /dev/null +++ b/resource/plist/MacOSXBundleInfo.plist.in @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleGetInfoString</key> + <string>${MACOSX_BUNDLE_INFO_STRING}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleLongVersionString</key> + <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CSResourcesFileMapped</key> + <true/> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>LSApplicationCategoryType</key> + <string>public.app-category.utilities</string> +</dict> +</plist>
\ No newline at end of file diff --git a/resource/plist/entitlement.plist b/resource/plist/entitlement.plist new file mode 100644 index 00000000..625c18bd --- /dev/null +++ b/resource/plist/entitlement.plist @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<plist version="1.0"> + <dict> + <key>com.apple.security.app-sandbox</key> + <true/> + </dict> +</plist>
\ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 346b1524..a6c70923 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,8 +31,13 @@ if (APPLICATION_BUILD) set(APP_ICON_RESOURCE_WINDOWS "${CMAKE_SOURCE_DIR}/gpgfrontend.rc") set_property(SOURCE gpgfrontend.rc APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/gpgfrontend.ico) - # Set Binary Output Path - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release) + if (NOT XCODE_BUILD) + # Set Binary Output Path + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release) + else () + # Set Binary Output Path + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}) + endif () message(STATUS "CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") endif () @@ -114,6 +119,9 @@ if (APPLICATION_BUILD) file(COPY ${CMAKE_SOURCE_DIR}/resource/css DESTINATION ${RESOURCE_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) file(COPY ${CMAKE_SOURCE_DIR}/resource/icons DESTINATION ${RESOURCE_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) file(COPY ${CMAKE_SOURCE_DIR}/TRANSLATORS DESTINATION ${RESOURCE_OUTPUT_DIRECTORY} FOLLOW_SYMLINK_CHAIN) + if (GPG_STANDALONE_MODE) + file(COPY ${CMAKE_SOURCE_DIR}/resource/gpg1.4 DESTINATION ${RESOURCE_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) + endif () endif () if (APPLICATION_BUILD) @@ -155,7 +163,7 @@ if (APPLICATION_BUILD) if (${CMAKE_BUILD_TYPE} STREQUAL "Release") if (MINGW) add_executable(${AppName} WIN32 ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) - elseif (APPLE) + elseif (APPLE AND NOT XCODE_BUILD) add_executable(${AppName} MACOSX_BUNDLE ${ICON_RESOURCE} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) set_target_properties(${AppName} PROPERTIES BUNDLE True @@ -183,6 +191,38 @@ if (APPLICATION_BUILD) COMMAND /bin/mkdir -p ./gpgfrontend/usr/lib WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMENT "Complement to build the required architecture") + elseif (APPLE AND XCODE_BUILD) + add_executable(${AppName} MACOSX_BUNDLE ${ICON_RESOURCE} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) + set_target_properties(${AppName} PROPERTIES + BUNDLE True + MACOSX_BUNDLE_GUI_IDENTIFIER pub.gpgfrontend.gpgfrontend + MACOSX_BUNDLE_BUNDLE_NAME ${AppName} + MACOSX_BUNDLE_LONG_VERSION_STRING ${BUILD_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_COPYRIGHT "GPL-3.0" + MACOSX_BUNDLE_INFO_STRING "An OpenPGP Crypto Tool" + MACOSX_BUNDLE_ICON_FILE "gpgfrontend.icns" + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/resource/plist/MacOSXBundleInfo.plist.in + ) + add_custom_command(TARGET ${AppName} POST_BUILD + COMMAND /bin/rm -rf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${AppName}.app/Contents/Resources + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMENT "Deleting Resources in App Bundle") + add_custom_command(TARGET ${AppName} POST_BUILD + COMMAND /bin/cp -rf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Resources ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${AppName}.app/Contents/ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMENT "Copying Resources into App Bundle Resource") + add_custom_command(TARGET ${AppName} POST_BUILD + COMMAND macdeployqt ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${AppName}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMENT "Resolving Qt Dependency") + set_target_properties(${AppName} PROPERTIES + XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)" + XCODE_ATTRIBUTE_SKIP_INSTALL "No" + XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep --entitlements ${CMAKE_SOURCE_DIR}/resource/plist/entitlement.plist" + XCODE_ATTRIBUTE_ENABLE_APP_SANDBOX "Yes" + ) else () add_executable(${AppName} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) endif () @@ -252,6 +292,8 @@ if (LINUX_INSTALL_SOFTWARE) LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) + install(FILES ${CMAKE_SOURCE_DIR}/TRANSLATORS + DESTINATION /usr/local/share/${AppName}/) install(FILES ${CMAKE_SOURCE_DIR}/resource/meta/pub.gpgfrontend.gpgfrontend.appdata.xml DESTINATION /usr/share/metainfo/) install(DIRECTORY ${CMAKE_SOURCE_DIR}/resource/desktop/ @@ -274,7 +316,7 @@ if (LINUX_INSTALL_SOFTWARE) 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_DEBIAN_PACKAGE_DEPENDS "gpg (>= 2.2), libqt5core5a (>= 5.9), libqt5gui5 (>= 5.9), libqt5widgets5 (>= 5.9), libqt5network5 (>= 5.9), libqt5printsupport5 (>= 5.9), libconfig++-dev (>=1.5)") set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") diff --git a/src/GpgFrontend.h.in b/src/GpgFrontend.h.in index 622c2b07..11934550 100644 --- a/src/GpgFrontend.h.in +++ b/src/GpgFrontend.h.in @@ -39,6 +39,8 @@ #ifdef WINDOWS #include <clocale> #undef vsnprintf +#undef sprintf +#undef snprintf #endif // logging diff --git a/src/gpg/CMakeLists.txt b/src/gpg/CMakeLists.txt index 19b9f00f..0ccc8171 100644 --- a/src/gpg/CMakeLists.txt +++ b/src/gpg/CMakeLists.txt @@ -9,8 +9,8 @@ set(UTILS_DIR ${CMAKE_SOURCE_DIR}/utils) set(GPGME_LIB_DIR ${UTILS_DIR}/gpgme/lib) -if (ESAY_LOGGING_PP) - message(STATUS "Link ESAY_LOGGING_PP") +if (EASY_LOGGING_PP) + message(STATUS "Link EASY_LOGGING_PP") set(THIRD_PARTY_LIBS easy_logging_pp config++) endif () @@ -33,6 +33,13 @@ elseif (APPLE) ${BOOST_LIBS} ${libgpgme} ${libgpg-error} ${libassuan} dl) + if (XCODE_BUILD) + set_target_properties(gpg_core + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) + endif () else () find_library(libgpgme NAMES libgpgme.a) find_library(libgpg-error NAMES libgpg-error.a) diff --git a/src/gpg/GpgConstants.cpp b/src/gpg/GpgConstants.cpp index fd3c56b4..100bf8f8 100644 --- a/src/gpg/GpgConstants.cpp +++ b/src/gpg/GpgConstants.cpp @@ -42,6 +42,10 @@ const char* GpgFrontend::GpgConstants::PGP_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----"; const char* GpgFrontend::GpgConstants::PGP_SIGNATURE_END = "-----END PGP SIGNATURE-----"; +const char* GpgFrontend::GpgConstants::PGP_PUBLIC_KEY_BEGIN = + "------BEGIN PGP PUBLIC KEY BLOCK-----"; +const char* GpgFrontend::GpgConstants::PGP_PRIVATE_KEY_BEGIN = + "-----BEGIN PGP PRIVATE KEY BLOCK-----"; const char* GpgFrontend::GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD = "GpgF_Scpt://"; @@ -215,6 +219,12 @@ GpgFrontend::GpgVerifyResult GpgFrontend::_new_result( return {result, _result_ref_deletor()}; } +GpgFrontend::GpgGenKeyResult GpgFrontend::_new_result( + gpgme_genkey_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 14895df7..d2f9b0b6 100644 --- a/src/gpg/GpgConstants.h +++ b/src/gpg/GpgConstants.h @@ -58,12 +58,14 @@ 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>; +using GpgGenKeyResult = std::shared_ptr<struct _gpgme_op_genkey_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); +GpgGenKeyResult _new_result(gpgme_genkey_result_t&& result); // Error Info Printer GpgError check_gpg_error(GpgError err); @@ -85,6 +87,10 @@ std::string get_only_file_name_with_path(const std::string& path); // Check int text_is_signed(BypeArrayRef text); +// Channels +const int GPGFRONTEND_DEFAULT_CHANNEL = 0; +const int GPGFRONTEND_NON_ASCII_CHANNEL = 2; + class GpgConstants { public: static const char* PGP_CRYPT_BEGIN; @@ -93,8 +99,11 @@ class GpgConstants { static const char* PGP_SIGNED_END; static const char* PGP_SIGNATURE_BEGIN; static const char* PGP_SIGNATURE_END; + static const char* PGP_PUBLIC_KEY_BEGIN; + static const char* PGP_PRIVATE_KEY_BEGIN; static const char* GPG_FRONTEND_SHORT_CRYPTO_HEAD; }; + } // namespace GpgFrontend #endif // GPG_CONSTANTS_H diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp index a8b86619..e3f10056 100644 --- a/src/gpg/GpgContext.cpp +++ b/src/gpg/GpgContext.cpp @@ -29,6 +29,7 @@ #include <functional> #include <string> +#include <utility> #include "GpgConstants.h" @@ -36,23 +37,19 @@ #include <windows.h> #endif -#define INT2VOIDP(i) (void*)(uintptr_t)(i) - namespace GpgFrontend { /** * Constructor * Set up gpgme-context, set paths to app-run path */ -GpgContext::GpgContext(bool independent_database, std::string db_path, - int channel) - : SingletonFunctionObject<GpgContext>(channel) { +GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { static bool _first = true; if (_first) { /* Initialize the locale environment. */ LOG(INFO) << "locale" << setlocale(LC_CTYPE, nullptr); - gpgme_check_version(nullptr); + info_.GpgMEVersion = gpgme_check_version(nullptr); gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr)); #ifdef LC_MESSAGES gpgme_set_locale(nullptr, LC_MESSAGES, setlocale(LC_MESSAGES, nullptr)); @@ -64,66 +61,160 @@ GpgContext::GpgContext(bool independent_database, std::string db_path, check_gpg_error(gpgme_new(&_p_ctx)); _ctx_ref = CtxRefHandler(_p_ctx); - auto engineInfo = gpgme_ctx_get_engine_info(*this); + if (args.gpg_alone) { + info_.AppPath = args.gpg_path; + auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, + info_.AppPath.c_str(), + info_.DatabasePath.c_str()); + assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); + } + auto engine_info = gpgme_ctx_get_engine_info(*this); // Check ENV before running - bool check_pass = false, find_openpgp = false, find_gpgconf = false, - find_assuan = false, find_cms = false; - while (engineInfo != nullptr) { - if (engineInfo->protocol == GPGME_PROTOCOL_GPGCONF && - strcmp(engineInfo->version, "1.0.0") != 0) - find_gpgconf = true; - if (engineInfo->protocol == GPGME_PROTOCOL_OpenPGP && - strcmp(engineInfo->version, "1.0.0") != 0) - find_openpgp = true, info.AppPath = engineInfo->file_name, - info.DatabasePath = "default", info.GnupgVersion = engineInfo->version; - if (engineInfo->protocol == GPGME_PROTOCOL_CMS && - strcmp(engineInfo->version, "1.0.0") != 0) - find_cms = true; - if (engineInfo->protocol == GPGME_PROTOCOL_ASSUAN) find_assuan = true; - engineInfo = engineInfo->next; + bool check_passed = false, find_openpgp = false, find_gpgconf = false, + find_cms = false; + + while (engine_info != nullptr) { + if (!strcmp(engine_info->version, "1.0.0")) { + engine_info = engine_info->next; + continue; + } + + DLOG(INFO) << gpgme_get_protocol_name(engine_info->protocol) + << std::string(engine_info->file_name == nullptr + ? "null" + : engine_info->file_name) + << std::string(engine_info->home_dir == nullptr + ? "null" + : engine_info->home_dir); + + switch (engine_info->protocol) { + case GPGME_PROTOCOL_OpenPGP: + find_openpgp = true; + info_.AppPath = engine_info->file_name; + info_.GnupgVersion = engine_info->version; + break; + case GPGME_PROTOCOL_CMS: + find_cms = true; + info_.CMSPath = engine_info->file_name; + break; + case GPGME_PROTOCOL_GPGCONF: + find_gpgconf = true; + info_.GpgConfPath = engine_info->file_name; + break; + case GPGME_PROTOCOL_ASSUAN: + break; + case GPGME_PROTOCOL_G13: + break; + case GPGME_PROTOCOL_UISERVER: + break; + case GPGME_PROTOCOL_SPAWN: + break; + case GPGME_PROTOCOL_DEFAULT: + break; + case GPGME_PROTOCOL_UNKNOWN: + break; + } + engine_info = engine_info->next; } - if (find_gpgconf && find_openpgp && find_cms && find_assuan) - check_pass = true; + // conditional check + if ((info_.GnupgVersion >= "2.0.0" && find_gpgconf && find_openpgp && + find_cms) || + (info_.GnupgVersion > "1.0.0" && find_gpgconf)) + check_passed = true; - if (!check_pass) { - good_ = false; + if (!check_passed) { + this->good_ = false; + LOG(ERROR) << "Env check failed"; return; } else { - LOG(INFO) << "Gnupg Version" << info.GnupgVersion; - - // Set Independent Database - if (independent_database) { - info.DatabasePath = db_path; - auto err = gpgme_ctx_set_engine_info( - _ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, info.AppPath.c_str(), - info.DatabasePath.c_str()); - assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); - } + DLOG(INFO) << "gnupg version" << info_.GnupgVersion; + init_ctx(); + good_ = true; + } +} + +void GpgContext::init_ctx() { + // Set Independent Database + if (info_.GnupgVersion <= "2.0.0" && args_.independent_database) { + info_.DatabasePath = args_.db_path; + DLOG(INFO) << "custom key db path" << info_.DatabasePath; + auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, + info_.AppPath.c_str(), + info_.DatabasePath.c_str()); + assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); + } else { + info_.DatabasePath = "default"; + } + if (args_.ascii) { /** Setting the output type must be done at the beginning */ /** think this means ascii-armor --> ? */ gpgme_set_armor(*this, 1); - // Speed up loading process - gpgme_set_offline(*this, 1); + } else { + /** Setting the output type must be done at the beginning */ + /** think this means ascii-armor --> ? */ + gpgme_set_armor(*this, 0); + } + + // Speed up loading process + gpgme_set_offline(*this, 1); + if (info_.GnupgVersion >= "2.0.0") { check_gpg_error(gpgme_set_keylist_mode( *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_WITH_SECRET | GPGME_KEYLIST_MODE_SIGS | GPGME_KEYLIST_MODE_SIG_NOTATIONS | GPGME_KEYLIST_MODE_WITH_TOFU)); - good_ = true; + } else { + check_gpg_error(gpgme_set_keylist_mode( + *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS | + GPGME_KEYLIST_MODE_SIG_NOTATIONS | + GPGME_KEYLIST_MODE_WITH_TOFU)); + } + + // for unit test + if (args_.test_mode) { + LOG(INFO) << "test mode"; + if (info_.GnupgVersion >= "2.1.0") SetPassphraseCb(test_passphrase_cb); + gpgme_set_status_cb(*this, test_status_cb, nullptr); } } bool GpgContext::good() const { return good_; } -void GpgContext::SetPassphraseCb(decltype(test_passphrase_cb) cb) const { - gpgme_set_passphrase_cb(*this, cb, nullptr); +void GpgContext::SetPassphraseCb(gpgme_passphrase_cb_t cb) const { + if (info_.GnupgVersion >= "2.1.0") { + if (gpgme_get_pinentry_mode(*this) != GPGME_PINENTRY_MODE_LOOPBACK) { + gpgme_set_pinentry_mode(*this, GPGME_PINENTRY_MODE_LOOPBACK); + } + gpgme_set_passphrase_cb(*this, cb, nullptr); + } else { + LOG(ERROR) << "Not supported for gnupg version" << info_.GnupgVersion; + } +} + +gpgme_error_t GpgContext::test_passphrase_cb(void *opaque, const char *uid_hint, + const char *passphrase_info, + int last_was_bad, int fd) { + size_t res; + std::string pass = "abcdefg\n"; + auto pass_len = pass.size(); + + size_t off = 0; + + do { + res = gpgme_io_write(fd, &pass[off], pass_len - off); + if (res > 0) off += res; + } while (res > 0 && off != pass_len); + + return off == pass_len ? 0 : gpgme_error_from_errno(errno); } -std::string GpgContext::getGpgmeVersion() { - return {gpgme_check_version(nullptr)}; +gpgme_error_t GpgContext::test_status_cb(void *hook, const char *keyword, + const char *args) { + LOG(INFO) << "keyword" << keyword; + return GPG_ERR_NO_ERROR; } } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/gpg/GpgContext.h b/src/gpg/GpgContext.h index 5812f49f..146f0794 100644 --- a/src/gpg/GpgContext.h +++ b/src/gpg/GpgContext.h @@ -32,34 +32,49 @@ namespace GpgFrontend { +struct GpgContextInitArgs { + // make no sense for gpg2 + bool independent_database = false; + std::string db_path = {}; + bool gpg_alone = false; + std::string gpg_path = {}; + bool test_mode = false; + bool ascii = true; + + GpgContextInitArgs() = default; +}; + /** * Custom Encapsulation of GpgME APIs */ class GpgContext : public SingletonFunctionObject<GpgContext> { public: - explicit GpgContext(bool independent_database = false, - std::string path = std::string(), int channel = 0); + explicit GpgContext(const GpgContextInitArgs& args = {}); + + explicit GpgContext(int channel) + : SingletonFunctionObject<GpgContext>(channel) {} ~GpgContext() override = default; [[nodiscard]] bool good() const; - [[nodiscard]] const GpgInfo& GetInfo() const { return info; } - - static std::string getGpgmeVersion(); + [[nodiscard]] const GpgInfo& GetInfo() const { return info_; } operator gpgme_ctx_t() const { return _ctx_ref.get(); } private: - GpgInfo info; + GpgInfo info_; + GpgContextInitArgs args_; - struct _ctx_ref_deletor { + void init_ctx(); + + struct _ctx_ref_deleter { void operator()(gpgme_ctx_t _ctx) { if (_ctx != nullptr) gpgme_release(_ctx); } }; - using CtxRefHandler = std::unique_ptr<struct gpgme_context, _ctx_ref_deletor>; + using CtxRefHandler = std::unique_ptr<struct gpgme_context, _ctx_ref_deleter>; CtxRefHandler _ctx_ref = nullptr; bool good_ = true; @@ -67,28 +82,12 @@ class GpgContext : public SingletonFunctionObject<GpgContext> { public: static gpgme_error_t test_passphrase_cb(void* opaque, const char* uid_hint, const char* passphrase_info, - int last_was_bad, int fd) { - LOG(INFO) << "test_passphrase_cb Called"; - size_t res; - std::string pass = "abcdefg\n"; - auto pass_len = pass.size(); - - size_t off = 0; - - (void)opaque; - (void)uid_hint; - (void)passphrase_info; - (void)last_was_bad; - - do { - res = gpgme_io_write(fd, &pass[off], pass_len - off); - if (res > 0) off += res; - } while (res > 0 && off != pass_len); + int last_was_bad, int fd); - return off == pass_len ? 0 : gpgme_error_from_errno(errno); - } + static gpgme_error_t test_status_cb(void* hook, const char* keyword, + const char* args); - void SetPassphraseCb(decltype(test_passphrase_cb) func) const; + void SetPassphraseCb(gpgme_passphrase_cb_t func) const; }; } // namespace GpgFrontend diff --git a/src/gpg/GpgCoreInit.cpp b/src/gpg/GpgCoreInit.cpp new file mode 100644 index 00000000..3f07d2b1 --- /dev/null +++ b/src/gpg/GpgCoreInit.cpp @@ -0,0 +1,52 @@ +/** + * 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 "GpgCoreInit.h" + +#include "GpgContext.h" + +void GpgFrontend::init_gpgfrontend_core() { + GpgFrontend::GpgContext::CreateInstance( + GPGFRONTEND_DEFAULT_CHANNEL, + [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { + GpgFrontend::GpgContextInitArgs args; + return std::make_unique<GpgFrontend::GpgContext>(args); + }); + + GpgFrontend::GpgContext::CreateInstance( + GPGFRONTEND_NON_ASCII_CHANNEL, + [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { + GpgFrontend::GpgContextInitArgs args; + args.ascii = false; + return std::make_unique<GpgFrontend::GpgContext>(args); + }); +} + +void GpgFrontend::new_default_settings_channel(int channel) { + GpgFrontend::GpgContext::CreateInstance( + channel, [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { + GpgFrontend::GpgContextInitArgs args; + return std::make_unique<GpgFrontend::GpgContext>(args); + }); +}
\ No newline at end of file diff --git a/src/gpg/GpgCoreInit.h b/src/gpg/GpgCoreInit.h new file mode 100644 index 00000000..577f46a3 --- /dev/null +++ b/src/gpg/GpgCoreInit.h @@ -0,0 +1,40 @@ +/** + * 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_GPGCOREINIT_H +#define GPGFRONTEND_GPGCOREINIT_H + +#include "GpgConstants.h" + +namespace GpgFrontend { + +// Init +void init_gpgfrontend_core(); + +void new_default_settings_channel( + int channel = GpgFrontend::GPGFRONTEND_DEFAULT_CHANNEL); + +} // namespace GpgFrontend + +#endif // GPGFRONTEND_GPGCOREINIT_H diff --git a/src/gpg/GpgFunctionObject.h b/src/gpg/GpgFunctionObject.h index 6f1d60af..404b2f84 100644 --- a/src/gpg/GpgFunctionObject.h +++ b/src/gpg/GpgFunctionObject.h @@ -34,63 +34,61 @@ #include <stdexcept> #include <string> +#include "GpgConstants.h" + namespace GpgFrontend { template <typename T> class SingletonFunctionObject { public: - static T& GetInstance(int channel = 0) { - if (!channel) { - std::lock_guard<std::mutex> guard(_instance_mutex); - if (_instance == nullptr) _instance = std::make_unique<T>(); - return *_instance; - } else { - // read _instances_map - decltype(_instances_map.end()) _it; - { - std::shared_lock lock(_instances_mutex); - _it = _instances_map.find(channel); - } - if (_it != _instances_map.end()) - return *_it->second; - else - return CreateInstance(channel); - } + static T& GetInstance( + int channel = GpgFrontend::GPGFRONTEND_DEFAULT_CHANNEL) { + static_assert(std::is_base_of<SingletonFunctionObject<T>, T>::value, + "T not derived from SingletonFunctionObject<T>"); + + auto _p_pbj = find_object_in_channel(channel); + if (_p_pbj == nullptr) + return *set_object_in_channel(channel, std::make_unique<T>(channel)); + else + return *_p_pbj; + } + + static T& CreateInstance(int channel, + std::function<std::unique_ptr<T>(void)> factory) { + static_assert(std::is_base_of<SingletonFunctionObject<T>, T>::value, + "T not derived from SingletonFunctionObject<T>"); + + auto _p_pbj = find_object_in_channel(channel); + if (_p_pbj == nullptr) + return *set_object_in_channel(channel, std::move(factory())); + else + return *_p_pbj; } static T& CreateInstance(int channel, std::unique_ptr<T> p_obj = nullptr) { - if (!channel) return *_instance; + static_assert(std::is_base_of<SingletonFunctionObject<T>, T>::value, + "T not derived from SingletonFunctionObject<T>"); + + auto _p_pbj = find_object_in_channel(channel); + if (_p_pbj == nullptr) + return *set_object_in_channel(channel, std::move(p_obj)); + else + return *_p_pbj; + } - // read _instances_map + static T& ReleaseChannel(int channel) { decltype(_instances_map.end()) _it; { std::shared_lock lock(_instances_mutex); _it = _instances_map.find(channel); } - if (_it == _instances_map.end()) { - { - std::lock_guard<std::mutex> guard(_default_channel_mutex); - int tmp = channel; - std::swap(_default_channel, tmp); - if (p_obj == nullptr) p_obj = std::make_unique<T>(); - std::swap(_default_channel, tmp); - } - T* obj = p_obj.get(); - - // change _instances_map - { - std::unique_lock lock(_instances_mutex); - _instances_map.insert({channel, std::move(p_obj)}); - } - return *obj; - } else { - return *_it->second; - } + if (_it != _instances_map.end()) _instances_map.erase(_it); + DLOG(INFO) << "channel" << channel << "released"; } static int GetDefaultChannel() { return _default_channel; } - int GetChannel() const { return channel_; } + [[nodiscard]] int GetChannel() const { return channel_; } SingletonFunctionObject(T&&) = delete; @@ -99,27 +97,52 @@ class SingletonFunctionObject { void operator=(const T&) = delete; protected: - SingletonFunctionObject() {} + SingletonFunctionObject() = default; - SingletonFunctionObject(int channel) : channel_(channel) {} + explicit SingletonFunctionObject(int channel) : channel_(channel) {} virtual ~SingletonFunctionObject() = default; + void SetChannel(int channel) { this->channel_ = channel; } + private: int channel_ = _default_channel; static int _default_channel; - static std::mutex _default_channel_mutex; static std::mutex _instance_mutex; static std::shared_mutex _instances_mutex; static std::unique_ptr<T> _instance; static std::map<int, std::unique_ptr<T>> _instances_map; -}; -template <typename T> -int SingletonFunctionObject<T>::_default_channel = 0; + static T* find_object_in_channel(int channel) { + // read _instances_map + decltype(_instances_map.end()) _it; + { + std::shared_lock lock(_instances_mutex); + _it = _instances_map.find(channel); + if (_it == _instances_map.end()) + return nullptr; + else + return _it->second.get(); + } + } + + static T* set_object_in_channel(int channel, std::unique_ptr<T> p_obj) { + { + if (p_obj == nullptr) p_obj = std::make_unique<T>(); + T* obj = p_obj.get(); + obj->SetChannel(channel); + { + std::unique_lock lock(_instances_mutex); + _instances_map.insert({channel, std::move(p_obj)}); + } + return obj; + } + } +}; template <typename T> -std::mutex SingletonFunctionObject<T>::_default_channel_mutex; +int SingletonFunctionObject<T>::_default_channel = + GpgFrontend::GPGFRONTEND_DEFAULT_CHANNEL; template <typename T> std::mutex SingletonFunctionObject<T>::_instance_mutex; diff --git a/src/gpg/GpgGenKeyInfo.cpp b/src/gpg/GpgGenKeyInfo.cpp index 4a0a99fc..f67aaa1f 100644 --- a/src/gpg/GpgGenKeyInfo.cpp +++ b/src/gpg/GpgGenKeyInfo.cpp @@ -32,18 +32,41 @@ #include <string> #include <vector> -const std::vector<std::string> GpgFrontend::GenKeyInfo::SupportedKeyAlgo = { - "RSA", "DSA", "ED25519"}; - -const std::vector<std::string> GpgFrontend::GenKeyInfo::SupportedSubkeyAlgo = { - "RSA", "DSA", "ED25519", "ELG"}; - void GpgFrontend::GenKeyInfo::setAlgo(const std::string &m_algo) { - LOG(INFO) << "GpgFrontend::GenKeyInfo::setAlgo m_algo" << m_algo; + LOG(INFO) << "set algo" << m_algo; + // Check algo if supported + std::string algo_args = std::string(m_algo); + boost::algorithm::to_upper(algo_args); + if (standalone_) { + if (!subkey_) { + auto support_algo = getSupportedKeyAlgoStandalone(); + auto it = std::find(support_algo.begin(), support_algo.end(), algo_args); + // Algo Not Supported + if (it == support_algo.end()) return; + } else { + auto support_algo = getSupportedSubkeyAlgoStandalone(); + auto it = std::find(support_algo.begin(), support_algo.end(), algo_args); + // Algo Not Supported + if (it == support_algo.end()) return; + } + } else { + if (!subkey_) { + auto support_algo = getSupportedKeyAlgo(); + auto it = std::find(support_algo.begin(), support_algo.end(), algo_args); + // Algo Not Supported + if (it == support_algo.end()) return; + } else { + auto support_algo = getSupportedSubkeyAlgo(); + auto it = std::find(support_algo.begin(), support_algo.end(), algo_args); + // Algo Not Supported + if (it == support_algo.end()) return; + } + } + // reset all options reset_options(); - if (!this->subKey) { + if (!this->subkey_) { this->setAllowCertification(true); } else { this->setAllowCertification(false); @@ -51,23 +74,20 @@ void GpgFrontend::GenKeyInfo::setAlgo(const std::string &m_algo) { this->allowChangeCertification = false; - std::string lower_algo = std::string(m_algo); - boost::algorithm::to_lower(lower_algo); - - LOG(INFO) << "GpgFrontend::GenKeyInfo::setAlgo lower_algo" << lower_algo; + if (!standalone_) boost::algorithm::to_lower(algo_args); - if (lower_algo == "rsa") { + if (algo_args == "rsa") { /** * RSA is the world’s premier asymmetric cryptographic algorithm, * and is built on the difficulty of factoring extremely large composites. * GnuPG supports RSA with key sizes of between 1024 and 4096 bits. */ - suggestMinKeySize = 1024; - suggestMaxKeySize = 4096; - suggestSizeAdditionStep = 1024; + suggest_min_key_size_ = 1024; + suggest_max_key_size_ = 4096; + suggest_size_addition_step_ = 1024; setKeySize(2048); - } else if (lower_algo == "dsa") { + } else if (algo_args == "dsa") { /** * Algorithm (DSA) as a government standard for digital signatures. * Originally, it supported key lengths between 512 and 1024 bits. @@ -77,42 +97,40 @@ void GpgFrontend::GenKeyInfo::setAlgo(const std::string &m_algo) { setAllowEncryption(false); allowChangeEncryption = false; - suggestMinKeySize = 1024; - suggestMaxKeySize = 3072; - suggestSizeAdditionStep = 1024; + suggest_min_key_size_ = 1024; + suggest_max_key_size_ = 3072; + suggest_size_addition_step_ = 1024; setKeySize(2048); - } else if (lower_algo == "ed25519") { + } else if (algo_args == "ed25519") { /** * GnuPG supports the Elgamal asymmetric encryption algorithm in key lengths * ranging from 1024 to 4096 bits. */ - setAllowEncryption(false); allowChangeEncryption = false; - suggestMinKeySize = -1; - suggestMaxKeySize = -1; - suggestSizeAdditionStep = -1; + suggest_min_key_size_ = -1; + suggest_max_key_size_ = -1; + suggest_size_addition_step_ = -1; setKeySize(-1); - } else if (lower_algo == "elg") { + } else if (algo_args == "elg") { /** * GnuPG supports the Elgamal asymmetric encryption algorithm in key lengths * ranging from 1024 to 4096 bits. */ - setAllowAuthentication(false); allowChangeAuthentication = false; setAllowSigning(false); allowChangeSigning = false; - suggestMinKeySize = 1024; - suggestMaxKeySize = 4096; - suggestSizeAdditionStep = 1024; + suggest_min_key_size_ = 1024; + suggest_max_key_size_ = 4096; + suggest_size_addition_step_ = 1024; setKeySize(2048); } - this->algo = lower_algo; + this->algo_ = algo_args; } void GpgFrontend::GenKeyInfo::reset_options() { @@ -128,37 +146,37 @@ void GpgFrontend::GenKeyInfo::reset_options() { allowChangeAuthentication = true; setAllowAuthentication(true); - passPhrase.clear(); + passphrase_.clear(); } std::string GpgFrontend::GenKeyInfo::getKeySizeStr() const { - if (keySize > 0) { - return std::to_string(keySize); + if (key_size_ > 0) { + return std::to_string(key_size_); } else { return {}; } } void GpgFrontend::GenKeyInfo::setKeySize(int m_key_size) { - if (m_key_size < suggestMinKeySize || m_key_size > suggestMaxKeySize) { + if (m_key_size < suggest_min_key_size_ || + m_key_size > suggest_max_key_size_) { return; } - GenKeyInfo::keySize = m_key_size; + GenKeyInfo::key_size_ = m_key_size; } void GpgFrontend::GenKeyInfo::setExpired( const boost::posix_time::ptime &m_expired) { using namespace boost::gregorian; - auto current = boost::posix_time::second_clock::local_time(); - if (isNonExpired() && m_expired < current + years(2)) { - GenKeyInfo::expired = m_expired; + if (!isNonExpired()) { + GenKeyInfo::expired_ = m_expired; } } void GpgFrontend::GenKeyInfo::setNonExpired(bool m_non_expired) { using namespace boost::posix_time; - if (!m_non_expired) this->expired = from_time_t(0); - GenKeyInfo::nonExpired = m_non_expired; + if (!m_non_expired) this->expired_ = from_time_t(0); + GenKeyInfo::non_expired_ = m_non_expired; } void GpgFrontend::GenKeyInfo::setAllowEncryption(bool m_allow_encryption) { @@ -170,3 +188,35 @@ void GpgFrontend::GenKeyInfo::setAllowCertification( if (allowChangeCertification) GenKeyInfo::allowCertification = m_allow_certification; } + +GpgFrontend::GenKeyInfo::GenKeyInfo(bool m_is_sub_key, bool m_standalone) + : standalone_(m_standalone), subkey_(m_is_sub_key) { + setAlgo("rsa"); +} + +const std::vector<std::string> &GpgFrontend::GenKeyInfo::getSupportedKeyAlgo() { + static const std::vector<std::string> support_key_algo = {"RSA", "DSA", + "ED25519"}; + return support_key_algo; +} + +const std::vector<std::string> + &GpgFrontend::GenKeyInfo::getSupportedSubkeyAlgo() { + static const std::vector<std::string> support_subkey_algo = {"RSA", "DSA", + "ED25519"}; + return support_subkey_algo; +} + +const std::vector<std::string> + &GpgFrontend::GenKeyInfo::getSupportedKeyAlgoStandalone() { + static const std::vector<std::string> support_subkey_algo_standalone = { + "RSA", "DSA"}; + return support_subkey_algo_standalone; +} + +const std::vector<std::string> + &GpgFrontend::GenKeyInfo::getSupportedSubkeyAlgoStandalone() { + static const std::vector<std::string> support_subkey_algo_standalone = { + "RSA", "DSA", "ELG-E"}; + return support_subkey_algo_standalone; +} diff --git a/src/gpg/GpgGenKeyInfo.h b/src/gpg/GpgGenKeyInfo.h index 14290e17..8f7d19ea 100644 --- a/src/gpg/GpgGenKeyInfo.h +++ b/src/gpg/GpgGenKeyInfo.h @@ -27,72 +27,97 @@ #include <boost/date_time.hpp> #include <boost/date_time/gregorian/greg_duration_types.hpp> +#include <boost/format.hpp> #include <string> #include <vector> namespace GpgFrontend { class GenKeyInfo { - bool subKey = true; - std::string userid; - std::string algo; - int keySize = 2048; - boost::posix_time::ptime expired = + bool standalone_ = false; + bool subkey_ = false; + std::string name_; + std::string email_; + std::string comment_; + + std::string algo_; + int key_size_ = 2048; + boost::posix_time::ptime expired_ = boost::posix_time::second_clock::local_time() + boost::gregorian::years(2); - bool nonExpired = false; + bool non_expired_ = false; - bool noPassPhrase = false; - bool allowNoPassPhrase = true; + bool no_passphrase_ = false; + bool allow_no_pass_phrase_ = true; - int suggestMaxKeySize = 4096; - int suggestSizeAdditionStep = 1024; - int suggestMinKeySize = 1024; + int suggest_max_key_size_ = 4096; + int suggest_size_addition_step_ = 1024; + int suggest_min_key_size_ = 1024; - std::string passPhrase; + std::string passphrase_; public: - static const std::vector<std::string> SupportedKeyAlgo; + static const std::vector<std::string> &getSupportedKeyAlgo(); - static const std::vector<std::string> SupportedSubkeyAlgo; + static const std::vector<std::string> &getSupportedSubkeyAlgo(); - [[nodiscard]] bool isSubKey() const { return subKey; } + static const std::vector<std::string> &getSupportedKeyAlgoStandalone(); - void setIsSubKey(bool m_sub_key) { GenKeyInfo::subKey = m_sub_key; } + static const std::vector<std::string> &getSupportedSubkeyAlgoStandalone(); - [[nodiscard]] const std::string &getUserid() const { return userid; } + [[nodiscard]] bool isSubKey() const { return subkey_; } - void setUserid(const std::string &m_userid) { GenKeyInfo::userid = m_userid; } + void setIsSubKey(bool m_sub_key) { GenKeyInfo::subkey_ = m_sub_key; } - [[nodiscard]] const std::string &getAlgo() const { return algo; } + [[nodiscard]] std::string getUserid() const { + auto uid_format = boost::format("%1%(%2%)<%3%>") % this->name_ % + this->comment_ % this->email_; + return uid_format.str(); + } + + void setName(const std::string &m_name) { this->name_ = m_name; } + + void setEmail(const std::string &m_email) { this->email_ = m_email; } + + void setComment(const std::string &m_comment) { this->comment_ = m_comment; } + + [[nodiscard]] std::string getName() const { return name_; } + + [[nodiscard]] std::string getEmail() const { return email_; } + + [[nodiscard]] std::string getComment() const { return comment_; } + + [[nodiscard]] const std::string &getAlgo() const { return algo_; } void setAlgo(const std::string &m_algo); [[nodiscard]] std::string getKeySizeStr() const; - [[nodiscard]] int getKeySize() const { return keySize; } + [[nodiscard]] int getKeySize() const { return key_size_; } void setKeySize(int m_key_size); [[nodiscard]] const boost::posix_time::ptime &getExpired() const { - return expired; + return expired_; } void setExpired(const boost::posix_time::ptime &m_expired); - [[nodiscard]] bool isNonExpired() const { return nonExpired; } + [[nodiscard]] bool isNonExpired() const { return non_expired_; } void setNonExpired(bool m_non_expired); - [[nodiscard]] bool isNoPassPhrase() const { return this->noPassPhrase; } + [[nodiscard]] bool isNoPassPhrase() const { return this->no_passphrase_; } void setNonPassPhrase(bool m_non_pass_phrase) { - GenKeyInfo::noPassPhrase = m_non_pass_phrase; + GenKeyInfo::no_passphrase_ = m_non_pass_phrase; } [[nodiscard]] bool isAllowSigning() const { return allowSigning; } - [[nodiscard]] bool isAllowNoPassPhrase() const { return allowNoPassPhrase; } + [[nodiscard]] bool isAllowNoPassPhrase() const { + return allow_no_pass_phrase_; + } void setAllowSigning(bool m_allow_signing) { if (allowChangeSigning) GenKeyInfo::allowSigning = m_allow_signing; @@ -115,10 +140,10 @@ class GenKeyInfo { GenKeyInfo::allowAuthentication = m_allow_authentication; } - [[nodiscard]] const std::string &getPassPhrase() const { return passPhrase; } + [[nodiscard]] const std::string &getPassPhrase() const { return passphrase_; } void setPassPhrase(const std::string &m_pass_phrase) { - GenKeyInfo::passPhrase = m_pass_phrase; + GenKeyInfo::passphrase_ = m_pass_phrase; } [[nodiscard]] bool isAllowChangeSigning() const { return allowChangeSigning; } @@ -134,12 +159,16 @@ class GenKeyInfo { return allowChangeAuthentication; } - [[nodiscard]] int getSuggestMaxKeySize() const { return suggestMaxKeySize; } + [[nodiscard]] int getSuggestMaxKeySize() const { + return suggest_max_key_size_; + } - [[nodiscard]] int getSuggestMinKeySize() const { return suggestMinKeySize; } + [[nodiscard]] int getSuggestMinKeySize() const { + return suggest_min_key_size_; + } [[nodiscard]] int getSizeChangeStep() const { - return suggestSizeAdditionStep; + return suggest_size_addition_step_; } private: @@ -158,9 +187,7 @@ class GenKeyInfo { void reset_options(); public: - explicit GenKeyInfo(bool m_is_sub_key = false) : subKey(m_is_sub_key) { - setAlgo("rsa"); - } + explicit GenKeyInfo(bool m_is_sub_key = false, bool m_standalone = false); }; } // namespace GpgFrontend diff --git a/src/gpg/GpgInfo.h b/src/gpg/GpgInfo.h index 6ecb9b92..67ac55d6 100644 --- a/src/gpg/GpgInfo.h +++ b/src/gpg/GpgInfo.h @@ -40,6 +40,12 @@ class GpgInfo { std::string DatabasePath; std::string GnupgVersion; + + std::string GpgConfPath; + + std::string CMSPath; + + std::string GpgMEVersion; }; #endif // GPGFRONTEND_ZH_CN_TS_GPGINFO_H diff --git a/src/gpg/function/BasicOperator.cpp b/src/gpg/function/BasicOperator.cpp index 56b7ca54..0383f8ab 100644 --- a/src/gpg/function/BasicOperator.cpp +++ b/src/gpg/function/BasicOperator.cpp @@ -75,8 +75,7 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Verify( BypeArrayRef& in_buffer, ByteArrayPtr& sig_buffer, GpgVerifyResult& result) const { gpgme_error_t err; - - LOG(INFO) << "in buffer size" << in_buffer.size(); + GpgData data_in(in_buffer.data(), in_buffer.size()); GpgData data_out; @@ -187,9 +186,11 @@ gpgme_error_t GpgFrontend::BasicOperator::EncryptSign( void GpgFrontend::BasicOperator::SetSigners(KeyArgsList& keys) { gpgme_signers_clear(ctx); for (const GpgKey& key : keys) { + DLOG(INFO) << "key" << key.fpr(); if (key.CanSignActual()) { - auto gpgmeError = gpgme_signers_add(ctx, gpgme_key_t(key)); - check_gpg_error(gpgmeError); + DLOG(INFO) << "signer"; + auto error = gpgme_signers_add(ctx, gpgme_key_t(key)); + check_gpg_error(error); } } if (keys.size() != gpgme_signers_count(ctx)) diff --git a/src/gpg/function/BasicOperator.h b/src/gpg/function/BasicOperator.h index 4ea70eea..41bd9b7f 100644 --- a/src/gpg/function/BasicOperator.h +++ b/src/gpg/function/BasicOperator.h @@ -34,6 +34,10 @@ namespace GpgFrontend { class BasicOperator : public SingletonFunctionObject<BasicOperator> { public: + explicit BasicOperator( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<BasicOperator>(channel) {} + gpg_error_t Encrypt(KeyListPtr keys, BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, GpgEncrResult& result); @@ -65,7 +69,7 @@ class BasicOperator : public SingletonFunctionObject<BasicOperator> { private: GpgContext& ctx = - GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/GpgCommandExecutor.h b/src/gpg/function/GpgCommandExecutor.h index f28caca8..dcdd318d 100644 --- a/src/gpg/function/GpgCommandExecutor.h +++ b/src/gpg/function/GpgCommandExecutor.h @@ -35,6 +35,9 @@ namespace GpgFrontend { class GpgCommandExecutor : public SingletonFunctionObject<GpgCommandExecutor> { public: + explicit GpgCommandExecutor( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgCommandExecutor>(channel) {} #ifndef WINDOWS void Execute(StringArgsRef arguments, @@ -44,7 +47,8 @@ class GpgCommandExecutor : public SingletonFunctionObject<GpgCommandExecutor> { #endif private: - GpgContext &ctx = GpgContext::GetInstance(); + GpgContext &ctx = + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/GpgFileOpera.cpp b/src/gpg/function/GpgFileOpera.cpp index 42b37c71..8babfa6d 100644 --- a/src/gpg/function/GpgFileOpera.cpp +++ b/src/gpg/function/GpgFileOpera.cpp @@ -30,15 +30,16 @@ #include "gpg/function/BasicOperator.h" GpgFrontend::GpgError GpgFrontend::GpgFileOpera::EncryptFile( - KeyListPtr keys, const std::string& path, GpgEncrResult& result) { - std::string in_buffer = read_all_data_in_file(path); + KeyListPtr keys, const std::string& in_path, const std::string& out_path, + GpgEncrResult& result, int _channel) { + std::string in_buffer = read_all_data_in_file(in_path); std::unique_ptr<std::string> out_buffer; - auto err = BasicOperator::GetInstance().Encrypt(std::move(keys), in_buffer, - out_buffer, result); + auto err = BasicOperator::GetInstance(_channel).Encrypt( + std::move(keys), in_buffer, out_buffer, result); if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) - if (!write_buffer_to_file(path + ".asc", *out_buffer)) { + if (!write_buffer_to_file(out_path, *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; @@ -46,8 +47,9 @@ GpgFrontend::GpgError GpgFrontend::GpgFileOpera::EncryptFile( } GpgFrontend::GpgError GpgFrontend::GpgFileOpera::DecryptFile( - const std::string& path, GpgDecrResult& result) { - std::string in_buffer = read_all_data_in_file(path); + const std::string& in_path, const std::string& out_path, + GpgDecrResult& result) { + std::string in_buffer = read_all_data_in_file(in_path); std::unique_ptr<std::string> out_buffer; auto err = @@ -55,14 +57,8 @@ GpgFrontend::GpgError GpgFrontend::GpgFileOpera::DecryptFile( assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); - std::string out_file_name = get_only_file_name_with_path(path), - file_extension = get_file_extension(path); - - if (!(file_extension == ".asc" || file_extension == ".gpg")) - out_file_name += ".out"; - if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) - if (!write_buffer_to_file(out_file_name, *out_buffer)) { + if (!write_buffer_to_file(out_path, *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; @@ -70,58 +66,49 @@ GpgFrontend::GpgError GpgFrontend::GpgFileOpera::DecryptFile( } gpgme_error_t GpgFrontend::GpgFileOpera::SignFile(KeyListPtr keys, - const std::string& path, - GpgSignResult& result) { - auto in_buffer = read_all_data_in_file(path); + const std::string& in_path, + const std::string& out_path, + GpgSignResult& result, + int _channel) { + auto in_buffer = read_all_data_in_file(in_path); std::unique_ptr<std::string> out_buffer; - auto err = BasicOperator::GetInstance().Sign( + auto err = BasicOperator::GetInstance(_channel).Sign( std::move(keys), in_buffer, out_buffer, GPGME_SIG_MODE_DETACH, result); if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) - if (!write_buffer_to_file(path + ".sig", *out_buffer)) { + if (!write_buffer_to_file(out_path, *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; return err; } -gpgme_error_t GpgFrontend::GpgFileOpera::VerifyFile(const std::string& path, - GpgVerifyResult& result) { - auto in_buffer = read_all_data_in_file(path); +gpgme_error_t GpgFrontend::GpgFileOpera::VerifyFile( + const std::string& data_path, const std::string& sign_path, + GpgVerifyResult& result, int _channel) { + auto in_buffer = read_all_data_in_file(data_path); std::unique_ptr<std::string> sign_buffer = nullptr; - - if (get_file_extension(path) == ".gpg") { - auto err = - BasicOperator::GetInstance().Verify(in_buffer, sign_buffer, result); - return err; - } else { + if (!sign_path.empty()) { sign_buffer = - std::make_unique<std::string>(read_all_data_in_file(path + ".sig")); - - auto err = - BasicOperator::GetInstance().Verify(in_buffer, sign_buffer, result); - return err; + std::make_unique<std::string>(read_all_data_in_file(sign_path)); } + auto err = BasicOperator::GetInstance(_channel).Verify(in_buffer, sign_buffer, + result); + return err; } -// TODO - gpg_error_t GpgFrontend::GpgFileOpera::EncryptSignFile( - KeyListPtr keys, KeyListPtr signer_keys, const std::string& path, - GpgEncrResult& encr_res, GpgSignResult& sign_res) { - auto in_buffer = read_all_data_in_file(path); + KeyListPtr keys, KeyListPtr signer_keys, const std::string& in_path, + const std::string& out_path, GpgEncrResult& encr_res, + GpgSignResult& sign_res, int _channel) { + auto in_buffer = read_all_data_in_file(in_path); std::unique_ptr<std::string> out_buffer = nullptr; - // TODO dealing with signer keys - auto err = BasicOperator::GetInstance().EncryptSign( + auto err = BasicOperator::GetInstance(_channel).EncryptSign( std::move(keys), std::move(signer_keys), in_buffer, out_buffer, encr_res, sign_res); - auto out_path = path + ".gpg"; - LOG(INFO) << "EncryptSignFile out_path" << out_path; - LOG(INFO) << "EncryptSignFile out_buffer size" << out_buffer->size(); - if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) if (!write_buffer_to_file(out_path, *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); @@ -131,28 +118,33 @@ gpg_error_t GpgFrontend::GpgFileOpera::EncryptSignFile( } gpg_error_t GpgFrontend::GpgFileOpera::DecryptVerifyFile( - const std::string& path, GpgDecrResult& decr_res, - GpgVerifyResult& verify_res) { - LOG(INFO) << "GpgFrontend::GpgFileOpera::DecryptVerifyFile Called"; + const std::string& in_path, const std::string& out_path, + GpgDecrResult& decr_res, GpgVerifyResult& verify_res) { + auto in_buffer = read_all_data_in_file(in_path); - auto in_buffer = read_all_data_in_file(path); - - LOG(INFO) << "GpgFrontend::GpgFileOpera::DecryptVerifyFile in_buffer" - << in_buffer.size(); std::unique_ptr<std::string> out_buffer = nullptr; auto err = BasicOperator::GetInstance().DecryptVerify(in_buffer, out_buffer, decr_res, verify_res); - std::string out_file_name = get_only_file_name_with_path(path), - file_extension = get_file_extension(path); + if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) + if (!write_buffer_to_file(out_path, *out_buffer)) { + throw std::runtime_error("write_buffer_to_file error"); + }; + + return err; +} +unsigned int GpgFrontend::GpgFileOpera::EncryptFileSymmetric( + const std::string& in_path, const std::string& out_path, + GpgFrontend::GpgEncrResult& result, int _channel) { + std::string in_buffer = read_all_data_in_file(in_path); + std::unique_ptr<std::string> out_buffer; - if (!(file_extension == ".asc" || file_extension == ".gpg")) - out_file_name = path + ".out"; - LOG(INFO) << "out_file_name" << out_file_name; + auto err = BasicOperator::GetInstance(_channel).EncryptSymmetric( + in_buffer, out_buffer, result); if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) - if (!write_buffer_to_file(out_file_name, *out_buffer)) { + if (!write_buffer_to_file(out_path, *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; diff --git a/src/gpg/function/GpgFileOpera.h b/src/gpg/function/GpgFileOpera.h index 4aaf09f1..f4508f42 100644 --- a/src/gpg/function/GpgFileOpera.h +++ b/src/gpg/function/GpgFileOpera.h @@ -33,22 +33,41 @@ namespace GpgFrontend { class GpgFileOpera : public SingletonFunctionObject<GpgFileOpera> { public: - static GpgError EncryptFile(KeyListPtr keys, const std::string& path, - GpgEncrResult& result); + explicit GpgFileOpera( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgFileOpera>(channel) {} - static GpgError DecryptFile(const std::string& path, GpgDecrResult& result); + static unsigned int EncryptFile(KeyListPtr keys, const std::string& in_path, + const std::string& out_path, + GpgEncrResult& result, + int _channel = GPGFRONTEND_DEFAULT_CHANNEL); - static GpgError SignFile(KeyListPtr keys, const std::string& path, - GpgSignResult& result); + static unsigned int EncryptFileSymmetric( + const std::string& in_path, const std::string& out_path, + GpgEncrResult& result, int _channel = GPGFRONTEND_DEFAULT_CHANNEL); - static GpgError VerifyFile(const std::string& path, GpgVerifyResult& result); + static GpgError DecryptFile(const std::string& in_path, + const std::string& out_path, + GpgDecrResult& result); + + static GpgError SignFile(KeyListPtr keys, const std::string& in_path, + const std::string& out_path, GpgSignResult& result, + int _channel = GPGFRONTEND_DEFAULT_CHANNEL); + + static GpgError VerifyFile(const std::string& data_path, + const std::string& sign_path, + GpgVerifyResult& result, + int _channel = GPGFRONTEND_DEFAULT_CHANNEL); static GpgError EncryptSignFile(KeyListPtr keys, KeyListPtr signer_keys, - const std::string& path, + const std::string& in_path, + const std::string& out_path, GpgEncrResult& encr_res, - GpgSignResult& sign_res); + GpgSignResult& sign_res, + int _channel = GPGFRONTEND_DEFAULT_CHANNEL); - static GpgError DecryptVerifyFile(const std::string& path, + static GpgError DecryptVerifyFile(const std::string& in_path, + const std::string& out_path, GpgDecrResult& decr_res, GpgVerifyResult& verify_res); }; diff --git a/src/gpg/function/GpgKeyGetter.cpp b/src/gpg/function/GpgKeyGetter.cpp index 664aff56..8a26dcd9 100644 --- a/src/gpg/function/GpgKeyGetter.cpp +++ b/src/gpg/function/GpgKeyGetter.cpp @@ -58,13 +58,15 @@ GpgFrontend::KeyLinkListPtr GpgFrontend::GpgKeyGetter::FetchKey() { gpgme_key_t key; while ((err = gpgme_op_keylist_next(ctx, &key)) == GPG_ERR_NO_ERROR) { - keys_list->push_back(GpgKey(std::move(key))); + keys_list->push_back(GetKey(key->fpr)); } assert(check_gpg_error_2_err_code(err, GPG_ERR_EOF) == GPG_ERR_EOF); err = gpgme_op_keylist_end(ctx); + assert(check_gpg_error_2_err_code(err, GPG_ERR_EOF) == GPG_ERR_NO_ERROR); + return keys_list; } diff --git a/src/gpg/function/GpgKeyGetter.h b/src/gpg/function/GpgKeyGetter.h index 3af51815..fcd518d3 100644 --- a/src/gpg/function/GpgKeyGetter.h +++ b/src/gpg/function/GpgKeyGetter.h @@ -33,7 +33,9 @@ namespace GpgFrontend { class GpgKeyGetter : public SingletonFunctionObject<GpgKeyGetter> { public: - GpgKeyGetter() = default; + explicit GpgKeyGetter( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgKeyGetter>(channel) {} GpgKey GetKey(const std::string& fpr); @@ -49,7 +51,7 @@ class GpgKeyGetter : public SingletonFunctionObject<GpgKeyGetter> { private: GpgContext& ctx = - GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/GpgKeyImportExportor.cpp b/src/gpg/function/GpgKeyImportExporter.cpp index 89d3b002..dd027eab 100644 --- a/src/gpg/function/GpgKeyImportExportor.cpp +++ b/src/gpg/function/GpgKeyImportExporter.cpp @@ -22,16 +22,17 @@ * */ -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "GpgConstants.h" +#include "gpg/function/GpgKeyGetter.h" /** * Import key pair * @param inBuffer input byte array * @return Import information */ -GpgFrontend::GpgImportInformation GpgFrontend::GpgKeyImportExportor::ImportKey( +GpgFrontend::GpgImportInformation GpgFrontend::GpgKeyImportExporter::ImportKey( StdBypeArrayPtr in_buffer) { if (in_buffer->empty()) return {}; @@ -60,25 +61,37 @@ GpgFrontend::GpgImportInformation GpgFrontend::GpgKeyImportExportor::ImportKey( * @param out_buffer output byte array * @return if success */ -bool GpgFrontend::GpgKeyImportExportor::ExportKeys( - KeyIdArgsListPtr& uid_list, ByteArrayPtr& out_buffer) const { +bool GpgFrontend::GpgKeyImportExporter::ExportKeys(KeyIdArgsListPtr& uid_list, + ByteArrayPtr& out_buffer, + bool secret) const { if (uid_list->empty()) return false; - // Alleviate another crash problem caused by an unknown array out-of-bounds - // access - auto all_success = true; - for (size_t i = 0; i < uid_list->size(); i++) { - GpgData data_out; - auto err = gpgme_op_export(ctx, (*uid_list)[i].c_str(), 0, data_out); - if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) all_success = false; - DLOG(INFO) << "exportKeys read_bytes" - << gpgme_data_seek(data_out, 0, SEEK_END); - - auto temp_out_buffer = data_out.Read2Buffer(); - std::swap(out_buffer, temp_out_buffer); + int _mode = 0; + if (secret) _mode |= GPGME_EXPORT_MODE_SECRET; + + auto keys = GpgKeyGetter::GetInstance().GetKeys(uid_list); + auto keys_array = new gpgme_key_t[keys->size() + 1]; + + int index = 0; + for (const auto& key : *keys) { + keys_array[index++] = gpgme_key_t(key); } + keys_array[index] = nullptr; + + GpgData data_out; + auto err = gpgme_op_export_keys(ctx, keys_array, _mode, data_out); + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) return false; + + delete[] keys_array; + + DLOG(INFO) << "exportKeys read_bytes" + << gpgme_data_seek(data_out, 0, SEEK_END); + + auto temp_out_buffer = data_out.Read2Buffer(); + + swap(temp_out_buffer, out_buffer); - return all_success; + return true; } /** @@ -87,11 +100,12 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKeys( * @param outBuffer output byte array * @return if success */ -bool GpgFrontend::GpgKeyImportExportor::ExportKeys( - const KeyArgsList& keys, ByteArrayPtr& out_buffer) const { +bool GpgFrontend::GpgKeyImportExporter::ExportKeys(const KeyArgsList& keys, + ByteArrayPtr& out_buffer, + bool secret) const { KeyIdArgsListPtr key_ids = std::make_unique<std::vector<std::string>>(); for (const auto& key : keys) key_ids->push_back(key.id()); - return ExportKeys(key_ids, out_buffer); + return ExportKeys(key_ids, out_buffer, secret); } /** @@ -100,7 +114,7 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKeys( * @param outBuffer output byte array * @return if successful */ -bool GpgFrontend::GpgKeyImportExportor::ExportSecretKey( +bool GpgFrontend::GpgKeyImportExporter::ExportSecretKey( const GpgKey& key, ByteArrayPtr& out_buffer) const { DLOG(INFO) << "Export Secret Key" << key.id().c_str(); @@ -117,7 +131,7 @@ bool GpgFrontend::GpgKeyImportExportor::ExportSecretKey( return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; } -bool GpgFrontend::GpgKeyImportExportor::ExportKey( +bool GpgFrontend::GpgKeyImportExporter::ExportKey( const GpgFrontend::GpgKey& key, GpgFrontend::ByteArrayPtr& out_buffer) const { GpgData data_out; @@ -131,7 +145,7 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKey( return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; } -bool GpgFrontend::GpgKeyImportExportor::ExportKeyOpenSSH( +bool GpgFrontend::GpgKeyImportExporter::ExportKeyOpenSSH( const GpgFrontend::GpgKey& key, GpgFrontend::ByteArrayPtr& out_buffer) const { GpgData data_out; @@ -145,7 +159,7 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKeyOpenSSH( return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; } -bool GpgFrontend::GpgKeyImportExportor::ExportSecretKeyShortest( +bool GpgFrontend::GpgKeyImportExporter::ExportSecretKeyShortest( const GpgFrontend::GpgKey& key, GpgFrontend::ByteArrayPtr& out_buffer) const { GpgData data_out; diff --git a/src/gpg/function/GpgKeyImportExportor.h b/src/gpg/function/GpgKeyImportExporter.h index ad43d539..64b3b8a9 100644 --- a/src/gpg/function/GpgKeyImportExportor.h +++ b/src/gpg/function/GpgKeyImportExporter.h @@ -79,14 +79,20 @@ class GpgImportInformation { GpgImportedKeyList importedKeys; }; -class GpgKeyImportExportor - : public SingletonFunctionObject<GpgKeyImportExportor> { +class GpgKeyImportExporter + : public SingletonFunctionObject<GpgKeyImportExporter> { public: + explicit GpgKeyImportExporter( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgKeyImportExporter>(channel) {} + GpgImportInformation ImportKey(StdBypeArrayPtr inBuffer); - bool ExportKeys(KeyIdArgsListPtr& uid_list, ByteArrayPtr& out_buffer) const; + bool ExportKeys(KeyIdArgsListPtr& uid_list, ByteArrayPtr& out_buffer, + bool secret = false) const; - bool ExportKeys(const KeyArgsList& keys, ByteArrayPtr& outBuffer) const; + bool ExportKeys(const KeyArgsList& keys, ByteArrayPtr& outBuffer, + bool secret = false) const; bool ExportKey(const GpgKey& key, ByteArrayPtr& out_buffer) const; @@ -99,7 +105,7 @@ class GpgKeyImportExportor private: GpgContext& ctx = - GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/GpgKeyManager.h b/src/gpg/function/GpgKeyManager.h index 01254962..b2444e8d 100644 --- a/src/gpg/function/GpgKeyManager.h +++ b/src/gpg/function/GpgKeyManager.h @@ -33,6 +33,10 @@ namespace GpgFrontend { class GpgKeyManager : public SingletonFunctionObject<GpgKeyManager> { public: + explicit GpgKeyManager( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgKeyManager>(channel) {} + /** * Sign a key pair(actually a certain uid) * @param target target key pair @@ -50,7 +54,8 @@ class GpgKeyManager : public SingletonFunctionObject<GpgKeyManager> { std::unique_ptr<boost::posix_time::ptime>& expires); private: - GpgContext& ctx = GpgContext::GetInstance(); + GpgContext& ctx = + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/GpgKeyOpera.cpp b/src/gpg/function/GpgKeyOpera.cpp index 4bad303d..e9fcbc1d 100644 --- a/src/gpg/function/GpgKeyOpera.cpp +++ b/src/gpg/function/GpgKeyOpera.cpp @@ -26,6 +26,7 @@ #include <boost/asio.hpp> #include <boost/date_time/posix_time/conversion.hpp> +#include <boost/format.hpp> #include <boost/process/async_pipe.hpp> #include <memory> #include <string> @@ -46,18 +47,21 @@ void GpgFrontend::GpgKeyOpera::DeleteKeys( for (const auto& tmp : *key_ids) { auto key = GpgKeyGetter::GetInstance().GetKey(tmp); if (key.good()) { - LOG(INFO) << "GpgKeyOpera DeleteKeys Get Key Good"; - err = check_gpg_error(gpgme_op_delete(ctx, gpgme_key_t(key), 1)); + err = check_gpg_error( + gpgme_op_delete_ext(ctx, gpgme_key_t(key), + GPGME_DELETE_ALLOW_SECRET | GPGME_DELETE_FORCE)); assert(gpg_err_code(err) == GPG_ERR_NO_ERROR); - } else - LOG(WARNING) << "GpgKeyOpera DeleteKeys Get Key Bad"; + } else { + LOG(WARNING) << "GpgKeyOpera DeleteKeys get key failed" << tmp; + } } } /** - * Set the expire date and time of a key pair(actually the master key) or subkey + * Set the expire date and time of a key pair(actually the primary key) or + * subkey * @param key target key pair - * @param subkey null if master key + * @param subkey null if primary key * @param expires date and time * @return if successful */ @@ -146,13 +150,12 @@ void GpgFrontend::GpgKeyOpera::GenerateRevokeCert( * @return error information */ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( - const std::unique_ptr<GenKeyInfo>& params) { + const std::unique_ptr<GenKeyInfo>& params, GpgGenKeyResult& result) { auto userid_utf8 = params->getUserid(); const char* userid = userid_utf8.c_str(); auto algo_utf8 = params->getAlgo() + params->getKeySizeStr(); - LOG(INFO) << "GpgFrontend::GpgKeyOpera::GenerateKey Params" - << params->getAlgo() << params->getKeySizeStr(); + LOG(INFO) << "params" << params->getAlgo() << params->getKeySizeStr(); const char* algo = algo_utf8.c_str(); unsigned long expires = 0; @@ -163,19 +166,57 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( system_clock::to_time_t(system_clock::now()); } - unsigned int flags = 0; + GpgError err; - if (!params->isSubKey()) flags |= GPGME_CREATE_CERT; - if (params->isAllowEncryption()) flags |= GPGME_CREATE_ENCR; - if (params->isAllowSigning()) flags |= GPGME_CREATE_SIGN; - if (params->isAllowAuthentication()) flags |= GPGME_CREATE_AUTH; - if (params->isNonExpired()) flags |= GPGME_CREATE_NOEXPIRE; - if (params->isNoPassPhrase()) flags |= GPGME_CREATE_NOPASSWD; + if (ctx.GetInfo().GnupgVersion >= "2.1.0") { + unsigned int flags = 0; + + if (!params->isSubKey()) flags |= GPGME_CREATE_CERT; + if (params->isAllowEncryption()) flags |= GPGME_CREATE_ENCR; + if (params->isAllowSigning()) flags |= GPGME_CREATE_SIGN; + if (params->isAllowAuthentication()) flags |= GPGME_CREATE_AUTH; + if (params->isNonExpired()) flags |= GPGME_CREATE_NOEXPIRE; + if (params->isNoPassPhrase()) flags |= GPGME_CREATE_NOPASSWD; + + LOG(INFO) << "args: " << userid << algo << expires << flags; + + err = gpgme_op_createkey(ctx, userid, algo, 0, expires, nullptr, flags); - LOG(INFO) << "GpgFrontend::GpgKeyOpera::GenerateKey Args: " << userid << algo - << expires << flags; + } else { + std::stringstream ss; + auto param_format = + boost::format{ + "<GnupgKeyParms format=\"internal\">\n" + "Key-Type: %1%\n" + "Key-Usage: sign\n" + "Key-Length: %2%\n" + "Name-Real: %3%\n" + "Name-Comment: %4%\n" + "Name-Email: %5%\n"} % + params->getAlgo() % params->getKeySize() % params->getName() % + params->getComment() % params->getEmail(); + ss << param_format; + + if (!params->isNonExpired()) { + auto date = params->getExpired().date(); + ss << boost::format{"Expire-Date: %1%\n"} % to_iso_string(date); + } else + ss << boost::format{"Expire-Date: 0\n"}; + if (!params->isNoPassPhrase()) + ss << boost::format{"Passphrase: %1%\n"} % params->getPassPhrase(); + + ss << "</GnupgKeyParms>"; + + DLOG(INFO) << "params" << std::endl << ss.str(); + + err = gpgme_op_genkey(ctx, ss.str().c_str(), nullptr, nullptr); + } + + if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) { + auto temp_result = _new_result(gpgme_op_genkey_result(ctx)); + std::swap(temp_result, result); + } - auto err = gpgme_op_createkey(ctx, userid, algo, 0, expires, nullptr, flags); return check_gpg_error(err); } @@ -234,3 +275,9 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyTOFUPolicy( auto err = gpgme_op_tofu_policy(ctx, gpgme_key_t(key), tofu_policy); return check_gpg_error(err); } + +void GpgFrontend::GpgKeyOpera::DeleteKey(const GpgFrontend::KeyId& key_id) { + auto keys = std::make_unique<KeyIdArgsList>(); + keys->push_back(key_id); + DeleteKeys(std::move(keys)); +} diff --git a/src/gpg/function/GpgKeyOpera.h b/src/gpg/function/GpgKeyOpera.h index 7decfd79..73b8bdb5 100644 --- a/src/gpg/function/GpgKeyOpera.h +++ b/src/gpg/function/GpgKeyOpera.h @@ -33,8 +33,14 @@ namespace GpgFrontend { class GenKeyInfo; class GpgKeyOpera : public SingletonFunctionObject<GpgKeyOpera> { public: + explicit GpgKeyOpera( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<GpgKeyOpera>(channel) {} + void DeleteKeys(KeyIdArgsListPtr key_ids); + void DeleteKey(const KeyId& key_id); + GpgError SetExpire(const GpgKey& key, const SubkeyId& subkey_fpr, std::unique_ptr<boost::posix_time::ptime>& expires); @@ -46,13 +52,15 @@ class GpgKeyOpera : public SingletonFunctionObject<GpgKeyOpera> { GpgFrontend::GpgError ModifyTOFUPolicy(const GpgKey& key, gpgme_tofu_policy_t tofu_policy); - GpgFrontend::GpgError GenerateKey(const std::unique_ptr<GenKeyInfo>& params); + GpgFrontend::GpgError GenerateKey(const std::unique_ptr<GenKeyInfo>& params, + GpgGenKeyResult& result); GpgFrontend::GpgError GenerateSubkey( const GpgKey& key, const std::unique_ptr<GenKeyInfo>& params); private: - GpgContext& ctx = GpgContext::GetInstance(); + GpgContext& ctx = + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/function/UidOperator.h b/src/gpg/function/UidOperator.h index 7d5df254..d1a92b38 100644 --- a/src/gpg/function/UidOperator.h +++ b/src/gpg/function/UidOperator.h @@ -32,6 +32,10 @@ namespace GpgFrontend { class UidOperator : public SingletonFunctionObject<UidOperator> { public: + explicit UidOperator( + int channel = SingletonFunctionObject::GetDefaultChannel()) + : SingletonFunctionObject<UidOperator>(channel) {} + /** * create a new uid in certain key pair * @param key target key pair @@ -68,7 +72,8 @@ class UidOperator : public SingletonFunctionObject<UidOperator> { bool setPrimaryUID(const GpgKey& key, const std::string& uid); private: - GpgContext& ctx = GpgContext::GetInstance(); + GpgContext& ctx = + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); }; } // namespace GpgFrontend diff --git a/src/gpg/model/GpgKey.h b/src/gpg/model/GpgKey.h index fb7d5735..0ce83372 100644 --- a/src/gpg/model/GpgKey.h +++ b/src/gpg/model/GpgKey.h @@ -108,6 +108,13 @@ class GpgKey { [[nodiscard]] bool CanAuthActual() const; + [[nodiscard]] bool HasCardKey() const { + auto subkeys = subKeys(); + return std::any_of( + subkeys->begin(), subkeys->end(), + [](const GpgSubKey& subkey) -> bool { return subkey.is_cardkey(); }); + } + [[nodiscard]] bool is_private_key() const { return _key_ref->secret; } [[nodiscard]] bool expired() const { return _key_ref->expired; } diff --git a/src/gpg/result_analyse/DecryptResultAnalyse.cpp b/src/gpg/result_analyse/DecryptResultAnalyse.cpp index 9c3e7dd0..f2e9f947 100644 --- a/src/gpg/result_analyse/DecryptResultAnalyse.cpp +++ b/src/gpg/result_analyse/DecryptResultAnalyse.cpp @@ -26,12 +26,12 @@ #include "gpg/function/GpgKeyGetter.h" -GpgFrontend::DecryptResultAnalyse::DecryptResultAnalyse(GpgError error, - GpgDecrResult result) - : error(error), result(std::move(result)) {} +GpgFrontend::DecryptResultAnalyse::DecryptResultAnalyse(GpgError m_error, + GpgDecrResult m_result) + : error(m_error), result(std::move(m_result)) {} void GpgFrontend::DecryptResultAnalyse::do_analyse() { - stream << "[#]" << _("Decrypt Operation"); + stream << "[#] " << _("Decrypt Operation"); if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) { stream << "[" << _("Success") << "]" << std::endl; @@ -55,11 +55,11 @@ void GpgFrontend::DecryptResultAnalyse::do_analyse() { stream << _("MIME") << ": " << _("true") << std::endl; } - auto reci = result->recipients; - if (reci != nullptr) stream << _("Recipient(s)") << ": " << std::endl; - while (reci != nullptr) { - print_reci(stream, reci); - reci = reci->next; + auto recipient = result->recipients; + if (recipient != nullptr) stream << _("Recipient(s)") << ": " << std::endl; + while (recipient != nullptr) { + print_recipient(stream, recipient); + recipient = recipient->next; } stream << "<------------" << std::endl; } @@ -67,12 +67,13 @@ void GpgFrontend::DecryptResultAnalyse::do_analyse() { stream << std::endl; } -bool GpgFrontend::DecryptResultAnalyse::print_reci(std::stringstream &stream, - gpgme_recipient_t reci) { - bool keyFound = true; - stream << " {>} " << _("Recipient") << ": "; +void GpgFrontend::DecryptResultAnalyse::print_recipient( + std::stringstream &stream, gpgme_recipient_t recipient) { + // check + if (recipient->keyid == nullptr) return; - auto key = GpgFrontend::GpgKeyGetter::GetInstance().GetKey(reci->keyid); + stream << " {>} " << _("Recipient") << ": "; + auto key = GpgFrontend::GpgKeyGetter::GetInstance().GetKey(recipient->keyid); if (key.good()) { stream << key.name().c_str(); if (!key.email().empty()) { @@ -81,14 +82,11 @@ bool GpgFrontend::DecryptResultAnalyse::print_reci(std::stringstream &stream, } else { stream << "<" << _("Unknown") << ">"; setStatus(0); - keyFound = false; } stream << std::endl; - stream << " " << _("Keu ID") << ": " << reci->keyid << std::endl; - stream << " " << _("Public Algo") << ": " - << gpgme_pubkey_algo_name(reci->pubkey_algo) << std::endl; - - return keyFound; + stream << " " << _("Key ID") << ": " << recipient->keyid << std::endl; + stream << " " << _("Public Key Algo") << ": " + << gpgme_pubkey_algo_name(recipient->pubkey_algo) << std::endl; } diff --git a/src/gpg/result_analyse/DecryptResultAnalyse.h b/src/gpg/result_analyse/DecryptResultAnalyse.h index 0864c23b..729d8853 100644 --- a/src/gpg/result_analyse/DecryptResultAnalyse.h +++ b/src/gpg/result_analyse/DecryptResultAnalyse.h @@ -32,13 +32,13 @@ namespace GpgFrontend { class DecryptResultAnalyse : public ResultAnalyse { public: - explicit DecryptResultAnalyse(GpgError error, GpgDecrResult result); + explicit DecryptResultAnalyse(GpgError m_error, GpgDecrResult m_result); protected: void do_analyse() final; private: - bool print_reci(std::stringstream &stream, gpgme_recipient_t reci); + void print_recipient(std::stringstream &stream, gpgme_recipient_t recipient); GpgError error; GpgDecrResult result; diff --git a/src/gpg/result_analyse/VerifyResultAnalyse.cpp b/src/gpg/result_analyse/VerifyResultAnalyse.cpp index 8f565b8e..99cbd528 100644 --- a/src/gpg/result_analyse/VerifyResultAnalyse.cpp +++ b/src/gpg/result_analyse/VerifyResultAnalyse.cpp @@ -35,7 +35,7 @@ GpgFrontend::VerifyResultAnalyse::VerifyResultAnalyse(GpgError error, : error(error), result(std::move(result)) {} void GpgFrontend::VerifyResultAnalyse::do_analyse() { - LOG(INFO) << _("Verify Result Analyse Started"); + LOG(INFO) << _("started"); stream << "[#] " << _("Verify Operation") << " "; @@ -46,16 +46,10 @@ void GpgFrontend::VerifyResultAnalyse::do_analyse() { setStatus(-1); } - if (result != nullptr && result->signatures) { + if (result != nullptr && result->signatures != nullptr) { stream << "------------>" << std::endl; auto sign = result->signatures; - if (sign == nullptr) { - stream << "[>] " << _("Not Signature Found") << std::endl; - setStatus(0); - return; - } - stream << "[>] " << _("Signed On") << "(" << _("UTC") << ")" << " " << boost::posix_time::to_iso_extended_string( @@ -163,6 +157,12 @@ void GpgFrontend::VerifyResultAnalyse::do_analyse() { sign = sign->next; } stream << "<------------" << std::endl; + } else { + stream << "[>] " + << _("Could not find information that can be used for verification.") + << std::endl; + setStatus(0); + return; } } diff --git a/src/main.cpp b/src/main.cpp index 10d54958..fa4195ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,8 +28,9 @@ #include "GpgFrontendBuildInfo.h" #include "gpg/GpgContext.h" -#include "gpg/function/GpgKeyGetter.h" +#include "gpg/GpgFunctionObject.h" #include "ui/MainWindow.h" +#include "ui/function/CtxCheckThread.h" #include "ui/settings/GlobalSettingStation.h" // Easy Logging Cpp @@ -78,7 +79,9 @@ int main(int argc, char* argv[]) { #if !defined(RELEASE) && defined(WINDOWS) // css - boost::filesystem::path css_path = GpgFrontend::UI::GlobalSettingStation::GetInstance().GetResourceDir() / "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()); @@ -86,21 +89,20 @@ int main(int argc, char* argv[]) { file.close(); #endif - auto* init_ctx_thread = QThread::create([]() { - // Create & Check Gnupg Context Status - if (!GpgFrontend::GpgContext::GetInstance().good()) { - QMessageBox::critical( - nullptr, _("ENV Loading Failed"), - _("Gnupg(gpg) is not installed correctly, please follow the " - "ReadME " - "instructions in Github to install Gnupg and then open " - "GpgFrontend.")); - QCoreApplication::quit(); - exit(0); - } - // Try fetching key - GpgFrontend::GpgKeyGetter::GetInstance().FetchKey(); - }); +#ifdef GPG_STANDALONE_MODE + LOG(INFO) << "GPG_STANDALONE_MODE Enabled"; + auto gpg_path = GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetStandaloneGpgBinDir(); + auto db_path = GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetStandaloneDatabaseDir(); + GpgFrontend::GpgContext::CreateInstance( + GpgFrontend::SingletonFunctionObject< + GpgFrontend::GpgContext>::GetDefaultChannel(), + std::make_unique<GpgFrontend::GpgContext>(true, db_path.string(), true, + gpg_path.string())); +#endif + + auto* init_ctx_thread = new GpgFrontend::UI::CtxCheckThread(); QApplication::connect(init_ctx_thread, &QThread::finished, init_ctx_thread, &QThread::deleteLater); @@ -118,9 +120,13 @@ int main(int argc, char* argv[]) { waiting_dialog_label->setWordWrap(true); waiting_dialog->setLabel(waiting_dialog_label); waiting_dialog->resize(420, 120); + QApplication::connect(init_ctx_thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); QApplication::connect(waiting_dialog, &QProgressDialog::canceled, [=]() { LOG(INFO) << "cancel clicked"; - init_ctx_thread->terminate(); + if (init_ctx_thread->isRunning()) init_ctx_thread->terminate(); QCoreApplication::quit(); exit(0); }); @@ -130,11 +136,10 @@ int main(int argc, char* argv[]) { waiting_dialog->setFocus(); init_ctx_thread->start(); - while (init_ctx_thread->isRunning()) { - QApplication::processEvents(); - } - waiting_dialog->finished(0); - waiting_dialog->deleteLater(); + QEventLoop loop; + QApplication::connect(init_ctx_thread, &QThread::finished, &loop, + &QEventLoop::quit); + loop.exec(); /** * internationalisation. loop to restart main window @@ -149,7 +154,6 @@ int main(int argc, char* argv[]) { int r = setjmp(recover_env); #endif if (!r) { - try { // i18n init_locale(); @@ -169,10 +173,12 @@ int main(int argc, char* argv[]) { "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.")); + return_from_event_loop_code = RESTART_CODE; continue; } } else { + QApplication::exit(RESTART_CODE); QMessageBox::information( nullptr, _("A serious error has occurred"), _("Oh no! GpgFrontend caught a serious error in the software, so it " diff --git a/src/smtp/CMakeLists.txt b/src/smtp/CMakeLists.txt index 8e341f98..afae322e 100644 --- a/src/smtp/CMakeLists.txt +++ b/src/smtp/CMakeLists.txt @@ -4,3 +4,11 @@ add_library(smtp STATIC ${SMTP_MIME_SOURCE}) target_link_libraries(smtp Qt5::Network Qt5::Core) + +if(XCODE_BUILD) +set_target_properties(smtp + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +endif()
\ No newline at end of file diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 529921e0..3e44a0d6 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -1,4 +1,5 @@ aux_source_directory(. UI_SOURCE) +aux_source_directory(./aes UI_SOURCE) aux_source_directory(./keypair_details UI_SOURCE) aux_source_directory(./widgets UI_SOURCE) aux_source_directory(./keygen UI_SOURCE) @@ -7,6 +8,7 @@ aux_source_directory(./help UI_SOURCE) aux_source_directory(./settings UI_SOURCE) aux_source_directory(./function UI_SOURCE) aux_source_directory(./details UI_SOURCE) +aux_source_directory(./data_struct UI_SOURCE) if (SMTP_SUPPORT) aux_source_directory(./smtp UI_SOURCE) @@ -19,4 +21,11 @@ target_link_libraries(${GPGFRONTEND_UI_LIB_NAME} target_include_directories(gpgfrontend-ui PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/${GPGFRONTEND_UI_LIB_NAME}_autogen/include) +if (XCODE_BUILD) + set_target_properties(gpgfrontend-ui + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +endif () 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 deleted file mode 100755 index 6930c3c9..00000000 --- a/src/ui/FileEncryptionDialog.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/** - * 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 "ui/FileEncryptionDialog.h" - -#include "gpg/function/BasicOperator.h" -#include "gpg/function/GpgKeyGetter.h" - -namespace GpgFrontend::UI { -FileEncryptionDialog::FileEncryptionDialog(KeyIdArgsListPtr keyList, - DialogAction action, QWidget* parent) - : QDialog(parent), mAction(action) { - if (mAction == Decrypt) { - setWindowTitle(_("Decrypt File")); - } else if (mAction == Encrypt) { - setWindowTitle(_("Encrypt File")); - } else if (mAction == Sign) { - setWindowTitle(_("Sign File")); - } else if (mAction == Verify) { - setWindowTitle(_("Verify File")); - } - - setModal(true); - - auto* buttonBox = - new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotExecuteAction())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - - auto* groupBox1 = new QGroupBox(_("Input Parameters")); - - /* Setup input & Outputfileselection*/ - inputFileEdit = new QLineEdit(); - auto* fb1 = new QPushButton("Select"); - connect(fb1, SIGNAL(clicked()), this, SLOT(slotSelectInputFile())); - auto* fl1 = new QLabel(_("Target File")); - fl1->setBuddy(inputFileEdit); - - outputFileEdit = new QLineEdit(); - auto* fb2 = new QPushButton("Select"); - connect(fb2, SIGNAL(clicked()), this, SLOT(slotSelectOutputFile())); - auto* fl2 = new QLabel(_("Output File")); - fl2->setBuddy(outputFileEdit); - - auto* gLayout = new QGridLayout(); - gLayout->addWidget(fl1, 0, 0); - gLayout->addWidget(inputFileEdit, 0, 1); - gLayout->addWidget(fb1, 0, 2); - signFileEdit = new QLineEdit(); - // verify does not need outfile, but signature file - if (mAction != Verify) { - gLayout->addWidget(fl2, 1, 0); - gLayout->addWidget(outputFileEdit, 1, 1); - gLayout->addWidget(fb2, 1, 2); - } else { - auto* sfb1 = new QPushButton("Select"); - connect(sfb1, SIGNAL(clicked()), this, SLOT(slotSelectSignFile())); - auto* sfl1 = new QLabel(_("Signature File(.sig) Path")); - sfl1->setBuddy(signFileEdit); - - gLayout->addWidget(sfl1, 1, 0); - gLayout->addWidget(signFileEdit, 1, 1); - gLayout->addWidget(sfb1, 1, 2); - } - groupBox1->setLayout(gLayout); - - /*Setup KeyList*/ - mKeyList = new KeyList(false, this); - if (mAction == Verify) { - 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->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->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); - - mKeyList->slotRefresh(); - mKeyList->setChecked(keyList); - - statusLabel = new QLabel(); - statusLabel->setStyleSheet("QLabel {color: red;}"); - - auto* vbox2 = new QVBoxLayout(); - vbox2->addWidget(groupBox1); - vbox2->addWidget(mKeyList); - vbox2->addWidget(statusLabel); - vbox2->addWidget(buttonBox); - vbox2->addStretch(0); - setLayout(vbox2); - - this->setMinimumWidth(480); - this->show(); -} - -void FileEncryptionDialog::slotSelectInputFile() { - QString path; - if (inputFileEdit->text().size() > 0) { - path = QFileInfo(inputFileEdit->text()).absolutePath(); - } - - // QString infileName = QFileDialog::getOpenFileName(this, _("Open File"), - // path, _("Files") + _("All Files (*)")); - QString infileName = QFileDialog::getOpenFileName(this, _("Open File"), path); - inputFileEdit->setText(infileName); - - // try to find a matching output-filename, if not yet done - if (!infileName.isEmpty() && outputFileEdit->text().size() == 0 && - signFileEdit->text().size() == 0) { - if (mAction == Encrypt) { - outputFileEdit->setText(infileName + ".asc"); - } else if (mAction == Sign) { - outputFileEdit->setText(infileName + ".sig"); - } else if (mAction == Verify) { - signFileEdit->setText(infileName + ".sig"); - } else { - if (infileName.endsWith(".asc", Qt::CaseInsensitive)) { - QString ofn = infileName; - ofn.chop(4); - outputFileEdit->setText(ofn); - } else { - outputFileEdit->setText(infileName + ".out"); - } - } - } -} - -void FileEncryptionDialog::slotSelectOutputFile() { - QString path; - if (outputFileEdit->text().size() > 0) { - path = QFileInfo(outputFileEdit->text()).absolutePath(); - } - - QString outfileName = - QFileDialog::getSaveFileName(this, _("Save File"), path, nullptr, nullptr, - QFileDialog::DontConfirmOverwrite); - outputFileEdit->setText(outfileName); -} - -void FileEncryptionDialog::slotSelectSignFile() { - QString path; - if (signFileEdit->text().size() > 0) { - path = QFileInfo(signFileEdit->text()).absolutePath(); - } - - QString signfileName = - QFileDialog::getSaveFileName(this, _("Open File"), path, nullptr, nullptr, - QFileDialog::DontConfirmOverwrite); - signFileEdit->setText(signfileName); - - if (inputFileEdit->text().size() == 0 && - signfileName.endsWith(".sig", Qt::CaseInsensitive)) { - QString sfn = signfileName; - sfn.chop(4); - inputFileEdit->setText(sfn); - } -} - -void FileEncryptionDialog::slotExecuteAction() { - QFile infile; - infile.setFileName(inputFileEdit->text()); - if (!infile.open(QIODevice::ReadOnly)) { - statusLabel->setText(_("Couldn't open file")); - inputFileEdit->setStyleSheet("QLineEdit { background: yellow }"); - return; - } - auto in_data = read_all_data_in_file(inputFileEdit->text().toStdString()); - auto out_data = std::make_unique<ByteArray>(); - - auto key_ids = mKeyList->getChecked(); - auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); - - if (mAction == Encrypt) { - qDebug() << "Action Encrypt"; - GpgEncrResult result = nullptr; - gpgme_error_t err = BasicOperator::GetInstance().Encrypt( - std::move(keys), in_data, out_data, result); - if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { - qDebug() << "Error" << gpgme_strerror(err); - - QMessageBox::warning(this, _("Error"), - _("Error Occurred During Encryption")); - return; - } - } - - else if (mAction == Decrypt) { - qDebug() << "Action Decrypt"; - GpgDecrResult result = nullptr; - gpgme_error_t err = - BasicOperator::GetInstance().Decrypt(in_data, out_data, result); - if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { - qDebug() << "Error" << gpgme_strerror(err); - QMessageBox::warning(this, _("Error"), - _("Error Occurred During Decryption")); - return; - } - } - - else if (mAction == Sign) { - qDebug() << "Action Sign"; - GpgSignResult result = nullptr; - gpgme_error_t err = BasicOperator::GetInstance().Sign( - std::move(keys), in_data, out_data, GPGME_SIG_MODE_DETACH, result); - if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { - qDebug() << "Error" << gpgme_strerror(err); - QMessageBox::warning(this, _("Error"), - _("Error Occurred During Signature")); - return; - } - } - - if (mAction == Verify) { - auto sign_data = std::make_unique<ByteArray>( - read_all_data_in_file(signFileEdit->text().toStdString())); - GpgVerifyResult result = nullptr; - auto error = - BasicOperator::GetInstance().Verify(in_data, sign_data, result); - new VerifyDetailsDialog(this, error, std::move(result)); - return; - } - - write_buffer_to_file(outputFileEdit->text().toStdString(), *out_data); - - accept(); -} - -void FileEncryptionDialog::slotShowKeyList() { mKeyList->show(); } - -void FileEncryptionDialog::slotHideKeyList() { mKeyList->hide(); } - -} // namespace GpgFrontend::UI diff --git a/src/ui/FileEncryptionDialog.h b/src/ui/FileEncryptionDialog.h deleted file mode 100755 index b09bb8db..00000000 --- a/src/ui/FileEncryptionDialog.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * 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 __FILEENCRYPTIONDIALOG_H__ -#define __FILEENCRYPTIONDIALOG_H__ - -#include "gpg/GpgContext.h" -#include "ui/GpgFrontendUI.h" -#include "ui/details/VerifyDetailsDialog.h" -#include "ui/widgets/KeyList.h" - -namespace GpgFrontend::UI { - -/** - * @brief - * - * @class FileEncryptionDialog fileencryptiondialog.h "fileencryptiondialog.h" - */ -class FileEncryptionDialog : public QDialog { - Q_OBJECT - - public: - enum DialogAction { Encrypt, Decrypt, Sign, Verify }; - - /** - * @brief - * - * @fn FileEncryptionDialog - * @param ctx - * @param keyList - * @param parent - */ - FileEncryptionDialog(KeyIdArgsListPtr keyList, DialogAction action, - QWidget* parent = nullptr); - - public slots: - - /** - * @details - * - * @fn selectInputFile - */ - void slotSelectInputFile(); - - /** - * @brief - * - * @fn selectOutputFile - */ - void slotSelectOutputFile(); - - /** - * @brief - * - * @fn selectSignFile - */ - void slotSelectSignFile(); - - /** - * @brief - * - * @fn executeAction - */ - void slotExecuteAction(); - - /** - * @brief - * - * @fn hideKeyList - */ - void slotHideKeyList(); - - /** - * @brief - * - * @fn showKeyList - */ - void slotShowKeyList(); - - private: - QLineEdit* outputFileEdit; - QLineEdit* inputFileEdit; - QLineEdit* signFileEdit; - DialogAction mAction; - QLabel* statusLabel; - - protected: - KeyList* mKeyList; -}; - -} // namespace GpgFrontend::UI - -#endif // __FILEENCRYPTIONDIALOG_H__ diff --git a/src/ui/GpgFrontendUI.h b/src/ui/GpgFrontendUI.h index 01f82822..90e15324 100644 --- a/src/ui/GpgFrontendUI.h +++ b/src/ui/GpgFrontendUI.h @@ -25,13 +25,15 @@ #ifndef GPGFRONTEND_GPGFRONTENDUI_H #define GPGFRONTEND_GPGFRONTENDUI_H -#include "GpgFrontend.h" - #include <QtCore> #include <QtNetwork> #include <QtPrintSupport> #include <QtWidgets> +#include "GpgFrontend.h" +#include "gpg/GpgConstants.h" +#include "gpg/GpgModel.h" + #undef LIBCONFIGXX_STATIC #define LIBCONFIGXX_STATIC #include <libconfig.h++> diff --git a/src/ui/KeyImportDetailDialog.h b/src/ui/KeyImportDetailDialog.h index fe63baaa..a75d466d 100644 --- a/src/ui/KeyImportDetailDialog.h +++ b/src/ui/KeyImportDetailDialog.h @@ -25,7 +25,7 @@ #ifndef __KEYIMPORTDETAILSDIALOG_H__ #define __KEYIMPORTDETAILSDIALOG_H__ -#include <gpg/function/GpgKeyImportExportor.h> +#include <gpg/function/GpgKeyImportExporter.h> #include "gpg/GpgContext.h" #include "ui/GpgFrontendUI.h" diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index c03b8e6b..3715b01a 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -27,20 +27,23 @@ #include <utility> #include "gpg/function/GpgKeyGetter.h" -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "gpg/function/GpgKeyOpera.h" #include "ui/SignalStation.h" #include "ui/UserInterfaceUtils.h" +#include "ui/aes/qaesencryption.h" +#include "ui/keygen/SubkeyGenerateDialog.h" #include "ui/settings/GlobalSettingStation.h" +#include "ui/widgets/ExportKeyPackageDialog.h" namespace GpgFrontend::UI { KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { /* the list of Keys available*/ - mKeyList = new KeyList(true, this); + key_list_ = new KeyList(KeyMenuAbility::ALL, this); - mKeyList->addListGroupTab(_("All"), KeyListRow::SECRET_OR_PUBLIC_KEY); + key_list_->addListGroupTab(_("All"), KeyListRow::SECRET_OR_PUBLIC_KEY); - mKeyList->addListGroupTab( + key_list_->addListGroupTab( _("Only Public Key"), KeyListRow::SECRET_OR_PUBLIC_KEY, KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage | KeyListColumn::Validity, @@ -49,7 +52,7 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { !(key.revoked() || key.disabled() || key.expired()); }); - mKeyList->addListGroupTab( + key_list_->addListGroupTab( _("Has Private Key"), KeyListRow::SECRET_OR_PUBLIC_KEY, KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage | KeyListColumn::Validity, @@ -58,8 +61,8 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { !(key.revoked() || key.disabled() || key.expired()); }); - mKeyList->addListGroupTab( - _("No Master Key"), KeyListRow::SECRET_OR_PUBLIC_KEY, + key_list_->addListGroupTab( + _("No Primary Key"), KeyListRow::SECRET_OR_PUBLIC_KEY, KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage | KeyListColumn::Validity, [](const GpgKey& key) -> bool { @@ -67,24 +70,24 @@ KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) { !(key.revoked() || key.disabled() || key.expired()); }); - mKeyList->addListGroupTab( + key_list_->addListGroupTab( _("Revoked"), KeyListRow::SECRET_OR_PUBLIC_KEY, KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage | KeyListColumn::Validity, [](const GpgKey& key) -> bool { return key.revoked(); }); - mKeyList->addListGroupTab( + key_list_->addListGroupTab( _("Expired"), KeyListRow::SECRET_OR_PUBLIC_KEY, KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage | KeyListColumn::Validity, [](const GpgKey& key) -> bool { return key.expired(); }); - setCentralWidget(mKeyList); - mKeyList->setDoubleClickedAction([this](const GpgKey& key, QWidget* parent) { + setCentralWidget(key_list_); + key_list_->setDoubleClickedAction([this](const GpgKey& key, QWidget* parent) { new KeyDetailsDialog(key, parent); }); - mKeyList->slotRefresh(); + key_list_->slotRefresh(); createActions(); createMenus(); @@ -141,18 +144,16 @@ 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); - mKeyList->addMenuAction(showKeyDetailsAct); + key_list_->addMenuAction(deleteSelectedKeysAct); + key_list_->addMenuAction(showKeyDetailsAct); connect(this, SIGNAL(signalKeyStatusUpdated()), SignalStation::GetInstance(), SIGNAL(KeyDatabaseRefresh())); - connect(SignalStation::GetInstance(), - &SignalStation::signalRefreshStatusBar, this, - [=](const QString& message, int timeout) { + connect(SignalStation::GetInstance(), &SignalStation::signalRefreshStatusBar, + this, [=](const QString& message, int timeout) { statusBar()->showMessage(message, timeout); }); } @@ -204,17 +205,24 @@ void KeyMgmt::createActions() { CommonUtils::GetInstance()->slotImportKeyFromKeyServer(this); }); + importKeysFromKeyPackageAct = new QAction(_("Key Package"), this); + importKeysFromKeyPackageAct->setIcon(QIcon(":key_package.png")); + importKeysFromKeyPackageAct->setToolTip( + _("Import Key(s) From a Key Package")); + connect(importKeysFromKeyPackageAct, &QAction::triggered, this, + &KeyMgmt::slotImportKeyPackage); + exportKeyToClipboardAct = new QAction(_("Export To Clipboard"), this); exportKeyToClipboardAct->setIcon(QIcon(":export_key_to_clipboard.png")); exportKeyToClipboardAct->setToolTip(_("Export Selected Key(s) To Clipboard")); connect(exportKeyToClipboardAct, SIGNAL(triggered()), this, SLOT(slotExportKeyToClipboard())); - exportKeyToFileAct = new QAction(_("Export To File"), this); - exportKeyToFileAct->setIcon(QIcon(":export_key_to_file.png")); - exportKeyToFileAct->setToolTip(_("Export Selected Key(s) To File")); + exportKeyToFileAct = new QAction(_("Export To Key Package"), this); + exportKeyToFileAct->setIcon(QIcon(":key_package.png")); + exportKeyToFileAct->setToolTip(_("Export Checked Key(s) To a Key Package")); connect(exportKeyToFileAct, SIGNAL(triggered()), this, - SLOT(slotExportKeyToFile())); + SLOT(slotExportKeyToKeyPackage())); exportKeyAsOpenSSHFormat = new QAction(_("Export As OpenSSH"), this); exportKeyAsOpenSSHFormat->setIcon(QIcon(":ssh-key.png")); @@ -254,6 +262,7 @@ void KeyMgmt::createMenus() { importKeyMenu->addAction(importKeyFromFileAct); importKeyMenu->addAction(importKeyFromClipboardAct); importKeyMenu->addAction(importKeyFromKeyServerAct); + importKeyMenu->addAction(importKeysFromKeyPackageAct); keyMenu->addAction(exportKeyToFileAct); keyMenu->addAction(exportKeyToClipboardAct); @@ -295,11 +304,11 @@ void KeyMgmt::createToolBars() { } void KeyMgmt::slotDeleteSelectedKeys() { - deleteKeysWithWarning(mKeyList->getSelected()); + deleteKeysWithWarning(key_list_->getSelected()); } void KeyMgmt::slotDeleteCheckedKeys() { - deleteKeysWithWarning(mKeyList->getChecked()); + deleteKeysWithWarning(key_list_->getChecked()); } void KeyMgmt::deleteKeysWithWarning(KeyIdArgsListPtr key_ids) { @@ -308,8 +317,6 @@ void KeyMgmt::deleteKeysWithWarning(KeyIdArgsListPtr key_ids) { * more than one selected... compare to seahorse "delete-dialog" */ - LOG(INFO) << "KeyMgmt::deleteKeysWithWarning Called"; - if (key_ids->empty()) return; QString keynames; for (const auto& key_id : *key_ids) { @@ -337,48 +344,44 @@ void KeyMgmt::deleteKeysWithWarning(KeyIdArgsListPtr key_ids) { } void KeyMgmt::slotShowKeyDetails() { - auto keys_selected = mKeyList->getSelected(); + auto keys_selected = key_list_->getSelected(); if (keys_selected->empty()) return; auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front()); if (!key.good()) { - QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); + QMessageBox::critical(this, _("Error"), _("Key Not Found.")); return; } new KeyDetailsDialog(key); } -void KeyMgmt::slotExportKeyToFile() { - ByteArrayPtr key_export_data = nullptr; - auto keys_checked = mKeyList->getChecked(); - if (!GpgKeyImportExportor::GetInstance().ExportKeys(keys_checked, - key_export_data)) { - return; - } - auto key = - GpgKeyGetter::GetInstance().GetKey(mKeyList->getSelected()->front()); - if (!key.good()) { - QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); +void KeyMgmt::slotExportKeyToKeyPackage() { + auto keys_checked = key_list_->getChecked(); + if (keys_checked->empty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please check some keys before doing this operation.")); return; } - QString fileString = QString::fromStdString(key.name() + " " + key.email() + - "(" + key.id() + ")_pub.asc"); - - QString file_name = QFileDialog::getSaveFileName( - this, _("Export Key To File"), fileString, - QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)"); - - write_buffer_to_file(file_name.toStdString(), *key_export_data); - + auto dialog = new ExportKeyPackageDialog(std::move(keys_checked), this); + dialog->exec(); emit signalStatusBarChanged(QString(_("key(s) exported"))); } void KeyMgmt::slotExportKeyToClipboard() { + + auto keys_checked = key_list_->getChecked(); + if (keys_checked->empty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please check some keys before doing this operation.")); + return; + } + ByteArrayPtr key_export_data = nullptr; - auto keys_checked = mKeyList->getChecked(); - if (!GpgKeyImportExportor::GetInstance().ExportKeys(keys_checked, + if (!GpgKeyImportExporter::GetInstance().ExportKeys(keys_checked, key_export_data)) { return; } @@ -396,20 +399,20 @@ void KeyMgmt::closeEvent(QCloseEvent* event) { } void KeyMgmt::slotGenerateSubKey() { - auto keys_selected = mKeyList->getSelected(); + auto keys_selected = key_list_->getSelected(); if (keys_selected->empty()) { QMessageBox::information( - nullptr, _("Invalid Operation"), + this, _("Invalid Operation"), _("Please select one KeyPair before doing this operation.")); return; } const auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front()); if (!key.good()) { - QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); + QMessageBox::critical(this, _("Error"), _("Key Not Found.")); return; } if (!key.is_private_key()) { - QMessageBox::critical(nullptr, _("Invalid Operation"), + QMessageBox::critical(this, _("Invalid Operation"), _("If a key pair does not have a private key then " "it will not be able to generate sub-keys.")); return; @@ -467,24 +470,26 @@ void KeyMgmt::slotSaveWindowState() { void KeyMgmt::slotExportAsOpenSSHFormat() { ByteArrayPtr key_export_data = nullptr; - auto keys_checked = mKeyList->getChecked(); + auto keys_checked = key_list_->getChecked(); if (keys_checked->empty()) { - QMessageBox::critical(nullptr, _("Error"), _("No Key Checked.")); + QMessageBox::critical( + this, _("Forbidden"), + _("Please select a key before performing this operation. If you select " + "multiple keys, only the first key will be exported.")); return; } auto key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); - if (!GpgKeyImportExportor::GetInstance().ExportKeyOpenSSH(key, + if (!GpgKeyImportExporter::GetInstance().ExportKeyOpenSSH(key, key_export_data)) { - QMessageBox::critical(nullptr, _("Error"), - _("An error occur in exporting.")); + QMessageBox::critical(this, _("Error"), _("An error occur in exporting.")); return; } if (key_export_data->empty()) { QMessageBox::critical( - nullptr, _("Error"), + this, _("Error"), _("This key may not be able to export as OpenSSH format. Please check " "the key-size of the subkey(s) used to sign.")); return; @@ -492,7 +497,7 @@ void KeyMgmt::slotExportAsOpenSSHFormat() { key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); if (!key.good()) { - QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); + QMessageBox::critical(this, _("Error"), _("Key Not Found.")); return; } QString fileString = QString::fromStdString(key.name() + " " + key.email() + @@ -508,4 +513,59 @@ void KeyMgmt::slotExportAsOpenSSHFormat() { } } +void KeyMgmt::slotImportKeyPackage() { + auto key_package_file_name = QFileDialog::getOpenFileName( + this, _("Import Key Package"), {}, + QString(_("Key Package")) + " (*.gfepack);;All Files (*)"); + + if (key_package_file_name.isEmpty()) return; + + auto encrypted_data = + read_all_data_in_file(key_package_file_name.toStdString()); + + if (encrypted_data.empty()) { + QMessageBox::critical(this, _("Error"), + _("No data was read from the key package.")); + return; + }; + + auto key_file_name = QFileDialog::getOpenFileName( + this, _("Import Key Package Passphrase File"), {}, + QString(_("Key Package Passphrase File")) + " (*.key);;All Files (*)"); + + auto passphrase = read_all_data_in_file(key_file_name.toStdString()); + + LOG(INFO) << "passphrase size" << passphrase.size(); + if (passphrase.size() != 256) { + QMessageBox::critical( + this, _("Wrong Passphrase"), + _("Please double check the passphrase you entered is correct.")); + return; + } + auto hash_key = QCryptographicHash::hash( + QByteArray::fromStdString(passphrase), QCryptographicHash::Sha256); + auto encoded = QByteArray::fromStdString(encrypted_data); + + QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::ECB, + QAESEncryption::Padding::ISO); + + auto decoded = encryption.removePadding(encryption.decode(encoded, hash_key)); + auto key_data = QByteArray::fromBase64(decoded); + + if (!key_data.startsWith(GpgConstants::PGP_PUBLIC_KEY_BEGIN) && + !key_data.startsWith(GpgConstants::PGP_PRIVATE_KEY_BEGIN)) { + QMessageBox::critical( + this, _("Wrong Passphrase"), + _("Please double check the passphrase you entered is correct.")); + return; + } + + auto key_data_ptr = std::make_unique<ByteArray>(key_data.toStdString()); + auto info = + GpgKeyImportExporter::GetInstance().ImportKey(std::move(key_data_ptr)); + + auto dialog = new KeyImportDetailDialog(info, false, this); + dialog->exec(); +} + } // namespace GpgFrontend::UI diff --git a/src/ui/KeyMgmt.h b/src/ui/KeyMgmt.h index 7edb1b5c..e3f250eb 100755 --- a/src/ui/KeyMgmt.h +++ b/src/ui/KeyMgmt.h @@ -44,7 +44,7 @@ class KeyMgmt : public QMainWindow { void slotGenerateSubKey(); - void slotExportKeyToFile(); + void slotExportKeyToKeyPackage(); void slotExportKeyToClipboard(); @@ -60,6 +60,8 @@ class KeyMgmt : public QMainWindow { void slotSaveWindowState(); + void slotImportKeyPackage(); + signals: void signalStatusBarChanged(QString); @@ -75,7 +77,7 @@ class KeyMgmt : public QMainWindow { void deleteKeysWithWarning(GpgFrontend::KeyIdArgsListPtr uidList); - KeyList* mKeyList; + KeyList* key_list_; QMenu* fileMenu{}; QMenu* keyMenu{}; QMenu* generateKeyMenu{}; @@ -92,6 +94,7 @@ class KeyMgmt : public QMainWindow { QAction* importKeyFromClipboardAct{}; QAction* importKeyFromFileAct{}; QAction* importKeyFromKeyServerAct{}; + QAction* importKeysFromKeyPackageAct{}; QAction* closeAct{}; QAction* showKeyDetailsAct{}; KeyServerImportDialog* importDialog{}; diff --git a/src/ui/KeyServerImportDialog.cpp b/src/ui/KeyServerImportDialog.cpp index 844f2ae5..9a118b93 100644 --- a/src/ui/KeyServerImportDialog.cpp +++ b/src/ui/KeyServerImportDialog.cpp @@ -26,7 +26,7 @@ #include <utility> -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "ui/SignalStation.h" #include "ui/settings/GlobalSettingStation.h" @@ -441,7 +441,7 @@ void KeyServerImportDialog::slotImport(const QStringList& keyIds, } } -void KeyServerImportDialog::slotImportFinished(QString keyid) { +void KeyServerImportDialog::slotImportFinished(const QString& keyid) { LOG(INFO) << _("Called"); auto* reply = qobject_cast<QNetworkReply*>(sender()); @@ -503,7 +503,7 @@ void KeyServerImportDialog::slotImportFinished(QString keyid) { void KeyServerImportDialog::importKeys(ByteArrayPtr in_data) { GpgImportInformation result = - GpgKeyImportExportor::GetInstance().ImportKey(std::move(in_data)); + GpgKeyImportExporter::GetInstance().ImportKey(std::move(in_data)); emit signalKeyImported(); QWidget* _parent = qobject_cast<QWidget*>(parent()); if (mAutomatic) { diff --git a/src/ui/KeyServerImportDialog.h b/src/ui/KeyServerImportDialog.h index 508fb42d..e3761f5c 100644 --- a/src/ui/KeyServerImportDialog.h +++ b/src/ui/KeyServerImportDialog.h @@ -53,7 +53,7 @@ class KeyServerImportDialog : public QDialog { void slotSearchFinished(); - void slotImportFinished(QString keyid); + void slotImportFinished(const QString& keyid); void slotSearch(); diff --git a/src/ui/KeyUploadDialog.cpp b/src/ui/KeyUploadDialog.cpp index 52aa2bd3..d2b8c0bf 100644 --- a/src/ui/KeyUploadDialog.cpp +++ b/src/ui/KeyUploadDialog.cpp @@ -27,15 +27,14 @@ #include <algorithm> #include "gpg/function/GpgKeyGetter.h" -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "ui/settings/GlobalSettingStation.h" namespace GpgFrontend::UI { KeyUploadDialog::KeyUploadDialog(const KeyIdArgsListPtr& keys_ids, QWidget* parent) - : QDialog(parent), - mKeys(GpgKeyGetter::GetInstance().GetKeys(keys_ids)) { + : QDialog(parent), mKeys(GpgKeyGetter::GetInstance().GetKeys(keys_ids)) { auto* pb = new QProgressBar(); pb->setRange(0, 0); pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -54,13 +53,12 @@ KeyUploadDialog::KeyUploadDialog(const KeyIdArgsListPtr& keys_ids, void KeyUploadDialog::slotUpload() { auto out_data = std::make_unique<ByteArray>(); - GpgKeyImportExportor::GetInstance().ExportKeys(*mKeys, out_data); + GpgKeyImportExporter::GetInstance().ExportKeys(*mKeys, out_data); uploadKeyToServer(*out_data); } void KeyUploadDialog::uploadKeyToServer( const GpgFrontend::ByteArray& keys_data) { - std::string target_keyserver; if (target_keyserver.empty()) { try { diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 8e6b6f81..2af6da78 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -50,7 +50,8 @@ void MainWindow::init() noexcept { setCentralWidget(edit); /* the list of Keys available*/ - mKeyList = new KeyList(true, this); + mKeyList = new KeyList( + KeyMenuAbility::REFRESH | KeyMenuAbility::UNCHECK_ALL, this); infoBoard = new InfoBoardWidget(this); @@ -115,21 +116,20 @@ void MainWindow::init() noexcept { emit loaded(); + // if not prohibit update checking + if (!prohibit_update_checking_) { #ifdef RELEASE - QString baseUrl = - "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; - QNetworkRequest request; - request.setUrl(QUrl(baseUrl)); - auto* replay = networkAccessManager->get(request); - auto version_thread = new VersionCheckThread(replay); - - connect(version_thread, SIGNAL(finished()), version_thread, - SLOT(deleteLater())); - connect(version_thread, &VersionCheckThread::upgradeVersion, this, - &MainWindow::slotVersionUpgrade); - - version_thread->start(); + auto version_thread = new VersionCheckThread(); + + connect(version_thread, SIGNAL(finished()), version_thread, + SLOT(deleteLater())); + connect(version_thread, &VersionCheckThread::upgradeVersion, this, + &MainWindow::slotVersionUpgrade); + + version_thread->start(); #endif + } + } catch (...) { LOG(FATAL) << _("Critical error occur while loading GpgFrontend."); QMessageBox::critical(nullptr, _("Loading Failed"), @@ -274,6 +274,11 @@ void MainWindow::restoreSettings() { general.add("save_key_checked", libconfig::Setting::TypeBoolean) = true; } + if (!general.exists("non_ascii_when_export")) { + general.add("non_ascii_when_export", libconfig::Setting::TypeBoolean) = + true; + } + bool save_key_checked = true; general.lookupValue("save_key_checked", save_key_checked); @@ -291,8 +296,24 @@ void MainWindow::restoreSettings() { LOG(INFO) << "get checked key id" << key_id; key_ids_ptr->push_back(key_id); } - mKeyList->setChecked(key_ids_ptr); + mKeyList->setChecked(std::move(key_ids_ptr)); } + + auto& smtp = settings["smtp"]; + + if (!smtp.exists("enable")) { + smtp.add("enable", libconfig::Setting::TypeBoolean) = true; + } + + prohibit_update_checking_ = false; + try { + prohibit_update_checking_ = + settings.lookup("network.prohibit_update_checking"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") + << _("prohibit_update_checking"); + } + } catch (...) { LOG(ERROR) << "cannot resolve settings"; } diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 68b062ce..5f9d9b50 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -29,7 +29,6 @@ #include "gpg/result_analyse/DecryptResultAnalyse.h" #include "gpg/result_analyse/EncryptResultAnalyse.h" #include "gpg/result_analyse/SignResultAnalyse.h" -#include "ui/FileEncryptionDialog.h" #include "ui/FindWidget.h" #include "ui/GpgFrontendUI.h" #include "ui/KeyMgmt.h" @@ -245,8 +244,7 @@ class MainWindow : public QMainWindow { /** * @details called when need to upgrade. */ - void slotVersionUpgrade(const QString& currentVersion, - const QString& latestVersion); + void slotVersionUpgrade(const SoftwareVersion& version); private: /** @@ -323,7 +321,7 @@ class MainWindow : public QMainWindow { QMenu* viewMenu{}; /** Submenu for view operations */ QMenu* importKeyMenu{}; /** Sumenu for import operations */ #ifdef SMTP_SUPPORT - QMenu* emailMenu{}; /** Sumenu for email operations */ + QMenu* emailMenu{}; /** Sumenu for email operations */ #endif QMenu* steganoMenu{}; /** Submenu for steganographic operations*/ @@ -332,6 +330,7 @@ class MainWindow : public QMainWindow { QToolBar* editToolBar{}; /** Toolbar holding edit actions */ QToolBar* specialEditToolBar{}; /** Toolbar holding special edit actions */ QToolBar* keyToolBar{}; /** Toolbar holding key operations */ + QToolBar* emailToolBar{}; /** Toolbar holding key operations */ QToolButton* importButton{}; /** Toolbutton for import dropdown menu in toolbar */ QDockWidget* keyListDock{}; /** Encrypt Dock*/ @@ -399,6 +398,7 @@ class MainWindow : public QMainWindow { bool attachmentDockCreated{}; bool restartNeeded{}; + bool prohibit_update_checking_ = false; }; } // namespace GpgFrontend::UI diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index 0931a179..967dcc32 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -41,7 +41,7 @@ std::unique_ptr<GpgFrontend::UI::CommonUtils> #ifdef SMTP_SUPPORT void send_an_email(QWidget* parent, InfoBoardWidget* info_board, - const QString& text) { + const QString& text, bool attach_signature) { info_board->addOptionalAction(_("Send Encrypted Mail"), [=]() { bool smtp_enabled = false; try { @@ -52,6 +52,8 @@ void send_an_email(QWidget* parent, InfoBoardWidget* info_board, } if (smtp_enabled) { auto dialog = new SendMailDialog(text, parent); + dialog->setContentEncryption(false); + dialog->setAttachSignature(attach_signature); dialog->show(); } else { QMessageBox::warning(nullptr, _("Function Disabled"), @@ -151,11 +153,20 @@ CommonUtils* CommonUtils::GetInstance() { CommonUtils::CommonUtils() : QWidget(nullptr) { connect(this, SIGNAL(signalKeyStatusUpdated()), SignalStation::GetInstance(), SIGNAL(KeyDatabaseRefresh())); + connect(this, &CommonUtils::signalGnupgNotInstall, this, []() { + QMessageBox::critical( + nullptr, _("ENV Loading Failed"), + _("Gnupg(gpg) is not installed correctly, please follow the " + "ReadME " + "instructions in Github to install Gnupg and then open " + "GpgFrontend.")); + QCoreApplication::quit(); + }); } void CommonUtils::slotImportKeys(QWidget* parent, const std::string& in_buffer) { - GpgImportInformation result = GpgKeyImportExportor::GetInstance().ImportKey( + GpgImportInformation result = GpgKeyImportExporter::GetInstance().ImportKey( std::make_unique<ByteArray>(in_buffer)); emit signalKeyStatusUpdated(); new KeyImportDetailDialog(result, false, parent); @@ -300,7 +311,7 @@ void CommonUtils::slotImportKeyFromKeyServer( // Try importing GpgImportInformation result = - GpgKeyImportExportor::GetInstance(ctx_channel) + GpgKeyImportExporter::GetInstance(ctx_channel) .ImportKey(std::move(key_data_ptr)); if (result.imported == 1) { diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index be114b0a..d64b06b0 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -40,7 +40,7 @@ class TextEdit; #ifdef SMTP_SUPPORT void send_an_email(QWidget* parent, InfoBoardWidget* info_board, - const QString& text); + const QString& text, bool attach_signature = true); #endif void show_verify_details(QWidget* parent, InfoBoardWidget* info_board, @@ -75,6 +75,8 @@ class CommonUtils : public QWidget { signals: void signalKeyStatusUpdated(); + void signalGnupgNotInstall(); + public slots: void slotImportKeys(QWidget* parent, const std::string& in_buffer); diff --git a/src/ui/aes/qaesencryption.cpp b/src/ui/aes/qaesencryption.cpp new file mode 100644 index 00000000..9103fb8e --- /dev/null +++ b/src/ui/aes/qaesencryption.cpp @@ -0,0 +1,626 @@ +#include "qaesencryption.h" +#include <QDebug> +#include <QVector> + +#ifdef USE_INTEL_AES_IF_AVAILABLE +#include "aesni/aesni-key-exp.h" +#include "aesni/aesni-enc-ecb.h" +#include "aesni/aesni-enc-cbc.h" +#endif + +/* + * Static Functions + * */ +QByteArray QAESEncryption::Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, + const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding) +{ + return QAESEncryption(level, mode, padding).encode(rawText, key, iv); +} + +QByteArray QAESEncryption::Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, + const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding) +{ + return QAESEncryption(level, mode, padding).decode(rawText, key, iv); +} + +QByteArray QAESEncryption::ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key) +{ + return QAESEncryption(level, mode).expandKey(key); +} + +QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding) +{ + if (rawText.isEmpty()) + return rawText; + + QByteArray ret(rawText); + switch (padding) + { + case Padding::ZERO: + //Works only if the last byte of the decoded array is not zero + while (ret.at(ret.length()-1) == 0x00) + ret.remove(ret.length()-1, 1); + break; + case Padding::PKCS7: +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + ret.remove(ret.length() - ret.back(), ret.back()); +#else + ret.remove(ret.length() - ret.at(ret.length() - 1), ret.at(ret.length() - 1)); +#endif + break; + case Padding::ISO: + { + // Find the last byte which is not zero + int marker_index = ret.length() - 1; + for (; marker_index >= 0; --marker_index) + { + if (ret.at(marker_index) != 0x00) + { + break; + } + } + + // And check if it's the byte for marking padding + if (ret.at(marker_index) == '\x80') + { + ret.truncate(marker_index); + } + break; + } + default: + //do nothing + break; + } + return ret; +} +/* + * End Static function declarations + * */ + +/* + * Local Functions + * */ + +namespace { + +quint8 xTime(quint8 x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +quint8 multiply(quint8 x, quint8 y) +{ + return (((y & 1) * x) ^ ((y>>1 & 1) * xTime(x)) ^ ((y>>2 & 1) * xTime(xTime(x))) ^ ((y>>3 & 1) + * xTime(xTime(xTime(x)))) ^ ((y>>4 & 1) * xTime(xTime(xTime(xTime(x)))))); +} + +} + +/* + * End Local functions + * */ + +QAESEncryption::QAESEncryption(Aes level, Mode mode, + Padding padding) + : m_nb(4), m_blocklen(16), m_level(level), m_mode(mode), m_padding(padding) + , m_aesNIAvailable(false), m_state(nullptr) +{ +#ifdef USE_INTEL_AES_IF_AVAILABLE + m_aesNIAvailable = check_aesni_support(); +#endif + + switch (level) + { + case AES_128: { + AES128 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + case AES_192: { + AES192 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + case AES_256: { + AES256 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + default: { + AES128 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + } + +} +QByteArray QAESEncryption::getPadding(int currSize, int alignment) +{ + int size = (alignment - currSize % alignment) % alignment; + switch(m_padding) + { + case Padding::ZERO: + return QByteArray(size, 0x00); + break; + case Padding::PKCS7: + if (size == 0) + size = alignment; + return QByteArray(size, size); + break; + case Padding::ISO: + if (size > 0) + return QByteArray (size - 1, 0x00).prepend('\x80'); + break; + default: + return QByteArray(size, 0x00); + break; + } + return QByteArray(); +} + +QByteArray QAESEncryption::expandKey(const QByteArray &key) +{ + +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (true){ + switch(m_level) { + case AES_128: { + AES128 aes128; + quint8 ret[aes128.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); + AES_128_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes128.expandedKey); + } + break; + case AES_192: { + AES192 aes192; + quint8 ret[aes192.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); + + AES_192_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes192.expandedKey); + } + break; + case AES_256: { + AES256 aes256; + quint8 ret[aes256.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); + + AES_256_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes256.expandedKey); + } + break; + default: + return QByteArray(); + break; + } + } else +#endif + { + + int i, k; + quint8 tempa[4]; // Used for the column/row operations + QByteArray roundKey(key); // The first round key is the key itself. + + // All other round keys are found from the previous round keys. + //i == Nk + for(i = m_nk; i < m_nb * (m_nr + 1); i++) + { + tempa[0] = (quint8) roundKey.at((i-1) * 4 + 0); + tempa[1] = (quint8) roundKey.at((i-1) * 4 + 1); + tempa[2] = (quint8) roundKey.at((i-1) * 4 + 2); + tempa[3] = (quint8) roundKey.at((i-1) * 4 + 3); + + if (i % m_nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + k = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = k; + + // Function Subword() + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + + tempa[0] = tempa[0] ^ Rcon[i/m_nk]; + } + + if (m_level == AES_256 && i % m_nk == 4) + { + // Function Subword() + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + roundKey.insert(i * 4 + 0, (quint8) roundKey.at((i - m_nk) * 4 + 0) ^ tempa[0]); + roundKey.insert(i * 4 + 1, (quint8) roundKey.at((i - m_nk) * 4 + 1) ^ tempa[1]); + roundKey.insert(i * 4 + 2, (quint8) roundKey.at((i - m_nk) * 4 + 2) ^ tempa[2]); + roundKey.insert(i * 4 + 3, (quint8) roundKey.at((i - m_nk) * 4 + 3) ^ tempa[3]); + } + return roundKey; + } +} + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +void QAESEncryption::addRoundKey(const quint8 round, const QByteArray &expKey) +{ + QByteArray::iterator it = m_state->begin(); + for(int i=0; i < 16; ++i) + it[i] = (quint8) it[i] ^ (quint8) expKey.at(round * m_nb * 4 + (i/4) * m_nb + (i%4)); +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +void QAESEncryption::subBytes() +{ + QByteArray::iterator it = m_state->begin(); + for(int i = 0; i < 16; i++) + it[i] = getSBoxValue((quint8) it[i]); +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +void QAESEncryption::shiftRows() +{ + QByteArray::iterator it = m_state->begin(); + quint8 temp; + //Keep in mind that QByteArray is column-driven!! + + //Shift 1 to left + temp = (quint8)it[1]; + it[1] = (quint8)it[5]; + it[5] = (quint8)it[9]; + it[9] = (quint8)it[13]; + it[13] = (quint8)temp; + + //Shift 2 to left + temp = (quint8)it[2]; + it[2] = (quint8)it[10]; + it[10] = (quint8)temp; + temp = (quint8)it[6]; + it[6] = (quint8)it[14]; + it[14] = (quint8)temp; + + //Shift 3 to left + temp = (quint8)it[3]; + it[3] = (quint8)it[15]; + it[15] = (quint8)it[11]; + it[11] = (quint8)it[7]; + it[7] = (quint8)temp; +} + +// MixColumns function mixes the columns of the state matrix +//optimized!! +void QAESEncryption::mixColumns() +{ + QByteArray::iterator it = m_state->begin(); + quint8 tmp, tm, t; + + for(int i = 0; i < 16; i += 4){ + t = (quint8)it[i]; + tmp = (quint8)it[i] ^ (quint8)it[i+1] ^ (quint8)it[i+2] ^ (quint8)it[i+3] ; + + tm = xTime( (quint8)it[i] ^ (quint8)it[i+1] ); + it[i] = (quint8)it[i] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime( (quint8)it[i+1] ^ (quint8)it[i+2]); + it[i+1] = (quint8)it[i+1] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime( (quint8)it[i+2] ^ (quint8)it[i+3]); + it[i+2] =(quint8)it[i+2] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime((quint8)it[i+3] ^ (quint8)t); + it[i+3] =(quint8)it[i+3] ^ (quint8)tm ^ (quint8)tmp; + } +} + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +void QAESEncryption::invMixColumns() +{ + QByteArray::iterator it = m_state->begin(); + quint8 a,b,c,d; + for(int i = 0; i < 16; i+=4){ + a = (quint8) it[i]; + b = (quint8) it[i+1]; + c = (quint8) it[i+2]; + d = (quint8) it[i+3]; + + it[i] = (quint8) (multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09)); + it[i+1] = (quint8) (multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d)); + it[i+2] = (quint8) (multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b)); + it[i+3] = (quint8) (multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e)); + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +void QAESEncryption::invSubBytes() +{ + QByteArray::iterator it = m_state->begin(); + for(int i = 0; i < 16; ++i) + it[i] = getSBoxInvert((quint8) it[i]); +} + +void QAESEncryption::invShiftRows() +{ + QByteArray::iterator it = m_state->begin(); + uint8_t temp; + + //Keep in mind that QByteArray is column-driven!! + + //Shift 1 to right + temp = (quint8)it[13]; + it[13] = (quint8)it[9]; + it[9] = (quint8)it[5]; + it[5] = (quint8)it[1]; + it[1] = (quint8)temp; + + //Shift 2 + temp = (quint8)it[10]; + it[10] = (quint8)it[2]; + it[2] = (quint8)temp; + temp = (quint8)it[14]; + it[14] = (quint8)it[6]; + it[6] = (quint8)temp; + + //Shift 3 + temp = (quint8)it[7]; + it[7] = (quint8)it[11]; + it[11] = (quint8)it[15]; + it[15] = (quint8)it[3]; + it[3] = (quint8)temp; +} + +QByteArray QAESEncryption::byteXor(const QByteArray &a, const QByteArray &b) +{ + QByteArray::const_iterator it_a = a.begin(); + QByteArray::const_iterator it_b = b.begin(); + QByteArray ret; + + //for(int i = 0; i < m_blocklen; i++) + for(int i = 0; i < std::min(a.size(), b.size()); i++) + ret.insert(i,it_a[i] ^ it_b[i]); + + return ret; +} + +// Cipher is the main function that encrypts the PlainText. +QByteArray QAESEncryption::cipher(const QByteArray &expKey, const QByteArray &in) +{ + + //m_state is the input buffer... + QByteArray output(in); + m_state = &output; + + // Add the First round key to the state before starting the rounds. + addRoundKey(0, expKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for(quint8 round = 1; round < m_nr; ++round){ + subBytes(); + shiftRows(); + mixColumns(); + addRoundKey(round, expKey); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + subBytes(); + shiftRows(); + addRoundKey(m_nr, expKey); + + return output; +} + +QByteArray QAESEncryption::invCipher(const QByteArray &expKey, const QByteArray &in) +{ + //m_state is the input buffer.... handle it! + QByteArray output(in); + m_state = &output; + + // Add the First round key to the state before starting the rounds. + addRoundKey(m_nr, expKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for(quint8 round=m_nr-1; round>0 ; round--){ + invShiftRows(); + invSubBytes(); + addRoundKey(round, expKey); + invMixColumns(); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + invShiftRows(); + invSubBytes(); + addRoundKey(0, expKey); + + return output; +} + +QByteArray QAESEncryption::printArray(uchar* arr, int size) +{ + QByteArray print(""); + for(int i=0; i<size; i++) + print.append(arr[i]); + + return print.toHex(); +} + +QByteArray QAESEncryption::encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv) +{ + if (m_mode >= CBC && (iv.isEmpty() || iv.size() != m_blocklen)) + return QByteArray(); + + QByteArray expandedKey = expandKey(key); + QByteArray alignedText(rawText); + + //Fill array with padding + alignedText.append(getPadding(rawText.size(), m_blocklen)); + + switch(m_mode) + { + case ECB: { +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (m_aesNIAvailable){ + unsigned char in[alignedText.size()]; + memcpy(in, alignedText.data(), alignedText.size()); + unsigned char out[alignedText.size()]; + memcpy(out, alignedText.data(), alignedText.size()); + char expKey[expandedKey.size()]; + memcpy(expKey, expandedKey.data(), expandedKey.size()); + AES_ECB_encrypt(in, out, alignedText.size(), + expKey, m_nr); + return QByteArray((char*)out, alignedText.size()); + } +#endif + QByteArray ret; + for(int i=0; i < alignedText.size(); i+= m_blocklen) + ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen))); + return ret; + } + break; + case CBC: { +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (m_aesNIAvailable){ + quint8 in[alignedText.size()]; + memcpy(in, alignedText.constData(), alignedText.size()); + quint8 ivec[iv.size()]; + memcpy(ivec, iv.data(), iv.size()); + char out[alignedText.size()]; + memset(out, 0x00, alignedText.size()); + char expKey[expandedKey.size()]; + memcpy(expKey, expandedKey.data(), expandedKey.size()); + AES_CBC_encrypt(in, + (unsigned char*) out, + ivec, + alignedText.size(), + expKey, + m_nr); + return QByteArray(out, alignedText.size()); + } +#endif + QByteArray ret; + QByteArray ivTemp(iv); + for(int i=0; i < alignedText.size(); i+= m_blocklen) { + alignedText.replace(i, m_blocklen, byteXor(alignedText.mid(i, m_blocklen),ivTemp)); + ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen))); + ivTemp = ret.mid(i, m_blocklen); + } + return ret; + } + break; + case CFB: { + QByteArray ret; + ret.append(byteXor(alignedText.left(m_blocklen), cipher(expandedKey, iv))); + for(int i=0; i < alignedText.size(); i+= m_blocklen) { + if (i+m_blocklen < alignedText.size()) + ret.append(byteXor(alignedText.mid(i+m_blocklen, m_blocklen), + cipher(expandedKey, ret.mid(i, m_blocklen)))); + } + return ret; + } + break; + case OFB: { + QByteArray ret; + QByteArray ofbTemp; + ofbTemp.append(cipher(expandedKey, iv)); + for (int i=m_blocklen; i < alignedText.size(); i += m_blocklen){ + ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen))); + } + ret.append(byteXor(alignedText, ofbTemp)); + return ret; + } + break; + default: break; + } + return QByteArray(); +} + +QByteArray QAESEncryption::decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv) +{ + if (m_mode >= CBC && (iv.isEmpty() || iv.size() != m_blocklen)) + return QByteArray(); + + QByteArray ret; + QByteArray expandedKey = expandKey(key); + + switch(m_mode) + { + case ECB: + for(int i=0; i < rawText.size(); i+= m_blocklen) + ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen))); + break; + case CBC: { + QByteArray ivTemp(iv); + for(int i=0; i < rawText.size(); i+= m_blocklen){ + ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen))); + ret.replace(i, m_blocklen, byteXor(ret.mid(i, m_blocklen),ivTemp)); + ivTemp = rawText.mid(i, m_blocklen); + } + } + break; + case CFB: { + ret.append(byteXor(rawText.mid(0, m_blocklen), cipher(expandedKey, iv))); + for(int i=0; i < rawText.size(); i+= m_blocklen){ + if (i+m_blocklen < rawText.size()) { + ret.append(byteXor(rawText.mid(i+m_blocklen, m_blocklen), + cipher(expandedKey, rawText.mid(i, m_blocklen)))); + } + } + } + break; + case OFB: { + QByteArray ofbTemp; + ofbTemp.append(cipher(expandedKey, iv)); + for (int i=m_blocklen; i < rawText.size(); i += m_blocklen){ + ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen))); + } + ret.append(byteXor(rawText, ofbTemp)); + } + break; + default: + //do nothing + break; + } + return ret; +} + +QByteArray QAESEncryption::removePadding(const QByteArray &rawText) +{ + return RemovePadding(rawText, (Padding) m_padding); +} diff --git a/src/ui/aes/qaesencryption.h b/src/ui/aes/qaesencryption.h new file mode 100644 index 00000000..ec8023b3 --- /dev/null +++ b/src/ui/aes/qaesencryption.h @@ -0,0 +1,165 @@ +#ifndef QAESENCRYPTION_H +#define QAESENCRYPTION_H + +#ifdef QtAES_EXPORTS +#include "qtaes_export.h" +#else +#define QTAESSHARED_EXPORT +#endif + +#include <QByteArray> +#include <QObject> + +#ifdef __linux__ +#ifndef __LP64__ +#define do_rdtsc _do_rdtsc +#endif +#endif + +class QTAESSHARED_EXPORT QAESEncryption : public QObject { + Q_OBJECT + public: + enum Aes { AES_128, AES_192, AES_256 }; + + enum Mode { ECB, CBC, CFB, OFB }; + + enum Padding { ZERO, PKCS7, ISO }; + + static QByteArray Crypt( + QAESEncryption::Aes level, QAESEncryption::Mode mode, + const QByteArray &rawText, const QByteArray &key, + const QByteArray &iv = QByteArray(), + QAESEncryption::Padding padding = QAESEncryption::ISO); + static QByteArray Decrypt( + QAESEncryption::Aes level, QAESEncryption::Mode mode, + const QByteArray &rawText, const QByteArray &key, + const QByteArray &iv = QByteArray(), + QAESEncryption::Padding padding = QAESEncryption::ISO); + static QByteArray ExpandKey(QAESEncryption::Aes level, + QAESEncryption::Mode mode, const QByteArray &key); + static QByteArray RemovePadding( + const QByteArray &rawText, + QAESEncryption::Padding padding = QAESEncryption::ISO); + + QAESEncryption(QAESEncryption::Aes level, QAESEncryption::Mode mode, + QAESEncryption::Padding padding = QAESEncryption::ISO); + + QByteArray encode(const QByteArray &rawText, const QByteArray &key, + const QByteArray &iv = QByteArray()); + QByteArray decode(const QByteArray &rawText, const QByteArray &key, + const QByteArray &iv = QByteArray()); + QByteArray removePadding(const QByteArray &rawText); + QByteArray expandKey(const QByteArray &key); + + QByteArray printArray(uchar *arr, int size); + Q_SIGNALS: + + public Q_SLOTS: + + private: + int m_nb; + int m_blocklen; + int m_level; + int m_mode; + int m_nk; + int m_keyLen; + quint8 m_nr; + int m_expandedKey; + int m_padding; + bool m_aesNIAvailable; + QByteArray *m_state; + + struct AES256 { + int nk = 8; + int keylen = 32; + int nr = 14; + int expandedKey = 240; + }; + + struct AES192 { + int nk = 6; + int keylen = 24; + int nr = 12; + int expandedKey = 209; + }; + + struct AES128 { + int nk = 4; + int keylen = 16; + int nr = 10; + int expandedKey = 176; + }; + + quint8 getSBoxValue(quint8 num) { return sbox[num]; } + quint8 getSBoxInvert(quint8 num) { return rsbox[num]; } + + void addRoundKey(const quint8 round, const QByteArray &expKey); + void subBytes(); + void shiftRows(); + void mixColumns(); + void invMixColumns(); + void invSubBytes(); + void invShiftRows(); + QByteArray getPadding(int currSize, int alignment); + QByteArray cipher(const QByteArray &expKey, const QByteArray &in); + QByteArray invCipher(const QByteArray &expKey, const QByteArray &in); + QByteArray byteXor(const QByteArray &a, const QByteArray &b); + + const quint8 sbox[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C + // D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16}; + + const quint8 rsbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, + 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, + 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, + 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, + 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, + 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, + 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0c, 0x7d}; + + // The round constant word array, Rcon[i], contains the values given by + // x to th e power (i-1) being powers of x (x is denoted as {02}) in the field + // GF(2^8) Only the first 14 elements are needed + const quint8 Rcon[14] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab}; +}; + +#endif // QAESENCRYPTION_H diff --git a/src/ui/data_struct/SoftwareVersion.cpp b/src/ui/data_struct/SoftwareVersion.cpp new file mode 100644 index 00000000..2e814cb9 --- /dev/null +++ b/src/ui/data_struct/SoftwareVersion.cpp @@ -0,0 +1,25 @@ +/** + * 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 "SoftwareVersion.h" diff --git a/src/ui/data_struct/SoftwareVersion.h b/src/ui/data_struct/SoftwareVersion.h new file mode 100644 index 00000000..be646b71 --- /dev/null +++ b/src/ui/data_struct/SoftwareVersion.h @@ -0,0 +1,59 @@ +/** + * 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_SOFTWAREVERSION_H +#define GPGFRONTEND_SOFTWAREVERSION_H + +#include <boost/date_time.hpp> + +namespace GpgFrontend::UI { +struct SoftwareVersion { + std::string latest_version; + std::string current_version; + bool latest_prerelease = false; + bool latest_draft = false; + bool current_prerelease = false; + bool current_draft = false; + bool load_info_done = false; + bool current_version_found = false; + std::string publish_date; + std::string release_note; + + [[nodiscard]] bool NeedUpgrade() const { + return load_info_done && !latest_prerelease && !latest_draft && + current_version < latest_version; + } + + [[nodiscard]] bool VersionWithDrawn() const { + return load_info_done && !current_version_found && current_prerelease && + !current_draft; + } + + [[nodiscard]] bool CurrentVersionReleased() const { + return load_info_done && current_version_found; + } +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SOFTWAREVERSION_H diff --git a/src/ui/function/CtxCheckThread.cpp b/src/ui/function/CtxCheckThread.cpp new file mode 100644 index 00000000..b51954e1 --- /dev/null +++ b/src/ui/function/CtxCheckThread.cpp @@ -0,0 +1,48 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "CtxCheckThread.h" + +#include "gpg/GpgContext.h" +#include "gpg/GpgCoreInit.h" +#include "gpg/function/GpgKeyGetter.h" +#include "ui/UserInterfaceUtils.h" + +GpgFrontend::UI::CtxCheckThread::CtxCheckThread() : QThread(nullptr) { + connect(this, &CtxCheckThread::signalGnupgNotInstall, + CommonUtils::GetInstance(), &CommonUtils::signalGnupgNotInstall); +} + +void GpgFrontend::UI::CtxCheckThread::run() { + // Init GpgFrontend Core + init_gpgfrontend_core(); + + // Create & Check Gnupg Context Status + if (!GpgContext::GetInstance().good()) { + emit signalGnupgNotInstall(); + } + // Try fetching key + else + GpgFrontend::GpgKeyGetter::GetInstance().FetchKey(); +} diff --git a/src/ui/function/CtxCheckThread.h b/src/ui/function/CtxCheckThread.h new file mode 100644 index 00000000..74bdb491 --- /dev/null +++ b/src/ui/function/CtxCheckThread.h @@ -0,0 +1,42 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_CTXCHECKTRHEAD_H +#define GPGFRONTEND_CTXCHECKTRHEAD_H +#include "ui/GpgFrontendUI.h" +namespace GpgFrontend::UI { +class CtxCheckThread : public QThread { + Q_OBJECT + public: + CtxCheckThread(); + + signals: + void signalGnupgNotInstall(); + + protected: + void run() override; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_CTXCHECKTRHEAD_H diff --git a/src/ui/function/FileReadThread.h b/src/ui/function/FileReadThread.h index ebfcfb3c..46ed6cbc 100644 --- a/src/ui/function/FileReadThread.h +++ b/src/ui/function/FileReadThread.h @@ -26,6 +26,7 @@ #define GPGFRONTEND_FILEREADTHREAD_H #include "ui/GpgFrontendUI.h" + namespace GpgFrontend::UI { class FileReadThread : public QThread { @@ -35,7 +36,7 @@ class FileReadThread : public QThread { explicit FileReadThread(std::string path); signals: - + void sendReadBlock(const QString& block); void readDone(); diff --git a/src/ui/function/ProxyConnectionTestThread.cpp b/src/ui/function/ProxyConnectionTestThread.cpp new file mode 100644 index 00000000..76cf525e --- /dev/null +++ b/src/ui/function/ProxyConnectionTestThread.cpp @@ -0,0 +1,56 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "ProxyConnectionTestThread.h" + +void GpgFrontend::UI::ProxyConnectionTestThread::run() { + QNetworkProxyQuery npq({QUrl(url_)}); + auto proxies_list = QNetworkProxyFactory::systemProxyForQuery(npq); + + if (proxies_list.isEmpty()) { + LOG(INFO) << "no proxy applied"; + } else { + LOG(INFO) << "proxies list hostname" + << proxies_list.front().hostName().toStdString(); + } + + LOG(INFO) << "proxies list size" << proxies_list.size(); + + auto manager = std::make_unique<QNetworkAccessManager>(nullptr); + QNetworkRequest url_request; + url_request.setUrl(QUrl(url_)); + auto _reply = manager->get(url_request); + + while (_reply->isRunning()) QApplication::processEvents(); + auto _buffer = _reply->readAll(); + if (_reply->error() == QNetworkReply::NoError && !_buffer.isEmpty()) { + result_ = "Reachable"; + } else { + result_ = "Not Reachable"; + } + + _reply->deleteLater(); + + emit signalProxyConnectionTestResult(result_); +} diff --git a/src/ui/function/ProxyConnectionTestThread.h b/src/ui/function/ProxyConnectionTestThread.h new file mode 100644 index 00000000..4ef75050 --- /dev/null +++ b/src/ui/function/ProxyConnectionTestThread.h @@ -0,0 +1,57 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_PROXYCONNECTIONTESTTHREAD_H +#define GPGFRONTEND_PROXYCONNECTIONTESTTHREAD_H + +class ProxyConnectionTestThread {}; + +#include <utility> + +#include "GpgFrontendUI.h" + +namespace GpgFrontend::UI { + +class ProxyConnectionTestThread : public QThread { + Q_OBJECT + public: + explicit ProxyConnectionTestThread(QString url, int timeout, + QWidget* parent = nullptr) + : QThread(parent), url_(std::move(url)), timeout_(timeout) {} + + signals: + void signalProxyConnectionTestResult(const QString& result); + + protected: + void run() override; + + private: + QString url_; + QString result_; + int timeout_ = 500; +}; + +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_PROXYCONNECTIONTESTTHREAD_H diff --git a/src/ui/function/SMTPSendMailThread.cpp b/src/ui/function/SMTPSendMailThread.cpp new file mode 100644 index 00000000..edfd3156 --- /dev/null +++ b/src/ui/function/SMTPSendMailThread.cpp @@ -0,0 +1,261 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "SMTPSendMailThread.h" + +#include <boost/format.hpp> + +#include "gpg/function/BasicOperator.h" +#include "gpg/function/GpgKeyGetter.h" +#include "gpg/function/GpgKeyImportExporter.h" + +namespace GpgFrontend::UI { + +void SMTPSendMailThread::run() { + SmtpClient smtp(host_.c_str(), port_, connection_type_); + + if (identify_) { + smtp.setUser(username_.c_str()); + smtp.setPassword(password_.c_str()); + } + + if (encrypt_content_ && public_key_ids_ != nullptr && + !public_key_ids_->empty() && !attach_signature_file_) { + message.getContent().setContentType( + "multipart/encrypted; " + "protocol=\"application/pgp-encrypted\""); + } + + if (attach_signature_file_ && !private_key_id_.empty()) { + message.getContent().setContentType( + "multipart/signed; " + "protocol=\"application/pgp-signature\""); + } + + int index = 0; + for (auto& text : texts_) { + const auto plain_text = text->getText().toStdString(); + + // encrypt + if (encrypt_content_ && public_key_ids_ != nullptr && + !public_key_ids_->empty()) { + ByteArrayPtr out_buffer = nullptr; + GpgEncrResult result; + auto in_buffer = std::make_unique<ByteArray>(plain_text); + auto keys = GpgKeyGetter::GetInstance().GetKeys(public_key_ids_); + auto err = BasicOperator::GetInstance().Encrypt( + std::move(keys), *in_buffer, out_buffer, result); + + if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { + emit signalSMTPResult("Fail to encrypt with gpg keys"); + return; + } + text->setText(out_buffer->c_str()); + + // The multipart/encrypted MIME body MUST consist of exactly two body + // parts, the first with content type "application/pgp-encrypted". This + // body contains the control information. A message complying with this + // standard MUST contain a "Version: 1" field in this body. Since the + // OpenPGP packet format contains all other information necessary for + // decrypting, no other information is required here. + auto control_text = std::make_unique<MimeText>("Version: 1\r\n"); + control_text->setContentType("application/pgp-encrypted"); + send_texts_.push_back(std::move(control_text)); + // The second MIME body part MUST contain the actual encrypted data. It + // MUST be labeled with a content type of "application/octet-stream". + text->setContentType("application/octet-stream"); + } + + send_texts_.push_back(std::move(text)); + + // sign + if (attach_signature_file_ && !private_key_id_.empty()) { + ByteArrayPtr out_buffer = nullptr; + GpgSignResult result; + + auto& plain_mime_text = send_texts_.back(); + // In particular, line endings in the encoded data + // MUST use the canonical <CR><LF> sequence where appropriate + auto encoded_text = plain_mime_text->getText(); + // As described in section 3 of this document, any trailing + // whitespace MUST then be removed from the signed material. + encoded_text = encoded_text.trimmed(); + encoded_text = encoded_text.replace('\n', "\r\n"); + // An implementation which elects to adhere to the OpenPGP convention + // has to make sure it inserts a <CR><LF> pair on the last line of the + // data to be signed and transmitted. + encoded_text.append("\r\n"); + plain_mime_text->setText(encoded_text); + + // This presents serious problems + // for multipart/signed, in particular, where the signature is + // invalidated when such an operation occurs. For this reason all data + // signed according to this protocol MUST be constrained to 7 bits (8- + // bit data MUST be encoded using either Quoted-Printable or Base64). + plain_mime_text->setEncoding(MimePart::_7Bit); + + // As described in [2], the digital signature MUST be calculated + // over both the data to be signed and its set of content headers. + auto text_calculated = plain_mime_text->toString().toStdString(); + + auto in_buffer = std::make_unique<ByteArray>(text_calculated); + auto key = GpgKeyGetter::GetInstance().GetKey(private_key_id_); + auto keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(key)); + + // The signature MUST be generated detached from the signed data + // so that the process does not alter the signed data in any way. + auto err = BasicOperator::GetInstance().Sign( + std::move(keys), *in_buffer, out_buffer, GPGME_SIG_MODE_DETACH, + result); + + if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { + emit signalSMTPResult("Fail to sign with gpg keys"); + return; + } + + auto sign_content_name = + boost::format("%1%_sign_%2%.asc") % private_key_id_ % index++; + + // Set MIME Options + send_texts_.push_back(std::make_unique<MimeText>(out_buffer->c_str())); + auto& sig_text = send_texts_.back(); + sig_text->setContentType("application/pgp-signature"); + sig_text->setEncoding(MimePart::_7Bit); + sig_text->setContentName(sign_content_name.str().c_str()); + + // set Message Integrity Check (MIC) algorithm + if (result->signatures != nullptr) { + // The "micalg" parameter for the "application/pgp-signature" + // protocol + // MUST contain exactly one hash-symbol of the format "pgp-<hash- + // identifier>", where <hash-identifier> identifies the Message + // Integrity Check (MIC) algorithm used to generate the signature. + // Hash-symbols are constructed from the text names registered in [1] + // or according to the mechanism defined in that document by + // converting the text name to lower case and prefixing it with the + // four characters "pgp-". + auto hash_algo_name = + std::string(gpgme_hash_algo_name(result->signatures->hash_algo)); + boost::algorithm::to_lower(hash_algo_name); + std::stringstream ss; + ss << message.getContent().getContentType().toStdString(); + ss << "; micalg=pgp-" << hash_algo_name; + message.getContent().setContentType(ss.str().c_str()); + } + } + } + + if (attach_public_key_file_ && !attached_public_key_ids_.empty()) { + auto key = GpgKeyGetter::GetInstance().GetKey(attached_public_key_ids_); + ByteArrayPtr out_buffer = nullptr; + GpgKeyImportExporter::GetInstance().ExportKey(key, out_buffer); + + auto public_key_file_name = + boost::format("%1%_pubkey.asc") % attached_public_key_ids_; + addFileContent(public_key_file_name.str().c_str(), out_buffer->c_str()); + auto& key_file = files_.back(); + key_file->setEncoding(MimePart::_7Bit); + key_file->setContentType("application/pgp-keys"); + } + + for (const auto& text : send_texts_) { + message.addPart(text.get()); + } + + for (const auto& file : files_) { + message.addPart(file.get()); + } + + // Now we can send the mail + if (!smtp.connectToHost()) { + emit signalSMTPResult("Fail to connect SMTP server"); + return; + } + if (!smtp.login()) { + emit signalSMTPResult("Fail to login"); + return; + } + if (!smtp.sendMail(message)) { + emit signalSMTPResult("Fail to send mail"); + return; + } + smtp.quit(); + emit signalSMTPResult("Succeed in sending a test email"); +} + +void SMTPSendMailThread::setBCC(const QString& bccs) { + QStringList bcc_string_list = bccs.split(';'); + for (const auto& bcc : bcc_string_list) { + if (!bcc.isEmpty()) message.addBcc(new EmailAddress(bcc.trimmed())); + } +} + +void SMTPSendMailThread::setCC(const QString& ccs) { + QStringList cc_string_list = ccs.split(';'); + for (const auto& cc : cc_string_list) { + if (!cc.isEmpty()) message.addCc(new EmailAddress(cc.trimmed())); + } +} + +void SMTPSendMailThread::setRecipient(const QString& recipients) { + QStringList rcpt_string_list = recipients.split(';'); + for (const auto& rcpt : rcpt_string_list) { + if (!rcpt.isEmpty()) message.addRecipient(new EmailAddress(rcpt.trimmed())); + } +} + +void SMTPSendMailThread::setSender(const QString& sender) { + message.setSender(new EmailAddress(sender)); +} + +void SMTPSendMailThread::addTextContent(const QString& content) { + auto text = std::make_unique<MimeText>(content.trimmed()); + texts_.push_back(std::move(text)); +} + +void SMTPSendMailThread::addFileContent(const QString& file_name, + const QByteArray& content) { + auto file = std::make_unique<MimeFile>(content, file_name); + files_.push_back(std::move(file)); +} + +void SMTPSendMailThread::setEncryptContent( + bool encrypt_content, GpgFrontend::KeyIdArgsListPtr public_key_ids) { + this->encrypt_content_ = encrypt_content; + this->public_key_ids_ = std::move(public_key_ids); +} + +void SMTPSendMailThread::setAttachSignatureFile( + bool attach_signature_file, GpgFrontend::KeyId private_key_id) { + this->attach_signature_file_ = attach_signature_file; + this->private_key_id_ = std::move(private_key_id); +} + +void SMTPSendMailThread::setAttachPublicKey( + bool attach_public_key_file, GpgFrontend::KeyId attached_public_key_ids) { + this->attach_public_key_file_ = attach_public_key_file; + this->attached_public_key_ids_ = std::move(attached_public_key_ids); +} +} // namespace GpgFrontend::UI diff --git a/src/ui/function/SMTPSendMailThread.h b/src/ui/function/SMTPSendMailThread.h new file mode 100644 index 00000000..e5c9e27f --- /dev/null +++ b/src/ui/function/SMTPSendMailThread.h @@ -0,0 +1,106 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_SMTPSENDMAILTHREAD_H +#define GPGFRONTEND_SMTPSENDMAILTHREAD_H + +#include <utility> + +#ifdef SMTP_SUPPORT +#include "smtp/SmtpMime" +#endif + +#include "ui/GpgFrontendUI.h" + +namespace GpgFrontend::UI { +class SMTPSendMailThread : public QThread { + Q_OBJECT + public: + explicit SMTPSendMailThread(std::string host, int port, + SmtpClient::ConnectionType connection_type, + bool identify, std::string username, + std::string password, QWidget* parent = nullptr) + : QThread(parent), + host_(std::move(host)), + port_(port), + connection_type_(connection_type), + identify_(identify), + username_(std::move(username)), + password_(std::move(password)) {} + + void setSender(const QString& sender); + + void setRecipient(const QString& recipients); + + void setCC(const QString& ccs); + + void setBCC(const QString& bccs); + + void setSubject(const QString& subject) { message.setSubject(subject); } + + void addTextContent(const QString& content); + + void addFileContent(const QString& file_name, const QByteArray& content); + + void setEncryptContent(bool encrypt_content, + GpgFrontend::KeyIdArgsListPtr public_key_ids); + + void setAttachSignatureFile(bool attach_signature_file, + GpgFrontend::KeyId private_key_id); + + void setAttachPublicKey(bool attach_public_key_file, + GpgFrontend::KeyId attached_public_key_ids); + + signals: + void signalSMTPResult(const QString& result); + + protected: + void run() override; + + private: + // SMTP Options + std::string host_; + int port_; + SmtpClient::ConnectionType connection_type_; + + bool identify_; + std::string username_; + std::string password_; + + MimeMessage message; + std::vector<std::unique_ptr<MimeText>> texts_; + std::vector<std::unique_ptr<MimeText>> send_texts_; + std::vector<std::unique_ptr<MimeFile>> files_; + + // GPG Options + bool encrypt_content_ = false; + GpgFrontend::KeyIdArgsListPtr public_key_ids_; + bool attach_signature_file_ = false; + GpgFrontend::KeyId private_key_id_; + bool attach_public_key_file_ = false; + GpgFrontend::KeyId attached_public_key_ids_; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SMTPSENDMAILTHREAD_H diff --git a/src/ui/function/SMTPTestThread.cpp b/src/ui/function/SMTPTestThread.cpp new file mode 100644 index 00000000..0eb267f2 --- /dev/null +++ b/src/ui/function/SMTPTestThread.cpp @@ -0,0 +1,46 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "SMTPTestThread.h" +namespace GpgFrontend::UI { + +void SMTPTestThread::run() { + SmtpClient smtp(host_.c_str(), port_, connection_type_); + if (identify_) { + smtp.setUser(username_.c_str()); + smtp.setPassword(password_.c_str()); + } + if (!smtp.connectToHost()) { + emit signalSMTPTestResult("Fail to connect SMTP server"); + return; + } + if (!smtp.login()) { + emit signalSMTPTestResult("Fail to login"); + return; + } + smtp.quit(); + emit signalSMTPTestResult("Succeed in testing connection"); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/function/SMTPTestThread.h b/src/ui/function/SMTPTestThread.h new file mode 100644 index 00000000..db61d97d --- /dev/null +++ b/src/ui/function/SMTPTestThread.h @@ -0,0 +1,70 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_SMTPTESTTHREAD_H +#define GPGFRONTEND_SMTPTESTTHREAD_H + +#include <utility> + +#ifdef SMTP_SUPPORT +#include "smtp/SmtpMime" +#endif + +#include "ui/GpgFrontendUI.h" +namespace GpgFrontend::UI { + +class SMTPTestThread : public QThread { + Q_OBJECT + public: + explicit SMTPTestThread(std::string host, int port, + SmtpClient::ConnectionType connection_type, + bool identify, std::string username, + std::string password, QWidget* parent = nullptr) + : QThread(parent), + host_(std::move(host)), + port_(port), + connection_type_(connection_type), + identify_(identify), + username_(std::move(username)), + password_(std::move(password)) {} + + signals: + void signalSMTPTestResult(const QString& result); + + protected: + void run() override; + + private: + std::string host_; + int port_; + SmtpClient::ConnectionType connection_type_; + + bool identify_; + std::string username_; + std::string password_; +}; + +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SMTPTESTTHREAD_H diff --git a/src/ui/function/TestListedKeyServerThread.cpp b/src/ui/function/TestListedKeyServerThread.cpp new file mode 100644 index 00000000..4f816860 --- /dev/null +++ b/src/ui/function/TestListedKeyServerThread.cpp @@ -0,0 +1,47 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "TestListedKeyServerThread.h" + +void GpgFrontend::UI::TestListedKeyServerThread::run() { + for (const auto& url : urls_) { + const auto keyserver_url = url; + + auto key_url = QUrl{keyserver_url}; + + LOG(INFO) << "key server domain" << key_url.host().toStdString(); + + QTcpSocket socket(nullptr); + socket.abort(); + socket.connectToHost(key_url.host(), 80); + if (socket.waitForConnected(timeout_)) { + result_.push_back("Reachable"); + } else { + result_.push_back("Not Reachable"); + } + socket.close(); + } + + emit signalKeyServerListTestResult(result_); +} diff --git a/src/ui/function/TestListedKeyServerThread.h b/src/ui/function/TestListedKeyServerThread.h new file mode 100644 index 00000000..99fd6c6d --- /dev/null +++ b/src/ui/function/TestListedKeyServerThread.h @@ -0,0 +1,55 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_TESTLISTEDKEYSERVERTHREAD_H +#define GPGFRONTEND_TESTLISTEDKEYSERVERTHREAD_H + +#include "GpgFrontendUI.h" + +namespace GpgFrontend::UI { + +class TestListedKeyServerThread : public QThread { + Q_OBJECT + public: + explicit TestListedKeyServerThread(const QStringList& urls, int timeout, + QWidget* parent = nullptr) + : QThread(parent), urls_(urls), timeout_(timeout) {} + + signals: + void signalKeyServerListTestResult(const QStringList& result); + + protected: + void run() override; + + private: + QStringList urls_; + QStringList result_; + int timeout_ = 500; +}; + +} // namespace GpgFrontend::UI + +class TestListedKeyServerThread {}; + +#endif // GPGFRONTEND_TESTLISTEDKEYSERVERTHREAD_H diff --git a/src/ui/function/VersionCheckThread.cpp b/src/ui/function/VersionCheckThread.cpp index bad60ef4..2a1cf9af 100644 --- a/src/ui/function/VersionCheckThread.cpp +++ b/src/ui/function/VersionCheckThread.cpp @@ -24,8 +24,7 @@ #include "VersionCheckThread.h" -#include <iterator> -#include <regex> +#include <QMetaType> #include "GpgFrontendBuildInfo.h" #include "json/json.hpp" @@ -33,45 +32,94 @@ namespace GpgFrontend::UI { void VersionCheckThread::run() { - using namespace nlohmann; - - LOG(INFO) << "get latest version from Github"; - auto current_version = std::string("v") + std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH); - while (mNetworkReply->isRunning()) { - QApplication::processEvents(); - } - - if (mNetworkReply->error() != QNetworkReply::NoError) { - LOG(ERROR) << "network error"; - return; + SoftwareVersion version; + version.current_version = current_version; + + auto manager = std::make_unique<QNetworkAccessManager>(nullptr); + + try { + using namespace nlohmann; + LOG(INFO) << "current version" << current_version; + + std::string latest_version_url = + "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; + std::string current_version_url = + "https://api.github.com/repos/saturneric/gpgfrontend/releases/tags/" + + current_version; + + QNetworkRequest latest_request, current_request; + latest_request.setUrl(QUrl(latest_version_url.c_str())); + current_request.setUrl(QUrl(current_version_url.c_str())); + auto _reply = manager->get(latest_request); + while (_reply->isRunning()) QApplication::processEvents(); + if (_reply->error() != QNetworkReply::NoError) { + LOG(ERROR) << "current version request error"; + version.latest_version = current_version; + } else { + latest_reply_bytes_ = _reply->readAll(); + auto latest_reply_json = + nlohmann::json::parse(latest_reply_bytes_.toStdString()); + + std::string latest_version = latest_reply_json["tag_name"]; + + LOG(INFO) << "latest version from Github" << latest_version; + + 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; + LOG(WARNING) << "latest version unknown"; + } + + bool prerelease = latest_reply_json["prerelease"], + draft = latest_reply_json["draft"]; + std::string publish_date = latest_reply_json["published_at"]; + std::string release_note = latest_reply_json["body"]; + version.latest_version = latest_version; + version.latest_prerelease = prerelease; + version.latest_draft = draft; + version.publish_date = publish_date; + version.release_note = release_note; + } + + _reply->deleteLater(); + + _reply = manager->get(current_request); + while (_reply->isRunning()) QApplication::processEvents(); + current_reply_bytes_ = _reply->readAll(); + if (_reply->error() != QNetworkReply::NoError) { + LOG(ERROR) << "current version request network error"; + version.current_version_found = false; + } else { + version.current_version_found = true; + auto current_reply_json = + nlohmann::json::parse(current_reply_bytes_.toStdString()); + bool current_prerelease = current_reply_json["prerelease"], + current_draft = current_reply_json["draft"]; + version.latest_prerelease = current_prerelease; + version.latest_draft = current_draft; + } + _reply->deleteLater(); + + // loading done + version.load_info_done = true; + + } catch (...) { + LOG(INFO) << "error occurred"; + version.load_info_done = false; } - - auto bytes = mNetworkReply->readAll(); - - auto reply_json = nlohmann::json::parse(bytes.toStdString()); - - std::string latest_version = reply_json["tag_name"]; - - LOG(INFO) << "latest version from Github" << latest_version; - - 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; - LOG(WARNING) << "latest version unknown"; - } - - emit upgradeVersion(current_version.c_str(), latest_version.c_str()); + emit upgradeVersion(version); } -VersionCheckThread::VersionCheckThread(QNetworkReply* networkReply) - : mNetworkReply(networkReply) {} +VersionCheckThread::VersionCheckThread() : QThread(nullptr) { + qRegisterMetaType<SoftwareVersion>("SoftwareVersion"); +}; } // namespace GpgFrontend::UI diff --git a/src/ui/function/VersionCheckThread.h b/src/ui/function/VersionCheckThread.h index 181ee947..0db9770c 100644 --- a/src/ui/function/VersionCheckThread.h +++ b/src/ui/function/VersionCheckThread.h @@ -26,6 +26,7 @@ #define GPGFRONTEND_VERSIONCHECKTHREAD_H #include "ui/GpgFrontendUI.h" +#include "ui/data_struct/SoftwareVersion.h" namespace GpgFrontend::UI { @@ -33,18 +34,17 @@ class VersionCheckThread : public QThread { Q_OBJECT public: - explicit VersionCheckThread(QNetworkReply* networkReply); + explicit VersionCheckThread(); signals: - void upgradeVersion(const QString& currentVersion, - const QString& latestVersion); + void upgradeVersion(SoftwareVersion version); protected: void run() override; private: - QNetworkReply* mNetworkReply; + QByteArray latest_reply_bytes_, current_reply_bytes_; }; } // namespace GpgFrontend::UI diff --git a/src/ui/help/AboutDialog.cpp b/src/ui/help/AboutDialog.cpp index 93e7ccb3..78ff5557 100644 --- a/src/ui/help/AboutDialog.cpp +++ b/src/ui/help/AboutDialog.cpp @@ -86,8 +86,8 @@ InfoTab::InfoTab(QWidget* parent) : QWidget(parent) { _("or send a mail to my mailing list at") + " <a " + "href=\"mailto:[email protected]\">[email protected]</a>." + "<br><br> " + _("Built with Qt") + " " + qVersion() + " " + _("and GPGME") + " " + - GpgFrontend::GpgContext::getGpgmeVersion().c_str() + "<br>" + - _("Built at") + " " + BUILD_TIMESTAMP + "</center>"); + GpgFrontend::GpgContext::GetInstance().GetInfo().GpgMEVersion.c_str() + + "<br>" + _("Built at") + " " + BUILD_TIMESTAMP + "</center>"); auto* layout = new QGridLayout(); auto* pixmapLabel = new QLabel(); @@ -163,14 +163,6 @@ UpdateTab::UpdateTab(QWidget* parent) : QWidget(parent) { latestVersionLabel->setWordWrap(true); upgradeLabel = new QLabel(); - upgradeLabel->setText( - "<center>" + - QString(_("The current version is less than the latest version on " - "github.")) + - "</center><center>" + _("Please click") + - " <a " - "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + - _("Here") + "</a> " + _("to download the latest version.") + "</center>"); upgradeLabel->setWordWrap(true); upgradeLabel->setOpenExternalLinks(true); upgradeLabel->setHidden(true); @@ -196,11 +188,7 @@ void UpdateTab::getLatestVersion() { LOG(INFO) << _("try to get latest version"); - QString base_url = - "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; - QNetworkRequest request; - request.setUrl(QUrl(base_url)); - auto version_thread = new VersionCheckThread(manager->get(request)); + auto version_thread = new VersionCheckThread(); connect(version_thread, SIGNAL(finished()), version_thread, SLOT(deleteLater())); @@ -210,15 +198,45 @@ void UpdateTab::getLatestVersion() { version_thread->start(); } -void UpdateTab::slotShowVersionStatus(const QString& current, - const QString& server) { +void UpdateTab::slotShowVersionStatus(const SoftwareVersion& version) { this->pb->setHidden(true); - latestVersionLabel->setText("<center><b>" + QString(_("Latest Version From Github")) + ": " + - server + "</b></center>"); - - if (current < server) { + version.latest_version.c_str() + "</b></center>"); + + if (version.NeedUpgrade()) { + upgradeLabel->setText( + "<center>" + + QString(_("The current version is less than the latest version on " + "github.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); + upgradeLabel->show(); + } else if (version.VersionWithDrawn()) { + upgradeLabel->setText( + "<center>" + + QString(_("This version has serious problems and has been withdrawn. " + "Please stop using it immediately.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); + upgradeLabel->show(); + } else if (!version.CurrentVersionReleased()) { + upgradeLabel->setText( + "<center>" + + QString(_("This version has not been released yet, it may be a beta " + "version. If you are not a tester and care about version " + "stability, please do not use this version.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); upgradeLabel->show(); } } diff --git a/src/ui/help/AboutDialog.h b/src/ui/help/AboutDialog.h index 78c98149..44ab0e3c 100644 --- a/src/ui/help/AboutDialog.h +++ b/src/ui/help/AboutDialog.h @@ -27,6 +27,8 @@ #include "gpg/GpgContext.h" #include "ui/GpgFrontendUI.h" +#include "ui/data_struct/SoftwareVersion.h" + namespace GpgFrontend::UI { /** @@ -73,7 +75,7 @@ class UpdateTab : public QWidget { void getLatestVersion(); private slots: - void slotShowVersionStatus(const QString& current, const QString& server); + void slotShowVersionStatus(const SoftwareVersion& version); signals: void replyFromUpdateServer(QByteArray data); diff --git a/src/ui/keygen/KeygenDialog.cpp b/src/ui/keygen/KeygenDialog.cpp index 98ea1d5c..2baa22f4 100644 --- a/src/ui/keygen/KeygenDialog.cpp +++ b/src/ui/keygen/KeygenDialog.cpp @@ -27,6 +27,7 @@ #include "gpg/function/GpgKeyOpera.h" #include "ui/SignalStation.h" #include "ui/WaitingDialog.h" +#include "ui/settings/GlobalSettingStation.h" namespace GpgFrontend::UI { @@ -34,6 +35,22 @@ KeyGenDialog::KeyGenDialog(QWidget* parent) : QDialog(parent) { buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + // max expire date time + 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"); + } + + max_date_time_ = longer_expiration_date + ? QDateTime::currentDateTime().toLocalTime().addYears(30) + : QDateTime::currentDateTime().toLocalTime().addYears(2); + this->setWindowTitle(_("Generate Key")); this->setModal(true); @@ -83,9 +100,8 @@ void KeyGenDialog::slotKeyGenAccept() { * primary keys should have a reasonable expiration date (no more than 2 years * in the future) */ - if (dateEdit->dateTime() > QDateTime::currentDateTime().addYears(2)) { - error_stream << " " << _("Expiration time no more than 2 years.") - << std::endl; + if (dateEdit->dateTime() > max_date_time_) { + error_stream << " " << _("Expiration time too long.") << std::endl; } auto err_string = error_stream.str(); @@ -94,24 +110,24 @@ void KeyGenDialog::slotKeyGenAccept() { /** * create the string for key generation */ + gen_key_info_->setName(nameEdit->text().toStdString()); + gen_key_info_->setEmail(emailEdit->text().toStdString()); + gen_key_info_->setComment(commentEdit->text().toStdString()); - genKeyInfo->setUserid( - QString("%1(%3)<%2>") - .arg(nameEdit->text(), emailEdit->text(), commentEdit->text()) - .toStdString()); - - genKeyInfo->setKeySize(keySizeSpinBox->value()); + gen_key_info_->setKeySize(keySizeSpinBox->value()); if (expireCheckBox->checkState()) { - genKeyInfo->setNonExpired(true); + gen_key_info_->setNonExpired(true); } else { - genKeyInfo->setExpired( + gen_key_info_->setExpired( boost::posix_time::from_time_t(dateEdit->dateTime().toTime_t())); } + GpgGenKeyResult result; gpgme_error_t error = false; - auto thread = QThread::create( - [&]() { error = GpgKeyOpera::GetInstance().GenerateKey(genKeyInfo); }); + auto thread = QThread::create([&]() { + error = GpgKeyOpera::GetInstance().GenerateKey(gen_key_info_, result); + }); thread->start(); auto* dialog = new WaitingDialog(_("Generating"), this); @@ -178,10 +194,10 @@ QGroupBox* KeyGenDialog::create_key_usage_group_box() { auto* auth = new QCheckBox(_("Authentication"), groupBox); auth->setTristate(false); - keyUsageCheckBoxes.push_back(encrypt); - keyUsageCheckBoxes.push_back(sign); - keyUsageCheckBoxes.push_back(cert); - keyUsageCheckBoxes.push_back(auth); + key_usage_check_boxes_.push_back(encrypt); + key_usage_check_boxes_.push_back(sign); + key_usage_check_boxes_.push_back(cert); + key_usage_check_boxes_.push_back(auth); grid->addWidget(encrypt, 0, 0); grid->addWidget(sign, 0, 1); @@ -195,95 +211,95 @@ QGroupBox* KeyGenDialog::create_key_usage_group_box() { void KeyGenDialog::slotEncryptionBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowEncryption(false); + gen_key_info_->setAllowEncryption(false); } else { - genKeyInfo->setAllowEncryption(true); + gen_key_info_->setAllowEncryption(true); } } void KeyGenDialog::slotSigningBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowSigning(false); + gen_key_info_->setAllowSigning(false); } else { - genKeyInfo->setAllowSigning(true); + gen_key_info_->setAllowSigning(true); } } void KeyGenDialog::slotCertificationBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowCertification(false); + gen_key_info_->setAllowCertification(false); } else { - genKeyInfo->setAllowCertification(true); + gen_key_info_->setAllowCertification(true); } } void KeyGenDialog::slotAuthenticationBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowAuthentication(false); + gen_key_info_->setAllowAuthentication(false); } else { - genKeyInfo->setAllowAuthentication(true); + gen_key_info_->setAllowAuthentication(true); } } void KeyGenDialog::slotActivatedKeyType(int index) { qDebug() << "key type index changed " << index; - genKeyInfo->setAlgo(this->keyTypeComboBox->itemText(index).toStdString()); + gen_key_info_->setAlgo(this->keyTypeComboBox->itemText(index).toStdString()); refresh_widgets_state(); } void KeyGenDialog::refresh_widgets_state() { qDebug() << "refresh_widgets_state called"; - if (genKeyInfo->isAllowEncryption()) - keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowEncryption()) + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeEncryption()) - keyUsageCheckBoxes[0]->setDisabled(false); + if (gen_key_info_->isAllowChangeEncryption()) + key_usage_check_boxes_[0]->setDisabled(false); else - keyUsageCheckBoxes[0]->setDisabled(true); + key_usage_check_boxes_[0]->setDisabled(true); - if (genKeyInfo->isAllowSigning()) - keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowSigning()) + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeSigning()) - keyUsageCheckBoxes[1]->setDisabled(false); + if (gen_key_info_->isAllowChangeSigning()) + key_usage_check_boxes_[1]->setDisabled(false); else - keyUsageCheckBoxes[1]->setDisabled(true); + key_usage_check_boxes_[1]->setDisabled(true); - if (genKeyInfo->isAllowCertification()) - keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowCertification()) + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeCertification()) - keyUsageCheckBoxes[2]->setDisabled(false); + if (gen_key_info_->isAllowChangeCertification()) + key_usage_check_boxes_[2]->setDisabled(false); else - keyUsageCheckBoxes[2]->setDisabled(true); + key_usage_check_boxes_[2]->setDisabled(true); - if (genKeyInfo->isAllowAuthentication()) - keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowAuthentication()) + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeAuthentication()) - keyUsageCheckBoxes[3]->setDisabled(false); + if (gen_key_info_->isAllowChangeAuthentication()) + key_usage_check_boxes_[3]->setDisabled(false); else - keyUsageCheckBoxes[3]->setDisabled(true); + key_usage_check_boxes_[3]->setDisabled(true); - if (genKeyInfo->isAllowNoPassPhrase()) + if (gen_key_info_->isAllowNoPassPhrase()) noPassPhraseCheckBox->setDisabled(false); else noPassPhraseCheckBox->setDisabled(true); - keySizeSpinBox->setRange(genKeyInfo->getSuggestMinKeySize(), - genKeyInfo->getSuggestMaxKeySize()); - keySizeSpinBox->setValue(genKeyInfo->getKeySize()); - keySizeSpinBox->setSingleStep(genKeyInfo->getSizeChangeStep()); + keySizeSpinBox->setRange(gen_key_info_->getSuggestMinKeySize(), + gen_key_info_->getSuggestMaxKeySize()); + keySizeSpinBox->setValue(gen_key_info_->getKeySize()); + keySizeSpinBox->setSingleStep(gen_key_info_->getSizeChangeStep()); } void KeyGenDialog::set_signal_slot() { @@ -293,13 +309,13 @@ void KeyGenDialog::set_signal_slot() { connect(expireCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotExpireBoxChanged())); - connect(keyUsageCheckBoxes[0], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[0], SIGNAL(stateChanged(int)), this, SLOT(slotEncryptionBoxChanged(int))); - connect(keyUsageCheckBoxes[1], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[1], SIGNAL(stateChanged(int)), this, SLOT(slotSigningBoxChanged(int))); - connect(keyUsageCheckBoxes[2], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[2], SIGNAL(stateChanged(int)), this, SLOT(slotCertificationBoxChanged(int))); - connect(keyUsageCheckBoxes[3], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[3], SIGNAL(stateChanged(int)), this, SLOT(slotAuthenticationBoxChanged(int))); connect(keyTypeComboBox, SIGNAL(currentIndexChanged(int)), this, @@ -308,9 +324,9 @@ void KeyGenDialog::set_signal_slot() { connect(noPassPhraseCheckBox, &QCheckBox::stateChanged, this, [this](int state) -> void { if (state == 0) { - genKeyInfo->setNonPassPhrase(false); + gen_key_info_->setNonPassPhrase(false); } else { - genKeyInfo->setNonPassPhrase(true); + gen_key_info_->setNonPassPhrase(true); } }); } @@ -327,18 +343,16 @@ QGroupBox* KeyGenDialog::create_basic_info_group_box() { keySizeSpinBox = new QSpinBox(this); keyTypeComboBox = new QComboBox(this); - for (auto& algo : GenKeyInfo::SupportedKeyAlgo) { + for (auto& algo : GenKeyInfo::getSupportedKeyAlgo()) { keyTypeComboBox->addItem(QString::fromStdString(algo)); } - if (!GenKeyInfo::SupportedKeyAlgo.empty()) { + if (!GenKeyInfo::getSupportedKeyAlgo().empty()) { keyTypeComboBox->setCurrentIndex(0); } - QDateTime maxDateTime = QDateTime::currentDateTime().addYears(2); - - dateEdit = new QDateTimeEdit(maxDateTime, this); + dateEdit = new QDateTimeEdit(QDateTime::currentDateTime().addYears(2), this); dateEdit->setMinimumDateTime(QDateTime::currentDateTime()); - dateEdit->setMaximumDateTime(maxDateTime); + dateEdit->setMaximumDateTime(max_date_time_); dateEdit->setDisplayFormat("dd/MM/yyyy hh:mm:ss"); dateEdit->setCalendarPopup(true); dateEdit->setEnabled(true); diff --git a/src/ui/keygen/KeygenDialog.h b/src/ui/keygen/KeygenDialog.h index c16a2e76..1b9fdb2a 100644 --- a/src/ui/keygen/KeygenDialog.h +++ b/src/ui/keygen/KeygenDialog.h @@ -55,9 +55,9 @@ class KeyGenDialog : public QDialog { 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])+)\]))"}; - QStringList errorMessages; /** List of errors occuring when checking entries + QStringList error_messages_; /** List of errors occuring when checking entries of lineedits */ - std::unique_ptr<GenKeyInfo> genKeyInfo = std::make_unique<GenKeyInfo>(); + std::unique_ptr<GenKeyInfo> gen_key_info_ = std::make_unique<GenKeyInfo>(); QDialogButtonBox* buttonBox; /** Box for standard buttons */ QLabel* errorLabel{}; /** Label containing error message */ @@ -72,9 +72,10 @@ class KeyGenDialog : public QDialog { QGroupBox* keyUsageGroupBox{}; /** Group of Widgets detecting the usage of the Key **/ + QDateTime max_date_time_; // ENCR, SIGN, CERT, AUTH - std::vector<QCheckBox*> keyUsageCheckBoxes; + std::vector<QCheckBox*> key_usage_check_boxes_; void generateKeyDialog(); diff --git a/src/ui/keygen/SubkeyGenerateDialog.cpp b/src/ui/keygen/SubkeyGenerateDialog.cpp index 9bac076e..70d3ee3c 100644 --- a/src/ui/keygen/SubkeyGenerateDialog.cpp +++ b/src/ui/keygen/SubkeyGenerateDialog.cpp @@ -28,11 +28,28 @@ #include "gpg/function/GpgKeyOpera.h" #include "ui/SignalStation.h" #include "ui/WaitingDialog.h" +#include "ui/settings/GlobalSettingStation.h" namespace GpgFrontend::UI { SubkeyGenerateDialog::SubkeyGenerateDialog(const KeyId& key_id, QWidget* parent) - : QDialog(parent), mKey(GpgKeyGetter::GetInstance().GetKey(key_id)) { + : QDialog(parent), key_(GpgKeyGetter::GetInstance().GetKey(key_id)) { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + // max expire date time + 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"); + } + + max_date_time_ = longer_expiration_date + ? QDateTime::currentDateTime().toLocalTime().addYears(30) + : QDateTime::currentDateTime().toLocalTime().addYears(2); + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -79,10 +96,10 @@ QGroupBox* SubkeyGenerateDialog::create_key_usage_group_box() { auto* auth = new QCheckBox(_("Authentication"), groupBox); auth->setTristate(false); - keyUsageCheckBoxes.push_back(encrypt); - keyUsageCheckBoxes.push_back(sign); - keyUsageCheckBoxes.push_back(cert); - keyUsageCheckBoxes.push_back(auth); + key_usage_check_boxes_.push_back(encrypt); + key_usage_check_boxes_.push_back(sign); + key_usage_check_boxes_.push_back(cert); + key_usage_check_boxes_.push_back(auth); grid->addWidget(encrypt, 0, 0); grid->addWidget(sign, 0, 1); @@ -99,18 +116,17 @@ QGroupBox* SubkeyGenerateDialog::create_basic_info_group_box() { keySizeSpinBox = new QSpinBox(this); keyTypeComboBox = new QComboBox(this); - for (auto& algo : GenKeyInfo::SupportedSubkeyAlgo) { + for (auto& algo : GenKeyInfo::getSupportedKeyAlgo()) { keyTypeComboBox->addItem(QString::fromStdString(algo)); } - if (!GenKeyInfo::SupportedKeyAlgo.empty()) { + if (!GenKeyInfo::getSupportedKeyAlgo().empty()) { keyTypeComboBox->setCurrentIndex(0); } - QDateTime maxDateTime = QDateTime::currentDateTime().addYears(2); - dateEdit = new QDateTimeEdit(maxDateTime, this); + dateEdit = new QDateTimeEdit(QDateTime::currentDateTime().addYears(2), this); dateEdit->setMinimumDateTime(QDateTime::currentDateTime()); - dateEdit->setMaximumDateTime(maxDateTime); + dateEdit->setMaximumDateTime(max_date_time_); dateEdit->setDisplayFormat("dd/MM/yyyy hh:mm:ss"); dateEdit->setCalendarPopup(true); dateEdit->setEnabled(true); @@ -144,13 +160,13 @@ void SubkeyGenerateDialog::set_signal_slot() { connect(expireCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotExpireBoxChanged())); - connect(keyUsageCheckBoxes[0], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[0], SIGNAL(stateChanged(int)), this, SLOT(slotEncryptionBoxChanged(int))); - connect(keyUsageCheckBoxes[1], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[1], SIGNAL(stateChanged(int)), this, SLOT(slotSigningBoxChanged(int))); - connect(keyUsageCheckBoxes[2], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[2], SIGNAL(stateChanged(int)), this, SLOT(slotCertificationBoxChanged(int))); - connect(keyUsageCheckBoxes[3], SIGNAL(stateChanged(int)), this, + connect(key_usage_check_boxes_[3], SIGNAL(stateChanged(int)), this, SLOT(slotAuthenticationBoxChanged(int))); connect(keyTypeComboBox, SIGNAL(currentIndexChanged(int)), this, @@ -168,50 +184,50 @@ void SubkeyGenerateDialog::slotExpireBoxChanged() { void SubkeyGenerateDialog::refresh_widgets_state() { qDebug() << "refresh_widgets_state called"; - if (genKeyInfo->isAllowEncryption()) - keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowEncryption()) + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeEncryption()) - keyUsageCheckBoxes[0]->setDisabled(false); + if (gen_key_info_->isAllowChangeEncryption()) + key_usage_check_boxes_[0]->setDisabled(false); else - keyUsageCheckBoxes[0]->setDisabled(true); + key_usage_check_boxes_[0]->setDisabled(true); - if (genKeyInfo->isAllowSigning()) - keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowSigning()) + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeSigning()) - keyUsageCheckBoxes[1]->setDisabled(false); + if (gen_key_info_->isAllowChangeSigning()) + key_usage_check_boxes_[1]->setDisabled(false); else - keyUsageCheckBoxes[1]->setDisabled(true); + key_usage_check_boxes_[1]->setDisabled(true); - if (genKeyInfo->isAllowCertification()) - keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowCertification()) + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeCertification()) - keyUsageCheckBoxes[2]->setDisabled(false); + if (gen_key_info_->isAllowChangeCertification()) + key_usage_check_boxes_[2]->setDisabled(false); else - keyUsageCheckBoxes[2]->setDisabled(true); + key_usage_check_boxes_[2]->setDisabled(true); - if (genKeyInfo->isAllowAuthentication()) - keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Checked); + if (gen_key_info_->isAllowAuthentication()) + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Checked); else - keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Unchecked); + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Unchecked); - if (genKeyInfo->isAllowChangeAuthentication()) - keyUsageCheckBoxes[3]->setDisabled(false); + if (gen_key_info_->isAllowChangeAuthentication()) + key_usage_check_boxes_[3]->setDisabled(false); else - keyUsageCheckBoxes[3]->setDisabled(true); + key_usage_check_boxes_[3]->setDisabled(true); - keySizeSpinBox->setRange(genKeyInfo->getSuggestMinKeySize(), - genKeyInfo->getSuggestMaxKeySize()); - keySizeSpinBox->setValue(genKeyInfo->getKeySize()); - keySizeSpinBox->setSingleStep(genKeyInfo->getSizeChangeStep()); + keySizeSpinBox->setRange(gen_key_info_->getSuggestMinKeySize(), + gen_key_info_->getSuggestMaxKeySize()); + keySizeSpinBox->setValue(gen_key_info_->getKeySize()); + keySizeSpinBox->setSingleStep(gen_key_info_->getSizeChangeStep()); } void SubkeyGenerateDialog::slotKeyGenAccept() { @@ -228,19 +244,19 @@ void SubkeyGenerateDialog::slotKeyGenAccept() { auto err_string = err_stream.str(); if (err_string.empty()) { - genKeyInfo->setKeySize(keySizeSpinBox->value()); + gen_key_info_->setKeySize(keySizeSpinBox->value()); if (expireCheckBox->checkState()) { - genKeyInfo->setNonExpired(true); + gen_key_info_->setNonExpired(true); } else { - genKeyInfo->setExpired( + gen_key_info_->setExpired( boost::posix_time::from_time_t(dateEdit->dateTime().toTime_t())); } GpgError error; auto thread = QThread::create([&]() { LOG(INFO) << "Thread Started"; - error = GpgKeyOpera::GetInstance().GenerateSubkey(mKey, genKeyInfo); + error = GpgKeyOpera::GetInstance().GenerateSubkey(key_, gen_key_info_); }); thread->start(); @@ -282,39 +298,39 @@ void SubkeyGenerateDialog::slotKeyGenAccept() { void SubkeyGenerateDialog::slotEncryptionBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowEncryption(false); + gen_key_info_->setAllowEncryption(false); } else { - genKeyInfo->setAllowEncryption(true); + gen_key_info_->setAllowEncryption(true); } } void SubkeyGenerateDialog::slotSigningBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowSigning(false); + gen_key_info_->setAllowSigning(false); } else { - genKeyInfo->setAllowSigning(true); + gen_key_info_->setAllowSigning(true); } } void SubkeyGenerateDialog::slotCertificationBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowCertification(false); + gen_key_info_->setAllowCertification(false); } else { - genKeyInfo->setAllowCertification(true); + gen_key_info_->setAllowCertification(true); } } void SubkeyGenerateDialog::slotAuthenticationBoxChanged(int state) { if (state == 0) { - genKeyInfo->setAllowAuthentication(false); + gen_key_info_->setAllowAuthentication(false); } else { - genKeyInfo->setAllowAuthentication(true); + gen_key_info_->setAllowAuthentication(true); } } void SubkeyGenerateDialog::slotActivatedKeyType(int index) { qDebug() << "key type index changed " << index; - genKeyInfo->setAlgo(this->keyTypeComboBox->itemText(index).toStdString()); + gen_key_info_->setAlgo(this->keyTypeComboBox->itemText(index).toStdString()); refresh_widgets_state(); } diff --git a/src/ui/keygen/SubkeyGenerateDialog.h b/src/ui/keygen/SubkeyGenerateDialog.h index ec7c9fb1..78f3ac92 100644 --- a/src/ui/keygen/SubkeyGenerateDialog.h +++ b/src/ui/keygen/SubkeyGenerateDialog.h @@ -41,9 +41,10 @@ class SubkeyGenerateDialog : public QDialog { void SubKeyGenerated(); private: - GpgKey mKey; + GpgKey key_; - std::unique_ptr<GenKeyInfo> genKeyInfo = std::make_unique<GenKeyInfo>(true); + std::unique_ptr<GenKeyInfo> gen_key_info_ = + std::make_unique<GenKeyInfo>(true); QGroupBox* keyUsageGroupBox{}; QDialogButtonBox* buttonBox; /** Box for standardbuttons */ @@ -54,12 +55,11 @@ class SubkeyGenerateDialog : public QDialog { QCheckBox* expireCheckBox{}; /** Checkbox, if key should expire */ // ENCR, SIGN, CERT, AUTH - std::vector<QCheckBox*> keyUsageCheckBoxes; + std::vector<QCheckBox*> key_usage_check_boxes_; + QDateTime max_date_time_; QGroupBox* create_key_usage_group_box(); - QGroupBox* create_basic_info_group_box(); - void set_signal_slot(); /** diff --git a/src/ui/keypair_details/KeyDetailsDialog.cpp b/src/ui/keypair_details/KeyDetailsDialog.cpp index 3864820f..83be8af0 100644 --- a/src/ui/keypair_details/KeyDetailsDialog.cpp +++ b/src/ui/keypair_details/KeyDetailsDialog.cpp @@ -24,6 +24,11 @@ #include "ui/keypair_details/KeyDetailsDialog.h" +#include "ui/keypair_details/KeyPairDetailTab.h" +#include "ui/keypair_details/KeyPairOperaTab.h" +#include "ui/keypair_details/KeyPairSubkeyTab.h" +#include "ui/keypair_details/KeyPairUIDTab.h" + namespace GpgFrontend::UI { KeyDetailsDialog::KeyDetailsDialog(const GpgKey& key, QWidget* parent) : QDialog(parent) { @@ -31,6 +36,7 @@ KeyDetailsDialog::KeyDetailsDialog(const GpgKey& key, QWidget* parent) tabWidget->addTab(new KeyPairDetailTab(key.id(), tabWidget), _("KeyPair")); tabWidget->addTab(new KeyPairUIDTab(key.id(), tabWidget), _("UIDs")); tabWidget->addTab(new KeyPairSubkeyTab(key.id(), tabWidget), _("Subkeys")); + tabWidget->addTab(new KeyPairOperaTab(key.id(), tabWidget), _("Operations")); auto* mainLayout = new QVBoxLayout; mainLayout->addWidget(tabWidget); @@ -42,7 +48,7 @@ KeyDetailsDialog::KeyDetailsDialog(const GpgKey& key, QWidget* parent) this->setLayout(mainLayout); this->setWindowTitle(_("Key Details")); this->setModal(true); - this->setMinimumSize({520, 800}); + this->setMinimumSize({520, 600}); this->resize(this->minimumSize()); this->show(); } diff --git a/src/ui/keypair_details/KeyDetailsDialog.h b/src/ui/keypair_details/KeyDetailsDialog.h index 51fc01cf..72ae3f57 100644 --- a/src/ui/keypair_details/KeyDetailsDialog.h +++ b/src/ui/keypair_details/KeyDetailsDialog.h @@ -25,9 +25,6 @@ #ifndef __KEYDETAILSDIALOG_H__ #define __KEYDETAILSDIALOG_H__ -#include "KeyPairDetailTab.h" -#include "KeyPairSubkeyTab.h" -#include "KeyPairUIDTab.h" #include "gpg/GpgContext.h" #include "ui/GpgFrontendUI.h" diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index e4b9206a..49593d01 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -25,17 +25,18 @@ #include "ui/keypair_details/KeyPairDetailTab.h" #include "gpg/function/GpgKeyGetter.h" -#include "gpg/function/GpgKeyImportExportor.h" -#include "gpg/function/GpgKeyOpera.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "ui/SignalStation.h" -#include "ui/UserInterfaceUtils.h" #include "ui/WaitingDialog.h" namespace GpgFrontend::UI { KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) - : QWidget(parent), mKey(GpgKeyGetter::GetInstance().GetKey(key_id)) { + : QWidget(parent), key_(GpgKeyGetter::GetInstance().GetKey(key_id)) { + + LOG(INFO) << key_.email() <<key_.is_private_key() << key_.has_master_key() << key_.subKeys()->front().is_private_key(); + ownerBox = new QGroupBox(_("Owner")); - keyBox = new QGroupBox(_("Master Key")); + keyBox = new QGroupBox(_("Primary Key")); fingerprintBox = new QGroupBox(_("Fingerprint")); additionalUidBox = new QGroupBox(_("Additional UIDs")); @@ -81,7 +82,7 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) 0); vboxKD->addWidget(new QLabel(QString(_("Last Update (Local Time)")) + ": "), 7, 0); - vboxKD->addWidget(new QLabel(QString(_("Master Key Existence")) + ": "), 8, + vboxKD->addWidget(new QLabel(QString(_("Primary Key Existence")) + ": "), 8, 0); keyidVarLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -133,66 +134,6 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) mvbox->addWidget(fingerprintBox); mvbox->addStretch(); - // Set Menu - createOperaMenu(); - - auto* opera_key_box = new QGroupBox(_("Operations")); - auto* vbox_p_k = new QVBoxLayout(); - - auto export_h_box_layout = new QHBoxLayout(); - vbox_p_k->addLayout(export_h_box_layout); - - auto* export_public_button = new QPushButton(_("Export Public Key")); - export_h_box_layout->addWidget(export_public_button); - connect(export_public_button, SIGNAL(clicked()), this, - SLOT(slotExportPublicKey())); - - if (mKey.is_private_key()) { - auto* export_private_button = new QPushButton(_("Export Private Key")); - export_private_button->setStyleSheet("text-align:center;"); - export_private_button->setMenu(secretKeyExportOperaMenu); - export_h_box_layout->addWidget(export_private_button); - - if (mKey.has_master_key()) { - auto* edit_expires_button = - new QPushButton(_("Modify Expiration Datetime (Master Key)")); - connect(edit_expires_button, SIGNAL(clicked()), this, - SLOT(slotModifyEditDatetime())); - auto* edit_password_button = new QPushButton(_("Modify Password")); - connect(edit_password_button, SIGNAL(clicked()), this, - SLOT(slotModifyPassword())); - - auto edit_h_box_layout = new QHBoxLayout(); - edit_h_box_layout->addWidget(edit_expires_button); - edit_h_box_layout->addWidget(edit_password_button); - vbox_p_k->addLayout(edit_h_box_layout); - } - } - - auto advance_h_box_layout = new QHBoxLayout(); - auto* key_server_opera_button = - new QPushButton(_("Key Server Operation (Pubkey)")); - key_server_opera_button->setStyleSheet("text-align:center;"); - key_server_opera_button->setMenu(keyServerOperaMenu); - advance_h_box_layout->addWidget(key_server_opera_button); - - if (mKey.is_private_key() && mKey.has_master_key()) { - auto* revoke_cert_gen_button = - new QPushButton(_("Generate Revoke Certificate")); - connect(revoke_cert_gen_button, SIGNAL(clicked()), this, - SLOT(slotGenRevokeCert())); - advance_h_box_layout->addWidget(revoke_cert_gen_button); - } - - auto* modify_tofu_button = new QPushButton(_("Modify TOFU Policy")); - connect(modify_tofu_button, SIGNAL(clicked()), this, - SLOT(slotModifyTOFUPolicy())); - - vbox_p_k->addLayout(advance_h_box_layout); - opera_key_box->setLayout(vbox_p_k); - mvbox->addWidget(opera_key_box); - vbox_p_k->addWidget(modify_tofu_button); - auto* expBox = new QHBoxLayout(); QPixmap pixmap(":warning.png"); @@ -217,142 +158,28 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) setLayout(mvbox); } -void KeyPairDetailTab::slotExportPublicKey() { - ByteArrayPtr keyArray = nullptr; - - if (!GpgKeyImportExportor::GetInstance().ExportKey(mKey, keyArray)) { - QMessageBox::critical(this, _("Error"), - _("An error occurred during the export operation.")); - return; - } - auto file_string = - mKey.name() + " " + mKey.email() + "(" + mKey.id() + ")_pub.asc"; - auto file_name = - QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(file_string), - QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") - .toStdString(); - - 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(file_name.c_str())); - return; - } -} - -void KeyPairDetailTab::slotExportShortPrivateKey() { - // Show a information box with explanation about private key - int ret = QMessageBox::information( - this, _("Exporting short private Key"), - "<h3>" + QString(_("You are about to export your")) + - "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + - _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + - _("Do you REALLY want to export your PRIVATE KEY in a Minimum " - "Size?") + - "<br />" + - _("For OpenPGP keys it removes all signatures except for the latest " - "self-signatures."), - QMessageBox::Cancel | QMessageBox::Ok); - - // export key, if ok was clicked - if (ret == QMessageBox::Ok) { - ByteArrayPtr keyArray = nullptr; - - if (!GpgKeyImportExportor::GetInstance().ExportSecretKeyShortest( - mKey, keyArray)) { - QMessageBox::critical( - this, _("Error"), - _("An error occurred during the export operation.")); - return; - } - auto file_string = mKey.name() + " " + mKey.email() + "(" + mKey.id() + - ")_short_secret.asc"; - auto file_name = - QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(file_string), - QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") - .toStdString(); - - 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(file_name.c_str())); - return; - } - } -} - -void KeyPairDetailTab::slotExportPrivateKey() { - // Show a information box with explanation about private key - int ret = QMessageBox::information( - this, _("Exporting private Key"), - "<h3>" + QString(_("You are about to export your")) + - "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + - _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + - _("Do you REALLY want to export your PRIVATE KEY?"), - QMessageBox::Cancel | QMessageBox::Ok); - - // export key, if ok was clicked - if (ret == QMessageBox::Ok) { - ByteArrayPtr keyArray = nullptr; - - if (!GpgKeyImportExportor::GetInstance().ExportSecretKey(mKey, keyArray)) { - QMessageBox::critical( - this, _("Error"), - _("An error occurred during the export operation.")); - return; - } - auto file_string = mKey.name() + " " + mKey.email() + "(" + mKey.id() + - ")_full_secret.asc"; - auto file_name = - QFileDialog::getSaveFileName( - this, _("Export Key To File"), QString::fromStdString(file_string), - QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") - .toStdString(); - - 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(file_name.c_str())); - return; - } - } -} - void KeyPairDetailTab::slotCopyFingerprint() { QString fpr = fingerPrintVarLabel->text().trimmed().replace(" ", QString()); QClipboard* cb = QApplication::clipboard(); cb->setText(fpr); } -void KeyPairDetailTab::slotModifyEditDatetime() { - auto dialog = new KeySetExpireDateDialog(mKey.id(), this); - dialog->show(); -} - void KeyPairDetailTab::slotRefreshKeyInfo() { - // Show the situation that master key not exists. - masterKeyExistVarLabel->setText(mKey.has_master_key() ? _("Exists") + // Show the situation that primary key not exists. + masterKeyExistVarLabel->setText(key_.has_master_key() ? _("Exists") : _("Not Exists")); - if (!mKey.has_master_key()) { - auto paletteExpired = masterKeyExistVarLabel->palette(); - paletteExpired.setColor(masterKeyExistVarLabel->foregroundRole(), Qt::red); - masterKeyExistVarLabel->setPalette(paletteExpired); + if (!key_.has_master_key()) { + auto palette_expired = masterKeyExistVarLabel->palette(); + palette_expired.setColor(masterKeyExistVarLabel->foregroundRole(), Qt::red); + masterKeyExistVarLabel->setPalette(palette_expired); } else { - auto paletteValid = masterKeyExistVarLabel->palette(); - paletteValid.setColor(masterKeyExistVarLabel->foregroundRole(), - Qt::darkGreen); - masterKeyExistVarLabel->setPalette(paletteValid); + auto palette_valid = masterKeyExistVarLabel->palette(); + palette_valid.setColor(masterKeyExistVarLabel->foregroundRole(), + Qt::darkGreen); + masterKeyExistVarLabel->setPalette(palette_valid); } - if (mKey.expired()) { + if (key_.expired()) { auto paletteExpired = expireVarLabel->palette(); paletteExpired.setColor(expireVarLabel->foregroundRole(), Qt::red); expireVarLabel->setPalette(paletteExpired); @@ -362,209 +189,77 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { expireVarLabel->setPalette(paletteValid); } - nameVarLabel->setText(QString::fromStdString(mKey.name())); - emailVarLabel->setText(QString::fromStdString(mKey.email())); + nameVarLabel->setText(QString::fromStdString(key_.name())); + emailVarLabel->setText(QString::fromStdString(key_.email())); - commentVarLabel->setText(QString::fromStdString(mKey.comment())); - keyidVarLabel->setText(QString::fromStdString(mKey.id())); + commentVarLabel->setText(QString::fromStdString(key_.comment())); + keyidVarLabel->setText(QString::fromStdString(key_.id())); std::stringstream usage_steam; - if (mKey.can_certify()) usage_steam << _("Certificate") << " "; - if (mKey.can_encrypt()) usage_steam << _("Encrypt") << " "; - if (mKey.can_sign()) usage_steam << _("Sign") << " "; - if (mKey.can_authenticate()) usage_steam << _("Auth") << " "; + if (key_.can_certify()) usage_steam << _("Certificate") << " "; + if (key_.can_encrypt()) usage_steam << _("Encrypt") << " "; + if (key_.can_sign()) usage_steam << _("Sign") << " "; + if (key_.can_authenticate()) usage_steam << _("Auth") << " "; usageVarLabel->setText(usage_steam.str().c_str()); std::stringstream actual_usage_steam; - if (mKey.CanCertActual()) actual_usage_steam << _("Certificate") << " "; - if (mKey.CanEncrActual()) actual_usage_steam << _("Encrypt") << " "; - if (mKey.CanSignActual()) actual_usage_steam << _("Sign") << " "; - if (mKey.CanAuthActual()) actual_usage_steam << _("Auth") << " "; + if (key_.CanCertActual()) actual_usage_steam << _("Certificate") << " "; + if (key_.CanEncrActual()) actual_usage_steam << _("Encrypt") << " "; + if (key_.CanSignActual()) actual_usage_steam << _("Sign") << " "; + if (key_.CanAuthActual()) actual_usage_steam << _("Auth") << " "; actualUsageVarLabel->setText(actual_usage_steam.str().c_str()); std::string key_size_val, key_expire_val, key_create_time_val, key_algo_val, key_last_update_val; - key_size_val = std::to_string(mKey.length()); + key_size_val = std::to_string(key_.length()); - if (to_time_t(boost::posix_time::ptime(mKey.expires())) == 0) { + if (to_time_t(boost::posix_time::ptime(key_.expires())) == 0) { expireVarLabel->setText(_("Never Expire")); } else { expireVarLabel->setText(QLocale::system().toString( - QDateTime::fromTime_t(to_time_t(mKey.expires())))); + QDateTime::fromTime_t(to_time_t(key_.expires())))); } - key_algo_val = mKey.pubkey_algo(); + key_algo_val = key_.pubkey_algo(); createdVarLabel->setText(QLocale::system().toString( - QDateTime::fromTime_t(to_time_t(mKey.create_time())))); + QDateTime::fromTime_t(to_time_t(key_.create_time())))); - if (to_time_t(boost::posix_time::ptime(mKey.last_update())) == 0) { + if (to_time_t(boost::posix_time::ptime(key_.last_update())) == 0) { lastUpdateVarLabel->setText(_("No Data")); } else { lastUpdateVarLabel->setText(QLocale::system().toString( - QDateTime::fromTime_t(to_time_t(mKey.last_update())))); + QDateTime::fromTime_t(to_time_t(key_.last_update())))); } keySizeVarLabel->setText(key_size_val.c_str()); algorithmVarLabel->setText(key_algo_val.c_str()); - fingerPrintVarLabel->setText(beautify_fingerprint(mKey.fpr()).c_str()); + fingerPrintVarLabel->setText(beautify_fingerprint(key_.fpr()).c_str()); iconLabel->hide(); expLabel->hide(); - if (mKey.expired()) { + if (key_.expired()) { iconLabel->show(); expLabel->show(); - expLabel->setText(_("Warning: The Master Key has expired.")); + expLabel->setText(_("Warning: The primary key has expired.")); } - if (mKey.revoked()) { + if (key_.revoked()) { iconLabel->show(); expLabel->show(); - expLabel->setText(_("Warning: The Master Key has been revoked.")); + expLabel->setText(_("Warning: The primary key has been revoked.")); } } -void KeyPairDetailTab::createOperaMenu() { - keyServerOperaMenu = new QMenu(this); - - auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this); - connect(uploadKeyPair, SIGNAL(triggered()), this, - SLOT(slotUploadKeyToServer())); - if (!(mKey.is_private_key() && mKey.has_master_key())) - uploadKeyPair->setDisabled(true); - - 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); - - secretKeyExportOperaMenu = new QMenu(this); - - auto* exportFullSecretKey = new QAction(_("Export Full Secret Key"), this); - connect(exportFullSecretKey, SIGNAL(triggered()), this, - SLOT(slotExportPrivateKey())); - if (!mKey.is_private_key()) exportFullSecretKey->setDisabled(true); - - auto* exportShortestSecretKey = - new QAction(_("Export Shortest Secret Key"), this); - connect(exportShortestSecretKey, SIGNAL(triggered()), this, - SLOT(slotExportShortPrivateKey())); - - secretKeyExportOperaMenu->addAction(exportFullSecretKey); - secretKeyExportOperaMenu->addAction(exportShortestSecretKey); -} - -void KeyPairDetailTab::slotUploadKeyToServer() { - auto keys = std::make_unique<KeyIdArgsList>(); - keys->push_back(mKey.id()); - auto* dialog = new KeyUploadDialog(keys, this); - dialog->show(); - dialog->slotUpload(); -} - -void KeyPairDetailTab::slotUpdateKeyFromServer() { - auto keys = std::make_unique<KeyIdArgsList>(); - keys->push_back(mKey.id()); - auto* dialog = new KeyServerImportDialog(this); - dialog->show(); - dialog->slotImport(keys); -} - -void KeyPairDetailTab::slotGenRevokeCert() { - auto literal = QString("%1 (*.rev)").arg(_("Revocation Certificates")); - QString m_output_file_name; - - QFileDialog dialog(this, "Generate revocation certificate", QString(), - literal); - dialog.setDefaultSuffix(".rev"); - dialog.setAcceptMode(QFileDialog::AcceptSave); - - if (dialog.exec()) m_output_file_name = dialog.selectedFiles().front(); - - if (!m_output_file_name.isEmpty()) - CommonUtils::GetInstance()->slotExecuteGpgCommand( - {"--command-fd", "0", "--status-fd", "1", "--no-tty", "-o", - m_output_file_name, "--gen-revoke", mKey.fpr().c_str()}, - [](QProcess* proc) -> void { - // Code From Gpg4Win - while (proc->canReadLine()) { - const QString line = QString::fromUtf8(proc->readLine()).trimmed(); - LOG(INFO) << "line" << line.toStdString(); - if (line == QLatin1String("[GNUPG:] GET_BOOL gen_revoke.okay")) { - proc->write("y\n"); - } else if (line == QLatin1String("[GNUPG:] GET_LINE " - "ask_revocation_reason.code")) { - proc->write("0\n"); - } else if (line == QLatin1String("[GNUPG:] GET_LINE " - "ask_revocation_reason.text")) { - proc->write("\n"); - } else if (line == - QLatin1String( - "[GNUPG:] GET_BOOL openfile.overwrite.okay")) { - // We asked before - proc->write("y\n"); - } else if (line == QLatin1String("[GNUPG:] GET_BOOL " - "ask_revocation_reason.okay")) { - proc->write("y\n"); - } - } - }); -} void KeyPairDetailTab::slotRefreshKey() { LOG(INFO) << _("Called"); - this->mKey = GpgKeyGetter::GetInstance().GetKey(mKey.id()); + this->key_ = GpgKeyGetter::GetInstance().GetKey(key_.id()); this->slotRefreshKeyInfo(); } -void KeyPairDetailTab::slotModifyPassword() { - auto err = GpgKeyOpera::GetInstance().ModifyPassword(mKey); - if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { - QMessageBox::critical(this, _("Not Successful"), - QString(_("Modify password not successfully."))); - } -} - -void KeyPairDetailTab::slotModifyTOFUPolicy() { - QStringList items; - items << _("Policy Auto") << _("Policy Good") << _("Policy Bad") - << _("Policy Ask") << _("Policy Unknown"); - - bool ok; - QString item = QInputDialog::getItem( - this, _("Modify TOFU Policy(Default is Auto)"), - _("Policy for the Key Pair:"), items, 0, false, &ok); - if (ok && !item.isEmpty()) { - LOG(INFO) << "selected policy" << item.toStdString(); - gpgme_tofu_policy_t tofu_policy = GPGME_TOFU_POLICY_AUTO; - if (item == _("Policy Auto")) { - tofu_policy = GPGME_TOFU_POLICY_AUTO; - } else if (item == _("Policy Good")) { - tofu_policy = GPGME_TOFU_POLICY_GOOD; - } else if (item == _("Policy Bad")) { - tofu_policy = GPGME_TOFU_POLICY_BAD; - } else if (item == _("Policy Ask")) { - tofu_policy = GPGME_TOFU_POLICY_ASK; - } else if (item == _("Policy Unknown")) { - tofu_policy = GPGME_TOFU_POLICY_UNKNOWN; - } - auto err = GpgKeyOpera::GetInstance().ModifyTOFUPolicy(mKey, tofu_policy); - if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { - QMessageBox::critical(this, _("Not Successful"), - QString(_("Modify TOFU policy not successfully."))); - } - } -} - } // namespace GpgFrontend::UI diff --git a/src/ui/keypair_details/KeyPairDetailTab.h b/src/ui/keypair_details/KeyPairDetailTab.h index 3e2f2298..151d8fc7 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.h +++ b/src/ui/keypair_details/KeyPairDetailTab.h @@ -36,42 +36,19 @@ namespace GpgFrontend::UI { class KeyPairDetailTab : public QWidget { Q_OBJECT - void createOperaMenu(); - private slots: /** - * @details Export the key to a file, which is chosen in a file dialog - */ - void slotExportPrivateKey(); - - void slotExportShortPrivateKey(); - - void slotExportPublicKey(); - - /** * @details Copy the fingerprint to clipboard */ void slotCopyFingerprint(); - void slotModifyEditDatetime(); - - void slotModifyPassword(); - void slotRefreshKeyInfo(); - void slotUploadKeyToServer(); - - void slotUpdateKeyFromServer(); - - void slotGenRevokeCert(); - void slotRefreshKey(); - void slotModifyTOFUPolicy(); - private: - GpgKey mKey; + GpgKey key_; QGroupBox* ownerBox; /** Groupbox containing owner information */ QGroupBox* keyBox; /** Groupbox containing key information */ diff --git a/src/ui/keypair_details/KeyPairOperaTab.cpp b/src/ui/keypair_details/KeyPairOperaTab.cpp new file mode 100644 index 00000000..57eb8164 --- /dev/null +++ b/src/ui/keypair_details/KeyPairOperaTab.cpp @@ -0,0 +1,349 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#include "KeyPairOperaTab.h" + +#include "gpg/function/GpgKeyImportExporter.h" +#include "gpg/function/GpgKeyOpera.h" +#include "ui/KeyUploadDialog.h" +#include "ui/SignalStation.h" +#include "ui/UserInterfaceUtils.h" +#include "ui/keypair_details/KeySetExpireDateDialog.h" + +namespace GpgFrontend::UI { + +KeyPairOperaTab::KeyPairOperaTab(const std::string& key_id, QWidget* parent) + : QWidget(parent), m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)) { + // Set Menu + createOperaMenu(); + auto m_vbox = new QVBoxLayout(this); + + auto* opera_key_box = new QGroupBox(_("General Operations")); + auto* vbox_p_k = new QVBoxLayout(); + + auto export_h_box_layout = new QHBoxLayout(); + vbox_p_k->addLayout(export_h_box_layout); + + auto* export_public_button = new QPushButton(_("Export Public Key")); + export_h_box_layout->addWidget(export_public_button); + connect(export_public_button, SIGNAL(clicked()), this, + SLOT(slotExportPublicKey())); + + if (m_key_.is_private_key()) { + auto* export_private_button = new QPushButton(_("Export Private Key")); + export_private_button->setStyleSheet("text-align:center;"); + export_private_button->setMenu(secretKeyExportOperaMenu); + export_h_box_layout->addWidget(export_private_button); + + if (m_key_.has_master_key()) { + auto* edit_expires_button = + new QPushButton(_("Modify Expiration Datetime (Primary Key)")); + connect(edit_expires_button, SIGNAL(clicked()), this, + SLOT(slotModifyEditDatetime())); + auto* edit_password_button = new QPushButton(_("Modify Password")); + connect(edit_password_button, SIGNAL(clicked()), this, + SLOT(slotModifyPassword())); + + vbox_p_k->addWidget(edit_expires_button); + vbox_p_k->addWidget(edit_password_button); + } + } + + auto advance_h_box_layout = new QHBoxLayout(); + auto* key_server_opera_button = + new QPushButton(_("Key Server Operation (Pubkey)")); + key_server_opera_button->setStyleSheet("text-align:center;"); + key_server_opera_button->setMenu(keyServerOperaMenu); + advance_h_box_layout->addWidget(key_server_opera_button); + + if (m_key_.is_private_key() && m_key_.has_master_key()) { + auto* revoke_cert_gen_button = + new QPushButton(_("Generate Revoke Certificate")); + connect(revoke_cert_gen_button, SIGNAL(clicked()), this, + SLOT(slotGenRevokeCert())); + advance_h_box_layout->addWidget(revoke_cert_gen_button); + } + + auto* modify_tofu_button = new QPushButton(_("Modify TOFU Policy")); + connect(modify_tofu_button, SIGNAL(clicked()), this, + SLOT(slotModifyTOFUPolicy())); + + vbox_p_k->addLayout(advance_h_box_layout); + opera_key_box->setLayout(vbox_p_k); + m_vbox->addWidget(opera_key_box); + vbox_p_k->addWidget(modify_tofu_button); + m_vbox->addStretch(0); + + setLayout(m_vbox); +} + +void KeyPairOperaTab::createOperaMenu() { + keyServerOperaMenu = new QMenu(this); + + auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this); + connect(uploadKeyPair, SIGNAL(triggered()), this, + SLOT(slotUploadKeyToServer())); + if (!(m_key_.is_private_key() && m_key_.has_master_key())) + uploadKeyPair->setDisabled(true); + + auto* updateKeyPair = new QAction(_("Sync Key Pair From Key Server"), this); + connect(updateKeyPair, SIGNAL(triggered()), this, + SLOT(slotUpdateKeyFromServer())); + + // when a key has primary key, it should always upload to keyserver. + if (m_key_.has_master_key()) { + updateKeyPair->setDisabled(true); + } + + keyServerOperaMenu->addAction(uploadKeyPair); + keyServerOperaMenu->addAction(updateKeyPair); + + secretKeyExportOperaMenu = new QMenu(this); + + auto* exportFullSecretKey = new QAction(_("Export Full Secret Key"), this); + connect(exportFullSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportPrivateKey())); + if (!m_key_.is_private_key()) exportFullSecretKey->setDisabled(true); + + auto* exportShortestSecretKey = + new QAction(_("Export Shortest Secret Key"), this); + connect(exportShortestSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportShortPrivateKey())); + + secretKeyExportOperaMenu->addAction(exportFullSecretKey); + secretKeyExportOperaMenu->addAction(exportShortestSecretKey); +} + +void KeyPairOperaTab::slotExportPublicKey() { + ByteArrayPtr keyArray = nullptr; + + if (!GpgKeyImportExporter::GetInstance().ExportKey(m_key_, keyArray)) { + QMessageBox::critical(this, _("Error"), + _("An error occurred during the export operation.")); + return; + } + auto file_string = + m_key_.name() + " " + m_key_.email() + "(" + m_key_.id() + ")_pub.asc"; + auto file_name = + QFileDialog::getSaveFileName( + this, _("Export Key To File"), QString::fromStdString(file_string), + QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") + .toStdString(); + + 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(file_name.c_str())); + return; + } +} + +void KeyPairOperaTab::slotExportShortPrivateKey() { + // Show a information box with explanation about private key + int ret = QMessageBox::information( + this, _("Exporting short private Key"), + "<h3>" + QString(_("You are about to export your")) + + "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + + _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + + _("Do you REALLY want to export your PRIVATE KEY in a Minimum " + "Size?") + + "<br />" + + _("For OpenPGP keys it removes all signatures except for the latest " + "self-signatures."), + QMessageBox::Cancel | QMessageBox::Ok); + + // export key, if ok was clicked + if (ret == QMessageBox::Ok) { + ByteArrayPtr keyArray = nullptr; + + if (!GpgKeyImportExporter::GetInstance().ExportSecretKeyShortest( + m_key_, keyArray)) { + QMessageBox::critical( + this, _("Error"), + _("An error occurred during the export operation.")); + return; + } + auto file_string = m_key_.name() + " " + m_key_.email() + "(" + + m_key_.id() + ")_short_secret.asc"; + auto file_name = + QFileDialog::getSaveFileName( + this, _("Export Key To File"), QString::fromStdString(file_string), + QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") + .toStdString(); + + 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(file_name.c_str())); + return; + } + } +} + +void KeyPairOperaTab::slotExportPrivateKey() { + // Show a information box with explanation about private key + int ret = QMessageBox::information( + this, _("Exporting private Key"), + "<h3>" + QString(_("You are about to export your")) + + "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + + _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + + _("Do you REALLY want to export your PRIVATE KEY?"), + QMessageBox::Cancel | QMessageBox::Ok); + + // export key, if ok was clicked + if (ret == QMessageBox::Ok) { + ByteArrayPtr keyArray = nullptr; + + if (!GpgKeyImportExporter::GetInstance().ExportSecretKey(m_key_, + keyArray)) { + QMessageBox::critical( + this, _("Error"), + _("An error occurred during the export operation.")); + return; + } + auto file_string = m_key_.name() + " " + m_key_.email() + "(" + + m_key_.id() + ")_full_secret.asc"; + auto file_name = + QFileDialog::getSaveFileName( + this, _("Export Key To File"), QString::fromStdString(file_string), + QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") + .toStdString(); + + 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(file_name.c_str())); + return; + } + } +} + +void KeyPairOperaTab::slotModifyEditDatetime() { + auto dialog = new KeySetExpireDateDialog(m_key_.id(), this); + dialog->show(); +} + +void KeyPairOperaTab::slotUploadKeyToServer() { + auto keys = std::make_unique<KeyIdArgsList>(); + keys->push_back(m_key_.id()); + auto* dialog = new KeyUploadDialog(keys, this); + dialog->show(); + dialog->slotUpload(); +} + +void KeyPairOperaTab::slotUpdateKeyFromServer() { + auto keys = std::make_unique<KeyIdArgsList>(); + keys->push_back(m_key_.id()); + auto* dialog = new KeyServerImportDialog(this); + dialog->show(); + dialog->slotImport(keys); +} + +void KeyPairOperaTab::slotGenRevokeCert() { + auto literal = QString("%1 (*.rev)").arg(_("Revocation Certificates")); + QString m_output_file_name; + + QFileDialog dialog(this, "Generate revocation certificate", QString(), + literal); + dialog.setDefaultSuffix(".rev"); + dialog.setAcceptMode(QFileDialog::AcceptSave); + + if (dialog.exec()) m_output_file_name = dialog.selectedFiles().front(); + + if (!m_output_file_name.isEmpty()) + CommonUtils::GetInstance()->slotExecuteGpgCommand( + {"--command-fd", "0", "--status-fd", "1", "--no-tty", "-o", + m_output_file_name, "--gen-revoke", m_key_.fpr().c_str()}, + [](QProcess* proc) -> void { + // Code From Gpg4Win + while (proc->canReadLine()) { + const QString line = QString::fromUtf8(proc->readLine()).trimmed(); + LOG(INFO) << "line" << line.toStdString(); + if (line == QLatin1String("[GNUPG:] GET_BOOL gen_revoke.okay")) { + proc->write("y\n"); + } else if (line == QLatin1String("[GNUPG:] GET_LINE " + "ask_revocation_reason.code")) { + proc->write("0\n"); + } else if (line == QLatin1String("[GNUPG:] GET_LINE " + "ask_revocation_reason.text")) { + proc->write("\n"); + } else if (line == + QLatin1String( + "[GNUPG:] GET_BOOL openfile.overwrite.okay")) { + // We asked before + proc->write("y\n"); + } else if (line == QLatin1String("[GNUPG:] GET_BOOL " + "ask_revocation_reason.okay")) { + proc->write("y\n"); + } + } + }); +} + +void KeyPairOperaTab::slotModifyPassword() { + auto err = GpgKeyOpera::GetInstance().ModifyPassword(m_key_); + if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { + QMessageBox::critical(this, _("Not Successful"), + QString(_("Modify password not successfully."))); + } +} + +void KeyPairOperaTab::slotModifyTOFUPolicy() { + QStringList items; + items << _("Policy Auto") << _("Policy Good") << _("Policy Bad") + << _("Policy Ask") << _("Policy Unknown"); + + bool ok; + QString item = QInputDialog::getItem( + this, _("Modify TOFU Policy(Default is Auto)"), + _("Policy for the Key Pair:"), items, 0, false, &ok); + if (ok && !item.isEmpty()) { + LOG(INFO) << "selected policy" << item.toStdString(); + gpgme_tofu_policy_t tofu_policy = GPGME_TOFU_POLICY_AUTO; + if (item == _("Policy Auto")) { + tofu_policy = GPGME_TOFU_POLICY_AUTO; + } else if (item == _("Policy Good")) { + tofu_policy = GPGME_TOFU_POLICY_GOOD; + } else if (item == _("Policy Bad")) { + tofu_policy = GPGME_TOFU_POLICY_BAD; + } else if (item == _("Policy Ask")) { + tofu_policy = GPGME_TOFU_POLICY_ASK; + } else if (item == _("Policy Unknown")) { + tofu_policy = GPGME_TOFU_POLICY_UNKNOWN; + } + auto err = GpgKeyOpera::GetInstance().ModifyTOFUPolicy(m_key_, tofu_policy); + if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) { + QMessageBox::critical(this, _("Not Successful"), + QString(_("Modify TOFU policy not successfully."))); + } + } +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/keypair_details/KeyPairOperaTab.h b/src/ui/keypair_details/KeyPairOperaTab.h new file mode 100644 index 00000000..f8a5ded9 --- /dev/null +++ b/src/ui/keypair_details/KeyPairOperaTab.h @@ -0,0 +1,69 @@ +/** + * 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]><[email protected]> starting on May 12, 2021. + * + */ + +#ifndef GPGFRONTEND_KEYPAIROPERATAB_H +#define GPGFRONTEND_KEYPAIROPERATAB_H + +#include "gpg/function/GpgKeyGetter.h" +#include "ui/GpgFrontendUI.h" + +namespace GpgFrontend::UI { +class KeyPairOperaTab : public QWidget { + Q_OBJECT + public: + KeyPairOperaTab(const std::string& key_id, QWidget* parent); + + void createOperaMenu(); + + private slots: + + /** + * @details Export the key to a file, which is chosen in a file dialog + */ + void slotExportPrivateKey(); + + void slotExportShortPrivateKey(); + + void slotExportPublicKey(); + + void slotModifyEditDatetime(); + + void slotModifyPassword(); + + void slotUploadKeyToServer(); + + void slotUpdateKeyFromServer(); + + void slotGenRevokeCert(); + + void slotModifyTOFUPolicy(); + + private: + GpgKey m_key_; + QMenu* keyServerOperaMenu{}; + QMenu* secretKeyExportOperaMenu{}; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_KEYPAIROPERATAB_H diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp index 8068d9a8..a80eb5e4 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp @@ -30,7 +30,10 @@ namespace GpgFrontend::UI { KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) - : QWidget(parent), mKey(GpgKeyGetter::GetInstance().GetKey(key_id)) { + : QWidget(parent), key_(GpgKeyGetter::GetInstance().GetKey(key_id)) { + + LOG(INFO) << key_.email() <<key_.is_private_key() << key_.has_master_key() << key_.subKeys()->front().is_private_key(); + createSubkeyList(); createSubkeyOperaMenu(); @@ -40,7 +43,7 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) auto uidButtonsLayout = new QGridLayout(); auto addSubkeyButton = new QPushButton(_("Generate A New Subkey")); - if (!mKey.is_private_key() || !mKey.has_master_key()) { + if (!key_.is_private_key() || !key_.has_master_key()) { addSubkeyButton->setDisabled(true); setHidden(addSubkeyButton); } @@ -68,17 +71,20 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) new QLabel(QString(_("Create Date (Local Time)")) + ": "), 5, 0); subkeyDetailLayout->addWidget(new QLabel(QString(_("Existence")) + ": "), 6, 0); - subkeyDetailLayout->addWidget(new QLabel(QString(_("Fingerprint")) + ": "), 7, + subkeyDetailLayout->addWidget( + new QLabel(QString(_("Key in Smart Card")) + ": "), 7, 0); + subkeyDetailLayout->addWidget(new QLabel(QString(_("Fingerprint")) + ": "), 8, 0); - keyidVarLabel = new QLabel(); - keySizeVarLabel = new QLabel(); - expireVarLabel = new QLabel(); - algorithmVarLabel = new QLabel(); - createdVarLabel = new QLabel(); - usageVarLabel = new QLabel(); - masterKeyExistVarLabel = new QLabel(); - fingerPrintVarLabel = new QLabel(); + keyidVarLabel = new QLabel(this); + keySizeVarLabel = new QLabel(this); + expireVarLabel = new QLabel(this); + algorithmVarLabel = new QLabel(this); + createdVarLabel = new QLabel(this); + usageVarLabel = new QLabel(this); + masterKeyExistVarLabel = new QLabel(this); + fingerPrintVarLabel = new QLabel(this); + cardKeyLabel = new QLabel(this); subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1, 1, 1); subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1, 1, 2); @@ -87,7 +93,8 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) subkeyDetailLayout->addWidget(createdVarLabel, 5, 1, 1, 2); subkeyDetailLayout->addWidget(usageVarLabel, 3, 1, 1, 2); subkeyDetailLayout->addWidget(masterKeyExistVarLabel, 6, 1, 1, 2); - subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1, 1, 2); + subkeyDetailLayout->addWidget(cardKeyLabel, 7, 1, 1, 2); + subkeyDetailLayout->addWidget(fingerPrintVarLabel, 8, 1, 1, 2); auto* copyKeyIdButton = new QPushButton(_("Copy")); copyKeyIdButton->setFlat(true); @@ -157,7 +164,7 @@ void KeyPairSubkeyTab::slotRefreshSubkeyList() { subkeyList->setSelectionMode(QAbstractItemView::SingleSelection); this->buffered_subkeys.clear(); - auto sub_keys = mKey.subKeys(); + auto sub_keys = key_.subKeys(); for (auto& sub_key : *sub_keys) { if (sub_key.disabled() || sub_key.revoked()) continue; this->buffered_subkeys.push_back(std::move(sub_key)); @@ -207,7 +214,7 @@ void KeyPairSubkeyTab::slotRefreshSubkeyList() { } void KeyPairSubkeyTab::slotAddSubkey() { - auto dialog = new SubkeyGenerateDialog(mKey.id(), this); + auto dialog = new SubkeyGenerateDialog(key_.id(), this); dialog->show(); } @@ -248,18 +255,32 @@ void KeyPairSubkeyTab::slotRefreshSubkeyDetail() { usageVarLabel->setText(usage_steam.str().c_str()); - // Show the situation that master key not exists. + // Show the situation that secret key not exists. masterKeyExistVarLabel->setText(subkey.secret() ? _("Exists") : _("Not Exists")); + + // Show the situation if key in a smart card. + cardKeyLabel->setText(subkey.is_cardkey() ? _("Yes") : _("No")); + if (!subkey.secret()) { - auto paletteExpired = masterKeyExistVarLabel->palette(); - paletteExpired.setColor(masterKeyExistVarLabel->foregroundRole(), Qt::red); - masterKeyExistVarLabel->setPalette(paletteExpired); + auto palette_expired = masterKeyExistVarLabel->palette(); + palette_expired.setColor(masterKeyExistVarLabel->foregroundRole(), Qt::red); + masterKeyExistVarLabel->setPalette(palette_expired); + } else { + auto palette_valid = masterKeyExistVarLabel->palette(); + palette_valid.setColor(masterKeyExistVarLabel->foregroundRole(), + Qt::darkGreen); + masterKeyExistVarLabel->setPalette(palette_valid); + } + + if (!subkey.is_cardkey()) { + auto palette_expired = cardKeyLabel->palette(); + palette_expired.setColor(cardKeyLabel->foregroundRole(), Qt::red); + cardKeyLabel->setPalette(palette_expired); } else { - auto paletteValid = masterKeyExistVarLabel->palette(); - paletteValid.setColor(masterKeyExistVarLabel->foregroundRole(), - Qt::darkGreen); - masterKeyExistVarLabel->setPalette(paletteValid); + auto palette_valid = cardKeyLabel->palette(); + palette_valid.setColor(cardKeyLabel->foregroundRole(), Qt::darkGreen); + cardKeyLabel->setPalette(palette_valid); } fingerPrintVarLabel->setText(QString::fromStdString(subkey.fpr())); @@ -279,7 +300,7 @@ void KeyPairSubkeyTab::slotEditSubkey() { LOG(INFO) << "Fpr" << getSelectedSubkey().fpr(); auto dialog = - new KeySetExpireDateDialog(mKey.id(), getSelectedSubkey().fpr(), this); + new KeySetExpireDateDialog(key_.id(), getSelectedSubkey().fpr(), this); dialog->show(); } @@ -302,7 +323,7 @@ const GpgSubKey& KeyPairSubkeyTab::getSelectedSubkey() { return buffered_subkeys[row]; } void KeyPairSubkeyTab::slotRefreshKeyInfo() { - mKey = GpgKeyGetter::GetInstance().GetKey(mKey.id()); + key_ = GpgKeyGetter::GetInstance().GetKey(key_.id()); } } // namespace GpgFrontend::UI diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.h b/src/ui/keypair_details/KeyPairSubkeyTab.h index 8922e723..f254fed0 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.h +++ b/src/ui/keypair_details/KeyPairSubkeyTab.h @@ -45,7 +45,7 @@ class KeyPairSubkeyTab : public QWidget { const GpgSubKey& getSelectedSubkey(); - GpgKey mKey; + GpgKey key_; QTableWidget* subkeyList{}; std::vector<GpgSubKey> buffered_subkeys; @@ -62,6 +62,7 @@ class KeyPairSubkeyTab : public QWidget { QLabel* fingerPrintVarLabel; /** Label containing the keys fingerprint */ QLabel* usageVarLabel; QLabel* masterKeyExistVarLabel; + QLabel* cardKeyLabel; private slots: diff --git a/src/ui/keypair_details/KeyUIDSignDialog.cpp b/src/ui/keypair_details/KeyUIDSignDialog.cpp index eb459701..b43a119e 100644 --- a/src/ui/keypair_details/KeyUIDSignDialog.cpp +++ b/src/ui/keypair_details/KeyUIDSignDialog.cpp @@ -34,7 +34,7 @@ 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(false, this); + mKeyList = new KeyList(KeyMenuAbility::NONE, this); mKeyList->addListGroupTab(_("Signers"), KeyListRow::ONLY_SECRET_KEY, KeyListColumn::NAME | KeyListColumn::EmailAddress, [key_id](const GpgKey& key) -> bool { diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp index 2a2c9be9..286cef3a 100644 --- a/src/ui/main_window/MainWindowFileSlotFunction.cpp +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -26,6 +26,7 @@ #include "gpg/function/GpgFileOpera.h" #include "gpg/function/GpgKeyGetter.h" #include "ui/UserInterfaceUtils.h" +#include "ui/settings/GlobalSettingStation.h" #include "ui/widgets/SignersPicker.h" namespace GpgFrontend::UI { @@ -57,46 +58,87 @@ void MainWindow::slotFileEncrypt() { if (!file_pre_check(this, path)) return; - if (QFile::exists(path + ".asc")) { - auto ret = QMessageBox::warning( - this, _("Warning"), - _("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); + // check selected keys + auto key_ids = mKeyList->getChecked(); + GpgEncrResult result = nullptr; + GpgError error; + bool if_error = false; - if (ret == QMessageBox::Cancel) return; + // Detect ascii mode + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + bool non_ascii_when_export = true; + try { + non_ascii_when_export = settings.lookup("general.non_ascii_when_export"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); } - auto key_ids = mKeyList->getChecked(); - auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); - if (keys->empty()) { - QMessageBox::critical(this, _("No Key Selected"), _("No Key Selected")); - return; + auto _channel = GPGFRONTEND_DEFAULT_CHANNEL; + auto _extension = ".asc"; + if (non_ascii_when_export) { + _channel = GPGFRONTEND_NON_ASCII_CHANNEL; + _extension = ".gpg"; } - for (const auto& key : *keys) { - if (!key.CanEncrActual()) { - QMessageBox::information( - this, _("Invalid Operation"), - QString( - _("The selected key contains a key that does not actually have a " - "encrypt usage.")) + - "<br/><br/>" + _("For example the Following Key:") + " <br/>" + - QString::fromStdString(key.uids()->front().uid())); - return; - } + auto out_path = path + _extension; + + if (QFile::exists(out_path)) { + boost::filesystem::path _out_path = out_path.toStdString(); + auto out_file_name = boost::format(_("The target file %1% already exists, " + "do you need to overwrite it?")) % + _out_path.filename(); + auto ret = + QMessageBox::warning(this, _("Warning"), out_file_name.str().c_str(), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; } - GpgEncrResult result = nullptr; - GpgError error; - bool if_error = false; - process_operation(this, _("Encrypting"), [&]() { - try { - error = GpgFileOpera::EncryptFile(std::move(keys), path.toStdString(), - result); - } catch (const std::runtime_error& e) { - if_error = true; + if (key_ids->empty()) { + // Symmetric Encrypt + auto ret = QMessageBox::information( + this, _("Symmetric Encryption"), + _("No Key Selected. Do you want to encrypt with a " + "symmetric cipher using a passphrase?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; + + process_operation(this, _("Symmetrically Encrypting"), [&]() { + try { + error = GpgFrontend::GpgFileOpera::EncryptFileSymmetric( + path.toStdString(), out_path.toStdString(), result, _channel); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } else { + auto p_keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); + + // check key abilities + for (const auto& key : *p_keys) { + bool key_can_encrypt = key.CanEncrActual(); + + if (!key_can_encrypt) { + QMessageBox::critical( + nullptr, _("Invalid KeyPair"), + QString(_("The selected keypair cannot be used for encryption.")) + + "<br/><br/>" + _("For example the Following Key:") + " <br/>" + + QString::fromStdString(key.uids()->front().uid())); + return; + } } - }); + + process_operation(this, _("Encrypting"), [&]() { + try { + error = + GpgFileOpera::EncryptFile(std::move(p_keys), path.toStdString(), + out_path.toStdString(), result, _channel); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } if (!if_error) { auto resultAnalyse = EncryptResultAnalyse(error, std::move(result)); @@ -116,16 +158,15 @@ void MainWindow::slotFileDecrypt() { if (!file_pre_check(this, path)) return; - QString outFileName, fileExtension = QFileInfo(path).suffix(); + boost::filesystem::path out_path = path.toStdString(); - if (fileExtension == "asc" || fileExtension == "gpg") { - int pos = path.lastIndexOf(QChar('.')); - outFileName = path.left(pos); + if (out_path.extension() == ".asc" || out_path.extension() == ".gpg") { + out_path = out_path.parent_path() / out_path.stem(); } else { - outFileName = path + ".out"; + out_path += ".out"; } - if (QFile::exists(outFileName)) { + if (exists(out_path)) { auto ret = QMessageBox::warning( this, _("Warning"), _("The target file already exists, do you need to overwrite it?"), @@ -139,7 +180,8 @@ void MainWindow::slotFileDecrypt() { bool if_error = false; process_operation(this, _("Decrypting"), [&]() { try { - error = GpgFileOpera::DecryptFile(path.toStdString(), result); + error = GpgFileOpera::DecryptFile(path.toStdString(), out_path.string(), + result); } catch (const std::runtime_error& e) { if_error = true; } @@ -168,7 +210,9 @@ void MainWindow::slotFileSign() { auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); if (keys->empty()) { - QMessageBox::critical(this, _("No Key Selected"), _("No Key Selected")); + QMessageBox::critical( + this, _("No Key Selected"), + _("Please select the key in the key toolbox on the right.")); return; } @@ -184,8 +228,26 @@ void MainWindow::slotFileSign() { } } - auto sig_file_path = boost::filesystem::path(path.toStdString() + ".sig"); - if (QFile::exists(sig_file_path.string().c_str())) { + // Detect ascii mode + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + bool non_ascii_when_export = true; + try { + non_ascii_when_export = settings.lookup("general.non_ascii_when_export"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); + } + + auto _channel = GPGFRONTEND_DEFAULT_CHANNEL; + auto _extension = ".asc"; + if (non_ascii_when_export) { + _channel = GPGFRONTEND_NON_ASCII_CHANNEL; + _extension = ".sig"; + } + + boost::filesystem::path in_path = path.toStdString(); + auto sig_file_path = in_path; + sig_file_path += _extension; + if (exists(sig_file_path)) { auto ret = QMessageBox::warning( this, _("Warning"), QString(_("The signature file \"%1\" exists, " @@ -202,8 +264,8 @@ void MainWindow::slotFileSign() { process_operation(this, _("Signing"), [&]() { try { - error = - GpgFileOpera::SignFile(std::move(keys), path.toStdString(), result); + error = GpgFileOpera::SignFile(std::move(keys), in_path.string(), + sig_file_path.string(), result, _channel); } catch (const std::runtime_error& e) { if_error = true; } @@ -229,60 +291,62 @@ void MainWindow::slotFileVerify() { auto fileTreeView = edit->slotCurPageFileTreeView(); auto path = fileTreeView->getSelected(); - QFileInfo fileInfo(path); + boost::filesystem::path in_path = path.toStdString(); + boost::filesystem::path sign_file_path = in_path, data_file_path; - QString signFilePath, dataFilePath; + // Detect ascii mode + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + bool non_ascii_when_export = true; + try { + non_ascii_when_export = settings.lookup("general.non_ascii_when_export"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); + } - if (fileInfo.suffix() == "gpg") { - dataFilePath = path; - signFilePath = path; - } else if (fileInfo.suffix() == "sig") { - int pos = path.lastIndexOf(QChar('.')); - dataFilePath = path.left(pos); - signFilePath = path; - } else { - dataFilePath = path; - signFilePath = path + ".sig"; + auto _channel = GPGFRONTEND_DEFAULT_CHANNEL; + if (non_ascii_when_export) { + _channel = GPGFRONTEND_NON_ASCII_CHANNEL; } - if (fileInfo.suffix() != "gpg") { + if (in_path.extension() == ".gpg") { + swap(data_file_path, sign_file_path); + } else if (in_path.extension() == ".sig" || in_path.extension() == ".asc") { + data_file_path = sign_file_path.parent_path() / sign_file_path.stem(); + } + + LOG(INFO) << "sign_file_path" << sign_file_path << sign_file_path.extension(); + + if (in_path.extension() != ".gpg") { bool ok; - QString text = - QInputDialog::getText(this, _("Origin file to verify"), _("Filepath"), - QLineEdit::Normal, dataFilePath, &ok); + QString text = QInputDialog::getText(this, _("Origin file to verify"), + _("Filepath"), QLineEdit::Normal, + data_file_path.string().c_str(), &ok); if (ok && !text.isEmpty()) { - dataFilePath = text; + data_file_path = text.toStdString(); } else { return; } } - QFileInfo dataFileInfo(dataFilePath), signFileInfo(signFilePath); - - if (!dataFileInfo.isFile() || !signFileInfo.isFile()) { + if (!is_regular_file(data_file_path) || + (!sign_file_path.empty() && !is_regular_file(sign_file_path))) { QMessageBox::critical( this, _("Error"), _("Please select the appropriate origin file or signature file. " "Ensure that both are in this directory.")); return; } - if (!dataFileInfo.isReadable()) { - QMessageBox::critical(this, _("Error"), - _("No permission to read target file.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, _("Error"), - _("No permission to read signature file.")); - return; - } + + DLOG(INFO) << "data path" << data_file_path; + DLOG(INFO) << "sign path" << sign_file_path; GpgVerifyResult result = nullptr; gpgme_error_t error; bool if_error = false; process_operation(this, _("Verifying"), [&]() { try { - error = GpgFileOpera::VerifyFile(dataFilePath.toStdString(), result); + error = GpgFileOpera::VerifyFile( + data_file_path.string(), sign_file_path.string(), result, _channel); } catch (const std::runtime_error& e) { if_error = true; } @@ -313,23 +377,18 @@ void MainWindow::slotFileEncryptSign() { if (!file_pre_check(this, path)) return; - if (QFile::exists(path + ".gpg")) { - auto ret = QMessageBox::warning( - this, _("Warning"), - _("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) return; - } - + // check selected keys auto key_ids = mKeyList->getChecked(); auto p_keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); if (p_keys->empty()) { - QMessageBox::critical(this, _("No Key Selected"), _("No Key Selected")); + QMessageBox::critical( + this, _("No Key Selected"), + _("Please select the key in the key toolbox on the right.")); return; } + // check key abilities for (const auto& key : *p_keys) { bool key_can_encrypt = key.CanEncrActual(); @@ -343,6 +402,33 @@ void MainWindow::slotFileEncryptSign() { } } + // Detect ascii mode + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + bool non_ascii_when_export = true; + try { + non_ascii_when_export = settings.lookup("general.non_ascii_when_export"); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); + } + + auto _channel = GPGFRONTEND_DEFAULT_CHANNEL; + auto _extension = ".asc"; + if (non_ascii_when_export) { + _channel = GPGFRONTEND_NON_ASCII_CHANNEL; + _extension = ".gpg"; + } + + boost::filesystem::path out_path = path.toStdString() + _extension; + + if (exists(out_path)) { + auto ret = QMessageBox::warning( + this, _("Warning"), + _("The target file already exists, do you need to overwrite it?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; + } + auto signersPicker = new SignersPicker(this); QEventLoop loop; connect(signersPicker, SIGNAL(finished(int)), &loop, SLOT(quit())); @@ -351,14 +437,6 @@ void MainWindow::slotFileEncryptSign() { auto signer_key_ids = signersPicker->getCheckedSigners(); auto p_signer_keys = GpgKeyGetter::GetInstance().GetKeys(signer_key_ids); - for (const auto& key : *p_keys) { - LOG(INFO) << "Keys " << key.email(); - } - - for (const auto& signer : *p_signer_keys) { - LOG(INFO) << "Signers " << signer.email(); - } - GpgEncrResult encr_result = nullptr; GpgSignResult sign_result = nullptr; @@ -369,18 +447,18 @@ void MainWindow::slotFileEncryptSign() { try { error = GpgFileOpera::EncryptSignFile( std::move(p_keys), std::move(p_signer_keys), path.toStdString(), - encr_result, sign_result); + out_path.string(), encr_result, sign_result, _channel); } catch (const std::runtime_error& e) { if_error = true; } }); if (!if_error) { - auto encrypt_res = EncryptResultAnalyse(error, std::move(encr_result)); + auto encrypt_result = EncryptResultAnalyse(error, std::move(encr_result)); auto sign_res = SignResultAnalyse(error, std::move(sign_result)); - encrypt_res.analyse(); + encrypt_result.analyse(); sign_res.analyse(); - process_result_analyse(edit, infoBoard, encrypt_res, sign_res); + process_result_analyse(edit, infoBoard, encrypt_result, sign_res); fileTreeView->update(); @@ -397,11 +475,12 @@ void MainWindow::slotFileDecryptVerify() { if (!file_pre_check(this, path)) return; - boost::filesystem::path out_path(path.toStdString()); - if (out_path.extension() == ".asc" || out_path.extension() == ".gpg") { - out_path = out_path.parent_path() / out_path.filename(); + boost::filesystem::path in_path(path.toStdString()); + boost::filesystem::path out_path = in_path; + if (in_path.extension() == ".asc" || in_path.extension() == ".gpg") { + out_path = in_path.parent_path() / out_path.stem(); } else { - out_path = out_path.replace_extension(".out").string(); + out_path += ".out"; } LOG(INFO) << "out path" << out_path; @@ -422,8 +501,8 @@ void MainWindow::slotFileDecryptVerify() { bool if_error = false; process_operation(this, _("Decrypting and Verifying"), [&]() { try { - error = GpgFileOpera::DecryptVerifyFile(path.toStdString(), d_result, - v_result); + error = GpgFileOpera::DecryptVerifyFile( + path.toStdString(), out_path.string(), d_result, v_result); } catch (const std::runtime_error& e) { if_error = true; } diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 2dd3f842..57fb5bae 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -37,7 +37,7 @@ #include "gpg/function/BasicOperator.h" #include "gpg/function/GpgKeyGetter.h" -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "ui/UserInterfaceUtils.h" #include "ui/help/AboutDialog.h" #include "ui/settings/GlobalSettingStation.h" @@ -59,11 +59,11 @@ void MainWindow::slotEncrypt() { if (key_ids->empty()) { // Symmetric Encrypt - auto ret = - QMessageBox::warning(this, _("Warning"), - _("No Key Selected. Do you want to encrypt with a " - "symmetric cipher using a passphrase?"), - QMessageBox::Ok | QMessageBox::Cancel); + auto ret = QMessageBox::information( + this, _("Symmetric Encryption"), + _("No Key Selected. Do you want to encrypt with a " + "symmetric cipher using a passphrase?"), + QMessageBox::Ok | QMessageBox::Cancel); if (ret == QMessageBox::Cancel) return; @@ -129,7 +129,9 @@ void MainWindow::slotSign() { auto key_ids = mKeyList->getPrivateChecked(); if (key_ids->empty()) { - QMessageBox::critical(this, _("No Key Selected"), _("No Key Selected")); + QMessageBox::critical( + this, _("No Key Selected"), + _("Please select the key in the key toolbox on the right.")); return; } @@ -270,7 +272,9 @@ void MainWindow::slotEncryptSign() { auto key_ids = mKeyList->getChecked(); if (key_ids->empty()) { - QMessageBox::critical(nullptr, _("No Key Selected"), _("No Key Selected")); + QMessageBox::critical( + nullptr, _("No Key Selected"), + _("Please select the key in the key toolbox on the right.")); return; } @@ -348,7 +352,7 @@ void MainWindow::slotEncryptSign() { infoBoard->resetOptionActionsMenu(); #ifdef SMTP_SUPPORT if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) - send_an_email(this, infoBoard, edit->curTextPage()->toPlainText()); + send_an_email(this, infoBoard, edit->curTextPage()->toPlainText(), false); #endif #ifdef ADVANCE_SUPPORT @@ -448,7 +452,7 @@ void MainWindow::slotAppendSelectedKeys() { auto exported = std::make_unique<ByteArray>(); auto key_ids = mKeyList->getSelected(); - GpgKeyImportExportor::GetInstance().ExportKeys(key_ids, exported); + GpgKeyImportExporter::GetInstance().ExportKeys(key_ids, exported); edit->curTextPage()->append(QString::fromStdString(*exported)); } @@ -495,13 +499,12 @@ void MainWindow::uploadKeyToServer() { void MainWindow::slotOpenFile(QString& path) { edit->slotOpenFile(path); } -void MainWindow::slotVersionUpgrade(const QString& currentVersion, - const QString& latestVersion) { +void MainWindow::slotVersionUpgrade(const SoftwareVersion& version) { LOG(INFO) << _("called"); - if (currentVersion < latestVersion) { + if (version.NeedUpgrade()) { statusBar()->showMessage( QString(_("GpgFrontend Upgradeable (New Version: %1).")) - .arg(latestVersion), + .arg(version.latest_version.c_str()), 30000); auto update_button = new QPushButton("Update GpgFrontend", this); connect(update_button, &QPushButton::clicked, [=]() { @@ -509,17 +512,24 @@ void MainWindow::slotVersionUpgrade(const QString& currentVersion, about_dialog->show(); }); statusBar()->addPermanentWidget(update_button, 0); - } else if (currentVersion > latestVersion) { + } else if (version.VersionWithDrawn()) { QMessageBox::warning( - this, _("Unreleased Version"), + this, _("Withdrawn Version"), QString( - _("This version(%1) has not been officially released and is not " - "recommended for use in a production environment. <br/>")) - .arg(currentVersion) + - QString( - _("You can download the latest version(%1) on Github Releases " - "Page.<br/>")) - .arg(latestVersion)); + _("This version(%1) may have been withdrawn by the developer due " + "to serious problems. Please stop using this version " + "immediately and use the latest stable version.")) + .arg(version.current_version.c_str()) + + "<br/>" + + QString(_("You can download the latest stable version(%1) on " + "Github Releases " + "Page.<br/>")) + .arg(version.latest_version.c_str())); + } else if (!version.CurrentVersionReleased()) { + statusBar()->showMessage( + QString(_("This maybe a BETA Version (Latest Stable Version: %1).")) + .arg(version.latest_version.c_str()), + 30000); } } diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 0c557d16..cb7962f7 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -258,7 +258,7 @@ void MainWindow::createActions() { /* Popup-Menu-Action for KeyList */ appendSelectedKeysAct = - new QAction(_("Append Selected Key(s) To Text"), this); + new QAction(_("Append Public Key To Text Editor"), this); appendSelectedKeysAct->setToolTip( _("Append The Selected Keys To Text in Editor")); connect(appendSelectedKeysAct, SIGNAL(triggered()), this, @@ -297,7 +297,7 @@ void MainWindow::createActions() { connect(addPgpHeaderAct, SIGNAL(triggered()), this, SLOT(slotAddPgpHeader())); #ifdef SMTP_SUPPORT - sendMailAct = new QAction(_("Send An Email"), this); + sendMailAct = new QAction(_("New Message"), this); sendMailAct->setIcon(QIcon(":email.png")); connect(sendMailAct, &QAction::triggered, this, [=]() { auto* dialog = new SendMailDialog({}, this); @@ -421,6 +421,11 @@ void MainWindow::createToolBars() { specialEditToolBar->hide(); viewMenu->addAction(specialEditToolBar->toggleViewAction()); + emailToolBar = addToolBar(_("Email")); + emailToolBar->setObjectName("emailToolBar"); + emailToolBar->addAction(sendMailAct); + viewMenu->addAction(emailToolBar->toggleViewAction()); + // Add dropdown menu for key import to keytoolbar importButton = new QToolButton(); importButton->setMenu(importKeyMenu); diff --git a/src/ui/settings/GlobalSettingStation.h b/src/ui/settings/GlobalSettingStation.h index a7cbe569..a89bf32d 100644 --- a/src/ui/settings/GlobalSettingStation.h +++ b/src/ui/settings/GlobalSettingStation.h @@ -48,6 +48,18 @@ class GlobalSettingStation : public QObject { return app_log_path; } + [[nodiscard]] boost::filesystem::path GetStandaloneDatabaseDir() const { + auto db_path = app_configure_path / "db"; + if (!boost::filesystem::exists(db_path)) { + boost::filesystem::create_directory(db_path); + } + return db_path; + } + + [[nodiscard]] boost::filesystem::path GetStandaloneGpgBinDir() const { + return app_resource_path / "gpg1.4" / "gpg"; + } + [[nodiscard]] boost::filesystem::path GetLocaleDir() const { return app_locale_path; } diff --git a/src/ui/settings/SettingsDialog.cpp b/src/ui/settings/SettingsDialog.cpp index 7b8a8624..01e4094f 100644 --- a/src/ui/settings/SettingsDialog.cpp +++ b/src/ui/settings/SettingsDialog.cpp @@ -29,6 +29,7 @@ #include "SettingsAppearance.h" #include "SettingsGeneral.h" #include "SettingsKeyServer.h" +#include "SettingsNetwork.h" #ifdef SMTP_SUPPORT #include "SettingsSendMail.h" @@ -37,13 +38,15 @@ namespace GpgFrontend::UI { SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { - tabWidget = new QTabWidget; + + tabWidget = new QTabWidget(); generalTab = new GeneralTab(); - appearanceTab = new AppearanceTab; + appearanceTab = new AppearanceTab(); #ifdef SMTP_SUPPORT - sendMailTab = new SendMailTab; + sendMailTab = new SendMailTab(); #endif - keyserverTab = new KeyserverTab; + keyserverTab = new KeyserverTab(); + networkTab = new NetworkTab(); #ifdef ADVANCED_SUPPORT advancedTab = new AdvancedTab; #endif @@ -55,6 +58,7 @@ SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { #endif tabWidget->addTab(keyserverTab, _("Key Server")); // tabWidget->addTab(gpgPathsTab, _("Gpg paths")); + tabWidget->addTab(networkTab, _("Network")); #ifdef ADVANCED_SUPPORT tabWidget->addTab(advancedTab, _("Advanced")); #endif @@ -114,6 +118,7 @@ void SettingsDialog::slotAccept() { #endif appearanceTab->applySettings(); keyserverTab->applySettings(); + networkTab->applySettings(); #ifdef ADVANCED_SUPPORT advancedTab->applySettings(); #endif diff --git a/src/ui/settings/SettingsDialog.h b/src/ui/settings/SettingsDialog.h index b8277906..da3bdba3 100755 --- a/src/ui/settings/SettingsDialog.h +++ b/src/ui/settings/SettingsDialog.h @@ -38,6 +38,7 @@ class SendMailTab; class AppearanceTab; class KeyserverTab; +class NetworkTab; #ifdef ADVANCED_SUPPORT class AdvancedTab; @@ -55,6 +56,7 @@ class SettingsDialog : public QDialog { #endif AppearanceTab* appearanceTab; KeyserverTab* keyserverTab; + NetworkTab* networkTab; #ifdef ADVANCED_SUPPORT AdvancedTab* advancedTab; #endif diff --git a/src/ui/settings/SettingsGeneral.cpp b/src/ui/settings/SettingsGeneral.cpp index 6b6568a4..4f8ef544 100644 --- a/src/ui/settings/SettingsGeneral.cpp +++ b/src/ui/settings/SettingsGeneral.cpp @@ -24,157 +24,49 @@ #include "SettingsGeneral.h" -#ifdef SERVER_SUPPORT -#include "server/ComUtils.h" -#endif - #ifdef MULTI_LANG_SUPPORT #include "SettingsDialog.h" #endif #include "GlobalSettingStation.h" -#include "gpg/function/GpgKeyGetter.h" -#include "ui/widgets/KeyList.h" +#include "ui_GeneralSettings.h" namespace GpgFrontend::UI { -GeneralTab::GeneralTab(QWidget* parent) : QWidget(parent) { -#ifdef SERVER_SUPPORT - /***************************************** - * GpgFrontend Server - *****************************************/ - auto* serverBox = new QGroupBox(_("GpgFrontend Server")); - auto* serverBoxLayout = new QVBoxLayout(); - serverSelectBox = new QComboBox(); - serverBoxLayout->addWidget(serverSelectBox); - serverBoxLayout->addWidget(new QLabel( - _("Server that provides short key and key exchange services"))); - - serverBox->setLayout(serverBoxLayout); -#endif - - /***************************************** - * Save-Checked-Keys-Box - *****************************************/ - auto* saveCheckedKeysBox = new QGroupBox(_("Save Checked Keys")); - auto* saveCheckedKeysBoxLayout = new QHBoxLayout(); - saveCheckedKeysCheckBox = new QCheckBox( - _("Save checked private keys on exit and restore them on next start."), - this); - saveCheckedKeysBoxLayout->addWidget(saveCheckedKeysCheckBox); - 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 = - new QGroupBox(_("Confirm drag'n'drop key import")); - auto* importConfirmationBoxLayout = new QHBoxLayout(); - importConfirmationCheckBox = new QCheckBox( - _("Import files dropped on the Key List without confirmation."), this); - importConfirmationBoxLayout->addWidget(importConfirmationCheckBox); - importConfirmationBox->setLayout(importConfirmationBoxLayout); +GeneralTab::GeneralTab(QWidget* parent) + : QWidget(parent), ui(std::make_shared<Ui_GeneralSettings>()) { + ui->setupUi(this); + + ui->saveCheckedKeysBox->setTitle(_("Save Checked Keys")); + ui->saveCheckedKeysCheckBox->setText( + _("Save checked private keys on exit and restore them on next start.")); + ui->longerKeyExpirationDateBox->setTitle(_("Longer Key Expiration Date")); + ui->longerKeyExpirationDateCheckBox->setText( + _("Unlock key expiration date setting up to 30 years.")); + ui->importConfirmationBox->setTitle(_("Confirm drag'n'drop key import")); + ui->importConfirmationCheckBox->setText( + _("Import files dropped on the Key List without confirmation.")); + + ui->asciiModeBox->setTitle(_("ASCII Mode")); + ui->asciiModeCheckBox->setText( + _("ASCII encoding is not used when file encrypting and " + "signing.")); + + ui->langBox->setTitle(_("Language")); + ui->langNoteLabel->setText( + "<b>" + QString(_("NOTE")) + _(": ") + "</b>" + + _("GpgFrontend will restart automatically if you change the language!")); #ifdef MULTI_LANG_SUPPORT - /***************************************** - * Language Select Box - *****************************************/ - auto* langBox = new QGroupBox(_("Language")); - auto* langBoxLayout = new QVBoxLayout(); - langSelectBox = new QComboBox(); - langSelectBox->setMaxVisibleItems(8); - langSelectBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); lang = SettingsDialog::listLanguages(); - for (const auto& l : lang) { - langSelectBox->addItem(l); + ui->langSelectBox->addItem(l); } - langSelectBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); - - langBoxLayout->addWidget(langSelectBox); - langBoxLayout->addWidget(new QLabel( - "<b>" + QString(_("NOTE")) + _(": ") + "</b>" + - _("GpgFrontend will restart automatically if you change the language!"))); - langBox->setLayout(langBoxLayout); - connect(langSelectBox, SIGNAL(currentIndexChanged(int)), this, + connect(ui->langSelectBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotLanguageChanged())); #endif -#ifdef SERVER_SUPPORT - /***************************************** - * Own Key Select Box - *****************************************/ - auto* ownKeyBox = new QGroupBox(_("Own key")); - auto* ownKeyBoxLayout = new QVBoxLayout(); - auto* ownKeyServiceTokenLayout = new QHBoxLayout(); - ownKeySelectBox = new QComboBox; - getServiceTokenButton = new QPushButton(_("Get Service Token")); - serviceTokenLabel = new QLabel(_("No Service Token Found")); - serviceTokenLabel->setAlignment(Qt::AlignCenter); - - ownKeyBox->setLayout(ownKeyBoxLayout); - - mKeyList = new KeyList(); - - // Fill the keyid hashmap - keyIds.insert({QString(), "<none>"}); - - auto private_keys = mKeyList->getAllPrivateKeys(); - - for (const auto& keyid : *private_keys) { - auto key = GpgKeyGetter::GetInstance().GetKey(keyid); - if (!key.good()) continue; - keyIds.insert({key.id(), key.uids()->front().uid()}); - } - for (const auto& k : keyIds) { - ownKeySelectBox->addItem(QString::fromStdString(k.second)); - keyIdsList.push_back(k.first); - } - connect(ownKeySelectBox, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotOwnKeyIdChanged())); - connect(getServiceTokenButton, SIGNAL(clicked(bool)), this, - SLOT(slotGetServiceToken())); - - ownKeyBoxLayout->addWidget(new QLabel( - _("Key pair for synchronization and identity authentication"))); - ownKeyBoxLayout->addWidget(ownKeySelectBox); - ownKeyBoxLayout->addLayout(ownKeyServiceTokenLayout); - ownKeyServiceTokenLayout->addWidget(getServiceTokenButton); - ownKeyServiceTokenLayout->addWidget(serviceTokenLabel); - ownKeyServiceTokenLayout->stretch(0); -#endif - - /***************************************** - * Mainlayout - *****************************************/ - auto* mainLayout = new QVBoxLayout; -#ifdef SERVER_SUPPORT - mainLayout->addWidget(serverBox); -#endif - mainLayout->addWidget(longerKeyExpirationDateBox); - mainLayout->addWidget(saveCheckedKeysBox); - mainLayout->addWidget(importConfirmationBox); -#ifdef MULTI_LANG_SUPPORT - mainLayout->addWidget(langBox); -#endif -#ifdef SERVER_SUPPORT - mainLayout->addWidget(ownKeyBox); -#endif - setSettings(); - mainLayout->addStretch(1); - setLayout(mainLayout); } /********************************** @@ -186,7 +78,8 @@ void GeneralTab::setSettings() { auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); try { bool save_key_checked = settings.lookup("general.save_key_checked"); - if (save_key_checked) saveCheckedKeysCheckBox->setCheckState(Qt::Checked); + if (save_key_checked) + ui->saveCheckedKeysCheckBox->setCheckState(Qt::Checked); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); } @@ -196,73 +89,45 @@ void GeneralTab::setSettings() { settings.lookup("general.longer_expiration_date"); LOG(INFO) << "longer_expiration_date" << longer_expiration_date; if (longer_expiration_date) - longerKeyExpirationDateCheckBox->setCheckState(Qt::Checked); + ui->longerKeyExpirationDateCheckBox->setCheckState(Qt::Checked); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("longer_expiration_date"); } -#ifdef SERVER_SUPPORT - auto serverList = - settings.value("general/gpgfrontendServerList").toStringList(); - if (serverList.empty()) { - serverList.append("service.gpgfrontend.pub"); - serverList.append("localhost"); - } - for (const auto& s : serverList) serverSelectBox->addItem(s); - - qDebug() << "Current Gpgfrontend Server" - << settings.value("general/currentGpgfrontendServer").toString(); - serverSelectBox->setCurrentText( - settings - .value("general/currentGpgfrontendServer", "service.gpgfrontend.pub") - .toString()); - - connect(serverSelectBox, - QOverload<const QString&>::of(&QComboBox::currentTextChanged), this, - [&](const QString& current) -> void { - settings.setValue("general/currentGpgfrontendServer", current); - }); -#endif - #ifdef MULTI_LANG_SUPPORT try { std::string lang_key = settings.lookup("general.lang"); QString lang_value = lang.value(lang_key.c_str()); LOG(INFO) << "lang settings current" << lang_value.toStdString(); if (!lang.empty()) { - langSelectBox->setCurrentIndex(langSelectBox->findText(lang_value)); + ui->langSelectBox->setCurrentIndex( + ui->langSelectBox->findText(lang_value)); } else { - langSelectBox->setCurrentIndex(0); + ui->langSelectBox->setCurrentIndex(0); } } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("lang"); } #endif -#ifdef SERVER_SUPPORT - auto own_key_id = settings.value("general/ownKeyId").toString().toStdString(); - if (own_key_id.empty()) { - ownKeySelectBox->setCurrentText("<none>"); - } else { - const auto uid = keyIds.find(own_key_id)->second; - ownKeySelectBox->setCurrentText(QString::fromStdString(uid)); - } - - serviceToken = - settings.value("general/serviceToken").toString().toStdString(); - if (!serviceToken.empty()) { - serviceTokenLabel->setText(QString::fromStdString(serviceToken)); - } -#endif - try { bool confirm_import_keys = settings.lookup("general.confirm_import_keys"); LOG(INFO) << "confirm_import_keys" << confirm_import_keys; if (confirm_import_keys) - importConfirmationCheckBox->setCheckState(Qt::Checked); + ui->importConfirmationCheckBox->setCheckState(Qt::Checked); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("confirm_import_keys"); } + + try { + bool non_ascii_when_export = + settings.lookup("general.non_ascii_when_export"); + LOG(INFO) << "non_ascii_when_export" << non_ascii_when_export; + if (non_ascii_when_export) + ui->asciiModeCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); + } } /*********************************** @@ -281,54 +146,41 @@ void GeneralTab::applySettings() { if (!general.exists("longer_expiration_date")) general.add("longer_expiration_date", libconfig::Setting::TypeBoolean) = - longerKeyExpirationDateCheckBox->isChecked(); + ui->longerKeyExpirationDateCheckBox->isChecked(); else { general["longer_expiration_date"] = - longerKeyExpirationDateCheckBox->isChecked(); + ui->longerKeyExpirationDateCheckBox->isChecked(); } if (!general.exists("save_key_checked")) general.add("save_key_checked", libconfig::Setting::TypeBoolean) = - saveCheckedKeysCheckBox->isChecked(); + ui->saveCheckedKeysCheckBox->isChecked(); else { - general["save_key_checked"] = saveCheckedKeysCheckBox->isChecked(); + general["save_key_checked"] = ui->saveCheckedKeysCheckBox->isChecked(); } -#ifdef SERVER_SUPPORT - qDebug() << "serverSelectBox currentText" << serverSelectBox->currentText(); - settings.setValue("general/currentGpgfrontendServer", - serverSelectBox->currentText()); - - auto* serverList = new QStringList(); - for (int i = 0; i < serverSelectBox->count(); i++) - serverList->append(serverSelectBox->itemText(i)); - settings.setValue("general/gpgfrontendServerList", *serverList); - delete serverList; -#endif + if (!general.exists("non_ascii_when_export")) + general.add("non_ascii_when_export", libconfig::Setting::TypeBoolean) = + ui->asciiModeCheckBox->isChecked(); + else { + general["non_ascii_when_export"] = ui->asciiModeCheckBox->isChecked(); + } #ifdef MULTI_LANG_SUPPORT if (!general.exists("lang")) general.add("lang", libconfig::Setting::TypeBoolean) = - lang.key(langSelectBox->currentText()).toStdString(); + lang.key(ui->langSelectBox->currentText()).toStdString(); else { - general["lang"] = lang.key(langSelectBox->currentText()).toStdString(); + general["lang"] = lang.key(ui->langSelectBox->currentText()).toStdString(); } #endif -#ifdef SERVER_SUPPORT - settings.setValue( - "general/ownKeyId", - QString::fromStdString(keyIdsList[ownKeySelectBox->currentIndex()])); - - settings.setValue("general/serviceToken", - QString::fromStdString(serviceToken)); -#endif - if (!general.exists("confirm_import_keys")) general.add("confirm_import_keys", libconfig::Setting::TypeBoolean) = - importConfirmationCheckBox->isChecked(); + ui->importConfirmationCheckBox->isChecked(); else { - general["confirm_import_keys"] = importConfirmationCheckBox->isChecked(); + general["confirm_import_keys"] = + ui->importConfirmationCheckBox->isChecked(); } } @@ -336,136 +188,4 @@ void GeneralTab::applySettings() { void GeneralTab::slotLanguageChanged() { emit signalRestartNeeded(true); } #endif -#ifdef SERVER_SUPPORT -void GeneralTab::slotOwnKeyIdChanged() { - // Set ownKeyId to currently selected - this->serviceTokenLabel->setText(_("No Service Token Found")); - serviceToken.clear(); -} -#endif - -#ifdef SERVER_SUPPORT -void GeneralTab::slotGetServiceToken() { - auto utils = new ComUtils(this); - - QUrl reqUrl(utils->getUrl(ComUtils::GetServiceToken)); - QNetworkRequest request(reqUrl); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - const auto keyId = keyIdsList[ownKeySelectBox->currentIndex()]; - - qDebug() << "KeyId" << keyIdsList[ownKeySelectBox->currentIndex()]; - - if (keyId.isEmpty()) { - QMessageBox::critical( - this, _("Invalid Operation"), - _("Own Key can not be None while getting service token.")); - return; - } - - QStringList selectedKeyIds(keyIdsList[ownKeySelectBox->currentIndex()]); - - QByteArray keyDataBuf; - mCtx->exportKeys(&selectedKeyIds, &keyDataBuf); - - GpgKey key = mCtx->getKeyRefById(keyId); - - if (!key.good) { - QMessageBox::critical(this, _("Error"), _("Key Not Exists")); - return; - } - - qDebug() << "keyDataBuf" << keyDataBuf; - - /** - * { - * "publicKey" : ... - * "sha": ... - * "signedFpr": ... - * "version": ... - * } - */ - - QCryptographicHash shaGen(QCryptographicHash::Sha256); - shaGen.addData(keyDataBuf); - - auto shaStr = shaGen.result().toHex(); - - auto signFprStr = ComUtils::getSignStringBase64(mCtx, key.fpr, key); - - rapidjson::Value pubkey, ver, sha, signFpr; - - rapidjson::Document doc; - doc.SetObject(); - - pubkey.SetString(keyDataBuf.constData(), keyDataBuf.count()); - - auto version = qApp->applicationVersion(); - ver.SetString(version.toUtf8().constData(), - qApp->applicationVersion().count()); - - sha.SetString(shaStr.constData(), shaStr.count()); - signFpr.SetString(signFprStr.constData(), signFprStr.count()); - - rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); - - doc.AddMember("publicKey", pubkey, allocator); - doc.AddMember("sha", sha, allocator); - doc.AddMember("signedFpr", signFpr, allocator); - doc.AddMember("version", ver, allocator); - - rapidjson::StringBuffer sb; - rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb); - doc.Accept(writer); - - QByteArray postData(sb.GetString()); - - QNetworkReply* reply = utils->getNetworkManager().post(request, postData); - - // Show Waiting Dailog - auto dialog = new WaitingDialog("Getting Token From Server", this); - dialog->show(); - - while (reply->isRunning()) { - QApplication::processEvents(); - } - - dialog->close(); - - if (utils->checkServerReply(reply->readAll().constData())) { - /** - * { - * "serviceToken" : ... - * "fpr": ... - * } - */ - - if (!utils->checkDataValueStr("serviceToken") || - !utils->checkDataValueStr("fpr")) { - QMessageBox::critical(this, _("Error"), - _("The communication content with the server does " - "not meet the requirements")); - return; - } - - QString serviceTokenTemp = utils->getDataValueStr("serviceToken"); - QString fpr = utils->getDataValueStr("fpr"); - auto key = mCtx->getKeyRefByFpr(fpr); - if (utils->checkServiceTokenFormat(serviceTokenTemp) && key.good) { - serviceToken = serviceTokenTemp; - qDebug() << "Get Service Token" << serviceToken; - // Auto update settings - settings.setValue("general/serviceToken", serviceToken); - serviceTokenLabel->setText(serviceToken); - QMessageBox::information(this, _("Notice"), - _("Succeed in getting service token")); - } else { - QMessageBox::critical( - this, _("Error"), - _("There is a problem with the communication with the server")); - } - } -} -#endif - } // namespace GpgFrontend::UI diff --git a/src/ui/settings/SettingsGeneral.h b/src/ui/settings/SettingsGeneral.h index bebe7609..a5e2b8d0 100644 --- a/src/ui/settings/SettingsGeneral.h +++ b/src/ui/settings/SettingsGeneral.h @@ -27,6 +27,8 @@ #include "ui/GpgFrontendUI.h" +class Ui_GeneralSettings; + namespace GpgFrontend::UI { class KeyList; @@ -41,24 +43,12 @@ class GeneralTab : public QWidget { void applySettings(); private: - QCheckBox* saveCheckedKeysCheckBox; - QCheckBox* importConfirmationCheckBox; - QCheckBox* longerKeyExpirationDateCheckBox; + std::shared_ptr<Ui_GeneralSettings> ui; #ifdef MULTI_LANG_SUPPORT - QComboBox* langSelectBox; QHash<QString, QString> lang; #endif -#ifdef SERVER_SUPPORT - QComboBox* serverSelectBox; - QComboBox* ownKeySelectBox; - QPushButton* getServiceTokenButton; - QLabel* serviceTokenLabel; - std::string serviceToken; - std::unordered_map<std::string, std::string> keyIds; -#endif - std::vector<std::string> keyIdsList; KeyList* mKeyList{}; @@ -70,13 +60,6 @@ class GeneralTab : public QWidget { void slotLanguageChanged(); #endif - -#ifdef SERVER_SUPPORT - - void slotOwnKeyIdChanged(); - -#endif - signals: void signalRestartNeeded(bool needed); diff --git a/src/ui/settings/SettingsKeyServer.cpp b/src/ui/settings/SettingsKeyServer.cpp index 1d520e35..0cba7b51 100644 --- a/src/ui/settings/SettingsKeyServer.cpp +++ b/src/ui/settings/SettingsKeyServer.cpp @@ -25,6 +25,7 @@ #include "SettingsKeyServer.h" #include "GlobalSettingStation.h" +#include "ui/function/TestListedKeyServerThread.h" #include "ui_KeyServerSettings.h" namespace GpgFrontend::UI { @@ -95,6 +96,7 @@ KeyserverTab::KeyserverTab(QWidget* parent) const auto item = ui->keyServerListTable->item(i, 1); if (!item->isSelected()) continue; this->keyServerStrList.removeAt(i); + break; } this->refreshTable(); }); @@ -231,35 +233,71 @@ void KeyserverTab::refreshTable() { } void KeyserverTab::slotTestListedKeyServer() { - ui->keyServerListTable->blockSignals(true); auto timeout = QInputDialog::getInt(this, _("Set TCP Timeout"), tr("timeout(ms): "), QLineEdit::Normal, 500, 2000); + QStringList urls; 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(); + const auto keyserver_url = ui->keyServerListTable->item(i, 1)->text(); + urls.push_back(keyserver_url); } - ui->keyServerListTable->blockSignals(false); + + auto thread = new TestListedKeyServerThread(urls, timeout, this); + connect(thread, + &GpgFrontend::UI::TestListedKeyServerThread:: + signalKeyServerListTestResult, + this, [=](const QStringList& result) { + const auto row_size = ui->keyServerListTable->rowCount(); + if (result.size() != row_size) return; + ui->keyServerListTable->blockSignals(true); + for (int i = 0; i < row_size; i++) { + const auto status = result[i]; + auto status_iem = ui->keyServerListTable->item(i, 3); + if (status == "Reachable") { + status_iem->setText(_("Reachable")); + status_iem->setForeground(QBrush(QColor::fromRgb(0, 255, 0))); + } else { + status_iem->setText(_("Not Reachable")); + status_iem->setForeground(QBrush(QColor::fromRgb(255, 0, 0))); + } + } + ui->keyServerListTable->blockSignals(false); + }); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // Waiting Dialog + auto* waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = + new QLabel(QString(_("Test Key Server Connection...")) + "<br /><br />" + + _("This test only tests the network connectivity of the key " + "server. Passing the test does not mean that the key server " + "is functionally available.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); + + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); + + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); } + void KeyserverTab::contextMenuEvent(QContextMenuEvent* event) { QWidget::contextMenuEvent(event); if (ui->keyServerListTable->selectedItems().length() > 0) { diff --git a/src/ui/settings/SettingsNetwork.cpp b/src/ui/settings/SettingsNetwork.cpp new file mode 100644 index 00000000..c457c1a0 --- /dev/null +++ b/src/ui/settings/SettingsNetwork.cpp @@ -0,0 +1,334 @@ +/** + * 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 "SettingsNetwork.h" + +#include "ui/function/ProxyConnectionTestThread.h" +#include "ui/settings//GlobalSettingStation.h" +#include "ui_NetworkSettings.h" + +GpgFrontend::UI::NetworkTab::NetworkTab(QWidget *parent) + : QWidget(parent), ui(std::make_shared<Ui_NetworkSettings>()) { + ui->setupUi(this); + + connect(ui->enableProxyCheckBox, &QCheckBox::stateChanged, this, + [=](int state) { switch_ui_enabled(state == Qt::Checked); }); + + connect( + ui->proxyTypeComboBox, &QComboBox::currentTextChanged, this, + [=](const QString ¤t_text) { switch_ui_proxy_type(current_text); }); + + connect(ui->checkProxyConnectionButton, &QPushButton::clicked, this, + &NetworkTab::slotTestProxyConnectionResult); + + ui->proxyGroupBox->setTitle(_("Proxy")); + ui->capabilityGroupBox->setTitle(_("Network Capability")); + ui->operationsGroupBox->setTitle(_("Operations")); + + ui->enableProxyCheckBox->setText(_("Enable Proxy")); + ui->proxyServerPortLabel->setText(_("Port")); + + ui->proxyServerAddressLabel->setText(_("Host Address")); + ui->proxyServerPortLabel->setText(_("Port")); + ui->proxyTypeLabel->setText(_("Proxy Type")); + ui->usernameLabel->setText(_("Username")); + ui->passwordLabel->setText(_("Password")); + + ui->forbidALLCheckBox->setText(_("Forbid all network connection.")); + ui->forbidALLCheckBox->setDisabled(true); + + ui->prohibitUpdateCheck->setText( + _("Prohibit checking for version updates when the program starts.")); + ui->checkProxyConnectionButton->setText(_("Check Proxy Connection")); + + setSettings(); +} + +void GpgFrontend::UI::NetworkTab::setSettings() { + auto &settings = GlobalSettingStation::GetInstance().GetUISettings(); + + try { + std::string proxy_host = settings.lookup("proxy.proxy_host"); + ui->proxyServerAddressEdit->setText(proxy_host.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_host"); + } + + try { + std::string std_username = settings.lookup("proxy.username"); + ui->usernameEdit->setText(std_username.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("username"); + } + + try { + std::string std_password = settings.lookup("proxy.password"); + ui->passwordEdit->setText(std_password.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("password"); + } + + try { + int port = settings.lookup("proxy.port"); + ui->portSpin->setValue(port); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("port"); + } + + ui->proxyTypeComboBox->setCurrentText("HTTP"); + try { + std::string proxy_type = settings.lookup("proxy.proxy_type"); + ui->proxyTypeComboBox->setCurrentText(proxy_type.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_type"); + } + switch_ui_proxy_type(ui->proxyTypeComboBox->currentText()); + + ui->enableProxyCheckBox->setCheckState(Qt::Unchecked); + try { + bool proxy_enable = settings.lookup("proxy.enable"); + if (proxy_enable) + ui->enableProxyCheckBox->setCheckState(Qt::Checked); + else + ui->enableProxyCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_enable"); + } + + { + auto state = ui->enableProxyCheckBox->checkState(); + switch_ui_enabled(state == Qt::Checked); + } + + ui->forbidALLCheckBox->setCheckState(Qt::Unchecked); + try { + bool forbid_all_connection = + settings.lookup("network.forbid_all_connection"); + if (forbid_all_connection) + ui->forbidALLCheckBox->setCheckState(Qt::Checked); + else + ui->forbidALLCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("forbid_all_connection"); + } + + ui->prohibitUpdateCheck->setCheckState(Qt::Unchecked); + try { + bool prohibit_update_checking = + settings.lookup("network.prohibit_update_checking"); + if (prohibit_update_checking) + ui->prohibitUpdateCheck->setCheckState(Qt::Checked); + else + ui->prohibitUpdateCheck->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("prohibit_update_checking"); + } +} + +void GpgFrontend::UI::NetworkTab::applySettings() { + LOG(INFO) << "called"; + + auto &settings = + GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("proxy") || + settings.lookup("proxy").getType() != libconfig::Setting::TypeGroup) + settings.add("proxy", libconfig::Setting::TypeGroup); + + auto &proxy = settings["proxy"]; + + if (!proxy.exists("proxy_host")) + proxy.add("proxy_host", libconfig::Setting::TypeString) = + ui->proxyServerAddressEdit->text().toStdString(); + else { + proxy["proxy_host"] = ui->proxyServerAddressEdit->text().toStdString(); + } + + if (!proxy.exists("username")) + proxy.add("username", libconfig::Setting::TypeString) = + ui->usernameEdit->text().toStdString(); + else { + proxy["username"] = ui->usernameEdit->text().toStdString(); + } + + if (!proxy.exists("password")) + proxy.add("password", libconfig::Setting::TypeString) = + ui->passwordEdit->text().toStdString(); + else { + proxy["password"] = ui->passwordEdit->text().toStdString(); + } + + if (!proxy.exists("port")) + proxy.add("port", libconfig::Setting::TypeInt) = ui->portSpin->value(); + else { + proxy["port"] = ui->portSpin->value(); + } + + if (!proxy.exists("proxy_type")) + proxy.add("proxy_type", libconfig::Setting::TypeString) = + ui->proxyTypeComboBox->currentText().toStdString(); + else { + proxy["proxy_type"] = ui->proxyTypeComboBox->currentText().toStdString(); + } + + if (!proxy.exists("enable")) + proxy.add("enable", libconfig::Setting::TypeBoolean) = + ui->enableProxyCheckBox->isChecked(); + else { + proxy["enable"] = ui->enableProxyCheckBox->isChecked(); + } + + if (!settings.exists("network") || + settings.lookup("network").getType() != libconfig::Setting::TypeGroup) + settings.add("network", libconfig::Setting::TypeGroup); + + auto &network = settings["network"]; + + if (!network.exists("forbid_all_connection")) + network.add("forbid_all_connection", libconfig::Setting::TypeBoolean) = + ui->forbidALLCheckBox->isChecked(); + else { + network["forbid_all_connection"] = ui->forbidALLCheckBox->isChecked(); + } + + if (!network.exists("prohibit_update_checking")) + network.add("prohibit_update_checking", libconfig::Setting::TypeBoolean) = + ui->prohibitUpdateCheck->isChecked(); + else { + network["prohibit_update_checking"] = ui->prohibitUpdateCheck->isChecked(); + } + + apply_proxy_settings(); + + LOG(INFO) << "done"; +} + +void GpgFrontend::UI::NetworkTab::slotTestProxyConnectionResult() { + apply_proxy_settings(); + + bool ok; + auto url = QInputDialog::getText(this, _("Test Server Url Accessibility"), + tr("Server Url"), QLineEdit::Normal, + "https://", &ok); + if (ok && !url.isEmpty()) { + auto thread = new ProxyConnectionTestThread(url, 800, this); + connect(thread, + &GpgFrontend::UI::ProxyConnectionTestThread:: + signalProxyConnectionTestResult, + this, [=](const QString &result) { + if (result == "Reachable") { + QMessageBox::information(this, _("Success"), + _("Successfully connect to the target " + "server through the proxy server.")); + } else { + QMessageBox::critical( + this, _("Failed"), + _("Unable to connect to the target server through the " + "proxy server. Proxy settings may be invalid.")); + } + }); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // Waiting Dialog + auto *waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = new QLabel( + QString(_("Test Proxy Server Connection...")) + "<br /><br />" + + _("Is using your proxy settings to access the url. Note that this test " + "operation will apply your proxy settings to the entire software.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); + + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); + + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); + } +} + +void GpgFrontend::UI::NetworkTab::apply_proxy_settings() { + // apply settings + QNetworkProxy _proxy; + if (ui->enableProxyCheckBox->isChecked() && + proxy_type_ != QNetworkProxy::DefaultProxy) { + _proxy.setType(proxy_type_); + _proxy.setHostName(ui->proxyServerAddressEdit->text()); + _proxy.setPort(ui->portSpin->value()); + if (!ui->usernameEdit->text().isEmpty()) { + _proxy.setUser(ui->usernameEdit->text()); + _proxy.setPassword(ui->passwordEdit->text()); + } + } else { + _proxy.setType(proxy_type_); + } + + QNetworkProxy::setApplicationProxy(_proxy); +} + +void GpgFrontend::UI::NetworkTab::switch_ui_enabled(bool enabled) { + ui->proxyServerAddressEdit->setDisabled(!enabled); + ui->portSpin->setDisabled(!enabled); + ui->proxyTypeComboBox->setDisabled(!enabled); + ui->usernameEdit->setDisabled(!enabled); + ui->passwordEdit->setDisabled(!enabled); + ui->checkProxyConnectionButton->setDisabled(!enabled); + if (!enabled) proxy_type_ = QNetworkProxy::NoProxy; +} + +void GpgFrontend::UI::NetworkTab::switch_ui_proxy_type( + const QString &type_text) { + if (type_text == "HTTP") { + ui->proxyServerAddressEdit->setDisabled(false); + ui->portSpin->setDisabled(false); + ui->usernameEdit->setDisabled(false); + ui->passwordEdit->setDisabled(false); + proxy_type_ = QNetworkProxy::HttpProxy; + } else if (type_text == "Socks5") { + ui->proxyServerAddressEdit->setDisabled(false); + ui->portSpin->setDisabled(false); + ui->usernameEdit->setDisabled(false); + ui->passwordEdit->setDisabled(false); + proxy_type_ = QNetworkProxy::Socks5Proxy; + } else { + ui->proxyServerAddressEdit->setDisabled(true); + ui->portSpin->setDisabled(true); + ui->usernameEdit->setDisabled(true); + ui->passwordEdit->setDisabled(true); + proxy_type_ = QNetworkProxy::DefaultProxy; + } +} diff --git a/src/ui/settings/SettingsNetwork.h b/src/ui/settings/SettingsNetwork.h new file mode 100644 index 00000000..cf136604 --- /dev/null +++ b/src/ui/settings/SettingsNetwork.h @@ -0,0 +1,57 @@ +/** + * 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_SETTINGSNETWORK_H +#define GPGFRONTEND_SETTINGSNETWORK_H + +#include "ui/GpgFrontendUI.h" + +class Ui_NetworkSettings; + +namespace GpgFrontend::UI { +class NetworkTab : public QWidget { + Q_OBJECT + + public: + explicit NetworkTab(QWidget* parent = nullptr); + + void setSettings(); + + void applySettings(); + + private slots: + + void slotTestProxyConnectionResult(); + + private: + std::shared_ptr<Ui_NetworkSettings> ui; + QNetworkProxy::ProxyType proxy_type_ = QNetworkProxy::HttpProxy; + + void apply_proxy_settings(); + void switch_ui_enabled(bool enabled); + void switch_ui_proxy_type(const QString& type_text); +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSNETWORK_H diff --git a/src/ui/settings/SettingsSendMail.cpp b/src/ui/settings/SettingsSendMail.cpp index 733ed82f..f0acb10d 100644 --- a/src/ui/settings/SettingsSendMail.cpp +++ b/src/ui/settings/SettingsSendMail.cpp @@ -28,6 +28,8 @@ #include "smtp/SmtpMime" #endif +#include "ui/function/SMTPSendMailThread.h" +#include "ui/function/SMTPTestThread.h" #include "ui/settings/GlobalSettingStation.h" #include "ui_SendMailSettings.h" @@ -37,18 +39,8 @@ SendMailTab::SendMailTab(QWidget* parent) : 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); - }); + connect(ui->enableCheckBox, &QCheckBox::stateChanged, this, + [=](int state) { switch_ui_enabled(state == Qt::Checked); }); #ifdef SMTP_SUPPORT connect(ui->checkConnectionButton, &QPushButton::clicked, this, @@ -57,10 +49,19 @@ SendMailTab::SendMailTab(QWidget* parent) &SendMailTab::slotSendTestMail); #endif - connect(ui->identityCheckBox, &QCheckBox::stateChanged, this, [=](int state) { - ui->usernameEdit->setDisabled(state != Qt::Checked); - ui->passwordEdit->setDisabled(state != Qt::Checked); - }); + connect(ui->identityCheckBox, &QCheckBox::stateChanged, this, + [=](int state) { switch_ui_identity_enabled(state == Qt::Checked); }); + + connect(ui->connextionSecurityComboBox, &QComboBox::currentTextChanged, this, + [=](const QString& current_text) { + if (current_text == "SSL") { + connection_type_ = SmtpClient::ConnectionType::SslConnection; + } else if (current_text == "TLS" || current_text == "STARTTLS") { + connection_type_ = SmtpClient::ConnectionType::TlsConnection; + } else { + connection_type_ = SmtpClient::ConnectionType::TcpConnection; + } + }); ui->generalGroupBox->setTitle(_("General")); ui->identityGroupBox->setTitle(_("Identity Information")); @@ -79,6 +80,8 @@ SendMailTab::SendMailTab(QWidget* parent) ui->senderLabel->setText(_("Default Sender Email")); ui->checkConnectionButton->setText(_("Check Connection")); ui->senTestMailButton->setText(_("Send Test Email")); + ui->gpgkeyIdLabel->setText(_("Default Sender GPG Key ID")); + 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 " @@ -138,7 +141,16 @@ void SendMailTab::setSettings() { } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("default_sender"); } - + + try { + std::string default_sender_gpg_key_id = + settings.lookup("smtp.default_sender_gpg_key_id"); + ui->gpgKeyIDEdit->setText(default_sender_gpg_key_id.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") + << _("default_sender_gpg_key_id"); + } + ui->identityCheckBox->setCheckState(Qt::Unchecked); try { bool identity_enable = settings.lookup("smtp.identity_enable"); @@ -149,6 +161,11 @@ void SendMailTab::setSettings() { } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("identity_enable"); } + + { + auto state = ui->identityCheckBox->checkState(); + switch_ui_identity_enabled(state == Qt::Checked); + } ui->enableCheckBox->setCheckState(Qt::Unchecked); try { @@ -160,6 +177,11 @@ void SendMailTab::setSettings() { } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); } + + { + auto state = ui->enableCheckBox->checkState(); + switch_ui_enabled(state == Qt::Checked); + } } void SendMailTab::applySettings() { @@ -214,6 +236,13 @@ void SendMailTab::applySettings() { smtp["default_sender"] = ui->defaultSenderEmailEdit->text().toStdString(); } + if (!smtp.exists("default_sender_gpg_key_id")) + smtp.add("default_sender_gpg_key_id", libconfig::Setting::TypeString) = + ui->gpgKeyIDEdit->text().toStdString(); + else { + smtp["default_sender_gpg_key_id"] = ui->gpgKeyIDEdit->text().toStdString(); + } + if (!smtp.exists("identity_enable")) smtp.add("identity_enable", libconfig::Setting::TypeBoolean) = ui->identityCheckBox->isChecked(); @@ -231,105 +260,144 @@ void SendMailTab::applySettings() { #ifdef SMTP_SUPPORT void SendMailTab::slotCheckConnection() { - 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 { - 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()); - } + auto host = ui->smtpServerAddressEdit->text().toStdString(); + auto port = ui->portSpin->value(); + auto connection_type = connection_type_; + bool identity_needed = ui->identityCheckBox->isChecked(); + auto username = ui->usernameEdit->text().toStdString(); + auto password = ui->passwordEdit->text().toStdString(); + + auto thread = new SMTPTestThread(host, port, connection_type, identity_needed, + username, password); + + // Waiting Dialog + auto* waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = + new QLabel(QString(_("Test SMTP Connection...")) + "<br /><br />" + + _("If the process does not end for a long time, please check " + "again whether your SMTP server configuration is correct.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &SMTPTestThread::signalSMTPTestResult, this, + &SendMailTab::slotTestSMTPConnectionResult); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); - if (!smtp.connectToHost()) { - QMessageBox::critical(this, _("Fail"), _("Fail to Connect SMTP Server")); - ui->senTestMailButton->setDisabled(true); - return; - } - if (!smtp.login()) { - QMessageBox::critical(this, _("Fail"), _("Fail to Login")); - ui->senTestMailButton->setDisabled(true); - return; - } + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); - QMessageBox::information(this, _("Success"), - _("Succeed in connecting and login")); - ui->senTestMailButton->setDisabled(false); + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); } #endif #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 { - connectionType = SmtpClient::ConnectionType::TcpConnection; - } - - SmtpClient smtp(ui->smtpServerAddressEdit->text(), ui->portSpin->value(), - connectionType); + auto host = ui->smtpServerAddressEdit->text().toStdString(); + auto port = ui->portSpin->value(); + auto connection_type = connection_type_; + bool identity_needed = ui->identityCheckBox->isChecked(); + auto username = ui->usernameEdit->text().toStdString(); + auto password = ui->passwordEdit->text().toStdString(); + auto sender_address = ui->defaultSenderEmailEdit->text(); - if (ui->identityCheckBox->isChecked()) { - smtp.setUser(ui->usernameEdit->text()); - smtp.setPassword(ui->passwordEdit->text()); - } + auto thread = new SMTPSendMailThread(host, port, connection_type, + identity_needed, username, password); + + // Waiting Dialog + auto* waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = + new QLabel(QString(_("Test SMTP Send Mail Ability...")) + "<br /><br />" + + _("If the process does not end for a long time, please check " + "again whether your SMTP server configuration is correct.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &SMTPSendMailThread::signalSMTPResult, this, + &SendMailTab::slotTestSMTPConnectionResult); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); - MimeMessage message; + thread->setSender(sender_address); + thread->setRecipient(sender_address); + thread->setSubject(_("Test Email from GpgFrontend")); + thread->addTextContent( + _("Hello, this is a test email from GpgFrontend. If you receive this " + "email, it means that you have configured the correct SMTP server " + "parameters.")); + + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); + + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); +} - 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")); +void SendMailTab::slotTestSMTPConnectionResult(const QString& result) { + if (result == "Fail to connect SMTP server") { + 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")); + } else if (result == "Fail to login") { + QMessageBox::critical(this, _("Fail"), _("Fail to Login.")); 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.")); + } else if (result == "Fail to send mail") { + QMessageBox::critical(this, _("Fail"), _("Fail to Login.")); + ui->senTestMailButton->setDisabled(true); + } else if (result == "Succeed in testing connection") { + QMessageBox::information(this, _("Success"), + _("Succeed in connecting and login")); + ui->senTestMailButton->setDisabled(false); + } else if (result == "Succeed in sending a test email") { + QMessageBox::information( + this, _("Success"), + _("Succeed in sending a test email to the SMTP Server")); + ui->senTestMailButton->setDisabled(false); + } else { + QMessageBox::critical(this, _("Fail"), _("Unknown error.")); ui->senTestMailButton->setDisabled(true); - return; } - smtp.quit(); +} + +void SendMailTab::switch_ui_enabled(bool enabled) { + ui->smtpServerAddressEdit->setDisabled(!enabled); + ui->portSpin->setDisabled(!enabled); + ui->connextionSecurityComboBox->setDisabled(!enabled); + + ui->identityCheckBox->setDisabled(!enabled); + ui->usernameEdit->setDisabled(!enabled); + ui->passwordEdit->setDisabled(!enabled); + + ui->defaultSenderEmailEdit->setDisabled(!enabled); + ui->gpgKeyIDEdit->setDisabled(!enabled); + ui->checkConnectionButton->setDisabled(!enabled); +} - // Close after sending email - QMessageBox::information( - this, _("Success"), - _("Succeed in sending a test email to the SMTP Server")); +void SendMailTab::switch_ui_identity_enabled(bool enabled) { + ui->usernameEdit->setDisabled(!enabled); + ui->passwordEdit->setDisabled(!enabled); } #endif diff --git a/src/ui/settings/SettingsSendMail.h b/src/ui/settings/SettingsSendMail.h index cc733f28..c866fa5f 100644 --- a/src/ui/settings/SettingsSendMail.h +++ b/src/ui/settings/SettingsSendMail.h @@ -1,10 +1,31 @@ -// -// Created by saturneric on 2021/11/28. -// +/** + * 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_SETTINGSSENDMAIL_H #define GPGFRONTEND_SETTINGSSENDMAIL_H +#include "smtp/SmtpMime" #include "ui/GpgFrontendUI.h" class Ui_SendMailSettings; @@ -22,6 +43,8 @@ class SendMailTab : public QWidget { private slots: + void slotTestSMTPConnectionResult(const QString& result); + #ifdef SMTP_SUPPORT void slotCheckConnection(); @@ -32,6 +55,12 @@ class SendMailTab : public QWidget { 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])+)\]))"}; + SmtpClient::ConnectionType connection_type_ = + SmtpClient::ConnectionType::TcpConnection; + + void switch_ui_enabled(bool enabled); + + void switch_ui_identity_enabled(bool enabled); signals: diff --git a/src/ui/smtp/EmailListEditor.cpp b/src/ui/smtp/EmailListEditor.cpp new file mode 100644 index 00000000..49399f11 --- /dev/null +++ b/src/ui/smtp/EmailListEditor.cpp @@ -0,0 +1,102 @@ +/** + * 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 "EmailListEditor.h" + +#include "ui_EmailListEditor.h" + +GpgFrontend::UI::EmailListEditor::EmailListEditor(const QString& email_list, + QWidget* parent) + : QDialog(parent), ui(std::make_shared<Ui_EmailListEditorDialog>()) { + ui->setupUi(this); + + QStringList email_string_list = email_list.split(';'); + + if (!email_string_list.isEmpty()) { + for (const auto& recipient : email_string_list) { + auto _recipient = recipient.trimmed(); + if (check_email_address(_recipient)) { + auto item = new QListWidgetItem(_recipient); + ui->emaillistWidget->addItem(item); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + } + } + + connect(ui->addEmailAddressButton, &QPushButton::clicked, this, [=]() { + auto item = new QListWidgetItem("new email address"); + ui->emaillistWidget->addItem(item); + item->setFlags(item->flags() | Qt::ItemIsEditable); + }); + + connect( + ui->actionDelete_Selected_Email_Address, &QAction::triggered, this, + [=]() { + const auto row_size = ui->emaillistWidget->count(); + for (int i = 0; i < row_size; i++) { + auto item = ui->emaillistWidget->item(i); + if (!item->isSelected()) continue; + delete ui->emaillistWidget->takeItem(ui->emaillistWidget->row(item)); + break; + } + }); + + ui->titleLabel->setText(_("Email List:")); + ui->tipsLabel->setText( + _("Tips: You can double-click the email address in the edit list, or " + "click the email to pop up the option menu.")); + ui->addEmailAddressButton->setText(_("Add An Email Address")); + this->setWindowTitle(_("Email List Editor")); + ui->actionDelete_Selected_Email_Address->setText(_("Delete")); + + popupMenu = new QMenu(this); + popupMenu->addAction(ui->actionDelete_Selected_Email_Address); + + this->exec(); +} + +bool GpgFrontend::UI::EmailListEditor::check_email_address( + const QString& email_address) { + return re_email.match(email_address).hasMatch(); +} + +QString GpgFrontend::UI::EmailListEditor::getEmailList() { + QString email_list; + for (int i = 0; i < ui->emaillistWidget->count(); ++i) { + QListWidgetItem* item = ui->emaillistWidget->item(i); + if (check_email_address(item->text())) { + email_list.append(item->text()); + email_list.append("; "); + } + } + return email_list; +} + +void GpgFrontend::UI::EmailListEditor::contextMenuEvent( + QContextMenuEvent* event) { + QWidget::contextMenuEvent(event); + if (ui->emaillistWidget->selectedItems().length() > 0) { + popupMenu->exec(event->globalPos()); + } +} diff --git a/src/ui/smtp/EmailListEditor.h b/src/ui/smtp/EmailListEditor.h new file mode 100644 index 00000000..d4e476a4 --- /dev/null +++ b/src/ui/smtp/EmailListEditor.h @@ -0,0 +1,54 @@ +/** + * 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_EMAILLISTEDITOR_H +#define GPGFRONTEND_EMAILLISTEDITOR_H + +#include "GpgFrontendUI.h" + +class Ui_EmailListEditorDialog; + +namespace GpgFrontend::UI { +class EmailListEditor : public QDialog { + Q_OBJECT + + public: + explicit EmailListEditor(const QString& email_list, QWidget* parent); + QString getEmailList(); + + private: + std::shared_ptr<Ui_EmailListEditorDialog> ui; + QMenu* popupMenu{}; + + 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])+)\]))"}; + + bool check_email_address(const QString& email_address); + + protected: + void contextMenuEvent(QContextMenuEvent* event) override; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_EMAILLISTEDITOR_H diff --git a/src/ui/smtp/RecipientsPicker.cpp b/src/ui/smtp/RecipientsPicker.cpp new file mode 100644 index 00000000..eaaa683a --- /dev/null +++ b/src/ui/smtp/RecipientsPicker.cpp @@ -0,0 +1,76 @@ +/** + * 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 "RecipientsPicker.h" + +#include "ui/widgets/KeyList.h" + +GpgFrontend::UI::RecipientsPicker::RecipientsPicker( + const GpgFrontend::KeyIdArgsListPtr& current_key_ids, QWidget* parent) + : QDialog(parent) { + auto confirm_button = new QPushButton(_("Confirm")); + connect(confirm_button, SIGNAL(clicked(bool)), this, SLOT(accept())); + + // Setup KeyList + key_list_ = new KeyList(KeyMenuAbility::NONE, this); + key_list_->addListGroupTab( + _("Recipient(s)"), KeyListRow::SECRET_OR_PUBLIC_KEY, + KeyListColumn::NAME | KeyListColumn::EmailAddress, + [](const GpgKey& key) -> bool { + return !key.is_private_key() && key.CanEncrActual(); + }); + key_list_->slotRefresh(); + + auto key_ids = std::make_unique<GpgFrontend::KeyIdArgsList>(); + for (const auto& key_id : *current_key_ids) { + key_ids->push_back(key_id); + } + key_list_->setChecked(std::move(key_ids)); + + auto* vbox2 = new QVBoxLayout(); + vbox2->addWidget(new QLabel(QString(_("Select Recipient(s)")) + ": ")); + vbox2->addWidget(key_list_); + vbox2->addWidget(new QLabel( + QString(_("We use the public key provided by the recipient to encrypt " + "the text.")) + + "\n" + + _("If you want to send to multiple recipients at the same time, you can " + "select multiple keys."))); + vbox2->addWidget(confirm_button); + vbox2->addStretch(0); + setLayout(vbox2); + + this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::CustomizeWindowHint); + + this->setModal(true); + this->setWindowTitle("Recipient(s) Picker"); + this->setMinimumWidth(480); + this->exec(); +} + +GpgFrontend::KeyIdArgsListPtr +GpgFrontend::UI::RecipientsPicker::getCheckedRecipients() { + return key_list_->getChecked(); +} diff --git a/src/ui/smtp/RecipientsPicker.h b/src/ui/smtp/RecipientsPicker.h new file mode 100644 index 00000000..bf128149 --- /dev/null +++ b/src/ui/smtp/RecipientsPicker.h @@ -0,0 +1,49 @@ +/** + * 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_RECIPENTSPICKER_H +#define GPGFRONTEND_RECIPENTSPICKER_H + +#include "GpgFrontendUI.h" + +namespace GpgFrontend::UI { + +class KeyList; + +class RecipientsPicker : public QDialog { + Q_OBJECT + + public: + explicit RecipientsPicker( + const GpgFrontend::KeyIdArgsListPtr& current_key_ids, + QWidget* parent = nullptr); + + GpgFrontend::KeyIdArgsListPtr getCheckedRecipients(); + + private: + KeyList* key_list_; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_RECIPENTSPICKER_H diff --git a/src/ui/smtp/SendMailDialog.cpp b/src/ui/smtp/SendMailDialog.cpp index 374b3875..3f0b87cd 100644 --- a/src/ui/smtp/SendMailDialog.cpp +++ b/src/ui/smtp/SendMailDialog.cpp @@ -24,12 +24,15 @@ #include "SendMailDialog.h" -#include <utility> - +#include "gpg/function/GpgKeyGetter.h" +#include "ui/smtp/EmailListEditor.h" +#include "ui/smtp/RecipientsPicker.h" +#include "ui/smtp/SenderPicker.h" #include "ui_SendMailDialog.h" #ifdef SMTP_SUPPORT #include "smtp/SmtpMime" +#include "ui/function/SMTPSendMailThread.h" #include "ui/settings/GlobalSettingStation.h" #endif @@ -40,9 +43,9 @@ SendMailDialog::SendMailDialog(const QString& text, QWidget* parent) // read from settings initSettings(); - if (smtpAddress.isEmpty()) { + if (smtp_address_.isEmpty()) { QMessageBox::critical( - this, _("Incomplete configuration"), + this->parentWidget(), _("Incomplete configuration"), _("The SMTP address is empty, please go to the setting interface to " "complete the configuration.")); @@ -57,7 +60,17 @@ SendMailDialog::SendMailDialog(const QString& text, QWidget* parent) ui->textEdit->setText(text); ui->errorLabel->setHidden(true); - ui->senderEdit->setText(defaultSender); + ui->senderEdit->setText(default_sender_); + + if (!default_sender_gpg_key_id.isEmpty()) { + auto key = GpgKeyGetter::GetInstance().GetKey( + default_sender_gpg_key_id.toStdString()); + if (key.good() && key.is_private_key() && key.CanSignActual()) { + sender_key_id_ = key.id(); + set_sender_value_label(); + } + } + connect(ui->ccButton, &QPushButton::clicked, [=]() { ui->ccInputWidget->setHidden(!ui->ccInputWidget->isHidden()); ui->ccEdit->clear(); @@ -72,6 +85,33 @@ SendMailDialog::SendMailDialog(const QString& text, QWidget* parent) &SendMailDialog::slotConfirm); #endif + connect(ui->senderKeySelectButton, &QPushButton::clicked, this, [=]() { + auto picker = new SenderPicker(sender_key_id_, this); + sender_key_id_ = picker->getCheckedSender(); + set_sender_value_label(); + }); + + connect(ui->recipientKeySelectButton, &QPushButton::clicked, this, [=]() { + auto picker = new RecipientsPicker(recipients_key_ids_, this); + recipients_key_ids_ = picker->getCheckedRecipients(); + set_recipients_value_label(); + }); + + connect(ui->recipientsEditButton, &QPushButton::clicked, this, [=]() { + auto editor = new EmailListEditor(ui->recipientEdit->text(), this); + ui->recipientEdit->setText(editor->getEmailList()); + }); + + connect(ui->ccEditButton, &QPushButton::clicked, this, [=]() { + auto editor = new EmailListEditor(ui->ccEdit->text(), this); + ui->ccEdit->setText(editor->getEmailList()); + }); + + connect(ui->bccEditButton, &QPushButton::clicked, this, [=]() { + auto editor = new EmailListEditor(ui->bccEdit->text(), this); + ui->bccEdit->setText(editor->getEmailList()); + }); + ui->ccButton->setText(_("CC")); ui->bccButton->setText(_("BCC")); ui->senderLabel->setText(_("Sender")); @@ -82,9 +122,27 @@ SendMailDialog::SendMailDialog(const QString& text, QWidget* parent) ui->tipsLabel->setText( _("Tips: You can fill in multiple email addresses, please separate them " "with \";\".")); - ui->sendMailButton->setText(_("Send Mail")); - - this->setWindowTitle(_("Send Mail")); + ui->sendMailButton->setText(_("Send Message")); + ui->senderKeySelectButton->setText(_("Select Sender GPG Key")); + ui->recipientKeySelectButton->setText(_("Select Recipient(s) GPG Key")); + ui->gpgOperaLabel->setText(_("GPG Operations")); + ui->attacSignatureCheckBox->setText(_("Attach signature")); + ui->attachSenderPublickeyCheckBox->setText(_("Attach sender's public key")); + ui->contentEncryptCheckBox->setText(_("Encrypt content")); + ui->recipientsEditButton->setText(_("Edit Recipients(s)")); + ui->ccEditButton->setText(_("Edit CC(s)")); + ui->bccEditButton->setText(_("Edit BCC(s)")); + ui->senderKeyLabel->setText(_("Sender GPG Key: ")); + ui->recipientKeysLabel->setText(_("Recipient(s) GPG Key: ")); + + auto pos = QPoint(100, 100); + LOG(INFO) << "parent" << parent; + if (parent) pos += parent->pos(); + LOG(INFO) << "pos default" << pos.x() << pos.y(); + + move(pos); + + this->setWindowTitle(_("New Message")); this->setAttribute(Qt::WA_DeleteOnClose); } @@ -155,129 +213,223 @@ void SendMailDialog::slotConfirm() { return; } - SmtpClient::ConnectionType connectionType = + SmtpClient::ConnectionType connection_type_ = SmtpClient::ConnectionType::TcpConnection; - if (connectionTypeSettings == "SSL") { - connectionType = SmtpClient::ConnectionType::SslConnection; - } else if (connectionTypeSettings == "TLS") { - connectionType = SmtpClient::ConnectionType::TlsConnection; - } else if (connectionTypeSettings == "STARTTLS") { - connectionType = SmtpClient::ConnectionType::TlsConnection; + if (connection_type_settings_ == "SSL") { + connection_type_ = SmtpClient::ConnectionType::SslConnection; + } else if (connection_type_settings_ == "TLS") { + connection_type_ = SmtpClient::ConnectionType::TlsConnection; + } else if (connection_type_settings_ == "STARTTLS") { + connection_type_ = SmtpClient::ConnectionType::TlsConnection; } else { - connectionType = SmtpClient::ConnectionType::TcpConnection; + connection_type_ = SmtpClient::ConnectionType::TcpConnection; } - SmtpClient smtp(smtpAddress, port, connectionType); - - // We need to set the username (your email address) and the password - // for smtp authentification. - - smtp.setUser(username); - smtp.setPassword(password); - - // Now we create a MimeMessage object. This will be the email. - - MimeMessage message; - - message.setSender(new EmailAddress(ui->senderEdit->text())); - for (const auto& reci : rcpt_string_list) { - if (!reci.isEmpty()) message.addRecipient(new EmailAddress(reci.trimmed())); - } - 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())); + auto host = smtp_address_.toStdString(); + auto port = port_; + auto connection_type = connection_type_; + bool identity_needed = identity_enable_; + auto username = username_.toStdString(); + auto password = password_.toStdString(); + auto sender_address = ui->senderEdit->text().toStdString(); + + auto thread = new SMTPSendMailThread( + host, port, connection_type, identity_needed, username, password, this); + + thread->setSender(ui->senderEdit->text()); + thread->setRecipient(ui->recipientEdit->text()); + thread->setCC(ui->ccEdit->text()); + thread->setBCC(ui->bccEdit->text()); + thread->setSubject(ui->subjectEdit->text()); + thread->addTextContent(ui->textEdit->toPlainText()); + + if (ui->contentEncryptCheckBox->checkState() == Qt::Checked) { + if (recipients_key_ids_ == nullptr || recipients_key_ids_->empty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("You have checked the encrypted email content, but you have not " + "selected the recipient's GPG key. This is dangerous and the mail " + "will not be encrypted. So the send operation is forbidden")); + return; + } else { + auto key_ids = std::make_unique<KeyIdArgsList>(); + for (const auto& key_id : *recipients_key_ids_) + key_ids->push_back(key_id); + thread->setEncryptContent(true, std::move(key_ids)); + } } - message.setSubject(ui->subjectEdit->text()); - // Now add some text to the email. - // First we create a MimeText object. + if (ui->attacSignatureCheckBox->checkState() == Qt::Checked) { + if (sender_key_id_.empty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("You checked the option to attach signature to the email, but did " + "not specify the sender's GPG Key. This will cause the content of " + "the email to be inconsistent with your expectations, so the " + "operation is prohibited.")); + return; + } else { + thread->setAttachSignatureFile(true, sender_key_id_); + } + } - MimeText text; - text.setText(ui->textEdit->toPlainText()); + if (ui->attachSenderPublickeyCheckBox->checkState() == Qt::Checked) { + if (sender_key_id_.empty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("You checked the option to attach your public key to the email, " + "but did not specify the sender's GPG Key. This will cause the " + "content of " + "the email to be inconsistent " + "with your expectations, so the operation is prohibited.")); + return; + } else { + thread->setAttachPublicKey(true, sender_key_id_); + } + } - // Now add it to the mail - message.addPart(&text); + // Waiting Dialog + auto* waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = + new QLabel(QString(_("Sending Email...")) + "<br /><br />" + + _("If the process does not end for a long time, please check " + "again whether your SMTP server configuration is correct.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &SMTPSendMailThread::signalSMTPResult, this, + &SendMailDialog::slotTestSMTPConnectionResult); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); - // Now we can send the mail - if (!smtp.connectToHost()) { - qDebug() << "Connect to SMTP Server Failed"; - QMessageBox::critical(this, _("Fail"), _("Fail to Connect SMTP Server")); - return; - } - if (!smtp.login()) { - qDebug() << "Login to SMTP Server Failed"; - QMessageBox::critical(this, _("Fail"), _("Fail to Login into SMTP Server")); - return; - } - if (!smtp.sendMail(message)) { - qDebug() << "Send Mail to SMTP Server Failed"; - QMessageBox::critical(this, _("Fail"), - _("Fail to Send Mail to SMTP Server")); - return; - } - smtp.quit(); + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); - // Close after sending email - QMessageBox::information(this, _("Success"), - _("Succeed in Sending Mail to SMTP Server")); - deleteLater(); + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); } void SendMailDialog::initSettings() { auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); try { - ability_enable = settings.lookup("smtp.enable"); + ability_enable_ = settings.lookup("smtp.enable"); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); } try { - identity_enable = settings.lookup("smtp.identity_enable"); + identity_enable_ = settings.lookup("smtp.identity_enable"); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("identity_enable"); } try { - smtpAddress = settings.lookup("smtp.mail_address").c_str(); + smtp_address_ = settings.lookup("smtp.mail_address").c_str(); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("mail_address"); } try { - username = settings.lookup("smtp.username").c_str(); + username_ = settings.lookup("smtp.username").c_str(); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("username"); } try { - password = settings.lookup("smtp.password").c_str(); + password_ = settings.lookup("smtp.password").c_str(); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("password"); } try { - port = settings.lookup("smtp.port"); + port_ = settings.lookup("smtp.port"); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("port"); } try { - connectionTypeSettings = settings.lookup("smtp.connection_type").c_str(); + connection_type_settings_ = settings.lookup("smtp.connection_type").c_str(); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("connection_type"); } try { - defaultSender = settings.lookup("smtp.default_sender").c_str(); + default_sender_ = settings.lookup("smtp.default_sender").c_str(); } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("default_sender"); } -} + try { + default_sender_gpg_key_id = + settings.lookup("smtp.default_sender_gpg_key_id").c_str(); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") + << _("default_sender_gpg_key_id"); + } +} #endif +void SendMailDialog::set_sender_value_label() { + auto key = GpgKeyGetter::GetInstance().GetKey(sender_key_id_); + if (key.good()) { + ui->senderKeyValueLabel->setText(key.uids()->front().uid().c_str()); + } +} + +void SendMailDialog::set_recipients_value_label() { + auto keys = GpgKeyGetter::GetInstance().GetKeys(recipients_key_ids_); + std::stringstream ss; + for (const auto& key : *keys) { + if (key.good()) { + ss << key.uids()->front().uid().c_str() << ";"; + } + } + ui->recipientsKeyValueLabel->setText(ss.str().c_str()); +} + +void SendMailDialog::slotTestSMTPConnectionResult(const QString& result) { + if (result == "Fail to connect SMTP server") { + QMessageBox::critical(this, _("Fail"), _("Fail to Connect SMTP Server.")); + } else if (result == "Fail to login") { + QMessageBox::critical(this, _("Fail"), _("Fail to Login.")); + } else if (result == "Fail to send mail") { + QMessageBox::critical(this, _("Fail"), _("Fail to Login.")); + } else if (result == "Succeed in testing connection") { + QMessageBox::information(this, _("Success"), + _("Succeed in connecting and login")); + } else if (result == "Succeed in sending a test email") { + QMessageBox::information( + this, _("Success"), + _("Succeed in sending the message to the SMTP Server")); + } else if (result == "Fail to encrypt with gpg keys") { + QMessageBox::critical( + this, _("Encryption Error"), + _("An error occurred while encrypting the content of the email. This " + "may be because you did not complete some operations during Gnupg " + "encryption.")); + } else { + QMessageBox::critical(this, _("Fail"), _("Unknown error.")); + } +} +void SendMailDialog::setContentEncryption(bool on) { + ui->contentEncryptCheckBox->setCheckState(on ? Qt::Checked : Qt::Unchecked); +} + +void SendMailDialog::setAttachSignature(bool on) { + ui->attacSignatureCheckBox->setCheckState(on ? Qt::Checked : Qt::Unchecked); +} + } // namespace GpgFrontend::UI diff --git a/src/ui/smtp/SendMailDialog.h b/src/ui/smtp/SendMailDialog.h index 979d4f88..04224f18 100644 --- a/src/ui/smtp/SendMailDialog.h +++ b/src/ui/smtp/SendMailDialog.h @@ -36,28 +36,43 @@ class SendMailDialog : public QDialog { public: explicit SendMailDialog(const QString& text, QWidget* parent = nullptr); + void setContentEncryption(bool on); + + void setAttachSignature(bool on); + private slots: void slotConfirm(); + void slotTestSMTPConnectionResult(const QString& result); + private: void initSettings(); std::shared_ptr<Ui_SendMailDialog> ui; - bool ability_enable = false; - bool identity_enable = false; - QString smtpAddress; - QString username; - QString password; - QString defaultSender; - QString connectionTypeSettings = "None"; - int port = 25; + bool ability_enable_ = false; + bool identity_enable_ = false; + QString smtp_address_; + QString username_; + QString password_; + QString default_sender_; + QString connection_type_settings_ = "None"; + QString default_sender_gpg_key_id = {}; + int port_ = 25; + + GpgFrontend::KeyId sender_key_id_; + GpgFrontend::KeyIdArgsListPtr recipients_key_ids_ = + std::make_unique<GpgFrontend::KeyIdArgsList>(); 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])+)\]))"}; bool check_email_address(const QString& str); + + void set_sender_value_label(); + + void set_recipients_value_label(); }; } // namespace GpgFrontend::UI diff --git a/src/ui/smtp/SenderPicker.cpp b/src/ui/smtp/SenderPicker.cpp new file mode 100644 index 00000000..fcd3ba61 --- /dev/null +++ b/src/ui/smtp/SenderPicker.cpp @@ -0,0 +1,76 @@ +/** + * 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 "SenderPicker.h" + +#include "ui/widgets/KeyList.h" + +GpgFrontend::UI::SenderPicker::SenderPicker(const KeyId& current_key_id, + QWidget* parent) + : QDialog(parent) { + auto confirm_button = new QPushButton(_("Confirm")); + connect(confirm_button, SIGNAL(clicked(bool)), this, SLOT(accept())); + + // Setup KeyList + key_list_ = new KeyList(KeyMenuAbility::NONE, this); + key_list_->addListGroupTab( + _("Sender"), KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | KeyListColumn::EmailAddress, + [](const GpgKey& key) -> bool { return key.CanSignActual(); }); + key_list_->slotRefresh(); + + auto key_ids = std::make_unique<GpgFrontend::KeyIdArgsList>(); + key_ids->push_back(current_key_id); + key_list_->setChecked(std::move(key_ids)); + + auto* vbox2 = new QVBoxLayout(); + vbox2->addWidget(new QLabel(QString(_("Select Sender")) + ": ")); + vbox2->addWidget(key_list_); + vbox2->addWidget(new QLabel( + QString( + _("As the sender of the mail, the private key is generally used.")) + + "\n" + + _(" The private key is generally used as a signature for the content of " + "the mail."))); + vbox2->addWidget(confirm_button); + vbox2->addStretch(0); + setLayout(vbox2); + + this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::CustomizeWindowHint); + + this->setModal(true); + this->setWindowTitle("Sender Picker"); + this->setMinimumWidth(480); + this->exec(); +} + +GpgFrontend::KeyId GpgFrontend::UI::SenderPicker::getCheckedSender() { + auto checked_keys = key_list_->getChecked(); + if (!checked_keys->empty()) { + return key_list_->getChecked()->front(); + } else { + return {}; + } +} diff --git a/src/ui/smtp/SenderPicker.h b/src/ui/smtp/SenderPicker.h new file mode 100644 index 00000000..3745bb34 --- /dev/null +++ b/src/ui/smtp/SenderPicker.h @@ -0,0 +1,47 @@ +/** + * 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_SENDERPICKER_H +#define GPGFRONTEND_SENDERPICKER_H + +#include "GpgFrontendUI.h" + +namespace GpgFrontend::UI { + +class KeyList; + +class SenderPicker : public QDialog { + Q_OBJECT + + public: + explicit SenderPicker(const KeyId& current_key_id, QWidget* parent = nullptr); + + GpgFrontend::KeyId getCheckedSender(); + + private: + KeyList* key_list_; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SENDERPICKER_H diff --git a/src/ui/widgets/EditorPage.cpp b/src/ui/widgets/EditorPage.cpp index 94c98036..b73974a7 100644 --- a/src/ui/widgets/EditorPage.cpp +++ b/src/ui/widgets/EditorPage.cpp @@ -32,7 +32,7 @@ namespace GpgFrontend::UI { EditorPage::EditorPage(QString filePath, QWidget* parent) - : QWidget(parent), fullFilePath(std::move(filePath)) { + : QWidget(parent), full_file_path_(std::move(filePath)) { // Set the Textedit properties textPage = new QTextEdit(); textPage->setAcceptRichText(false); @@ -51,12 +51,12 @@ EditorPage::EditorPage(QString filePath, QWidget* parent) this->setAttribute(Qt::WA_DeleteOnClose); } -const QString& EditorPage::getFilePath() const { return fullFilePath; } +const QString& EditorPage::getFilePath() const { return full_file_path_; } QTextEdit* EditorPage::getTextPage() { return textPage; } void EditorPage::setFilePath(const QString& filePath) { - fullFilePath = filePath; + full_file_path_ = filePath; } void EditorPage::showNotificationWidget(QWidget* widget, @@ -110,34 +110,34 @@ void EditorPage::slotFormatGpgHeader() { void EditorPage::ReadFile() { LOG(INFO) << "Called"; - readDone = false; + read_done_ = false; auto text_page = this->getTextPage(); text_page->setReadOnly(true); - auto thread = new FileReadThread(this->fullFilePath.toStdString()); + auto thread = new FileReadThread(this->full_file_path_.toStdString()); connect(thread, &FileReadThread::sendReadBlock, this, &EditorPage::slotInsertText); connect(thread, &FileReadThread::readDone, this, [=]() { - LOG(INFO) << "Thread read done"; + LOG(INFO) << "thread read done"; text_page->document()->setModified(false); text_page->setReadOnly(false); }); connect(thread, &FileReadThread::finished, this, [=]() { - LOG(INFO) << "Thread finished"; + LOG(INFO) << "thread finished"; thread->deleteLater(); - readDone = true; - readThread = nullptr; + read_done_ = true; + read_hread_ = nullptr; }); connect(this, &EditorPage::destroyed, [=]() { - LOG(INFO) << "RequestInterruption for readThread"; + LOG(INFO) << "request interruption for read thread"; thread->requestInterruption(); - readThread = nullptr; + read_hread_ = nullptr; }); - this->readThread = thread; + this->read_hread_ = thread; thread->start(); } @@ -145,9 +145,9 @@ void EditorPage::slotInsertText(const QString& text) { this->getTextPage()->insertPlainText(text); } void EditorPage::PrepareToDestroy() { - if (readThread) { - readThread->requestInterruption(); - readThread = nullptr; + if (read_hread_) { + read_hread_->requestInterruption(); + read_hread_ = nullptr; } } diff --git a/src/ui/widgets/EditorPage.h b/src/ui/widgets/EditorPage.h index a0a05dab..d1bc1ac2 100644 --- a/src/ui/widgets/EditorPage.h +++ b/src/ui/widgets/EditorPage.h @@ -86,17 +86,17 @@ class EditorPage : public QWidget { void ReadFile(); - [[nodiscard]] bool ReadDone() const { return this->readDone; } + [[nodiscard]] bool ReadDone() const { return this->read_done_; } void PrepareToDestroy(); private: QTextEdit* textPage; /** The textedit of the tab */ QVBoxLayout* mainLayout; /** The layout for the tab */ - QString fullFilePath; /** The path to the file handled in the tab */ + QString full_file_path_; /** The path to the file handled in the tab */ bool signMarked{}; /** true, if the signed header is marked, false if not */ - bool readDone = false; - QThread* readThread = nullptr; + bool read_done_ = false; + QThread* read_hread_ = nullptr; private slots: diff --git a/src/ui/widgets/ExportKeyPackageDialog.cpp b/src/ui/widgets/ExportKeyPackageDialog.cpp new file mode 100644 index 00000000..d99e966a --- /dev/null +++ b/src/ui/widgets/ExportKeyPackageDialog.cpp @@ -0,0 +1,170 @@ +/** + * 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 "ExportKeyPackageDialog.h" + +#include <boost/format.hpp> + +#include "gpg/function/GpgKeyGetter.h" +#include "gpg/function/GpgKeyImportExporter.h" +#include "ui/aes/qaesencryption.h" +#include "ui_ExportKeyPackageDialog.h" + +GpgFrontend::UI::ExportKeyPackageDialog::ExportKeyPackageDialog( + KeyIdArgsListPtr key_ids, QWidget* parent) + : QDialog(parent), + ui(std::make_shared<Ui_exportKeyPackageDialog>()), + key_ids_(std::move(key_ids)), + mt(rd()) { + ui->setupUi(this); + + generate_key_package_name(); + + connect(ui->gnerateNameButton, &QPushButton::clicked, this, + [=]() { generate_key_package_name(); }); + + connect(ui->setOutputPathButton, &QPushButton::clicked, this, [=]() { + auto file_name = QFileDialog::getSaveFileName( + this, _("Export Key Package"), ui->nameValueLabel->text() + ".gfepack", + QString(_("Key Package")) + " (*.gfepack);;All Files (*)"); + ui->outputPathLabel->setText(file_name); + }); + + connect(ui->generatePassphraseButton, &QPushButton::clicked, this, [=]() { + passphrase_ = generate_passphrase(256); + auto file_name = QFileDialog::getSaveFileName( + this, _("Export Key Package Passphrase"), + ui->nameValueLabel->text() + ".key", + QString(_("Key File")) + " (*.key);;All Files (*)"); + ui->passphraseValueLabel->setText(file_name); + write_buffer_to_file(file_name.toStdString(), passphrase_); + }); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, [=]() { + if (ui->outputPathLabel->text().isEmpty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please select an output path before exporting.")); + return; + } + + if (ui->passphraseValueLabel->text().isEmpty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please generate a password to protect your key before exporting, " + "it is very important. Don't forget to back up your password in a " + "safe place.")); + return; + } + + auto key_id_exported = std::make_unique<KeyIdArgsList>(); + auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids_); + for (const auto& key : *keys) { + if (ui->noPublicKeyCheckBox->isChecked() && !key.is_private_key()) { + continue; + } + key_id_exported->push_back(key.id()); + } + + ByteArrayPtr key_export_data = nullptr; + if (!GpgKeyImportExporter::GetInstance().ExportKeys( + key_ids_, key_export_data, + ui->includeSecretKeyCheckBox->isChecked())) { + QMessageBox::critical(this, _("Error"), _("Export Key(s) Failed.")); + this->close(); + return; + } + + auto key = QByteArray::fromStdString(passphrase_), + data = + QString::fromStdString(*key_export_data).toLocal8Bit().toBase64(); + + auto hash_key = QCryptographicHash::hash(key, QCryptographicHash::Sha256); + QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::ECB, + QAESEncryption::Padding::ISO); + auto encoded = encryption.encode(data, hash_key); + + write_buffer_to_file(ui->outputPathLabel->text().toStdString(), + encoded.toStdString()); + + QMessageBox::information( + this, _("Success"), + QString(_( + "The Key Package has been successfully generated and has been " + "protected by encryption algorithms. You can safely transfer your " + "Key Package.")) + + "<br />" + "<b>" + + _("But the key file cannot be leaked under any " + "circumstances. Please delete the Key Package and key file as " + "soon " + "as possible after completing the transfer operation.") + + "</b>"); + }); + + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, + [=]() { this->close(); }); + + ui->nameLabel->setText(_("Key Package Name")); + ui->selectOutputPathLabel->setText(_("Output Path")); + ui->passphraseLabel->setText(_("Passphrase")); + ui->tipsLabel->setText( + _("Tips: You can use Key Package to safely and conveniently transfer " + "your public and private keys between devices.")); + ui->generatePassphraseButton->setText(_("Generate and Save Passphrase")); + ui->gnerateNameButton->setText(_("Generate Key Package Name")); + ui->setOutputPathButton->setText(_("Select Output Path")); + + ui->includeSecretKeyCheckBox->setText( + _("Include secret key (Think twice before acting)")); + ui->noPublicKeyCheckBox->setText( + _("Exclude keys that do not have a private key")); + + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(_("exportKeyPackageDialog")); +} + +std::string GpgFrontend::UI::ExportKeyPackageDialog::generate_passphrase( + const int len) { + std::uniform_int_distribution<int> dist(999, 99999); + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + std::string tmp_str; + tmp_str.reserve(len); + + for (int i = 0; i < len; ++i) { + tmp_str += alphanum[dist(mt) % (sizeof(alphanum) - 1)]; + } + + return tmp_str; +} + +void GpgFrontend::UI::ExportKeyPackageDialog::generate_key_package_name() { + std::uniform_int_distribution<int> dist(999, 99999); + auto file_string = boost::format("KeyPackage_%1%") % dist(mt); + ui->nameValueLabel->setText(file_string.str().c_str()); + ui->outputPathLabel->clear(); + ui->passphraseValueLabel->clear(); +} diff --git a/src/ui/widgets/ExportKeyPackageDialog.h b/src/ui/widgets/ExportKeyPackageDialog.h new file mode 100644 index 00000000..a254b453 --- /dev/null +++ b/src/ui/widgets/ExportKeyPackageDialog.h @@ -0,0 +1,55 @@ +/** + * 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_EXPORTKEYPACKAGEDIALOG_H +#define GPGFRONTEND_EXPORTKEYPACKAGEDIALOG_H + +#include "GpgFrontendUI.h" + +class Ui_exportKeyPackageDialog; + +namespace GpgFrontend::UI { + +class ExportKeyPackageDialog : public QDialog { + Q_OBJECT + + public: + explicit ExportKeyPackageDialog(KeyIdArgsListPtr key_ids, QWidget* parent); + + std::string generate_passphrase(const int len); + + private: + std::shared_ptr<Ui_exportKeyPackageDialog> ui; + KeyIdArgsListPtr key_ids_; + + std::random_device rd; + std::mt19937 mt; + + std::string passphrase_; + + void generate_key_package_name(); +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_EXPORTKEYPACKAGEDIALOG_H diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index ca6dedc1..3e140bd4 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -156,48 +156,57 @@ void FilePage::slotGoPath() { void FilePage::createPopupMenu() { popUpMenu = new QMenu(); - openItemAct = new QAction(_("Open"), this); - connect(openItemAct, &QAction::triggered, this, &FilePage::slotOpenItem); - renameItemAct = new QAction(_("Rename"), this); - connect(renameItemAct, &QAction::triggered, this, &FilePage::slotRenameItem); - deleteItemAct = new QAction(_("Delete"), this); - connect(deleteItemAct, &QAction::triggered, this, &FilePage::slotDeleteItem); - encryptItemAct = new QAction(_("Encrypt Sign"), this); - connect(encryptItemAct, &QAction::triggered, this, + ui->actionOpenFile->setText(_("Open")); + connect(ui->actionOpenFile, &QAction::triggered, this, + &FilePage::slotOpenItem); + ui->actionRenameFile->setText(_("Rename")); + connect(ui->actionRenameFile, &QAction::triggered, this, + &FilePage::slotRenameItem); + ui->actionDeleteFile->setText(_("Delete")); + connect(ui->actionDeleteFile, &QAction::triggered, this, + &FilePage::slotDeleteItem); + + ui->actionEncrypt->setText(_("Encrypt")); + connect(ui->actionEncrypt, &QAction::triggered, this, &FilePage::slotEncryptItem); - decryptItemAct = - new QAction(QString(_("Decrypt Verify")) + " " + _("(.gpg .asc)"), this); - connect(decryptItemAct, &QAction::triggered, this, + ui->actionEncryptSign->setText(_("Encrypt Sign")); + connect(ui->actionEncryptSign, &QAction::triggered, this, + &FilePage::slotEncryptSignItem); + ui->actionDecrypt->setText(QString(_("Decrypt Verify")) + " " + + _("(.gpg .asc)")); + connect(ui->actionDecrypt, &QAction::triggered, this, &FilePage::slotDecryptItem); - signItemAct = new QAction(_("Sign"), this); - connect(signItemAct, &QAction::triggered, this, &FilePage::slotSignItem); - verifyItemAct = - new QAction(QString(_("Verify")) + " " + _("(.sig .gpg .asc)"), this); - connect(verifyItemAct, &QAction::triggered, this, &FilePage::slotVerifyItem); - - hashCalculateAct = new QAction(_("Calculate Hash"), this); - connect(hashCalculateAct, &QAction::triggered, this, + ui->actionSign->setText(_("Sign")); + connect(ui->actionSign, &QAction::triggered, this, &FilePage::slotSignItem); + ui->actionVerify->setText(QString(_("Verify")) + " " + _("(.sig .gpg .asc)")); + connect(ui->actionVerify, &QAction::triggered, this, + &FilePage::slotVerifyItem); + + ui->actionCalculateHash->setText(_("Calculate Hash")); + connect(ui->actionCalculateHash, &QAction::triggered, this, &FilePage::slotCalculateHash); - mkdirAct = new QAction(_("Make New Directory"), this); - connect(mkdirAct, &QAction::triggered, this, &FilePage::slotMkdir); + ui->actionMakeDirectory->setText(_("Make New Directory")); + connect(ui->actionMakeDirectory, &QAction::triggered, this, + &FilePage::slotMkdir); - createEmptyFileAct = new QAction(_("Create Empty File"), this); - connect(createEmptyFileAct, &QAction::triggered, this, + ui->actionCreateEmptyFile->setText(_("Create Empty File")); + connect(ui->actionCreateEmptyFile, &QAction::triggered, this, &FilePage::slotCreateEmptyFile); - popUpMenu->addAction(openItemAct); - popUpMenu->addAction(renameItemAct); - popUpMenu->addAction(deleteItemAct); + popUpMenu->addAction(ui->actionOpenFile); + popUpMenu->addAction(ui->actionRenameFile); + popUpMenu->addAction(ui->actionDeleteFile); popUpMenu->addSeparator(); - popUpMenu->addAction(encryptItemAct); - popUpMenu->addAction(decryptItemAct); - popUpMenu->addAction(signItemAct); - popUpMenu->addAction(verifyItemAct); + popUpMenu->addAction(ui->actionEncrypt); + popUpMenu->addAction(ui->actionEncryptSign); + popUpMenu->addAction(ui->actionDecrypt); + popUpMenu->addAction(ui->actionSign); + popUpMenu->addAction(ui->actionVerify); popUpMenu->addSeparator(); - popUpMenu->addAction(mkdirAct); - popUpMenu->addAction(createEmptyFileAct); - popUpMenu->addAction(hashCalculateAct); + popUpMenu->addAction(ui->actionMakeDirectory); + popUpMenu->addAction(ui->actionCreateEmptyFile); + popUpMenu->addAction(ui->actionCalculateHash); optionPopUpMenu = new QMenu(); @@ -232,32 +241,37 @@ void FilePage::onCustomContextMenu(const QPoint& point) { dirModel->fileInfo(index).absoluteFilePath().toStdString()); LOG(INFO) << "right click" << selectedPath; if (index.isValid()) { - openItemAct->setEnabled(true); - renameItemAct->setEnabled(true); - deleteItemAct->setEnabled(true); + ui->actionOpenFile->setEnabled(true); + ui->actionRenameFile->setEnabled(true); + ui->actionDeleteFile->setEnabled(true); QFileInfo info(QString::fromStdString(selectedPath.string())); - encryptItemAct->setEnabled( - info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); - encryptItemAct->setEnabled( - info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); - decryptItemAct->setEnabled(info.isFile() && info.suffix() == "gpg"); - signItemAct->setEnabled(info.isFile() && - (info.suffix() != "gpg" && info.suffix() != "sig")); - verifyItemAct->setEnabled( - info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg")); - hashCalculateAct->setEnabled(info.isFile() && info.isReadable()); + ui->actionEncrypt->setEnabled(info.isFile() && (info.suffix() != "gpg" && + info.suffix() != "sig" && + info.suffix() != "asc")); + ui->actionEncryptSign->setEnabled( + info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig" && + info.suffix() != "asc")); + ui->actionDecrypt->setEnabled( + info.isFile() && (info.suffix() == "gpg" || info.suffix() == "asc")); + ui->actionSign->setEnabled(info.isFile() && (info.suffix() != "gpg" && + info.suffix() != "sig" && + info.suffix() != "asc")); + ui->actionVerify->setEnabled(info.isFile() && (info.suffix() == "sig" || + info.suffix() == "gpg" || + info.suffix() == "asc")); + ui->actionCalculateHash->setEnabled(info.isFile() && info.isReadable()); } else { - openItemAct->setEnabled(false); - renameItemAct->setEnabled(false); - deleteItemAct->setEnabled(false); - - encryptItemAct->setEnabled(false); - encryptItemAct->setEnabled(false); - decryptItemAct->setEnabled(false); - signItemAct->setEnabled(false); - verifyItemAct->setEnabled(false); - hashCalculateAct->setEnabled(false); + ui->actionOpenFile->setEnabled(false); + ui->actionRenameFile->setEnabled(false); + ui->actionDeleteFile->setEnabled(false); + + ui->actionEncrypt->setEnabled(false); + ui->actionEncryptSign->setEnabled(false); + ui->actionDecrypt->setEnabled(false); + ui->actionSign->setEnabled(false); + ui->actionVerify->setEnabled(false); + ui->actionCalculateHash->setEnabled(false); } popUpMenu->exec(ui->fileTreeView->viewport()->mapToGlobal(point)); } @@ -331,6 +345,11 @@ void FilePage::slotDeleteItem() { void FilePage::slotEncryptItem() { auto mainWindow = qobject_cast<MainWindow*>(firstParent); + if (mainWindow != nullptr) mainWindow->slotFileEncrypt(); +} + +void FilePage::slotEncryptSignItem() { + auto mainWindow = qobject_cast<MainWindow*>(firstParent); if (mainWindow != nullptr) mainWindow->slotFileEncryptSign(); } diff --git a/src/ui/widgets/FilePage.h b/src/ui/widgets/FilePage.h index 03caf36b..1f2b51f8 100644 --- a/src/ui/widgets/FilePage.h +++ b/src/ui/widgets/FilePage.h @@ -61,6 +61,7 @@ class FilePage : public QWidget { void slotRenameItem(); void slotDeleteItem(); void slotEncryptItem(); + void slotEncryptSignItem(); void slotDecryptItem(); void slotSignItem(); void slotVerifyItem(); @@ -88,16 +89,6 @@ class FilePage : public QWidget { QMenu* popUpMenu{}; QMenu* optionPopUpMenu{}; - QAction* encryptItemAct{}; - QAction* decryptItemAct{}; - QAction* signItemAct{}; - QAction* verifyItemAct{}; - QAction* hashCalculateAct{}; - QAction* mkdirAct{}; - QAction* openItemAct{}; - QAction* renameItemAct{}; - QAction* deleteItemAct{}; - QAction* createEmptyFileAct{}; QWidget* firstParent; }; diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index d46d5f92..7718cba3 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -35,7 +35,6 @@ InfoBoardWidget::InfoBoardWidget(QWidget* parent) ui->setupUi(this); ui->actionButtonLayout->addStretch(); - ui->actionLabel->setText(_("InfoBoard's Actions Menu")); ui->copyButton->setText(_("Copy")); ui->saveButton->setText(_("Save File")); ui->clearButton->setText(_("Clear")); @@ -163,7 +162,7 @@ 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; + if (file_path.isEmpty()) return; QFile file(file_path); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 0e4c5bba..89634f95 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -27,6 +27,7 @@ #include <boost/format.hpp> #include <utility> +#include "gpg/GpgCoreInit.h" #include "gpg/function/GpgKeyGetter.h" #include "ui/SignalStation.h" #include "ui/UserInterfaceUtils.h" @@ -37,18 +38,35 @@ namespace GpgFrontend::UI { int KeyList::key_list_id = 2048; -KeyList::KeyList(bool menu, QWidget* parent) +KeyList::KeyList(KeyMenuAbility::AbilityType menu_ability, QWidget* parent) : QWidget(parent), _m_key_list_id(key_list_id++), ui(std::make_shared<Ui_KeyList>()), - menu_status(menu) { + menu_ability_(menu_ability) { init(); } void KeyList::init() { +#ifdef GPG_STANDALONE_MODE + LOG(INFO) << "GPG_STANDALONE_MODE Enabled"; + auto gpg_path = GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetStandaloneGpgBinDir(); + auto db_path = GpgFrontend::UI::GlobalSettingStation::GetInstance() + .GetStandaloneDatabaseDir(); + GpgContext::CreateInstance( + _m_key_list_id, std::make_unique<GpgContext>(true, db_path.string(), true, + gpg_path.string())); +#else + new_default_settings_channel(_m_key_list_id); +#endif + ui->setupUi(this); - ui->menuWidget->setHidden(!menu_status); + ui->menuWidget->setHidden(!menu_ability_); + ui->refreshKeyListButton->setHidden(~menu_ability_ & KeyMenuAbility::REFRESH); + ui->syncButton->setHidden(~menu_ability_ & KeyMenuAbility::SYNC_PUBLIC_KEY); + ui->uncheckButton->setHidden(~menu_ability_ & KeyMenuAbility::UNCHECK_ALL); + ui->keyGroupTab->clear(); popupMenu = new QMenu(this); @@ -59,6 +77,10 @@ void KeyList::init() { SLOT(slotRefresh())); connect(ui->refreshKeyListButton, &QPushButton::clicked, this, &KeyList::slotRefresh); + connect(ui->uncheckButton, &QPushButton::clicked, this, + &KeyList::slotUncheckALL); + connect(ui->checkALLButton, &QPushButton::clicked, this, + &KeyList::slotCheckALL); connect(ui->syncButton, &QPushButton::clicked, this, &KeyList::slotSyncWithKeyServer); connect(this, &KeyList::signalRefreshStatusBar, SignalStation::GetInstance(), @@ -66,10 +88,17 @@ void KeyList::init() { setAcceptDrops(true); - ui->keyListOperationsLabel->setText(_("Key List Menu: ")); ui->refreshKeyListButton->setText(_("Refresh")); + ui->refreshKeyListButton->setToolTip( + _("Refresh the key list to synchronize changes.")); ui->syncButton->setText(_("Sync Public Key")); - ui->syncButton->setToolTip(_("Sync public key with your default keyserver")); + ui->syncButton->setToolTip(_("Sync public key with your default keyserver.")); + ui->uncheckButton->setText(_("Uncheck ALL")); + ui->uncheckButton->setToolTip( + _("Cancel all checked items in the current tab at once.")); + ui->checkALLButton->setText(_("Check ALL")); + ui->checkALLButton->setToolTip( + _("Check all items in the current tab at once")); } void KeyList::addListGroupTab( @@ -214,17 +243,14 @@ void KeyList::setChecked(const KeyIdArgsListPtr& keyIds, } } -void KeyList::setChecked(const KeyIdArgsListPtr& keyIds) { - if (ui->keyGroupTab->size().isEmpty()) return; +void KeyList::setChecked(KeyIdArgsListPtr key_ids) { auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); - const auto& buffered_keys = - mKeyTables[ui->keyGroupTab->currentIndex()].buffered_keys; - - if (!keyIds->empty()) { - for (int i = 0; i < key_list->rowCount(); i++) { - if (std::find(keyIds->begin(), keyIds->end(), buffered_keys[i].id()) != - keyIds->end()) { - key_list->item(i, 0)->setCheckState(Qt::Checked); + if (key_list == nullptr) return; + if (!mKeyTables.empty()) { + for (auto& key_table : mKeyTables) { + if (key_table.key_list == key_list) { + key_table.SetChecked(std::move(key_ids)); + break; } } } @@ -364,7 +390,7 @@ void KeyList::dragEnterEvent(QDragEnterEvent* event) { void KeyList::importKeys(const QByteArray& inBuffer) { auto std_buffer = std::make_unique<ByteArray>(inBuffer.toStdString()); GpgImportInformation result = - GpgKeyImportExportor::GetInstance(_m_key_list_id) + GpgKeyImportExporter::GetInstance(_m_key_list_id) .ImportKey(std::move(std_buffer)); new KeyImportDetailDialog(result, false, this); } @@ -447,31 +473,56 @@ void KeyList::slotSyncWithKeyServer() { }); } -KeyIdArgsListPtr KeyTable::GetChecked() { - auto ret = std::make_unique<KeyIdArgsList>(); - for (int i = 0; i < key_list->rowCount(); i++) { - if (key_list->item(i, 0)->checkState() == Qt::Checked) { - ret->push_back(buffered_keys[i].id()); +void KeyList::slotUncheckALL() { + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); + if (key_list == nullptr) return; + if (!mKeyTables.empty()) { + for (auto& key_table : mKeyTables) { + if (key_table.key_list == key_list) { + key_table.UncheckALL(); + break; + } } } - return ret; } -void KeyTable::SetChecked(const KeyIdArgsListPtr& key_ids) { - if (!key_ids->empty()) { - for (int i = 0; i < key_list->rowCount(); i++) { - if (std::find(key_ids->begin(), key_ids->end(), buffered_keys[i].id()) != - key_ids->end()) { - key_list->item(i, 0)->setCheckState(Qt::Checked); +void KeyList::slotCheckALL() { + auto key_list = qobject_cast<QTableWidget*>(ui->keyGroupTab->currentWidget()); + if (key_list == nullptr) return; + if (!mKeyTables.empty()) { + for (auto& key_table : mKeyTables) { + if (key_table.key_list == key_list) { + key_table.CheckALL(); + break; } } } } +KeyIdArgsListPtr& KeyTable::GetChecked() { + LOG(INFO) << "called"; + if (checked_key_ids_ == nullptr) + checked_key_ids_ = std::make_unique<KeyIdArgsList>(); + auto& ret = checked_key_ids_; + for (int i = 0; i < key_list->rowCount(); i++) { + auto key_id = buffered_keys[i].id(); + if (key_list->item(i, 0)->checkState() == Qt::Checked && + std::find(ret->begin(), ret->end(), key_id) == ret->end()) { + ret->push_back(key_id); + } + } + return ret; +} + +void KeyTable::SetChecked(KeyIdArgsListPtr key_ids) { + LOG(INFO) << "called"; + checked_key_ids_ = std::move(key_ids); +} + void KeyTable::Refresh(KeyLinkListPtr m_keys) { LOG(INFO) << "Called"; - auto checked_key_list = GetChecked(); + auto& checked_key_list = GetChecked(); // while filling the table, sort enabled causes errors key_list->setSortingEnabled(false); @@ -532,6 +583,11 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) { if (it->is_private_key() && !it->has_master_key()) { type_steam << "#"; } + + if(it->HasCardKey()) { + type_steam << "^"; + } + auto* tmp1 = new QTableWidgetItem(type_str); key_list->setItem(row_index, 1, tmp1); @@ -577,8 +633,27 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) { ++row_index; } - SetChecked(checked_key_list); + if (!checked_key_list->empty()) { + for (int i = 0; i < key_list->rowCount(); i++) { + if (std::find(checked_key_list->begin(), checked_key_list->end(), + buffered_keys[i].id()) != checked_key_list->end()) { + key_list->item(i, 0)->setCheckState(Qt::Checked); + } + } + } LOG(INFO) << "End"; } + +void KeyTable::UncheckALL() const { + for (int i = 0; i < key_list->rowCount(); i++) { + key_list->item(i, 0)->setCheckState(Qt::Unchecked); + } +} + +void KeyTable::CheckALL() const { + for (int i = 0; i < key_list->rowCount(); i++) { + key_list->item(i, 0)->setCheckState(Qt::Checked); + } +} } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyList.h b/src/ui/widgets/KeyList.h index 617bb274..4b41decd 100644 --- a/src/ui/widgets/KeyList.h +++ b/src/ui/widgets/KeyList.h @@ -53,12 +53,24 @@ struct KeyListColumn { static constexpr InfoType FingerPrint = 1 << 5; }; +struct KeyMenuAbility { + using AbilityType = unsigned int; + + static constexpr AbilityType ALL = ~0; + static constexpr AbilityType NONE = 0; + static constexpr AbilityType REFRESH = 1 << 0; + static constexpr AbilityType SYNC_PUBLIC_KEY = 1 << 1; + static constexpr AbilityType UNCHECK_ALL = 1 << 3; + static constexpr AbilityType CHECK_ALL = 1 << 5; +}; + struct KeyTable { QTableWidget* key_list; KeyListRow::KeyType select_type; KeyListColumn::InfoType info_type; std::vector<GpgKey> buffered_keys; std::function<bool(const GpgKey&)> filter; + KeyIdArgsListPtr checked_key_ids_; KeyTable( QTableWidget* _key_list, KeyListRow::KeyType _select_type, @@ -73,16 +85,21 @@ struct KeyTable { void Refresh(KeyLinkListPtr m_keys = nullptr); - KeyIdArgsListPtr GetChecked(); + KeyIdArgsListPtr& GetChecked(); + + void UncheckALL() const; + + void CheckALL() const; - void SetChecked(const KeyIdArgsListPtr& key_ids); + void SetChecked(KeyIdArgsListPtr key_ids); }; class KeyList : public QWidget { Q_OBJECT public: - explicit KeyList(bool menu, QWidget* parent = nullptr); + explicit KeyList(KeyMenuAbility::AbilityType menu_ability, + QWidget* parent = nullptr); void addListGroupTab( const QString& name, @@ -102,7 +119,7 @@ class KeyList : public QWidget { static KeyIdArgsListPtr getChecked(const KeyTable& key_table); KeyIdArgsListPtr getPrivateChecked(); KeyIdArgsListPtr getAllPrivateKeys(); - void setChecked(const KeyIdArgsListPtr& keyIds); + void setChecked(KeyIdArgsListPtr key_ids); static void setChecked(const KeyIdArgsListPtr& keyIds, const KeyTable& key_table); KeyIdArgsListPtr getSelected(); @@ -123,6 +140,8 @@ class KeyList : public QWidget { private: void init(); void importKeys(const QByteArray& inBuffer); + void slotUncheckALL(); + void slotCheckALL(); static int key_list_id; int _m_key_list_id; @@ -134,7 +153,7 @@ class KeyList : public QWidget { QMenu* popupMenu{}; GpgFrontend::KeyLinkListPtr _buffered_keys_list; std::function<void(const GpgKey&, QWidget*)> mAction = nullptr; - bool menu_status = false; + KeyMenuAbility::AbilityType menu_ability_ = KeyMenuAbility::ALL; private slots: diff --git a/src/ui/widgets/SignersPicker.cpp b/src/ui/widgets/SignersPicker.cpp index e769d05c..b035bf19 100644 --- a/src/ui/widgets/SignersPicker.cpp +++ b/src/ui/widgets/SignersPicker.cpp @@ -24,31 +24,31 @@ #include "ui/widgets/SignersPicker.h" +#include "ui/widgets/KeyList.h" + namespace GpgFrontend::UI { SignersPicker::SignersPicker(QWidget* parent) : QDialog(parent) { - auto confirmButton = new QPushButton(_("Confirm")); - connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(accept())); + auto confirm_button = new QPushButton(_("Confirm")); + connect(confirm_button, SIGNAL(clicked(bool)), this, SLOT(accept())); /*Setup KeyList*/ - mKeyList = new KeyList(false, this); - mKeyList->addListGroupTab( + key_list_ = new KeyList(false, this); + key_list_->addListGroupTab( _("Signers"), KeyListRow::ONLY_SECRET_KEY, KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage, - [](const GpgKey& key) -> bool { - if (!key.CanSignActual()) - return false; - else - return true; - }); - mKeyList->slotRefresh(); + [](const GpgKey& key) -> bool { return key.CanSignActual(); }); + key_list_->slotRefresh(); auto* vbox2 = new QVBoxLayout(); vbox2->addWidget(new QLabel(QString(_("Select Signer(s)")) + ": ")); - vbox2->addWidget(mKeyList); + vbox2->addWidget(key_list_); vbox2->addWidget(new QLabel( - _("If any key is selected, the default key will be used for signing."))); - vbox2->addWidget(confirmButton); + QString( + _("Please select one or more private keys you use for signing.")) + + "\n" + + _("If no key is selected, the default key will be used for signing."))); + vbox2->addWidget(confirm_button); vbox2->addStretch(0); setLayout(vbox2); @@ -62,7 +62,7 @@ SignersPicker::SignersPicker(QWidget* parent) : QDialog(parent) { } GpgFrontend::KeyIdArgsListPtr SignersPicker::getCheckedSigners() { - return mKeyList->getPrivateChecked(); + return key_list_->getPrivateChecked(); } } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/SignersPicker.h b/src/ui/widgets/SignersPicker.h index 055b6ef6..08972a76 100644 --- a/src/ui/widgets/SignersPicker.h +++ b/src/ui/widgets/SignersPicker.h @@ -25,12 +25,12 @@ #ifndef GPGFRONTEND_ZH_CN_TS_SIGNERSPIRCKER_H #define GPGFRONTEND_ZH_CN_TS_SIGNERSPIRCKER_H -#include "GpgFrontend.h" -#include "gpg/GpgContext.h" -#include "ui/widgets/KeyList.h" +#include "GpgFrontendUI.h" namespace GpgFrontend::UI { +class KeyList; + class SignersPicker : public QDialog { Q_OBJECT @@ -40,7 +40,7 @@ class SignersPicker : public QDialog { GpgFrontend::KeyIdArgsListPtr getCheckedSigners(); private: - KeyList* mKeyList; + KeyList* key_list_; }; } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 036c69d4..be6ec181 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -55,8 +55,8 @@ void TextEdit::slotNewTab() { tabWidget->setTabIcon(index, QIcon(":file.png")); tabWidget->setCurrentIndex(tabWidget->count() - 1); page->getTextPage()->setFocus(); - connect(page->getTextPage()->document(), SIGNAL(modificationChanged(bool)), - this, SLOT(slotShowModified())); + connect(page->getTextPage()->document(), &QTextDocument::modificationChanged, + this, &TextEdit::slotShowModified); } void TextEdit::slotNewHelpTab(const QString& title, const QString& path) const { @@ -81,6 +81,10 @@ void TextEdit::slotOpenFile(QString& path) { auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); if (result) { auto* page = new EditorPage(path); + connect(page->getTextPage()->document(), + &QTextDocument::modificationChanged, this, + &TextEdit::slotShowModified); + QApplication::setOverrideCursor(Qt::WaitCursor); auto index = tabWidget->addTab(page, strippedName(path)); tabWidget->setTabIcon(index, QIcon(":file.png")); @@ -295,20 +299,20 @@ bool TextEdit::maybeSaveCurrentTab(bool askToSave) { bool TextEdit::maybeSaveAnyTab() { // get a list of all unsaved documents and their tabids - QHash<int, QString> unsavedDocs = this->unsavedDocuments(); + QHash<int, QString> unsaved_docs = this->unsavedDocuments(); /* * no unsaved documents, so app can be closed */ - if (unsavedDocs.empty()) { + if (unsaved_docs.empty()) { return true; } /* * only 1 unsaved document -> set modified tab as current * and show normal unsaved doc dialog */ - if (unsavedDocs.size() == 1) { - int modifiedTab = unsavedDocs.keys().at(0); + if (unsaved_docs.size() == 1) { + int modifiedTab = unsaved_docs.keys().at(0); tabWidget->setCurrentIndex(modifiedTab); return maybeSaveCurrentTab(true); } @@ -316,11 +320,11 @@ bool TextEdit::maybeSaveAnyTab() { /* * more than one unsaved documents */ - if (unsavedDocs.size() > 1) { - QHashIterator<int, QString> i(unsavedDocs); + if (unsaved_docs.size() > 1) { + QHashIterator<int, QString> i(unsaved_docs); QuitDialog* dialog; - dialog = new QuitDialog(this, unsavedDocs); + dialog = new QuitDialog(this, unsaved_docs); int result = dialog->exec(); // if result is QDialog::Rejected, discard or cancel was clicked @@ -429,8 +433,8 @@ void TextEdit::loadFile(const QString& fileName) { // statusBar()->showMessage(_("File loaded"), 2000); } -QString TextEdit::strippedName(const QString& fullFileName) { - return QFileInfo(fullFileName).fileName(); +QString TextEdit::strippedName(const QString& full_file_name) { + return QFileInfo(full_file_name).fileName(); } void TextEdit::slotPrint() { @@ -469,8 +473,8 @@ void TextEdit::slotShowModified() const { void TextEdit::slotSwitchTabUp() const { if (tabWidget->count() > 1) { - int newindex = (tabWidget->currentIndex() + 1) % (tabWidget->count()); - tabWidget->setCurrentIndex(newindex); + int new_index = (tabWidget->currentIndex() + 1) % (tabWidget->count()); + tabWidget->setCurrentIndex(new_index); } } diff --git a/src/ui/widgets/TextEdit.h b/src/ui/widgets/TextEdit.h index 3cff74e7..e877ccc1 100644 --- a/src/ui/widgets/TextEdit.h +++ b/src/ui/widgets/TextEdit.h @@ -185,7 +185,7 @@ class TextEdit : public QWidget { * @param a filename path * @return QString containing the filename */ - static QString strippedName(const QString& fullFileName); + static QString strippedName(const QString& full_file_name); /** * @brief diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8a5859f4..cee1720b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,10 +11,17 @@ add_executable( ${TEST_SOURCE} ) -if(GPG_CORE) +if (GPG_CORE) target_link_libraries(${AppName} gpg_core) -endif() +endif () -target_link_libraries(${AppName} gtest gtest_main) +if (GPG_STANDALONE_MODE) + file(COPY ${CMAKE_SOURCE_DIR}/test/gpg DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ FOLLOW_SYMLINK_CHAIN) +endif () + +target_link_libraries(${AppName} ${Boost_LIBRARIES} gtest gtest_main) +if (APPLE) + target_link_libraries(${AppName} intl) +endif () add_test(AllTestsInGpgFrontend ${AppName}) diff --git a/test/GpgCoreTest.cpp b/test/GpgCoreTest.cpp new file mode 100644 index 00000000..ab9dbf28 --- /dev/null +++ b/test/GpgCoreTest.cpp @@ -0,0 +1,28 @@ +/** + * 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 "GpgFrontendTest.h" + +// Should be used once and once-only +INITIALIZE_EASYLOGGINGPP diff --git a/test/GpgCoreTestBasicOpera.cpp b/test/GpgCoreTestBasicOpera.cpp index 40972982..283ceb82 100644 --- a/test/GpgCoreTestBasicOpera.cpp +++ b/test/GpgCoreTestBasicOpera.cpp @@ -31,18 +31,18 @@ #include "gpg/GpgConstants.h" #include "gpg/function/BasicOperator.h" #include "gpg/function/GpgKeyGetter.h" -#include "gpg/model/GpgKey.h" +#include "gpg/result_analyse/DecryptResultAnalyse.h" using namespace GpgFrontend; TEST_F(GpgCoreTest, CoreEncryptDecrTest) { - auto encrpyt_key = GpgKeyGetter::GetInstance(default_channel) + auto encrypt_key = GpgKeyGetter::GetInstance(default_channel) .GetPubkey("467F14220CE8DCF780CF4BAD8465C55B25C9B7D1"); ByteArray encrypt_text = "Hello GpgFrontend!"; ByteArrayPtr encr_out_data; GpgEncrResult e_result; - std::vector<GpgKey> keys; - keys.push_back(std::move(encrpyt_key)); + KeyListPtr keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(encrypt_key)); auto err = BasicOperator::GetInstance(default_channel) .Encrypt(std::move(keys), encrypt_text, encr_out_data, e_result); @@ -59,14 +59,65 @@ TEST_F(GpgCoreTest, CoreEncryptDecrTest) { ASSERT_EQ(*decr_out_data, encrypt_text); } +TEST_F(GpgCoreTest, CoreEncryptDecrTest_KeyNotFound_1) { + ByteArrayPtr encr_out_data = std::make_unique<ByteArray>( + "-----BEGIN PGP MESSAGE-----\n" + "\n" + "hQEMA6UM/S9sZ32MAQf9Fb6gp6nvgKTQBv2mmjXia6ODXYq6kNeLsPVzLCbHyWOs\n" + "0GDED11R1NksA3EQxFf4fzLkDpbo68r5bWy7c28c99Fr68IRET19Tw6Gu65MQezD\n" + "Rdzo1oVqmK9sfKqOT3+0S2H+suFYw5kfBztMZLVGGl9R9fOXdKcj0fqGs2br3e9D\n" + "ArBFqq07Bae2DD1J8mckWB2x9Uem4vjRiY+vEJcEdAS1N5xu1n7qzzyDgcRcS34X\n" + "PNBQeTrFMc2RS7mnip2DbyZVEjORobhguK6xZyqXXbvFacStGWDLptV3dcCn4JRO\n" + "dIORyt5wugqAtgE4qEGTvr/pJ/oXPw4Wve/trece/9I/AR38vW8ntVmDa/hV75iZ\n" + "4QGAhQ8grD4kq31GHXHUOmBX51XXW9SINmplC8elEx3R460EUZJjjb0OvTih+eZH\n" + "=8n2H\n" + "-----END PGP MESSAGE-----"); + + GpgDecrResult d_result; + ByteArrayPtr decr_out_data; + auto err = BasicOperator::GetInstance(default_channel) + .Decrypt(*encr_out_data, decr_out_data, d_result); + ASSERT_EQ(check_gpg_error_2_err_code(err), GPG_ERR_NO_SECKEY); + ASSERT_NE(d_result->recipients, nullptr); + ASSERT_EQ(std::string(d_result->recipients->keyid), "A50CFD2F6C677D8C"); +} + +TEST_F(GpgCoreTest, CoreEncryptDecrTest_KeyNotFound_ResultAnalyse) { + ByteArrayPtr encr_out_data = std::make_unique<ByteArray>( + "-----BEGIN PGP MESSAGE-----\n" + "\n" + "hQEMA6UM/S9sZ32MAQf9Fb6gp6nvgKTQBv2mmjXia6ODXYq6kNeLsPVzLCbHyWOs\n" + "0GDED11R1NksA3EQxFf4fzLkDpbo68r5bWy7c28c99Fr68IRET19Tw6Gu65MQezD\n" + "Rdzo1oVqmK9sfKqOT3+0S2H+suFYw5kfBztMZLVGGl9R9fOXdKcj0fqGs2br3e9D\n" + "ArBFqq07Bae2DD1J8mckWB2x9Uem4vjRiY+vEJcEdAS1N5xu1n7qzzyDgcRcS34X\n" + "PNBQeTrFMc2RS7mnip2DbyZVEjORobhguK6xZyqXXbvFacStGWDLptV3dcCn4JRO\n" + "dIORyt5wugqAtgE4qEGTvr/pJ/oXPw4Wve/trece/9I/AR38vW8ntVmDa/hV75iZ\n" + "4QGAhQ8grD4kq31GHXHUOmBX51XXW9SINmplC8elEx3R460EUZJjjb0OvTih+eZH\n" + "=8n2H\n" + "-----END PGP MESSAGE-----"); + + GpgDecrResult d_result; + ByteArrayPtr decr_out_data; + auto err = BasicOperator::GetInstance(default_channel) + .Decrypt(*encr_out_data, decr_out_data, d_result); + ASSERT_EQ(check_gpg_error_2_err_code(err), GPG_ERR_NO_SECKEY); + ASSERT_NE(d_result->recipients, nullptr); + ASSERT_EQ(std::string(d_result->recipients->keyid), "A50CFD2F6C677D8C"); + + DecryptResultAnalyse analyse{err, d_result}; + analyse.analyse(); + ASSERT_EQ(analyse.getStatus(), -1); + ASSERT_FALSE(analyse.getResultReport().empty()); +} + TEST_F(GpgCoreTest, CoreSignVerifyNormalTest) { - auto encrpyt_key = GpgKeyGetter::GetInstance(default_channel) + auto encrypt_key = GpgKeyGetter::GetInstance(default_channel) .GetPubkey("467F14220CE8DCF780CF4BAD8465C55B25C9B7D1"); ByteArray sign_text = "Hello GpgFrontend!"; ByteArrayPtr sign_out_data; GpgSignResult s_result; - std::vector<GpgKey> keys; - keys.push_back(std::move(encrpyt_key)); + KeyListPtr keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(encrypt_key)); auto err = BasicOperator::GetInstance(default_channel) .Sign(std::move(keys), sign_text, sign_out_data, GPGME_SIG_MODE_NORMAL, s_result); @@ -85,13 +136,13 @@ TEST_F(GpgCoreTest, CoreSignVerifyNormalTest) { } TEST_F(GpgCoreTest, CoreSignVerifyDetachTest) { - auto encrpyt_key = GpgKeyGetter::GetInstance(default_channel) + auto encrypt_key = GpgKeyGetter::GetInstance(default_channel) .GetPubkey("467F14220CE8DCF780CF4BAD8465C55B25C9B7D1"); ByteArray sign_text = "Hello GpgFrontend!"; ByteArrayPtr sign_out_data; GpgSignResult s_result; - std::vector<GpgKey> keys; - keys.push_back(std::move(encrpyt_key)); + KeyListPtr keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(encrypt_key)); auto err = BasicOperator::GetInstance(default_channel) .Sign(std::move(keys), sign_text, sign_out_data, GPGME_SIG_MODE_DETACH, s_result); @@ -114,8 +165,8 @@ TEST_F(GpgCoreTest, CoreSignVerifyClearTest) { ByteArray sign_text = "Hello GpgFrontend!"; ByteArrayPtr sign_out_data; GpgSignResult s_result; - std::vector<GpgKey> keys; - keys.push_back(std::move(sign_key)); + KeyListPtr keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(sign_key)); auto err = BasicOperator::GetInstance(default_channel) .Sign(std::move(keys), sign_text, sign_out_data, GPGME_SIG_MODE_CLEAR, s_result); @@ -134,12 +185,12 @@ TEST_F(GpgCoreTest, CoreSignVerifyClearTest) { } TEST_F(GpgCoreTest, CoreEncryptSignDecrVerifyTest) { - auto encrpyt_key = GpgKeyGetter::GetInstance(default_channel) + auto encrypt_key = GpgKeyGetter::GetInstance(default_channel) .GetPubkey("467F14220CE8DCF780CF4BAD8465C55B25C9B7D1"); auto sign_key = GpgKeyGetter::GetInstance(default_channel) .GetKey("8933EB283A18995F45D61DAC021D89771B680FFB"); // Question? - // ASSERT_FALSE(encrpyt_key.is_private_key()); + // ASSERT_FALSE(encrypt_key.is_private_key()); ASSERT_TRUE(sign_key.is_private_key()); ASSERT_TRUE(sign_key.CanSignActual()); ByteArray encrypt_text = "Hello GpgFrontend!"; @@ -147,9 +198,10 @@ TEST_F(GpgCoreTest, CoreEncryptSignDecrVerifyTest) { GpgEncrResult e_result; GpgSignResult s_result; - std::vector<GpgKey> keys, sign_keys; - keys.push_back(std::move(encrpyt_key)); - sign_keys.push_back(std::move(sign_key)); + KeyListPtr keys = std::make_unique<KeyArgsList>(), + sign_keys = std::make_unique<KeyArgsList>(); + keys->push_back(std::move(encrypt_key)); + sign_keys->push_back(std::move(sign_key)); auto err = BasicOperator::GetInstance(default_channel) .EncryptSign(std::move(keys), std::move(sign_keys), diff --git a/test/GpgCoreTestImportExport.cpp b/test/GpgCoreTestImportExport.cpp index 9d01cc3e..1e226247 100644 --- a/test/GpgCoreTestImportExport.cpp +++ b/test/GpgCoreTestImportExport.cpp @@ -28,7 +28,7 @@ #include "GpgFrontendTest.h" #include "gpg/GpgConstants.h" #include "gpg/function/GpgKeyGetter.h" -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" #include "gpg/model/GpgKey.h" TEST_F(GpgCoreTest, CoreExportSecretTest) {}
\ No newline at end of file diff --git a/test/GpgCoreTestKeyModel.cpp b/test/GpgCoreTestKeyModel.cpp index 4e85a8cf..79cd7dcd 100644 --- a/test/GpgCoreTestKeyModel.cpp +++ b/test/GpgCoreTestKeyModel.cpp @@ -25,15 +25,11 @@ #include "GpgFrontendTest.h" #include "gpg/function/GpgKeyGetter.h" -// Should be used once and once-only -INITIALIZE_EASYLOGGINGPP - TEST_F(GpgCoreTest, CoreInitTest) { auto& ctx = GpgFrontend::GpgContext::GetInstance(default_channel); auto& ctx_default = GpgFrontend::GpgContext::GetInstance(); ASSERT_TRUE(ctx.good()); ASSERT_TRUE(ctx_default.good()); - ASSERT_EQ(ctx_default.GetInfo().DatabasePath, "default"); } TEST_F(GpgCoreTest, GpgDataTest) { @@ -75,18 +71,19 @@ TEST_F(GpgCoreTest, GpgKeyTest) { ASSERT_EQ(key.email(), "[email protected]"); ASSERT_EQ(key.id(), "81704859182661FB"); ASSERT_EQ(key.fpr(), "9490795B78F8AFE9F93BD09281704859182661FB"); - ASSERT_EQ(key.expires(), boost::gregorian::from_simple_string("2023-09-05")); + ASSERT_EQ(key.expires(), + boost::posix_time::from_iso_string("20230905T040000")); ASSERT_EQ(key.pubkey_algo(), "RSA"); ASSERT_EQ(key.length(), 3072); ASSERT_EQ(key.last_update(), - boost::gregorian::from_simple_string("1970-01-01")); + boost::posix_time::from_iso_string("19700101T000000")); ASSERT_EQ(key.create_time(), - boost::gregorian::from_simple_string("2021-09-05")); + boost::posix_time::from_iso_string("20210905T060153")); ASSERT_EQ(key.owner_trust(), "Unknown"); using namespace boost::posix_time; - ASSERT_EQ(key.expired(), key.expires() < second_clock::local_time().date()); + ASSERT_EQ(key.expired(), key.expires() < second_clock::local_time()); } TEST_F(GpgCoreTest, GpgSubKeyTest) { @@ -100,7 +97,7 @@ TEST_F(GpgCoreTest, GpgSubKeyTest) { ASSERT_FALSE(sub_key.revoked()); ASSERT_FALSE(sub_key.disabled()); ASSERT_EQ(sub_key.timestamp(), - boost::gregorian::from_simple_string("2021-09-05")); + boost::posix_time::from_iso_string("20210905T060153")); ASSERT_FALSE(sub_key.is_cardkey()); ASSERT_TRUE(sub_key.is_private_key()); @@ -112,11 +109,11 @@ TEST_F(GpgCoreTest, GpgSubKeyTest) { ASSERT_FALSE(sub_key.can_authenticate()); ASSERT_FALSE(sub_key.can_sign()); ASSERT_TRUE(sub_key.can_encrypt()); - ASSERT_EQ(key.expires(), boost::gregorian::from_simple_string("2023-09-05")); + ASSERT_EQ(key.expires(), + boost::posix_time::from_iso_string("20230905T040000")); using namespace boost::posix_time; - ASSERT_EQ(sub_key.expired(), - sub_key.expires() < second_clock::local_time().date()); + ASSERT_EQ(sub_key.expired(), sub_key.expires() < second_clock::local_time()); } TEST_F(GpgCoreTest, GpgUIDTest) { diff --git a/test/GpgCoreTestKeyModelAlone.cpp b/test/GpgCoreTestKeyModelAlone.cpp new file mode 100644 index 00000000..3d5bd175 --- /dev/null +++ b/test/GpgCoreTestKeyModelAlone.cpp @@ -0,0 +1,158 @@ +/** + * 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 "GpgFrontendTest.h" +#include "gpg/function/GpgKeyGetter.h" + +TEST_F(GpgCoreTest, CoreInitTestAlone) { + auto& ctx = GpgFrontend::GpgContext::GetInstance(gpg_alone_channel); + ASSERT_TRUE(ctx.good()); +} + +TEST_F(GpgCoreTest, GpgDataTestAlone) { + auto data_buff = std::string( + "cqEh8fyKWtmiXrW2zzlszJVGJrpXDDpzgP7ZELGxhfZYFi8rMrSVKDwrpFZBSWMG"); + + GpgFrontend::GpgData data(data_buff.data(), data_buff.size()); + + auto out_buffer = data.Read2Buffer(); + ASSERT_EQ(out_buffer->size(), 64); +} + +TEST_F(GpgCoreTest, GpgKeyFetchTestAlone) { + auto keys = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).FetchKey(); + ASSERT_EQ(keys->size(), 4); +} + +TEST_F(GpgCoreTest, GpgKeyTestAlone) { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel) + .GetKey("9490795B78F8AFE9F93BD09281704859182661FB"); + ASSERT_TRUE(key.good()); + ASSERT_TRUE(key.is_private_key()); + ASSERT_TRUE(key.has_master_key()); + + ASSERT_FALSE(key.disabled()); + ASSERT_FALSE(key.revoked()); + + ASSERT_EQ(key.protocol(), "OpenPGP"); + + ASSERT_EQ(key.subKeys()->size(), 2); + ASSERT_EQ(key.uids()->size(), 1); + + ASSERT_TRUE(key.can_certify()); + ASSERT_TRUE(key.can_encrypt()); + ASSERT_TRUE(key.can_sign()); + ASSERT_FALSE(key.can_authenticate()); + ASSERT_TRUE(key.CanEncrActual()); + ASSERT_TRUE(key.CanEncrActual()); + ASSERT_TRUE(key.CanSignActual()); + ASSERT_FALSE(key.CanAuthActual()); + + ASSERT_EQ(key.name(), "GpgFrontendTest"); + ASSERT_TRUE(key.comment().empty()); + ASSERT_EQ(key.email(), "[email protected]"); + ASSERT_EQ(key.id(), "81704859182661FB"); + ASSERT_EQ(key.fpr(), "9490795B78F8AFE9F93BD09281704859182661FB"); + ASSERT_EQ(key.expires(), + boost::posix_time::from_iso_string("20230905T040000")); + ASSERT_EQ(key.pubkey_algo(), "RSA"); + ASSERT_EQ(key.length(), 3072); + ASSERT_EQ(key.last_update(), + boost::posix_time::from_iso_string("19700101T000000")); + ASSERT_EQ(key.create_time(), + boost::posix_time::from_iso_string("20210905T060153")); + + ASSERT_EQ(key.owner_trust(), "Unknown"); + + using namespace boost::posix_time; + ASSERT_EQ(key.expired(), key.expires() < second_clock::local_time()); +} + +TEST_F(GpgCoreTest, GpgSubKeyTestAlone) { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel) + .GetKey("9490795B78F8AFE9F93BD09281704859182661FB"); + auto sub_keys = key.subKeys(); + ASSERT_EQ(sub_keys->size(), 2); + + auto& sub_key = sub_keys->back(); + + ASSERT_FALSE(sub_key.revoked()); + ASSERT_FALSE(sub_key.disabled()); + ASSERT_EQ(sub_key.timestamp(), + boost::posix_time::from_iso_string("20210905T060153")); + + ASSERT_FALSE(sub_key.is_cardkey()); + ASSERT_TRUE(sub_key.is_private_key()); + ASSERT_EQ(sub_key.id(), "2B36803235B5E25B"); + ASSERT_EQ(sub_key.fpr(), "50D37E8F8EE7340A6794E0592B36803235B5E25B"); + ASSERT_EQ(sub_key.length(), 3072); + ASSERT_EQ(sub_key.pubkey_algo(), "RSA"); + ASSERT_FALSE(sub_key.can_certify()); + ASSERT_FALSE(sub_key.can_authenticate()); + ASSERT_FALSE(sub_key.can_sign()); + ASSERT_TRUE(sub_key.can_encrypt()); + ASSERT_EQ(key.expires(), + boost::posix_time::from_iso_string("20230905T040000")); + + using namespace boost::posix_time; + ASSERT_EQ(sub_key.expired(), sub_key.expires() < second_clock::local_time()); +} + +TEST_F(GpgCoreTest, GpgUIDTestAlone) { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel) + .GetKey("9490795B78F8AFE9F93BD09281704859182661FB"); + auto uids = key.uids(); + ASSERT_EQ(uids->size(), 1); + auto& uid = uids->front(); + + ASSERT_EQ(uid.name(), "GpgFrontendTest"); + ASSERT_TRUE(uid.comment().empty()); + ASSERT_EQ(uid.email(), "[email protected]"); + ASSERT_EQ(uid.uid(), "GpgFrontendTest <[email protected]>"); + ASSERT_FALSE(uid.invalid()); + ASSERT_FALSE(uid.revoked()); +} + +TEST_F(GpgCoreTest, GpgKeySignatureTestAlone) { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel) + .GetKey("9490795B78F8AFE9F93BD09281704859182661FB"); + auto uids = key.uids(); + ASSERT_EQ(uids->size(), 1); + auto& uid = uids->front(); + + // No key signature support + auto signatures = uid.signatures(); + ASSERT_EQ(signatures->size(), 0); +} + +TEST_F(GpgCoreTest, GpgKeyGetterTestAlone) { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel) + .GetKey("9490795B78F8AFE9F93BD09281704859182661FB"); + ASSERT_TRUE(key.good()); + auto keys = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).FetchKey(); + ASSERT_GE(keys->size(), secret_keys_.size()); + ASSERT_TRUE(find(keys->begin(), keys->end(), key) != keys->end()); +} diff --git a/test/GpgCoreTestKeygen.cpp b/test/GpgCoreTestKeygen.cpp new file mode 100644 index 00000000..b703ee40 --- /dev/null +++ b/test/GpgCoreTestKeygen.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 "GpgFrontendTest.h" +#include "gpg/GpgGenKeyInfo.h" +#include "gpg/function/GpgKeyGetter.h" +#include "gpg/function/GpgKeyOpera.h" + +TEST_F(GpgCoreTest, GenerateKeyTest) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment(""); + keygen_info->setKeySize(1024); + keygen_info->setAlgo("rsa"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(true); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTest_1) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello gpgfrontend"); + keygen_info->setAlgo("rsa"); + keygen_info->setKeySize(4096); + keygen_info->setNonExpired(false); + keygen_info->setExpired(boost::posix_time::second_clock::local_time() + + boost::posix_time::hours(24)); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTest_4) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment(""); + keygen_info->setAlgo("dsa"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTest_5) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment(""); + keygen_info->setAlgo("ed25519"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} diff --git a/test/GpgCoreTestKeygenAlone.cpp b/test/GpgCoreTestKeygenAlone.cpp new file mode 100644 index 00000000..4a725ba7 --- /dev/null +++ b/test/GpgCoreTestKeygenAlone.cpp @@ -0,0 +1,156 @@ +/** + * 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 "GpgFrontendTest.h" +#include "gpg/GpgGenKeyInfo.h" +#include "gpg/function/GpgKeyGetter.h" +#include "gpg/function/GpgKeyOpera.h" + +TEST_F(GpgCoreTest, GenerateKeyTestAlone) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(gpg_alone_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(false, true); + keygen_info->setName("foobar"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello"); + keygen_info->setAlgo("rsa"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(true); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTestAlone_1) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(gpg_alone_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(false, true); + keygen_info->setName("foobar"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello gpgfrontend"); + keygen_info->setAlgo("rsa"); + keygen_info->setNonExpired(false); + keygen_info->setPassPhrase("abcdefg"); + keygen_info->setExpired(boost::posix_time::second_clock::local_time() + + boost::posix_time::hours(24)); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTestAlone_2) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(gpg_alone_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(false, true); + keygen_info->setName("foobar"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hi"); + keygen_info->setAlgo("rsa"); + keygen_info->setKeySize(3072); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + keygen_info->setPassPhrase("abcdefg"); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTestAlone_3) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(gpg_alone_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(false, true); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello"); + keygen_info->setAlgo("rsa"); + keygen_info->setKeySize(4096); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + keygen_info->setPassPhrase("abcdefg"); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTestAlone_4) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(gpg_alone_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(false, true); + keygen_info->setName("foobar"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello"); + keygen_info->setAlgo("dsa"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + keygen_info->setPassPhrase("abcdefg"); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(gpg_alone_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +}
\ No newline at end of file diff --git a/test/GpgFrontendTest.h b/test/GpgFrontendTest.h index 181c513e..84476106 100644 --- a/test/GpgFrontendTest.h +++ b/test/GpgFrontendTest.h @@ -29,16 +29,18 @@ #include <gpg-error.h> #include <gtest/gtest.h> -#include <boost/date_time/gregorian/parsers.hpp> +#include <boost/date_time.hpp> #include <boost/dll.hpp> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> +#include <libconfig.h++> #include <memory> #include <string> #include <vector> #include "gpg/GpgConstants.h" -#include "gpg/function/GpgKeyImportExportor.h" +#include "gpg/function/GpgKeyImportExporter.h" +#include "gpg/function/GpgKeyOpera.h" class GpgCoreTest : public ::testing::Test { protected: @@ -55,13 +57,24 @@ class GpgCoreTest : public ::testing::Test { // Data File Directory Location boost::filesystem::path data_path; - int default_channel = 0; + const int default_channel = 0; + + const int gpg_alone_channel = 512; GpgCoreTest() = default; - virtual ~GpgCoreTest() = default; + ~GpgCoreTest() override = default; + + void SetUp() override { + 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"); + el::Loggers::reconfigureLogger("default", defaultConf); - virtual void SetUp() { using namespace libconfig; Config cfg; ASSERT_NO_THROW(cfg.readFile(config_path.c_str())); @@ -75,21 +88,42 @@ class GpgCoreTest : public ::testing::Test { configure_independent_database(root); + configure_alone_gpg(root); + dealing_private_keys(root); import_data(); + import_data_alone(); } - virtual void TearDown() {} + void TearDown() override { + auto key_ids = std::make_unique<GpgFrontend::KeyIdArgsList>(); + key_ids->push_back("81704859182661FB"); + key_ids->push_back("06F1C7E7240C94E8"); + key_ids->push_back("8465C55B25C9B7D1"); + key_ids->push_back("021D89771B680FFB"); + GpgFrontend::GpgKeyOpera::GetInstance(default_channel) + .DeleteKeys(std::move(key_ids)); + } private: void import_data() { - GpgFrontend::GpgContext::GetInstance(default_channel) - .SetPassphraseCb(GpgFrontend::GpgContext::test_passphrase_cb); + for (const auto& secret_key : secret_keys_) { + auto secret_key_copy = + std::make_unique<GpgFrontend::ByteArray>(*secret_key); + GpgFrontend::GpgKeyImportExporter::GetInstance(default_channel) + .ImportKey(std::move(secret_key_copy)); + } + } + + void import_data_alone() { for (auto& secret_key : secret_keys_) { - GpgFrontend::GpgKeyImportExportor::GetInstance(default_channel) - .ImportKey(std::move(secret_key)); + auto secret_key_copy = + std::make_unique<GpgFrontend::ByteArray>(*secret_key); + GpgFrontend::GpgKeyImportExporter::GetInstance(gpg_alone_channel) + .ImportKey(std::move(secret_key_copy)); } } + void dealing_private_keys(const libconfig::Setting& root) { if (root.exists("load_keys.private_keys")) { auto& private_keys = root.lookup("load_keys.private_keys"); @@ -106,21 +140,60 @@ class GpgCoreTest : public ::testing::Test { } } + void configure_alone_gpg(const libconfig::Setting& root) { + bool alone_gpg = false; + if (root.exists("alone_gpg")) { + root.lookupValue("alone_gpg", alone_gpg); + if (alone_gpg && root.exists("alone_gpg")) { + std::string alone_gpg_path; + root.lookupValue("alone_gpg_path", alone_gpg_path); + auto gpg_path = parent_path / alone_gpg_path; + + std::string relative_db_path; + root.lookupValue("alone_gpg_db_path", relative_db_path); + auto db_path = parent_path / relative_db_path; + if (!boost::filesystem::exists(db_path)) { + boost::filesystem::create_directory(db_path); + } else { + boost::filesystem::remove_all(db_path); + boost::filesystem::create_directory(db_path); + } + GpgFrontend::GpgContext::CreateInstance( + gpg_alone_channel, + [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { + GpgFrontend::GpgContextInitArgs args; + args.gpg_alone = true; + args.independent_database = true; + args.db_path = db_path.string(); + args.gpg_path = gpg_path.string(); + args.test_mode = true; + return std::make_unique<GpgFrontend::GpgContext>(args); + }); + } + } + } + void configure_independent_database(const libconfig::Setting& root) { bool independent_database = false; if (root.exists("independent_database")) { root.lookupValue("independent_database", independent_database); if (independent_database && root.exists("independent_db_path")) { - default_channel = 1; std::string relative_db_path; root.lookupValue("independent_db_path", relative_db_path); auto db_path = parent_path / relative_db_path; if (!boost::filesystem::exists(db_path)) { boost::filesystem::create_directory(db_path); + } else { + boost::filesystem::remove_all(db_path); + boost::filesystem::create_directory(db_path); } + GpgFrontend::GpgContext::CreateInstance( - 1, - std::make_unique<GpgFrontend::GpgContext>(true, db_path.c_str())); + default_channel, [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { + GpgFrontend::GpgContextInitArgs args; + args.test_mode = true; + return std::make_unique<GpgFrontend::GpgContext>(args); + }); } } } diff --git a/test/conf/core.cfg b/test/conf/core.cfg index 69395963..7a9b76d6 100644 --- a/test/conf/core.cfg +++ b/test/conf/core.cfg @@ -1,8 +1,11 @@ # core test configuration file version = "1.0"; independent_database = true; -independent_db_path = "db" -data_path = "data" +independent_db_path = "db"; +alone_gpg = true; +alone_gpg_path = "gpg/gpg1"; +alone_gpg_db_path = "alone_db"; +data_path = "data"; load_keys = { private_keys = ( diff --git a/third_party/AppImageUpdate b/third_party/AppImageUpdate deleted file mode 160000 -Subproject 1b97acc55c89f742d51c3849eb62eb58464d866 diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index f7c031b7..01e8c2ca 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -1,4 +1,4 @@ -if (ESAY_LOGGING_PP) +if (EASY_LOGGING_PP) message(STATUS "Build easyloggingpp") add_subdirectory(easyloggingpp) endif () diff --git a/third_party/easyloggingpp/CMakeLists.txt b/third_party/easyloggingpp/CMakeLists.txt index 9b96f9c4..40b4bd14 100644 --- a/third_party/easyloggingpp/CMakeLists.txt +++ b/third_party/easyloggingpp/CMakeLists.txt @@ -1,4 +1,11 @@ aux_source_directory(. EASY_LOGGING_CPP_SOURCE) add_library(easy_logging_pp STATIC ${EASY_LOGGING_CPP_SOURCE}) -target_link_libraries(easy_logging_pp stdc++)
\ No newline at end of file +target_link_libraries(easy_logging_pp stdc++) +if(XCODE_BUILD) +set_target_properties(easy_logging_pp + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +endif()
\ No newline at end of file diff --git a/ui/EmailListEditor.ui b/ui/EmailListEditor.ui new file mode 100644 index 00000000..5cc0ddef --- /dev/null +++ b/ui/EmailListEditor.ui @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>EmailListEditorDialog</class> + <widget class="QDialog" name="EmailListEditorDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>618</width> + <height>498</height> + </rect> + </property> + <property name="windowTitle"> + <string>Email List Editor</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="titleLabel"> + <property name="text"> + <string>Email List:</string> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="emaillistWidget"> + <property name="editTriggers"> + <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> + </property> + <property name="dragEnabled"> + <bool>true</bool> + </property> + <property name="isWrapping" stdset="0"> + <bool>false</bool> + </property> + <property name="spacing"> + <number>6</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="addEmailAddressButton"> + <property name="text"> + <string>Add An Email Address</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="tipsLabel"> + <property name="text"> + <string>Tips: You can double-click the email address in the edit list, or click the email to pop up the option menu.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + <action name="actionDelete_Selected_Email_Address"> + <property name="text"> + <string>Delete Selected Email Address</string> + </property> + </action> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/ExportKeyPackageDialog.ui b/ui/ExportKeyPackageDialog.ui new file mode 100644 index 00000000..a40d0154 --- /dev/null +++ b/ui/ExportKeyPackageDialog.ui @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>exportKeyPackageDialog</class> + <widget class="QDialog" name="exportKeyPackageDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>527</width> + <height>385</height> + </rect> + </property> + <property name="windowTitle"> + <string>Export As Key Package</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" 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> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Key Package Name</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="gnerateNameButton"> + <property name="text"> + <string>Generate Key Package Name</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="nameValueLabel"> + <property name="text"> + <string>KeyPackage_0000</string> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="selectOutputPathLabel"> + <property name="text"> + <string>Output Path</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="setOutputPathButton"> + <property name="text"> + <string>Select Output Path</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="outputPathLabel"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="passphraseLabel"> + <property name="text"> + <string>Passphrase</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="generatePassphraseButton"> + <property name="text"> + <string>Generate and Save Passphrase</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="passphraseValueLabel"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="includeSecretKeyCheckBox"> + <property name="text"> + <string>Include secret key (Think twice before acting)</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="noPublicKeyCheckBox"> + <property name="text"> + <string>Exclude keys that do not have a private key</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="tipsLabel"> + <property name="text"> + <string>Tips: You can use Key Package to safely and conveniently transfer your public and private keys between devices.</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>exportKeyPackageDialog</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>exportKeyPackageDialog</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/FilePage.ui b/ui/FilePage.ui index 6d7f7d7a..cb4517b1 100644 --- a/ui/FilePage.ui +++ b/ui/FilePage.ui @@ -143,6 +143,61 @@ </layout> </item> </layout> + <action name="actionEncrypt"> + <property name="text"> + <string>Encrypt</string> + </property> + </action> + <action name="actionEncryptSign"> + <property name="text"> + <string>EncryptSign</string> + </property> + </action> + <action name="actionDecrypt"> + <property name="text"> + <string>Decrypt</string> + </property> + </action> + <action name="actionSign"> + <property name="text"> + <string>Sign</string> + </property> + </action> + <action name="actionVerify"> + <property name="text"> + <string>Verify</string> + </property> + </action> + <action name="actionOpenFile"> + <property name="text"> + <string>OpenFile</string> + </property> + </action> + <action name="actionRenameFile"> + <property name="text"> + <string>RenameFile</string> + </property> + </action> + <action name="actionDeleteFile"> + <property name="text"> + <string>DeleteFile</string> + </property> + </action> + <action name="actionCalculateHash"> + <property name="text"> + <string>CalculateHash</string> + </property> + </action> + <action name="actionMakeDirectory"> + <property name="text"> + <string>MakeDirectory</string> + </property> + </action> + <action name="actionCreateEmptyFile"> + <property name="text"> + <string>CreateEmptyFile</string> + </property> + </action> </widget> <resources> <include location="../gpgfrontend.qrc"/> diff --git a/ui/GeneralSettings.ui b/ui/GeneralSettings.ui new file mode 100644 index 00000000..4bfb5f43 --- /dev/null +++ b/ui/GeneralSettings.ui @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>GeneralSettings</class> + <widget class="QWidget" name="GeneralSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>643</width> + <height>656</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="longerKeyExpirationDateBox"> + <property name="title"> + <string>Longer Key Expiration Date</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="longerKeyExpirationDateCheckBox"> + <property name="text"> + <string>Unlock key expiration date setting up to 30 years.</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="saveCheckedKeysBox"> + <property name="title"> + <string>Save Checked Keys</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="saveCheckedKeysCheckBox"> + <property name="text"> + <string>Save checked private keys on exit and restore them on next start.</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="importConfirmationBox"> + <property name="title"> + <string>Confirm drag'n'drop key import</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="importConfirmationCheckBox"> + <property name="text"> + <string>Import files dropped on the Key List without confirmation.</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="asciiModeBox"> + <property name="title"> + <string>ASCII Mode</string> + </property> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="asciiModeCheckBox"> + <property name="text"> + <string>ASCII encoding is not used when file encrypting and signing.</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="langBox"> + <property name="title"> + <string>Language</string> + </property> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QComboBox" name="langSelectBox"> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContents</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="langNoteLabel"> + <property name="text"> + <string>NOTE: GpgFrontend will restart automatically if you change the language!</string> + </property> + </widget> + </item> + </layout> + </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> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/InfoBoard.ui b/ui/InfoBoard.ui index 1aac29aa..1c87974b 100644 --- a/ui/InfoBoard.ui +++ b/ui/InfoBoard.ui @@ -32,6 +32,9 @@ <property name="sizeConstraint"> <enum>QLayout::SetNoConstraint</enum> </property> + <property name="leftMargin"> + <number>6</number> + </property> <item alignment="Qt::AlignLeft"> <widget class="QWidget" name="horizontalWidget" native="true"> <property name="sizePolicy"> @@ -59,16 +62,6 @@ <property name="bottomMargin"> <number>0</number> </property> - <item alignment="Qt::AlignLeft"> - <widget class="QLabel" name="actionLabel"> - <property name="text"> - <string>Actions</string> - </property> - <property name="scaledContents"> - <bool>false</bool> - </property> - </widget> - </item> <item alignment="Qt::AlignRight"> <widget class="QPushButton" name="copyButton"> <property name="text"> @@ -83,7 +76,7 @@ </property> </widget> </item> - <item alignment="Qt::AlignRight|Qt::AlignBottom"> + <item> <widget class="QPushButton" name="clearButton"> <property name="text"> <string>Clear</string> diff --git a/ui/KeyList.ui b/ui/KeyList.ui index 99932453..d24a2825 100644 --- a/ui/KeyList.ui +++ b/ui/KeyList.ui @@ -18,9 +18,9 @@ <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QWidget" name="menuWidget" native="true"> - <layout class="QHBoxLayout" name="menu"> + <layout class="QVBoxLayout" name="verticalLayout_3"> <property name="spacing"> - <number>0</number> + <number>6</number> </property> <property name="leftMargin"> <number>0</number> @@ -35,38 +35,49 @@ <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> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="refreshKeyListButton"> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="syncButton"> + <property name="text"> + <string>Sync Public Key</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="uncheckButton"> + <property name="text"> + <string>Uncheck ALL</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="checkALLButton"> + <property name="text"> + <string>Check ALL</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> </item> </layout> </widget> diff --git a/ui/NetworkSettings.ui b/ui/NetworkSettings.ui new file mode 100644 index 00000000..9e27d11b --- /dev/null +++ b/ui/NetworkSettings.ui @@ -0,0 +1,325 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NetworkSettings</class> + <widget class="QWidget" name="NetworkSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>549</width> + <height>727</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="proxyGroupBox"> + <property name="title"> + <string>Proxy</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="enableProxyCheckBox"> + <property name="text"> + <string>Enable Proxy</string> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget_3" 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="proxyTypeLabel"> + <property name="text"> + <string>Proxy Type</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="proxyTypeComboBox"> + <property name="currentText"> + <string>System Default</string> + </property> + <item> + <property name="text"> + <string>System Default</string> + </property> + </item> + <item> + <property name="text"> + <string>HTTP</string> + </property> + </item> + <item> + <property name="text"> + <string>Socks5</string> + </property> + </item> + </widget> + </item> + </layout> + </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="proxyServerAddressLabel"> + <property name="text"> + <string>Host Address</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="proxyServerAddressEdit"> + <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="proxyServerPortLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>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>1080</number> + </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="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_5" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <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> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="capabilityGroupBox"> + <property name="title"> + <string>Network Capability</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="2" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="forbidALLCheckBox"> + <property name="text"> + <string>Forbid all network connection.</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="prohibitUpdateCheck"> + <property name="text"> + <string>Prohibit checking for version updates when the program starts.</string> + </property> + </widget> + </item> + </layout> + </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_5"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QPushButton" name="checkProxyConnectionButton"> + <property name="text"> + <string>Check Proxy Connection</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/ReceiveMailDialog.ui b/ui/ReceiveMailDialog.ui new file mode 100644 index 00000000..61c23738 --- /dev/null +++ b/ui/ReceiveMailDialog.ui @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReceiveMailDialog</class> + <widget class="QDialog" name="ReceiveMailDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>643</width> + <height>657</height> + </rect> + </property> + <property name="windowTitle"> + <string>Receive Mail</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeWidget" name="treeWidget"> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QListView" name="listView"/> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/SendMailDialog.ui b/ui/SendMailDialog.ui index 8eb004f7..d29a5f63 100644 --- a/ui/SendMailDialog.ui +++ b/ui/SendMailDialog.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>958</width> - <height>607</height> + <width>995</width> + <height>760</height> </rect> </property> <property name="cursor"> @@ -17,7 +17,7 @@ <enum>Qt::NoContextMenu</enum> </property> <property name="windowTitle"> - <string>Send Mail</string> + <string>New Message</string> </property> <property name="sizeGripEnabled"> <bool>false</bool> @@ -80,6 +80,9 @@ <property name="text"> <string>CC</string> </property> + <property name="checkable"> + <bool>true</bool> + </property> </widget> </item> <item> @@ -87,6 +90,9 @@ <property name="text"> <string>BCC</string> </property> + <property name="checkable"> + <bool>true</bool> + </property> </widget> </item> </layout> @@ -96,7 +102,7 @@ <widget class="QWidget" name="horizontalWidget_4" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_4"> <property name="spacing"> - <number>0</number> + <number>6</number> </property> <property name="leftMargin"> <number>0</number> @@ -113,7 +119,7 @@ <item> <widget class="QLabel" name="recipientLabel"> <property name="text"> - <string>Recipient</string> + <string>Recipient(s)</string> </property> <property name="margin"> <number>5</number> @@ -123,6 +129,13 @@ <item> <widget class="QLineEdit" name="recipientEdit"/> </item> + <item> + <widget class="QPushButton" name="recipientsEditButton"> + <property name="text"> + <string>Edit Recipients(s)</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -154,7 +167,7 @@ <item> <widget class="QLabel" name="subjectLabel"> <property name="text"> - <string>Subject</string> + <string>Mail Subject</string> </property> <property name="margin"> <number>5</number> @@ -168,6 +181,64 @@ </widget> </item> <item> + <widget class="QWidget" name="horizontalWidget_6" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="gpgOperaLabel"> + <property name="text"> + <string>GPG Operations</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="senderKeySelectButton"> + <property name="text"> + <string>Select Sender GPG Key</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="recipientKeySelectButton"> + <property name="text"> + <string>Select Recipient(s) GPG Key</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="Line" name="line_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -178,7 +249,7 @@ <widget class="QWidget" name="ccInputWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <property name="spacing"> - <number>0</number> + <number>6</number> </property> <property name="leftMargin"> <number>0</number> @@ -205,6 +276,13 @@ <item> <widget class="QLineEdit" name="ccEdit"/> </item> + <item> + <widget class="QPushButton" name="ccEditButton"> + <property name="text"> + <string>Edit CC(s)</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -212,7 +290,7 @@ <widget class="QWidget" name="bccInputWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_3"> <property name="spacing"> - <number>0</number> + <number>6</number> </property> <property name="leftMargin"> <number>0</number> @@ -239,6 +317,13 @@ <item> <widget class="QLineEdit" name="bccEdit"/> </item> + <item> + <widget class="QPushButton" name="bccEditButton"> + <property name="text"> + <string>Edit BCC(s)</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -260,6 +345,57 @@ <widget class="QTextEdit" name="textEdit"/> </item> <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> + <widget class="QLabel" name="senderKeyLabel"> + <property name="text"> + <string>Sender GPG Key: </string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="senderKeyValueLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> + <widget class="QLabel" name="recipientKeysLabel"> + <property name="text"> + <string>Recipient(s) GPG Key: </string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="recipientsKeyValueLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> <widget class="QLabel" name="errorLabel"> <property name="text"> <string/> @@ -267,11 +403,58 @@ </widget> </item> <item> + <widget class="Line" name="line_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QCheckBox" name="contentEncryptCheckBox"> + <property name="text"> + <string>Encrypt content</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="attacSignatureCheckBox"> + <property name="text"> + <string>Attach signature</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="attachSenderPublickeyCheckBox"> + <property name="text"> + <string>Attach sender's public key</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> <item alignment="Qt::AlignRight"> <widget class="QPushButton" name="sendMailButton"> <property name="text"> - <string>Send</string> + <string>Send Mail</string> </property> </widget> </item> diff --git a/ui/SendMailSettings.ui b/ui/SendMailSettings.ui index 96703f7f..6d409946 100644 --- a/ui/SendMailSettings.ui +++ b/ui/SendMailSettings.ui @@ -328,6 +328,40 @@ </item> </layout> </item> + <item row="1" column="0"> + <widget class="QWidget" name="horizontalWidget_6" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <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="gpgkeyIdLabel"> + <property name="text"> + <string>Default Sender GPG Key ID</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="gpgKeyIDEdit"/> + </item> + </layout> + </widget> + </item> </layout> </widget> </item> |