diff options
Diffstat (limited to 'src')
56 files changed, 4730 insertions, 3259 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b225a09..200f5beb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,8 @@ -set(ALL_SOURCE_FILE) - add_subdirectory(gpg) add_subdirectory(ui) add_subdirectory(smtp) +add_subdirectory(server) +add_subdirectory(advance) aux_source_directory(. BASE_SOURCE) @@ -90,7 +90,7 @@ if(${CMAKE_BUILD_TYPE} STREQUAL "Release") add_executable(${AppName} MACOSX_BUNDLE ${ICON_RESOURCE} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) set_target_properties(${AppName} PROPERTIES BUNDLE True - MACOSX_BUNDLE_GUI_IDENTIFIER org.gnupg.gpgfrontend + 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} @@ -121,23 +121,26 @@ else() add_executable(${AppName} ${BASE_SOURCE} ${RESOURCE_FILES} ${QT5_MOCS}) endif() +set(GPGFRONTEND_LIBS smtp gpgfrontend-ui advance server gpg) +set(QT_DEPENDENCY_LIBS Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core) + IF (MINGW) message(STATUS "Link Application Static Library For MINGW") target_link_libraries(${AppName} - smtp gpgfrontend-ui gpg - Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core + ${GPGFRONTEND_LIBS} + ${QT_DEPENDENCY_LIBS} crypto ssl) elseif(APPLE) message(STATUS "Link Application Static Library For macOS") target_link_libraries(${AppName} - smtp gpgfrontend-ui gpg - Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core + ${GPGFRONTEND_LIBS} + ${QT_DEPENDENCY_LIBS} crypto ssl) else() message(STATUS "Link Application Static Library For UNIX") target_link_libraries(${AppName} - smtp gpgfrontend-ui gpg - Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core + ${GPGFRONTEND_LIBS} + ${QT_DEPENDENCY_LIBS} crypto ssl pthread) endif() diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index eb8b96b1..73f55672 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -51,7 +51,7 @@ MainWindow::MainWindow() auto version_thread = new VersionCheckThread(replay); - connect(version_thread, SIGNAL(finished(QPrivateSignal)), version_thread, SLOT(deleteLater())); + connect(version_thread, SIGNAL(finished()), version_thread, SLOT(deleteLater())); connect(version_thread, SIGNAL(upgradeVersion(const QString &, const QString &)), this, SLOT(slotVersionUpgrade(const QString &, const QString &))); version_thread->start(); diff --git a/src/advance/CMakeLists.txt b/src/advance/CMakeLists.txt new file mode 100644 index 00000000..696fc8e5 --- /dev/null +++ b/src/advance/CMakeLists.txt @@ -0,0 +1,6 @@ +aux_source_directory(. ADVANCE_SOURCE) + +add_library(advance STATIC ${ADVANCE_SOURCE}) + +target_link_libraries(advance + Qt5::Network Qt5::Widgets Qt5::Core)
\ No newline at end of file diff --git a/src/advance/UnknownSignersChecker.cpp b/src/advance/UnknownSignersChecker.cpp new file mode 100644 index 00000000..1b087b5c --- /dev/null +++ b/src/advance/UnknownSignersChecker.cpp @@ -0,0 +1,81 @@ +/** + * 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 "advance/UnknownSignersChecker.h" + + +UnknownSignersChecker::UnknownSignersChecker(GpgME::GpgContext *ctx, gpgme_verify_result_t result) : + appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini"), mCtx(ctx), + mResult(result) { + +} + +void UnknownSignersChecker::start() { + + auto sign = mResult->signatures; + bool canContinue = true; + + while (sign && canContinue) { + + switch (gpg_err_code(sign->status)) { + case GPG_ERR_BAD_SIGNATURE: + break; + case GPG_ERR_NO_ERROR: + if (!(sign->status & GPGME_SIGSUM_KEY_MISSING)) + check_signer(sign); + break; + case GPG_ERR_NO_PUBKEY: + + case GPG_ERR_CERT_REVOKED: + case GPG_ERR_SIG_EXPIRED: + case GPG_ERR_KEY_EXPIRED: + check_signer(sign); + break; + case GPG_ERR_GENERAL: + canContinue = false; + break; + default: + break; + } + sign = sign->next; + } + + if(!unknownFprs.isEmpty()) { + PubkeyGetter pubkeyGetter(mCtx, unknownFprs); + pubkeyGetter.start(); + if (!pubkeyGetter.result()) { + + } + } +} + +void UnknownSignersChecker::check_signer(gpgme_signature_t sign) { + + auto key = mCtx->getKeyByFpr(sign->fpr); + if (!key.good) { + qDebug() << "Find Unknown FingerPrint " << sign->fpr; + unknownFprs.append(sign->fpr); + } + +} diff --git a/src/gpg/CMakeLists.txt b/src/gpg/CMakeLists.txt index 49e98b23..2bcacade 100644 --- a/src/gpg/CMakeLists.txt +++ b/src/gpg/CMakeLists.txt @@ -1,4 +1,5 @@ aux_source_directory(./result_analyse GPG_SOURCE) +aux_source_directory(./gpg_context GPG_SOURCE) aux_source_directory(. GPG_SOURCE) add_library(gpg STATIC ${GPG_SOURCE}) diff --git a/src/gpg/GpgConstants.cpp b/src/gpg/GpgConstants.cpp index 1ed06182..1d59dab5 100644 --- a/src/gpg/GpgConstants.cpp +++ b/src/gpg/GpgConstants.cpp @@ -30,4 +30,5 @@ const char *GpgConstants::PGP_SIGNED_BEGIN = "-----BEGIN PGP SIGNED MESSAGE----- const char *GpgConstants::PGP_SIGNED_END = "-----END PGP SIGNATURE-----"; const char *GpgConstants::PGP_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----"; const char *GpgConstants::PGP_SIGNATURE_END = "-----END PGP SIGNATURE-----"; +const char *GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD = "GpgF_Scpt://"; diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp deleted file mode 100644 index 0462433d..00000000 --- a/src/gpg/GpgContext.cpp +++ /dev/null @@ -1,1234 +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 "gpg/GpgContext.h" - -#include <functional> -#include <unistd.h> /* contains read/write */ - -#ifdef _WIN32 - -#include <windows.h> - -#endif - -#define INT2VOIDP(i) (void*)(uintptr_t)(i) - -namespace GpgME { - -/** Constructor - * Set up gpgme-context, set paths to app-run path - */ - GpgContext::GpgContext() { - /** get application path */ - QString appPath = qApp->applicationDirPath(); - - /** The function `gpgme_check_version' must be called before any other - * function in the library, because it initializes the thread support - * subsystem in GPGME. (from the info page) */ - gpgme_check_version(nullptr); - - // the locale set here is used for the other setlocale calls which have nullptr - // -> nullptr means use default, which is configured here - setlocale(LC_ALL, settings.value("int/lang").toLocale().name().toUtf8().constData()); - - /** set locale, because tests do also */ - gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr)); - //qDebug() << "Locale set to" << LC_CTYPE << " - " << setlocale(LC_CTYPE, nullptr); -#ifndef _WIN32 - gpgme_set_locale(nullptr, LC_MESSAGES, setlocale(LC_MESSAGES, nullptr)); -#endif - - err = gpgme_new(&mCtx); - checkErr(err); - - gpgme_engine_info_t engineInfo; - engineInfo = gpgme_ctx_get_engine_info(mCtx); - - // Check ENV before running - bool check_pass = false, find_openpgp = false, find_gpgconf = false, find_assuan = false, find_cms = false; - while (engineInfo != nullptr) { - qDebug() << gpgme_get_protocol_name(engineInfo->protocol) << engineInfo->file_name << engineInfo->protocol - << engineInfo->home_dir << engineInfo->version; - 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) { - gpgExec = engineInfo->file_name; - find_openpgp = true; - } - 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; - } - - if (find_gpgconf && find_openpgp && find_cms && find_assuan) - check_pass = true; - - if (!check_pass) { - good = false; - return; - } else good = true; - - - /** Setting the output type must be done at the beginning */ - /** think this means ascii-armor --> ? */ - gpgme_set_armor(mCtx, 1); - /** passphrase-callback */ - gpgme_set_passphrase_cb(mCtx, passphraseCb, this); - - /** check if app is called with -d from command line */ - if (qApp->arguments().contains("-d")) { - qDebug() << "gpgme_data_t debug on"; - debug = true; - } else { - debug = false; - } - - connect(this, SIGNAL(signalKeyDBChanged()), - this, SLOT(slotRefreshKeyList()), Qt::DirectConnection); - connect(this, SIGNAL(signalKeyUpdated(QString)), - this, SLOT(slotUpdateKeyList(QString)), Qt::DirectConnection); - slotRefreshKeyList(); - } - -/** Destructor - * Release gpgme-context - */ - GpgContext::~GpgContext() { - if (mCtx) gpgme_release(mCtx); - mCtx = nullptr; - } - - bool GpgContext::isGood() const { - return good; - } - -/** Import Key from QByteArray - * - */ - GpgImportInformation GpgContext::importKey(QByteArray inBuffer) { - auto *importInformation = new GpgImportInformation(); - err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1); - checkErr(err); - err = gpgme_op_import(mCtx, in); - gpgme_import_result_t result; - - result = gpgme_op_import_result(mCtx); - if (result->unchanged) { - importInformation->unchanged = result->unchanged; - } - if (result->considered) { - importInformation->considered = result->considered; - } - if (result->no_user_id) { - importInformation->no_user_id = result->no_user_id; - } - if (result->imported) { - importInformation->imported = result->imported; - } - if (result->imported_rsa) { - importInformation->imported_rsa = result->imported_rsa; - } - if (result->unchanged) { - importInformation->unchanged = result->unchanged; - } - if (result->new_user_ids) { - importInformation->new_user_ids = result->new_user_ids; - } - if (result->new_sub_keys) { - importInformation->new_sub_keys = result->new_sub_keys; - } - if (result->new_signatures) { - importInformation->new_signatures = result->new_signatures; - } - if (result->new_revocations) { - importInformation->new_revocations = result->new_revocations; - } - if (result->secret_read) { - importInformation->secret_read = result->secret_read; - } - if (result->secret_imported) { - importInformation->secret_imported = result->secret_imported; - } - if (result->secret_unchanged) { - importInformation->secret_unchanged = result->secret_unchanged; - } - if (result->not_imported) { - importInformation->not_imported = result->not_imported; - } - gpgme_import_status_t status = result->imports; - while (status != nullptr) { - GpgImportedKey key; - key.importStatus = static_cast<int>(status->status); - key.fpr = status->fpr; - importInformation->importedKeys.emplace_back(key); - status = status->next; - } - checkErr(err); - emit signalKeyDBChanged(); - gpgme_data_release(in); - return *importInformation; - } - -/** Generate New Key with values params - * - */ - bool GpgContext::generateKey(GenKeyInfo *params) { - - auto userid_utf8 = params->getUserid().toUtf8(); - const char *userid = userid_utf8.constData(); - auto algo_utf8 = (params->getAlgo() + params->getKeySizeStr()).toUtf8(); - const char *algo = algo_utf8.constData(); - unsigned long expires = QDateTime::currentDateTime().secsTo(params->getExpired()); - 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; - } - - err = gpgme_op_createkey(mCtx, userid, algo, 0, expires, nullptr, flags); - - if (err != GPG_ERR_NO_ERROR) { - checkErr(err); - return false; - } else { - emit signalKeyDBChanged(); - return true; - } - } - -/** Export Key to QByteArray - * - */ - bool GpgContext::exportKeys(QStringList *uidList, QByteArray *outBuffer) { - size_t read_bytes; - gpgme_data_t dataOut = nullptr; - outBuffer->resize(0); - - if (uidList->count() == 0) { - QMessageBox::critical(nullptr, "Export Keys Error", "No Keys Selected"); - return false; - } - - for (int i = 0; i < uidList->count(); i++) { - err = gpgme_data_new(&dataOut); - checkErr(err); - - err = gpgme_op_export(mCtx, uidList->at(i).toUtf8().constData(), 0, dataOut); - checkErr(err); - - read_bytes = gpgme_data_seek(dataOut, 0, SEEK_END); - - err = readToBuffer(dataOut, outBuffer); - checkErr(err); - gpgme_data_release(dataOut); - } - return true; - } - - /** - * List all availabe Keys (VERY much like kgpgme) - */ - void GpgContext::fetch_keys() { - - gpgme_error_t gpgmeError; - - gpgme_key_t key; - - qDebug() << "Clear List and Map"; - - mKeyList.clear(); - mKeyMap.clear(); - - auto &keys = mKeyList; - auto &keys_map = mKeyMap; - - qDebug() << "Set Keylist Mode"; - - gpgmeError = gpgme_set_keylist_mode(mCtx, - GPGME_KEYLIST_MODE_LOCAL - | GPGME_KEYLIST_MODE_WITH_SECRET - | GPGME_KEYLIST_MODE_SIGS - | GPGME_KEYLIST_MODE_SIG_NOTATIONS - | GPGME_KEYLIST_MODE_WITH_TOFU); - if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { - checkErr(gpgmeError); - return; - } - - qDebug() << "Operate KeyList Start"; - - gpgmeError = gpgme_op_keylist_start(mCtx, nullptr, 0); - if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { - checkErr(gpgmeError); - return; - } - - qDebug() << "Start Loop"; - - while ((gpgmeError = gpgme_op_keylist_next(mCtx, &key)) == GPG_ERR_NO_ERROR) { - if (!key->subkeys) - continue; - - qDebug() << "Append Key" << key->subkeys->keyid; - - keys.emplace_back(key); - keys_map.insert(keys.back().id, &keys.back()); - gpgme_key_unref(key); - } - - - if (gpg_err_code(gpgmeError) != GPG_ERR_EOF) { - checkErr(gpgmeError); - return; - } - - qDebug() << "Operate KeyList End"; - - gpgmeError = gpgme_op_keylist_end(mCtx); - if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { - checkErr(gpgmeError); - return; - } - - gpgmeError = gpgme_op_keylist_end(mCtx); - if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { - checkErr(gpgmeError); - return; - } - - mKeyList = keys; - } - -/** Delete keys - */ - - void GpgContext::deleteKeys(QStringList *uidList) { - - gpgme_error_t error; - gpgme_key_t key; - - for (const auto &tmp : *uidList) { - - error = gpgme_op_keylist_start(mCtx, tmp.toUtf8().constData(), 0); - if (error != GPG_ERR_NO_ERROR) { - checkErr(error); - continue; - } - - error = gpgme_op_keylist_next(mCtx, &key); - if (error != GPG_ERR_NO_ERROR) { - checkErr(error); - continue; - } - - error = gpgme_op_keylist_end(mCtx); - if (error != GPG_ERR_NO_ERROR) { - checkErr(error); - continue; - } - - error = gpgme_op_delete(mCtx, key, 1); - if (error != GPG_ERR_NO_ERROR) { - checkErr(error); - continue; - } - - } - emit signalKeyDBChanged(); - } - -/** Encrypt inBuffer for reciepients-uids, write - * result to outBuffer - */ - gpg_error_t GpgContext::encrypt(QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, - gpgme_encrypt_result_t *result) { - - gpgme_data_t dataIn = nullptr, dataOut = nullptr; - outBuffer->resize(0); - - //gpgme_encrypt_result_t e_result; - gpgme_key_t recipients[keys.count() + 1]; - - int index = 0; - for (const auto &key : keys) { - recipients[index++] = key.key_refer; - } - - //Last entry dataIn array has to be nullptr - recipients[keys.count()] = nullptr; - - //If the last parameter isnt 0, a private copy of data is made - if (mCtx) { - err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); - checkErr(err); - if (!err) { - err = gpgme_data_new(&dataOut); - checkErr(err); - if (!err) { - err = gpgme_op_encrypt(mCtx, recipients, GPGME_ENCRYPT_ALWAYS_TRUST, dataIn, dataOut); - checkErr(err); - if (!err) { - err = readToBuffer(dataOut, outBuffer); - checkErr(err); - } - } - } - } - if (dataIn) { - gpgme_data_release(dataIn); - } - if (dataOut) { - gpgme_data_release(dataOut); - } - - if (result != nullptr) { - *result = gpgme_op_encrypt_result(mCtx); - } - return err; - } - -/** Decrypt QByteAarray, return QByteArray - * mainly from http://basket.kde.org/ (kgpgme.cpp) - */ - gpgme_error_t - GpgContext::decrypt(const QByteArray &inBuffer, QByteArray *outBuffer, gpgme_decrypt_result_t *result) { - gpgme_data_t dataIn = nullptr, dataOut = nullptr; - gpgme_decrypt_result_t m_result = nullptr; - - outBuffer->resize(0); - if (mCtx != nullptr) { - err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_data_new(&dataOut); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_op_decrypt(mCtx, dataIn, dataOut); - m_result = gpgme_op_decrypt_result(mCtx); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = readToBuffer(dataOut, outBuffer); - } - } - } - } - - if (!settings.value("general/rememberPassword").toBool()) { - clearPasswordCache(); - } - - if (dataIn) { - gpgme_data_release(dataIn); - } - if (dataOut) { - gpgme_data_release(dataOut); - } - - if (result != nullptr) { - *result = m_result; - } - return err; - } - -/** Read gpgme-Data to QByteArray - * mainly from http://basket.kde.org/ (kgpgme.cpp) - */ -#define BUF_SIZE (32 * 1024) - - gpgme_error_t GpgContext::readToBuffer(gpgme_data_t dataIn, QByteArray *outBuffer) { - off_t ret; - gpgme_error_t gpgErrNoError = GPG_ERR_NO_ERROR; - - ret = gpgme_data_seek(dataIn, 0, SEEK_SET); - if (ret) { - gpgErrNoError = gpgme_err_code_from_errno(errno); - checkErr(gpgErrNoError, "failed dataseek dataIn readToBuffer"); - } else { - char buf[BUF_SIZE + 2]; - - while ((ret = gpgme_data_read(dataIn, buf, BUF_SIZE)) > 0) { - const size_t size = outBuffer->size(); - outBuffer->resize(static_cast<int>(size + ret)); - memcpy(outBuffer->data() + size, buf, ret); - } - if (ret < 0) { - gpgErrNoError = gpgme_err_code_from_errno(errno); - checkErr(gpgErrNoError, "failed data_read dataIn readToBuffer"); - } - } - return gpgErrNoError; - } - -/** The Passphrase window, if not provided by env-Var GPG_AGENT_INFO - * originally copied from http://basket.kde.org/ (kgpgme.cpp), but modified - */ - gpgme_error_t GpgContext::passphraseCb(void *hook, const char *uid_hint, - const char *passphrase_info, - int last_was_bad, int fd) { - auto *gpg = static_cast<GpgContext *>(hook); - return gpg->passphrase(uid_hint, passphrase_info, last_was_bad, fd); - } - - gpgme_error_t GpgContext::passphrase(const char *uid_hint, - const char * /*passphrase_info*/, - int last_was_bad, int fd) { - gpgme_error_t returnValue = GPG_ERR_CANCELED; - QString passwordDialogMessage; - QString gpgHint = QString::fromUtf8(uid_hint); - bool result; - -#ifdef _WIN32 - DWORD written; - auto hd = INT2VOIDP(fd); -#endif - - if (last_was_bad) { - passwordDialogMessage += "<i>" + tr("Wrong password") + ".</i><br><br>\n\n"; - clearPasswordCache(); - } - - /** if uid provided */ - if (!gpgHint.isEmpty()) { - // remove UID, leave only username & email - gpgHint.remove(0, gpgHint.indexOf(" ")); - passwordDialogMessage += "<b>" + tr("Enter Password for") + "</b><br>" + gpgHint + "<br>"; - } - - if (mPasswordCache.isEmpty()) { - QString password = QInputDialog::getText(QApplication::activeWindow(), tr("Enter Password"), - passwordDialogMessage, QLineEdit::Password, - "", &result); - - if (result) mPasswordCache = password.toUtf8(); - } else { - result = true; - } - - if (result) { - -#ifndef _WIN32 - if (write(fd, mPasswordCache.data(), mPasswordCache.length()) == -1) { - qDebug() << "something is terribly broken"; - } -#else - WriteFile(hd, mPasswordCache.data(), mPasswordCache.length(), &written, 0); -#endif - - returnValue = GPG_ERR_NO_ERROR; - } - -#ifndef _WIN32 - if (write(fd, "\n", 1) == -1) { - qDebug() << "something is terribly broken"; - } -#else - WriteFile(hd, "\n", 1, &written, 0); - - /* program will hang on cancel if hd not closed */ - if (!result) { - CloseHandle(hd); - } -#endif - - return returnValue; - } - -/** also from kgpgme.cpp, seems to clear password from mem */ - void GpgContext::clearPasswordCache() { - if (mPasswordCache.size() > 0) { - mPasswordCache.fill('\0'); - mPasswordCache.truncate(0); - } - } - -// error-handling - void GpgContext::checkErr(gpgme_error_t gpgmeError, const QString &comment) { - //if (gpgmeError != GPG_ERR_NO_ERROR && gpgmeError != GPG_ERR_CANCELED) { - if (gpgmeError != GPG_ERR_NO_ERROR) { - qDebug() << "[Error " << gpg_err_code(gpgmeError) - << "] Source: " << gpgme_strsource(gpgmeError) << " Description: " << gpgErrString(gpgmeError); - } - } - - void GpgContext::checkErr(gpgme_error_t gpgmeError) { - //if (gpgmeError != GPG_ERR_NO_ERROR && gpgmeError != GPG_ERR_CANCELED) { - if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { - qDebug() << "[Error " << gpg_err_code(gpgmeError) - << "] Source: " << gpgme_strsource(gpgmeError) << " Description: " << gpgErrString(gpgmeError); - } - } - - QString GpgContext::gpgErrString(gpgme_error_t err) { - return QString::fromUtf8(gpgme_strerror(err)); - } - - bool GpgContext::exportSecretKey(const GpgKey &key, QByteArray *outBuffer) { - qDebug() << "Export Secret Key" << key.id; - gpgme_key_t target_key[2] = { - key.key_refer, - nullptr - }; - - gpgme_data_t dataOut; - gpgme_data_new(&dataOut); - // export private key to outBuffer - gpgme_error_t error = gpgme_op_export_keys(mCtx, target_key, GPGME_EXPORT_MODE_SECRET, dataOut); - - if (gpgme_err_code(error) != GPG_ERR_NO_ERROR) { - checkErr(error); - gpgme_data_release(dataOut); - return false; - } - - readToBuffer(dataOut, outBuffer); - gpgme_data_release(dataOut); - return true; - } - -/** return type should be gpgme_error_t*/ - QProcess * GpgContext::executeGpgCommand(const QStringList &arguments, QByteArray *stdOut, QByteArray *stdErr, - const std::function<void(QProcess *)> &interactFunc) { - QStringList args; - args << arguments; - - auto *gpgProcess = new QProcess(this); - qDebug() << "gpgExec" << gpgExec << args; - - gpgProcess->setReadChannel(QProcess::StandardOutput); - connect(gpgProcess, SIGNAL(finished(int,QProcess::ExitStatus)), - gpgProcess, SLOT(deleteLater())); - connect(gpgProcess, &QProcess::readyReadStandardOutput, this, [gpgProcess, interactFunc]() { - qDebug() << "Function Called" << &gpgProcess; - // interactFunc(gpgProcess); - }); - - gpgProcess->start(gpgExec, args); - - if (gpgProcess->waitForStarted()){ - qDebug() << "Gpg Process Started Success"; - } else { - qDebug() << "Gpg Process Started Failed"; - } - - return gpgProcess; - } - -/*** - * if sigbuffer not set, the inbuffer should contain signed text - * - * TODO: return type should contain: - * -> list of sigs - * -> valid - * -> errors - */ - gpgme_error_t GpgContext::verify(QByteArray *inBuffer, QByteArray *sigBuffer, gpgme_verify_result_t *result) { - - gpgme_data_t dataIn; - gpgme_error_t gpgmeError; - gpgme_signature_t sign; - gpgme_verify_result_t m_result; - - gpgmeError = gpgme_data_new_from_mem(&dataIn, inBuffer->data(), inBuffer->size(), 1); - checkErr(gpgmeError); - - if (sigBuffer != nullptr) { - gpgme_data_t sigdata; - gpgmeError = gpgme_data_new_from_mem(&sigdata, sigBuffer->data(), sigBuffer->size(), 1); - checkErr(gpgmeError); - gpgmeError = gpgme_op_verify(mCtx, sigdata, dataIn, nullptr); - } else { - gpgmeError = gpgme_op_verify(mCtx, dataIn, nullptr, dataIn); - } - - checkErr(gpgmeError); - - m_result = gpgme_op_verify_result(mCtx); - - if (result != nullptr) { - *result = m_result; - } - - return gpgmeError; - } - - gpg_error_t - GpgContext::sign(const QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, bool detached, - gpgme_sign_result_t *result) { - - gpgme_error_t gpgmeError; - gpgme_data_t dataIn, dataOut; - gpgme_sign_result_t m_result; - gpgme_sig_mode_t mode; - - if (keys.isEmpty()) { - QMessageBox::critical(nullptr, tr("Key Selection"), tr("No Private Key Selected")); - return false; - } - - // at start or end? - - setSigners(keys); - - gpgmeError = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); - checkErr(gpgmeError); - gpgmeError = gpgme_data_new(&dataOut); - checkErr(gpgmeError); - - /* - `GPGME_SIG_MODE_NORMAL' - A normal signature is made, the output includes the plaintext - and the signature. - - `GPGME_SIG_MODE_DETACH' - A detached signature is made. - - `GPGME_SIG_MODE_CLEAR' - A clear text signature is made. The ASCII armor and text - mode settings of the context are ignored. - */ - - if (detached) { - mode = GPGME_SIG_MODE_DETACH; - } else { - mode = GPGME_SIG_MODE_CLEAR; - } - - gpgmeError = gpgme_op_sign(mCtx, dataIn, dataOut, mode); - checkErr(gpgmeError); - - if (gpgmeError == GPG_ERR_CANCELED) { - return false; - } - - if (gpgmeError != GPG_ERR_NO_ERROR) { - QMessageBox::critical(nullptr, tr("Error in signing:"), QString::fromUtf8(gpgme_strerror(gpgmeError))); - return false; - } - - m_result = gpgme_op_sign_result(mCtx); - - if (result != nullptr) { - *result = m_result; - } - - gpgmeError = readToBuffer(dataOut, outBuffer); - checkErr(gpgmeError); - - gpgme_data_release(dataIn); - gpgme_data_release(dataOut); - - if (!settings.value("general/rememberPassword").toBool()) { - clearPasswordCache(); - } - - return gpgmeError; - } - - /* - * if there is no '\n' before the PGP-Begin-Block, but for example a whitespace, - * GPGME doesn't recognise the Message as encrypted. This function adds '\n' - * before the PGP-Begin-Block, if missing. - */ - void GpgContext::preventNoDataErr(QByteArray *in) { - int block_start = in->indexOf(GpgConstants::PGP_CRYPT_BEGIN); - if (block_start > 0 && in->at(block_start - 1) != '\n') { - in->insert(block_start, '\n'); - } - block_start = in->indexOf(GpgConstants::PGP_SIGNED_BEGIN); - if (block_start > 0 && in->at(block_start - 1) != '\n') { - in->insert(block_start, '\n'); - } - } - - /* - * isSigned returns: - * - 0, if text isn't signed at all - * - 1, if text is partially signed - * - 2, if text is completly signed - */ - int GpgContext::textIsSigned(const QByteArray &text) { - if (text.trimmed().startsWith(GpgConstants::PGP_SIGNED_BEGIN) && - text.trimmed().endsWith(GpgConstants::PGP_SIGNED_END)) { - return 2; - } - if (text.contains(GpgConstants::PGP_SIGNED_BEGIN) && text.contains(GpgConstants::PGP_SIGNED_END)) { - return 1; - } - return 0; - } - - QString GpgContext::beautifyFingerprint(QString fingerprint) { - uint len = fingerprint.length(); - if ((len > 0) && (len % 4 == 0)) - for (uint n = 0; 4 * (n + 1) < len; ++n) - fingerprint.insert(static_cast<int>(5u * n + 4u), ' '); - return fingerprint; - } - - void GpgContext::slotRefreshKeyList() { - qDebug() << "Refreshing Keys"; - this->fetch_keys(); - emit signalKeyInfoChanged(); - } - -/** - * note: is_private_key status is not returned - */ - GpgKey GpgContext::getKeyByFpr(const QString &fpr) { - for (const auto &key : mKeyList) { - if (key.fpr == fpr) { - return key; - } else { - for (auto &subkey : key.subKeys) { - if (subkey.fpr == fpr) { - return key; - } - } - } - } - return GpgKey(nullptr); - } - - /** - * note: is_private_key status is not returned - */ - const GpgKey &GpgContext::getKeyById(const QString &id) { - - for (const auto &key : mKeyList) { - if (key.id == id) { - return key; - } else { - auto subkeys = key.subKeys; - for (const auto &subkey : subkeys) { - if (subkey.id == id) { - return key; - } - } - } - } - - throw std::runtime_error("key not found"); - } - - QString GpgContext::getGpgmeVersion() { - return QString(gpgme_check_version(nullptr)); - } - - bool GpgContext::signKey(const GpgKey &target, const QString &uid, const QDateTime *expires) { - - unsigned int flags = 0; - - unsigned int expires_time_t = 0; - if (expires == nullptr) { - flags |= GPGME_KEYSIGN_NOEXPIRE; - } else { - expires_time_t = QDateTime::currentDateTime().secsTo(*expires); - } - - auto gpgmeError = - gpgme_op_keysign(mCtx, target.key_refer, uid.toUtf8().constData(), expires_time_t, flags); - - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(target.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - const GpgKeyList &GpgContext::getKeys() const { - return mKeyList; - } - - void GpgContext::getSigners(QVector<GpgKey> &signer) { - auto count = gpgme_signers_count(mCtx); - signer.clear(); - for (auto i = 0; i < count; i++) { - auto key = gpgme_signers_enum(mCtx, i); - auto it = mKeyMap.find(key->subkeys->keyid); - if (it == mKeyMap.end()) { - qDebug() << "Inconsistent state"; - signer.push_back(GpgKey(key)); - } else { - signer.push_back(*it.value()); - } - } - } - - void GpgContext::setSigners(const QVector<GpgKey> &keys) { - gpgme_signers_clear(mCtx); - for (const auto &key : keys) { - if (checkIfKeyCanSign(key)) { - auto gpgmeError = gpgme_signers_add(mCtx, key.key_refer); - checkErr(gpgmeError); - } - } - if (keys.length() != gpgme_signers_count(mCtx)) { - qDebug() << "No All Keys Added"; - } - } - - void GpgContext::slotUpdateKeyList(const QString &key_id) { - auto it = mKeyMap.find(key_id); - if (it != mKeyMap.end()) { - gpgme_key_t new_key_refer; - auto gpgmeErr = gpgme_get_key(mCtx, key_id.toUtf8().constData(), &new_key_refer, 0); - - if (gpgme_err_code(gpgmeErr) == GPG_ERR_EOF) { - gpgmeErr = gpgme_get_key(mCtx, key_id.toUtf8().constData(), &new_key_refer, 1); - - if (gpgme_err_code(gpgmeErr) == GPG_ERR_EOF) { - throw std::runtime_error("key_id not found in key database"); - } - - } - - if (new_key_refer != nullptr) { - it.value()->swapKeyRefer(new_key_refer); - emit signalKeyInfoChanged(); - } - - } - } - - bool GpgContext::addUID(const GpgKey &key, const GpgUID &uid) { - QString userid = QString("%1 (%3) <%2>").arg(uid.name, uid.email, uid.comment); - auto gpgmeError = gpgme_op_adduid(mCtx, key.key_refer, userid.toUtf8().constData(), 0); - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - - } - - bool GpgContext::revUID(const GpgKey &key, const GpgUID &uid) { - auto gpgmeError = gpgme_op_revuid(mCtx, key.key_refer, uid.uid.toUtf8().constData(), 0); - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - bool GpgContext::setPrimaryUID(const GpgKey &key, const GpgUID &uid) { - auto gpgmeError = gpgme_op_set_uid_flag(mCtx, key.key_refer, - uid.uid.toUtf8().constData(), "primary", nullptr); - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - bool GpgContext::revSign(const GpgKey &key, const GpgKeySignature &signature) { - - auto signing_key = getKeyById(signature.keyid); - - auto gpgmeError = gpgme_op_revsig(mCtx, key.key_refer, - signing_key.key_refer, - signature.uid.toUtf8().constData(), 0); - if (gpg_err_code(gpgmeError) == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - bool GpgContext::generateSubkey(const GpgKey &key, GenKeyInfo *params) { - - if (!params->isSubKey()) { - return false; - } - - auto algo_utf8 = (params->getAlgo() + params->getKeySizeStr()).toUtf8(); - const char *algo = algo_utf8.constData(); - unsigned long expires = QDateTime::currentDateTime().secsTo(params->getExpired()); - 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; - } - - flags |= GPGME_CREATE_NOPASSWD; - - - auto gpgmeError = gpgme_op_createsubkey(mCtx, key.key_refer, - algo, 0, expires, flags); - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - bool GpgContext::setExpire(const GpgKey &key, const GpgSubKey *subkey, QDateTime *expires) { - unsigned long expires_time = 0; - if (expires != nullptr) { - qDebug() << "Expire Datetime" << expires->toString(); - expires_time = QDateTime::currentDateTime().secsTo(*expires); - } - - const char *subfprs = nullptr; - - if (subkey != nullptr) { - subfprs = subkey->fpr.toUtf8().constData(); - } - - auto gpgmeError = gpgme_op_setexpire(mCtx, key.key_refer, - expires_time, subfprs, 0); - if (gpgmeError == GPG_ERR_NO_ERROR) { - emit signalKeyUpdated(key.id); - return true; - } else { - checkErr(gpgmeError); - return false; - } - } - - bool GpgContext::checkIfKeyCanSign(const GpgKey &key) { - if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { - return subkey.secret && subkey.can_sign && !subkey.disabled && !subkey.revoked && !subkey.expired; - })) - return true; - return false; - } - - bool GpgContext::checkIfKeyCanCert(const GpgKey &key) { - return key.has_master_key && !key.expired && !key.revoked && !key.disabled; - } - - bool GpgContext::checkIfKeyCanAuth(const GpgKey &key) { - if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { - return subkey.secret && subkey.can_authenticate && !subkey.disabled && !subkey.revoked && !subkey.expired; - })) - return true; - return false; - } - - bool GpgContext::checkIfKeyCanEncr(const GpgKey &key) { - if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { - return subkey.can_encrypt && !subkey.disabled && !subkey.revoked && !subkey.expired; - })) - return true; - return false; - } - - gpgme_error_t GpgContext::encryptSign(QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, - gpgme_encrypt_result_t *encr_result, gpgme_sign_result_t *sign_result) { - gpgme_data_t dataIn = nullptr, dataOut = nullptr; - outBuffer->resize(0); - - setSigners(keys); - - //gpgme_encrypt_result_t e_result; - gpgme_key_t recipients[keys.count() + 1]; - - /* set key for user */ - int index = 0; - for (const auto &key : keys) { - recipients[index++] = key.key_refer; - } - //Last entry dataIn array has to be nullptr - recipients[keys.count()] = nullptr; - - //If the last parameter isnt 0, a private copy of data is made - if (mCtx != nullptr) { - err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); - if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_data_new(&dataOut); - if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_op_encrypt_sign(mCtx, recipients, GPGME_ENCRYPT_ALWAYS_TRUST, dataIn, dataOut); - if (encr_result != nullptr) - *encr_result = gpgme_op_encrypt_result(mCtx); - if (sign_result != nullptr) - *sign_result = gpgme_op_sign_result(mCtx); - if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { - err = readToBuffer(dataOut, outBuffer); - } - } - } - } - - if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) - checkErr(err); - - if (dataIn) { - gpgme_data_release(dataIn); - } - if (dataOut) { - gpgme_data_release(dataOut); - } - - return err; - } - - gpgme_error_t - GpgContext::decryptVerify(const QByteArray &inBuffer, QByteArray *outBuffer, gpgme_decrypt_result_t *decrypt_result, - gpgme_verify_result_t *verify_result) { - gpgme_data_t dataIn = nullptr, dataOut = nullptr; - - outBuffer->resize(0); - if (mCtx != nullptr) { - err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_data_new(&dataOut); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = gpgme_op_decrypt_verify(mCtx, dataIn, dataOut); - if (decrypt_result != nullptr) - *decrypt_result = gpgme_op_decrypt_result(mCtx); - if (verify_result != nullptr) - *verify_result = gpgme_op_verify_result(mCtx); - if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { - err = readToBuffer(dataOut, outBuffer); - } - } - } - } - - if (!settings.value("general/rememberPassword").toBool()) { - clearPasswordCache(); - } - - if (dataIn) { - gpgme_data_release(dataIn); - } - if (dataOut) { - gpgme_data_release(dataOut); - } - - return err; - } - - bool GpgContext::exportKeys(const QVector<GpgKey> &keys, QByteArray &outBuffer) { - size_t read_bytes; - gpgme_data_t dataOut = nullptr; - outBuffer.resize(0); - - if (keys.count() == 0) { - QMessageBox::critical(nullptr, "Export Keys Error", "No Keys Selected"); - return false; - } - - for (const auto &key : keys) { - err = gpgme_data_new(&dataOut); - checkErr(err); - - err = gpgme_op_export(mCtx, key.id.toUtf8().constData(), 0, dataOut); - checkErr(err); - - read_bytes = gpgme_data_seek(dataOut, 0, SEEK_END); - - err = readToBuffer(dataOut, &outBuffer); - checkErr(err); - gpgme_data_release(dataOut); - } - return true; - } - - QProcess * GpgContext::generateRevokeCert(const GpgKey &key, const QString &outputFileName) { - QByteArray out, stdErr; - auto process = executeGpgCommand({ - "--command-fd", - "0", - "--status-fd", "1", - "-o", - outputFileName, - "--gen-revoke", - key.fpr - }, &out, &stdErr, - [](QProcess *proc) { - qDebug() << "Function Called" << proc; - while (proc->canReadLine()) { - const QString line = QString::fromUtf8(proc->readLine()).trimmed(); - // Command-fd is a stable interface, while this is all kind of hacky we - // are on a deadline :-/ - 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"); - } - } - }); - - qDebug() << "GenerateRevokeCert Process" << process; - - return process; - } -} diff --git a/src/gpg/GpgFileOpera.cpp b/src/gpg/GpgFileOpera.cpp index 9395bd17..af50c79a 100644 --- a/src/gpg/GpgFileOpera.cpp +++ b/src/gpg/GpgFileOpera.cpp @@ -114,7 +114,7 @@ gpgme_error_t GpgFileOpera::signFile(GpgME::GpgContext *ctx, QVector<GpgKey> &ke auto outBuffer = QByteArray(); infile.close(); - auto error = ctx->sign(keys, inBuffer, &outBuffer, true, result); + auto error = ctx->sign(keys, inBuffer, &outBuffer, GPGME_SIG_MODE_DETACH, result); if (gpg_err_code(error) != GPG_ERR_NO_ERROR) return error; @@ -185,7 +185,10 @@ gpg_error_t GpgFileOpera::encryptSignFile(GpgME::GpgContext *ctx, QVector<GpgKey auto outBuffer = QByteArray(); infile.close(); - auto error = ctx->encryptSign(keys, inBuffer, &outBuffer, encr_res, sign_res); + QVector<GpgKey> signerKeys; + + // TODO dealing with signer keys + auto error = ctx->encryptSign(keys, signerKeys, inBuffer, &outBuffer, encr_res, sign_res); if (gpg_err_code(error) != GPG_ERR_NO_ERROR) return error; diff --git a/src/ui/keygen/KeygenThread.cpp b/src/gpg/GpgInfo.cpp index d7b9d840..00a15ef9 100644 --- a/src/ui/keygen/KeygenThread.cpp +++ b/src/gpg/GpgInfo.cpp @@ -22,15 +22,4 @@ * */ -#include "ui/keygen/KeygenThread.h" - -KeyGenThread::KeyGenThread(GenKeyInfo* keyGenParams, GpgME::GpgContext *ctx) -: mCtx(ctx), keyGenParams(keyGenParams), QThread(nullptr) { - connect(this, &KeyGenThread::finished, this, &KeyGenThread::deleteLater); -} - -void KeyGenThread::run() { - bool success = mCtx->generateKey(keyGenParams); - emit signalKeyGenerated(success); - emit finished({}); -} +#include "gpg/GpgInfo.h" diff --git a/src/gpg/GpgUID.cpp b/src/gpg/GpgUID.cpp index f8f7e8fa..0dc6abfd 100644 --- a/src/gpg/GpgUID.cpp +++ b/src/gpg/GpgUID.cpp @@ -1,6 +1,26 @@ -// -// Created by eric on 2021/5/22. -// +/** + * 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 "gpg/GpgUID.h" diff --git a/src/gpg/gpg_context/GpgContext.cpp b/src/gpg/gpg_context/GpgContext.cpp new file mode 100644 index 00000000..14b54b32 --- /dev/null +++ b/src/gpg/gpg_context/GpgContext.cpp @@ -0,0 +1,410 @@ +/** + * 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 "gpg/GpgContext.h" +#include "ui/WaitingDialog.h" + +#include <functional> +#include <unistd.h> /* contains read/write */ + +#ifdef _WIN32 + +#include <windows.h> + +#endif + +#define INT2VOIDP(i) (void*)(uintptr_t)(i) + +namespace GpgME { + + /** Constructor + * Set up gpgme-context, set paths to app-run path + */ + GpgContext::GpgContext() { + + /** The function `gpgme_check_version' must be called before any other + * function in the library, because it initializes the thread support + * subsystem in GPGME. (from the info page) */ + gpgme_check_version(nullptr); + + // the locale set here is used for the other setlocale calls which have nullptr + // -> nullptr means use default, which is configured here + setlocale(LC_ALL, settings.value("int/lang").toLocale().name().toUtf8().constData()); + + /** set locale, because tests do also */ + gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr)); + //qDebug() << "Locale set to" << LC_CTYPE << " - " << setlocale(LC_CTYPE, nullptr); +#ifndef _WIN32 + gpgme_set_locale(nullptr, LC_MESSAGES, setlocale(LC_MESSAGES, nullptr)); +#endif + + err = gpgme_new(&mCtx); + checkErr(err); + + gpgme_engine_info_t engineInfo; + engineInfo = gpgme_ctx_get_engine_info(mCtx); + +// Check ENV before running + bool check_pass = false, find_openpgp = false, find_gpgconf = false, find_assuan = false, find_cms = false; + while (engineInfo != nullptr) { + qDebug() << gpgme_get_protocol_name(engineInfo->protocol) << engineInfo->file_name << engineInfo->protocol + << engineInfo->home_dir << engineInfo->version; + 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; + 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; + } + + if (find_gpgconf && find_openpgp && find_cms && find_assuan) + check_pass = true; + + if (!check_pass) { + good = false; + return; + } else good = true; + + +/** Setting the output type must be done at the beginning */ +/** think this means ascii-armor --> ? */ + gpgme_set_armor(mCtx, 1); +/** passphrase-callback */ + gpgme_set_passphrase_cb(mCtx, passphraseCb, this); + +/** check if app is called with -d from command line */ + if (qApp->arguments().contains("-d")) { + qDebug() << "gpgme_data_t debug on"; + debug = true; + } else { + debug = false; + } + + connect(this, SIGNAL(signalKeyDBChanged()), + this, SLOT(slotRefreshKeyList()), Qt::DirectConnection); + connect(this, SIGNAL(signalKeyUpdated(QString)), + this, SLOT(slotUpdateKeyList(QString)), Qt::DirectConnection); + slotRefreshKeyList(); + } + + /** Destructor + * Release gpgme-context + */ + GpgContext::~GpgContext() { + if (mCtx) gpgme_release(mCtx); + mCtx = nullptr; + } + + bool GpgContext::isGood() const { + return good; + } + + /** Read gpgme-Data to QByteArray + * mainly from http://basket.kde.org/ (kgpgme.cpp) + */ +#define BUF_SIZE (32 * 1024) + + gpgme_error_t GpgContext::readToBuffer(gpgme_data_t dataIn, QByteArray *outBuffer) { + gpgme_off_t ret; + gpgme_error_t gpgErrNoError = GPG_ERR_NO_ERROR; + + ret = gpgme_data_seek(dataIn, 0, SEEK_SET); + if (ret) { + gpgErrNoError = gpgme_err_code_from_errno(errno); + checkErr(gpgErrNoError, "failed dataseek dataIn readToBuffer"); + } else { + char buf[BUF_SIZE + 2]; + + while ((ret = gpgme_data_read(dataIn, buf, BUF_SIZE)) > 0) { + const size_t size = outBuffer->size(); + outBuffer->resize(static_cast<int>(size + ret)); + memcpy(outBuffer->data() + size, buf, ret); + } + if (ret < 0) { + gpgErrNoError = gpgme_err_code_from_errno(errno); + checkErr(gpgErrNoError, "failed data_read dataIn readToBuffer"); + } + } + return gpgErrNoError; + } + + /** + * The Passphrase window, if not provided by env-Var GPG_AGENT_INFO + * originally copied from http://basket.kde.org/ (kgpgme.cpp), but modified + */ + gpgme_error_t GpgContext::passphraseCb(void *hook, const char *uid_hint, + const char *passphrase_info, + int last_was_bad, int fd) { + auto *gpg = static_cast<GpgContext *>(hook); + return gpg->passphrase(uid_hint, passphrase_info, last_was_bad, fd); + } + + gpgme_error_t GpgContext::passphrase(const char *uid_hint, + const char * /*passphrase_info*/, + int last_was_bad, int fd) { + + gpgme_error_t returnValue = GPG_ERR_CANCELED; + QString passwordDialogMessage; + QString gpgHint = QString::fromUtf8(uid_hint); + bool result; + +#ifdef _WIN32 + DWORD written; + auto hd = INT2VOIDP(fd); +#endif + + if (last_was_bad) { + passwordDialogMessage += "<i>" + tr("Wrong password") + ".</i><br><br>\n\n"; + clearPasswordCache(); + } + + /** if uid provided */ + if (!gpgHint.isEmpty()) { + // remove UID, leave only username & email + gpgHint.remove(0, gpgHint.indexOf(" ")); + passwordDialogMessage += "<b>" + tr("Enter Password for") + "</b><br>" + gpgHint + "<br>"; + } + + if (mPasswordCache.isEmpty()) { + QString password = QInputDialog::getText(QApplication::activeWindow(), tr("Enter Password"), + passwordDialogMessage, QLineEdit::Password, + "", &result); + + if (result) mPasswordCache = password.toUtf8(); + } else result = true; + + if (result) { + +#ifndef _WIN32 + if (write(fd, mPasswordCache.data(), mPasswordCache.length()) == -1) qDebug() << "something is terribly broken"; +#else + WriteFile(hd, mPasswordCache.data(), mPasswordCache.length(), &written, 0); +#endif + returnValue = GPG_ERR_NO_ERROR; + } + +#ifndef _WIN32 + if (write(fd, "\n", 1) == -1) qDebug() << "something is terribly broken"; +#else + WriteFile(hd, "\n", 1, &written, 0); + + /* program will hang on cancel if hd not closed */ + if (!result) CloseHandle(hd); +#endif + + return returnValue; + } + + /** also from kgpgme.cpp, seems to clear password from mem */ + void GpgContext::clearPasswordCache() { + if (mPasswordCache.size() > 0) { + mPasswordCache.fill('\0'); + mPasswordCache.truncate(0); + } + } + + // error-handling + void GpgContext::checkErr(gpgme_error_t gpgmeError, const QString &comment) { + //if (gpgmeError != GPG_ERR_NO_ERROR && gpgmeError != GPG_ERR_CANCELED) { + if (gpgmeError != GPG_ERR_NO_ERROR) { + qDebug() << "[Error " << gpg_err_code(gpgmeError) + << "] Source: " << gpgme_strsource(gpgmeError) << " Description: " << gpgErrString(gpgmeError); + } + } + + void GpgContext::checkErr(gpgme_error_t gpgmeError) { + //if (gpgmeError != GPG_ERR_NO_ERROR && gpgmeError != GPG_ERR_CANCELED) { + if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { + qDebug() << "[Error " << gpg_err_code(gpgmeError) + << "] Source: " << gpgme_strsource(gpgmeError) << " Description: " << gpgErrString(gpgmeError); + } + } + + QString GpgContext::gpgErrString(gpgme_error_t err) { + return QString::fromUtf8(gpgme_strerror(err)); + } + + /** return type should be gpgme_error_t*/ + void + GpgContext::executeGpgCommand(const QStringList &arguments, const std::function<void(QProcess *)> &interactFunc) { + QEventLoop looper; + auto dialog = new WaitingDialog(tr("Processing"), nullptr); + dialog->show(); + auto *gpgProcess = new QProcess(&looper); + gpgProcess->setProcessChannelMode(QProcess::MergedChannels); + connect(gpgProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), &looper, &QEventLoop::quit); + connect(gpgProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), dialog, + &WaitingDialog::deleteLater); + connect(gpgProcess, &QProcess::errorOccurred, []() -> void { qDebug("Error in Process"); }); + connect(gpgProcess, &QProcess::errorOccurred, &looper, &QEventLoop::quit); + connect(gpgProcess, &QProcess::started, []() -> void { qDebug() << "Gpg Process Started Success"; }); + connect(gpgProcess, &QProcess::readyReadStandardOutput, [interactFunc, gpgProcess]() { + qDebug() << "Function Called"; + interactFunc(gpgProcess); + }); + gpgProcess->setProgram(info.appPath); + gpgProcess->setArguments(arguments); + gpgProcess->start(); + looper.exec(); + dialog->close(); + + } + + + /* + * if there is no '\n' before the PGP-Begin-Block, but for example a whitespace, + * GPGME doesn't recognise the Message as encrypted. This function adds '\n' + * before the PGP-Begin-Block, if missing. + */ + void GpgContext::preventNoDataErr(QByteArray *in) { + int block_start = in->indexOf(GpgConstants::PGP_CRYPT_BEGIN); + if (block_start > 0 && in->at(block_start - 1) != '\n') { + in->insert(block_start, '\n'); + } + block_start = in->indexOf(GpgConstants::PGP_SIGNED_BEGIN); + if (block_start > 0 && in->at(block_start - 1) != '\n') { + in->insert(block_start, '\n'); + } + } + + /* + * isSigned returns: + * - 0, if text isn't signed at all + * - 1, if text is partially signed + * - 2, if text is completly signed + */ + int GpgContext::textIsSigned(const QByteArray &text) { + if (text.trimmed().startsWith(GpgConstants::PGP_SIGNED_BEGIN) && + text.trimmed().endsWith(GpgConstants::PGP_SIGNED_END)) + return 2; + else if (text.contains(GpgConstants::PGP_SIGNED_BEGIN) && text.contains(GpgConstants::PGP_SIGNED_END)) + return 1; + + else return 0; + } + + QString GpgContext::beautifyFingerprint(QString fingerprint) { + uint len = fingerprint.length(); + if ((len > 0) && (len % 4 == 0)) + for (uint n = 0; 4 * (n + 1) < len; ++n) fingerprint.insert(static_cast<int>(5u * n + 4u), ' '); + return fingerprint; + } + + void GpgContext::slotRefreshKeyList() { + qDebug() << "Refreshing Keys"; + this->fetch_keys(); + emit signalKeyInfoChanged(); + } + + QString GpgContext::getGpgmeVersion() { + return {gpgme_check_version(nullptr)}; + } + + const GpgKeyList &GpgContext::getKeys() const { + return mKeyList; + } + + void GpgContext::getSigners(QVector<GpgKey> &signer, gpgme_ctx_t ctx) { + auto count = gpgme_signers_count(ctx); + signer.clear(); + for (auto i = 0; i < count; i++) { + auto key = gpgme_signers_enum(ctx, i); + auto it = mKeyMap.find(key->subkeys->keyid); + if (it == mKeyMap.end()) { + qDebug() << "Inconsistent state"; + signer.push_back(GpgKey(key)); + } else { + signer.push_back(*it.value()); + } + } + } + + void GpgContext::setSigners(const QVector<GpgKey> &keys, gpgme_ctx_t ctx) { + gpgme_signers_clear(ctx); + for (const auto &key : keys) { + if (checkIfKeyCanSign(key)) { + auto gpgmeError = gpgme_signers_add(ctx, key.key_refer); + checkErr(gpgmeError); + } + } + if (keys.length() != gpgme_signers_count(ctx)) { + qDebug() << "No All Keys Added"; + } + } + + void GpgContext::slotUpdateKeyList(const QString &key_id) { + auto it = mKeyMap.find(key_id); + if (it != mKeyMap.end()) { + gpgme_key_t new_key_refer; + auto gpgmeErr = gpgme_get_key(mCtx, key_id.toUtf8().constData(), &new_key_refer, 0); + + if (gpgme_err_code(gpgmeErr) == GPG_ERR_EOF) { + gpgmeErr = gpgme_get_key(mCtx, key_id.toUtf8().constData(), &new_key_refer, 1); + + if (gpgme_err_code(gpgmeErr) == GPG_ERR_EOF) { + throw std::runtime_error("key_id not found in key database"); + } + + } + + if (new_key_refer != nullptr) { + it.value()->swapKeyRefer(new_key_refer); + emit signalKeyInfoChanged(); + } + + } + } + + bool GpgContext::revSign(const GpgKey &key, const GpgKeySignature &signature) { + + auto signing_key = getKeyById(signature.keyid); + + auto gpgmeError = gpgme_op_revsig(mCtx, key.key_refer, + signing_key.key_refer, + signature.uid.toUtf8().constData(), 0); + if (gpg_err_code(gpgmeError) == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } + } + + gpgme_ctx_t GpgME::GpgContext::create_ctx() { + gpgme_ctx_t ctx; + err = gpgme_new(&ctx); + checkErr(err); + + gpgme_set_armor(ctx, 1); + gpgme_set_passphrase_cb(ctx, passphraseCb, this); + return ctx; + } + + +} diff --git a/src/gpg/gpg_context/GpgContextBasicOpera.cpp b/src/gpg/gpg_context/GpgContextBasicOpera.cpp new file mode 100644 index 00000000..d9bf0bdb --- /dev/null +++ b/src/gpg/gpg_context/GpgContextBasicOpera.cpp @@ -0,0 +1,315 @@ +/** + * 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 "gpg/GpgContext.h" + +/** + * Encrypt data + * @param keys keys used + * @param inBuffer input byte array + * @param outBuffer output byte array + * @param result opera result + * @return error information + */ +gpg_error_t GpgME::GpgContext::encrypt(QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, + gpgme_encrypt_result_t *result) { + + gpgme_data_t dataIn = nullptr, dataOut = nullptr; + outBuffer->resize(0); + + // gpgme_encrypt_result_t e_result; + gpgme_key_t recipients[keys.count() + 1]; + + int index = 0; + for (const auto &key : keys) recipients[index++] = key.key_refer; + + // Last entry dataIn array has to be nullptr + recipients[keys.count()] = nullptr; + + // If the last parameter isnt 0, a private copy of data is made + if (mCtx) { + err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); + checkErr(err); + if (!err) { + err = gpgme_data_new(&dataOut); + checkErr(err); + if (!err) { + err = gpgme_op_encrypt(mCtx, recipients, GPGME_ENCRYPT_ALWAYS_TRUST, dataIn, dataOut); + checkErr(err); + if (!err) { + err = readToBuffer(dataOut, outBuffer); + checkErr(err); + } + } + } + } + if (dataIn) gpgme_data_release(dataIn); + if (dataOut) gpgme_data_release(dataOut); + + if (result != nullptr) *result = gpgme_op_encrypt_result(mCtx); + return err; +} + +/** + * Decrypt data + * @param keys keys used + * @param inBuffer input byte array + * @param outBuffer output byte array + * @param result opera result + * @return error information + */ +gpgme_error_t GpgME::GpgContext::decrypt(const QByteArray &inBuffer, QByteArray *outBuffer, + gpgme_decrypt_result_t *result) { + gpgme_data_t dataIn = nullptr, dataOut = nullptr; + gpgme_decrypt_result_t m_result = nullptr; + + outBuffer->resize(0); + if (mCtx != nullptr) { + err = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_data_new(&dataOut); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_op_decrypt(mCtx, dataIn, dataOut); + m_result = gpgme_op_decrypt_result(mCtx); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) err = readToBuffer(dataOut, outBuffer); + } + } + } + + if (!settings.value("general/rememberPassword").toBool()) clearPasswordCache(); + + if (dataIn) gpgme_data_release(dataIn); + if (dataOut) gpgme_data_release(dataOut); + + if (result != nullptr) *result = m_result; + + return err; +} + +/** + * Verify data + * @param keys keys used + * @param inBuffer input byte array + * @param sigBuffer signature byte array (detected by format) + * @param result opera result + * @return error information + */ +gpgme_error_t GpgME::GpgContext::verify(QByteArray *inBuffer, QByteArray *sigBuffer, gpgme_verify_result_t *result) { + + gpgme_data_t dataIn; + gpgme_error_t gpgmeError; + gpgme_verify_result_t m_result; + + gpgmeError = gpgme_data_new_from_mem(&dataIn, inBuffer->data(), inBuffer->size(), 1); + checkErr(gpgmeError); + + if (sigBuffer != nullptr) { + gpgme_data_t sigdata; + gpgmeError = gpgme_data_new_from_mem(&sigdata, sigBuffer->data(), sigBuffer->size(), 1); + checkErr(gpgmeError); + gpgmeError = gpgme_op_verify(mCtx, sigdata, dataIn, nullptr); + } else { + gpgmeError = gpgme_op_verify(mCtx, dataIn, nullptr, dataIn); + } + + checkErr(gpgmeError); + + m_result = gpgme_op_verify_result(mCtx); + + if (result != nullptr) { + *result = m_result; + } + + return gpgmeError; +} + +/** + * Sign data + * @param keys keys used + * @param inBuffer input byte array + * @param outBuffer output byte array + * @param mode sign mode + * @param result opera result + * @return + */ +gpg_error_t GpgME::GpgContext::sign(const QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, + gpgme_sig_mode_t mode, gpgme_sign_result_t *result, bool default_ctx) { + + gpgme_error_t gpgmeError; + gpgme_data_t dataIn, dataOut; + gpgme_sign_result_t m_result; + + auto _ctx = mCtx; + + if(!default_ctx) + _ctx = create_ctx(); + + if (keys.isEmpty()) { + QMessageBox::critical(nullptr, tr("Key Selection"), tr("No Private Key Selected")); + return false; + } + + // Set Singers of this opera + setSigners(keys, _ctx); + + gpgmeError = gpgme_data_new_from_mem(&dataIn, inBuffer.data(), inBuffer.size(), 1); + checkErr(gpgmeError); + gpgmeError = gpgme_data_new(&dataOut); + checkErr(gpgmeError); + + /** + `GPGME_SIG_MODE_NORMAL' + A normal signature is made, the output includes the plaintext + and the signature. + + `GPGME_SIG_MODE_DETACH' + A detached signature is made. + + `GPGME_SIG_MODE_CLEAR' + A clear text signature is made. The ASCII armor and text + mode settings of the context are ignored. + */ + + gpgmeError = gpgme_op_sign(_ctx, dataIn, dataOut, mode); + checkErr(gpgmeError); + + if (gpgmeError == GPG_ERR_CANCELED) return false; + + if (gpgmeError != GPG_ERR_NO_ERROR) { + QMessageBox::critical(nullptr, tr("Error in signing:"), QString::fromUtf8(gpgme_strerror(gpgmeError))); + return false; + } + + if(default_ctx) + m_result = gpgme_op_sign_result(_ctx); + else m_result = nullptr; + + if (result != nullptr) *result = m_result; + + if(!default_ctx) gpgme_release(_ctx); + + gpgmeError = readToBuffer(dataOut, outBuffer); + checkErr(gpgmeError); + + gpgme_data_release(dataIn); + gpgme_data_release(dataOut); + + // Of no use yet + if (!settings.value("general/rememberPassword").toBool()) clearPasswordCache(); + + return gpgmeError; +} + +/** + * Encrypt and sign data + * @param keys keys used + * @param inBuffer input byte array + * @param outBuffer output byte array + * @param encr_result encrypt opera result + * @param sign_result sign opera result + * @return + */ +gpgme_error_t +GpgME::GpgContext::encryptSign(QVector<GpgKey> &keys, QVector<GpgKey> &signers, const QByteArray &inBuffer, + QByteArray *outBuffer, gpgme_encrypt_result_t *encr_result, + gpgme_sign_result_t *sign_result) { + gpgme_data_t data_in = nullptr, data_out = nullptr; + outBuffer->resize(0); + + setSigners(signers, mCtx); + + //gpgme_encrypt_result_t e_result; + gpgme_key_t recipients[keys.count() + 1]; + + // set key for user + int index = 0; + for (const auto &key : keys) recipients[index++] = key.key_refer; + + // Last entry dataIn array has to be nullptr + recipients[keys.count()] = nullptr; + + // If the last parameter isnt 0, a private copy of data is made + if (mCtx != nullptr) { + err = gpgme_data_new_from_mem(&data_in, inBuffer.data(), inBuffer.size(), 1); + if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_data_new(&data_out); + if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_op_encrypt_sign(mCtx, recipients, GPGME_ENCRYPT_ALWAYS_TRUST, data_in, data_out); + if (encr_result != nullptr) + *encr_result = gpgme_op_encrypt_result(mCtx); + if (sign_result != nullptr) + *sign_result = gpgme_op_sign_result(mCtx); + if (gpg_err_code(err) == GPG_ERR_NO_ERROR) { + err = readToBuffer(data_out, outBuffer); + } + } + } + } + + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) checkErr(err); + + if (data_in) gpgme_data_release(data_in); + if (data_out) gpgme_data_release(data_out); + + return err; +} + +/** + * Decrypt and verify data + * @param inBuffer input byte array + * @param outBuffer output byte array + * @param decrypt_result decrypt opera result + * @param verify_result verify opera result + * @return error info + */ +gpgme_error_t GpgME::GpgContext::decryptVerify(const QByteArray &inBuffer, QByteArray *outBuffer, + gpgme_decrypt_result_t *decrypt_result, + gpgme_verify_result_t *verify_result) { + gpgme_data_t data_in = nullptr, data_out = nullptr; + + outBuffer->resize(0); + if (mCtx != nullptr) { + err = gpgme_data_new_from_mem(&data_in, inBuffer.data(), inBuffer.size(), 1); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_data_new(&data_out); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + err = gpgme_op_decrypt_verify(mCtx, data_in, data_out); + if (decrypt_result != nullptr) + *decrypt_result = gpgme_op_decrypt_result(mCtx); + if (verify_result != nullptr) + *verify_result = gpgme_op_verify_result(mCtx); + if (gpgme_err_code(err) == GPG_ERR_NO_ERROR) { + err = readToBuffer(data_out, outBuffer); + } + } + } + } + + if (!settings.value("general/rememberPassword").toBool()) clearPasswordCache(); + + if (data_in) gpgme_data_release(data_in); + if (data_out) gpgme_data_release(data_out); + + return err; +} diff --git a/src/gpg/gpg_context/GpgContextKeyInfo.cpp b/src/gpg/gpg_context/GpgContextKeyInfo.cpp new file mode 100644 index 00000000..f6942e4e --- /dev/null +++ b/src/gpg/gpg_context/GpgContextKeyInfo.cpp @@ -0,0 +1,110 @@ +/** + * 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 "gpg/GpgContext.h" + +/** + * check if key can sign(actually) + * @param key target key + * @return if key sign + */ +bool GpgME::GpgContext::checkIfKeyCanSign(const GpgKey &key) { + if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { + return subkey.secret && subkey.can_sign && !subkey.disabled && !subkey.revoked && !subkey.expired; + })) + return true; + else return false; +} + +/** + * check if key can certify(actually) + * @param key target key + * @return if key certify + */ +bool GpgME::GpgContext::checkIfKeyCanCert(const GpgKey &key) { + return key.has_master_key && !key.expired && !key.revoked && !key.disabled; +} + +/** + * check if key can authenticate(actually) + * @param key target key + * @return if key authenticate + */ +bool GpgME::GpgContext::checkIfKeyCanAuth(const GpgKey &key) { + if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { + return subkey.secret && subkey.can_authenticate && !subkey.disabled && !subkey.revoked && !subkey.expired; + })) + return true; + else return false; +} + +/** + * check if key can encrypt(actually) + * @param key target key + * @return if key encrypt + */ +bool GpgME::GpgContext::checkIfKeyCanEncr(const GpgKey &key) { + if (std::any_of(key.subKeys.begin(), key.subKeys.end(), [](const GpgSubKey &subkey) -> bool { + return subkey.can_encrypt && !subkey.disabled && !subkey.revoked && !subkey.expired; + })) + return true; + else return false; +} + +/** + * Get target key + * @param fpr master key's fingerprint + * @return the key + */ +GpgKey GpgME::GpgContext::getKeyByFpr(const QString &fpr) { + for (const auto &key : mKeyList) { + if (key.fpr == fpr) return key; + else + for (auto &subkey : key.subKeys) { + if (subkey.fpr == fpr) return key; + } + } + return GpgKey(nullptr); +} + + +/** + * Get target key + * @param id master key's id + * @return the key + */ +GpgKey GpgME::GpgContext::getKeyById(const QString &id) { + + for (const auto &key : mKeyList) { + if (key.id == id) return key; + else { + auto sub_keys = key.subKeys; + for (const auto &subkey : sub_keys) { + if (subkey.id == id) return key; + } + } + } + + return GpgKey(nullptr); +} diff --git a/src/gpg/gpg_context/GpgContextKeyOpera.cpp b/src/gpg/gpg_context/GpgContextKeyOpera.cpp new file mode 100644 index 00000000..a224231d --- /dev/null +++ b/src/gpg/gpg_context/GpgContextKeyOpera.cpp @@ -0,0 +1,410 @@ +/** + * 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 "gpg/GpgContext.h" + +/** + * Import key pair + * @param inBuffer input byte array + * @return Import information + */ +GpgImportInformation GpgME::GpgContext::importKey(QByteArray inBuffer) { + auto *importInformation = new GpgImportInformation(); + err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1); + checkErr(err); + err = gpgme_op_import(mCtx, in); + gpgme_import_result_t result; + + result = gpgme_op_import_result(mCtx); + + if (result->unchanged) importInformation->unchanged = result->unchanged; + if (result->considered) importInformation->considered = result->considered; + if (result->no_user_id) importInformation->no_user_id = result->no_user_id; + if (result->imported) importInformation->imported = result->imported; + if (result->imported_rsa) importInformation->imported_rsa = result->imported_rsa; + if (result->unchanged) importInformation->unchanged = result->unchanged; + if (result->new_user_ids) importInformation->new_user_ids = result->new_user_ids; + if (result->new_sub_keys) importInformation->new_sub_keys = result->new_sub_keys; + if (result->new_signatures) importInformation->new_signatures = result->new_signatures; + if (result->new_revocations) importInformation->new_revocations = result->new_revocations; + if (result->secret_read) importInformation->secret_read = result->secret_read; + if (result->secret_imported) importInformation->secret_imported = result->secret_imported; + if (result->secret_unchanged) importInformation->secret_unchanged = result->secret_unchanged; + if (result->not_imported) importInformation->not_imported = result->not_imported; + + gpgme_import_status_t status = result->imports; + while (status != nullptr) { + GpgImportedKey key; + key.importStatus = static_cast<int>(status->status); + key.fpr = status->fpr; + importInformation->importedKeys.emplace_back(key); + status = status->next; + } + checkErr(err); + emit signalKeyDBChanged(); + gpgme_data_release(in); + return *importInformation; +} + +/** + * Generate a new key pair + * @param params key generation args + * @return error information + */ +gpgme_error_t GpgME::GpgContext::generateKey(GenKeyInfo *params) { + + auto userid_utf8 = params->getUserid().toUtf8(); + const char *userid = userid_utf8.constData(); + auto algo_utf8 = (params->getAlgo() + params->getKeySizeStr()).toUtf8(); + const char *algo = algo_utf8.constData(); + unsigned long expires = QDateTime::currentDateTime().secsTo(params->getExpired()); + 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; + + err = gpgme_op_createkey(mCtx, userid, algo, 0, expires, nullptr, flags); + + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { + checkErr(err); + return err; + } else { + emit signalKeyDBChanged(); + return err; + } +} + +/** + * Export Key + * @param uidList key ids + * @param outBuffer output byte array + * @return if success + */ +bool GpgME::GpgContext::exportKeys(QStringList *uidList, QByteArray *outBuffer) { + gpgme_data_t dataOut = nullptr; + outBuffer->resize(0); + + if (uidList->count() == 0) { + QMessageBox::critical(nullptr, "Export Keys Error", "No Keys Selected"); + return false; + } + + // Alleviate another crash problem caused by an unknown array out-of-bounds access + gpgme_ctx_t ctx = create_ctx(); + + for (int i = 0; i < uidList->count(); i++) { + err = gpgme_data_new(&dataOut); + checkErr(err); + + err = gpgme_op_export(ctx, uidList->at(i).toUtf8().constData(), 0, dataOut); + checkErr(err); + + qDebug() << "exportKeys read_bytes" << gpgme_data_seek(dataOut, 0, SEEK_END); + + err = readToBuffer(dataOut, outBuffer); + checkErr(err); + gpgme_data_release(dataOut); + } + + gpgme_release(ctx); + + return true; +} + +/** + * Get and store all key pairs info + */ +void GpgME::GpgContext::fetch_keys() { + + gpgme_error_t gpgmeError; + + gpgme_key_t key; + + qDebug() << "Clear List and Map"; + + mKeyList.clear(); + mKeyMap.clear(); + + auto &keys = mKeyList; + auto &keys_map = mKeyMap; + + qDebug() << "Set Key Listing Mode"; + + gpgmeError = gpgme_set_keylist_mode(mCtx, + GPGME_KEYLIST_MODE_LOCAL + | GPGME_KEYLIST_MODE_WITH_SECRET + | GPGME_KEYLIST_MODE_SIGS + | GPGME_KEYLIST_MODE_SIG_NOTATIONS + | GPGME_KEYLIST_MODE_WITH_TOFU); + if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { + checkErr(gpgmeError); + return; + } + + qDebug() << "Operate KeyList Start"; + + gpgmeError = gpgme_op_keylist_start(mCtx, nullptr, 0); + if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { + checkErr(gpgmeError); + return; + } + + qDebug() << "Start Loop"; + + while ((gpgmeError = gpgme_op_keylist_next(mCtx, &key)) == GPG_ERR_NO_ERROR) { + if (!key->subkeys) + continue; + + qDebug() << "Append Key" << key->subkeys->keyid; + + keys.emplace_back(key); + keys_map.insert(keys.back().id, &keys.back()); + gpgme_key_unref(key); + } + + + if (gpg_err_code(gpgmeError) != GPG_ERR_EOF) { + checkErr(gpgmeError); + return; + } + + gpgmeError = gpgme_op_keylist_end(mCtx); + if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) { + checkErr(gpgmeError); + return; + } + + qDebug() << "Operate KeyList End"; + + mKeyList = keys; +} + +/** + * Delete keys + * @param uidList key ids + */ +void GpgME::GpgContext::deleteKeys(QStringList *uidList) { + + gpgme_error_t error; + gpgme_key_t key; + + for (const auto &tmp : *uidList) { + + error = gpgme_op_keylist_start(mCtx, tmp.toUtf8().constData(), 0); + if (error != GPG_ERR_NO_ERROR) { + checkErr(error); + continue; + } + + error = gpgme_op_keylist_next(mCtx, &key); + if (error != GPG_ERR_NO_ERROR) { + checkErr(error); + continue; + } + + error = gpgme_op_keylist_end(mCtx); + if (error != GPG_ERR_NO_ERROR) { + checkErr(error); + continue; + } + + error = gpgme_op_delete(mCtx, key, 1); + if (error != GPG_ERR_NO_ERROR) { + checkErr(error); + continue; + } + + } + emit signalKeyDBChanged(); +} + +/** + * Export keys + * @param keys keys used + * @param outBuffer output byte array + * @return if success + */ +bool GpgME::GpgContext::exportKeys(const QVector<GpgKey> &keys, QByteArray &outBuffer) { + gpgme_data_t data_out = nullptr; + outBuffer.resize(0); + + if (keys.empty()) { + QMessageBox::critical(nullptr, "Export Keys Error", "No Keys Selected"); + return false; + } + + for (const auto &key : keys) { + err = gpgme_data_new(&data_out); + checkErr(err); + + err = gpgme_op_export(mCtx, key.id.toUtf8().constData(), 0, data_out); + checkErr(err); + + gpgme_data_seek(data_out, 0, SEEK_END); + + err = readToBuffer(data_out, &outBuffer); + checkErr(err); + gpgme_data_release(data_out); + } + + return true; +} + +/** + * Set the expire date and time of a key pair(actually the master key) or subkey + * @param key target key pair + * @param subkey null if master key + * @param expires date and time + * @return if successful + */ +bool GpgME::GpgContext::setExpire(const GpgKey &key, const GpgSubKey *subkey, QDateTime *expires) { + unsigned long expires_time = 0; + if (expires != nullptr) { + qDebug() << "Expire Datetime" << expires->toString(); + expires_time = QDateTime::currentDateTime().secsTo(*expires); + } + + const char *subfprs = nullptr; + + if (subkey != nullptr) subfprs = subkey->fpr.toUtf8().constData(); + + auto gpgmeError = gpgme_op_setexpire(mCtx, key.key_refer, + expires_time, subfprs, 0); + if (gpgmeError == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } +} + +/** + * Export the secret key of a key pair(including subkeys) + * @param key target key pair + * @param outBuffer output byte array + * @return if successful + */ +bool GpgME::GpgContext::exportSecretKey(const GpgKey &key, QByteArray *outBuffer) { + qDebug() << "Export Secret Key" << key.id; + gpgme_key_t target_key[2] = { + key.key_refer, + nullptr + }; + + gpgme_data_t dataOut; + gpgme_data_new(&dataOut); + + // export private key to outBuffer + gpgme_error_t error = gpgme_op_export_keys(mCtx, target_key, GPGME_EXPORT_MODE_SECRET, dataOut); + + if (gpgme_err_code(error) != GPG_ERR_NO_ERROR) { + checkErr(error); + gpgme_data_release(dataOut); + return false; + } + + readToBuffer(dataOut, outBuffer); + gpgme_data_release(dataOut); + return true; +} + +/** + * Sign a key pair(actually a certain uid) + * @param target target key pair + * @param uid target + * @param expires expire date and time of the signature + * @return if successful + */ +bool GpgME::GpgContext::signKey(const GpgKey &target, const QVector<GpgKey> &keys, const QString &uid, + const QDateTime *expires) { + + setSigners(keys, mCtx); + + unsigned int flags = 0; + + unsigned int expires_time_t = 0; + if (expires == nullptr) flags |= GPGME_KEYSIGN_NOEXPIRE; + else expires_time_t = QDateTime::currentDateTime().secsTo(*expires); + + auto gpgmeError = + gpgme_op_keysign(mCtx, target.key_refer, uid.toUtf8().constData(), expires_time_t, flags); + + if (gpgmeError == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(target.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } +} + +/** + * Generate revoke cert of a key pair + * @param key target key pair + * @param outputFileName out file name(path) + * @return the process doing this job + */ +void GpgME::GpgContext::generateRevokeCert(const GpgKey &key, const QString &outputFileName) { + executeGpgCommand({ + "--command-fd", + "0", + "--status-fd", + "1", + //"--no-tty", + "-o", + outputFileName, + "--gen-revoke", + key.fpr + }, + [](QProcess *proc) -> void { + qDebug() << "Function Called" << proc; + // Code From Gpg4Win + while (proc->canReadLine()) { + const QString line = QString::fromUtf8(proc->readLine()).trimmed(); + 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"); + } + } + // Code From Gpg4Win + } + ); +} diff --git a/src/gpg/gpg_context/GpgContextSubkeyOpera.cpp b/src/gpg/gpg_context/GpgContextSubkeyOpera.cpp new file mode 100644 index 00000000..10243f5e --- /dev/null +++ b/src/gpg/gpg_context/GpgContextSubkeyOpera.cpp @@ -0,0 +1,61 @@ +/** + * 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 "gpg/GpgContext.h" + +/** + * Generate a new subkey of a certain key pair + * @param key target key pair + * @param params opera args + * @return error info + */ +gpgme_error_t GpgME::GpgContext::generateSubkey(const GpgKey &key, GenKeyInfo *params) { + + if (!params->isSubKey()) return GPG_ERR_CANCELED; + + auto algo_utf8 = (params->getAlgo() + params->getKeySizeStr()).toUtf8(); + const char *algo = algo_utf8.constData(); + unsigned long expires = QDateTime::currentDateTime().secsTo(params->getExpired()); + 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; + + flags |= GPGME_CREATE_NOPASSWD; + + + auto gpgmeError = gpgme_op_createsubkey(mCtx, key.key_refer, + algo, 0, expires, flags); + if (gpgme_err_code(gpgmeError) == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return gpgmeError; + } else { + checkErr(gpgmeError); + return gpgmeError; + } +} + diff --git a/src/gpg/gpg_context/GpgContextUIDOpera.cpp b/src/gpg/gpg_context/GpgContextUIDOpera.cpp new file mode 100644 index 00000000..b96f5f8f --- /dev/null +++ b/src/gpg/gpg_context/GpgContextUIDOpera.cpp @@ -0,0 +1,80 @@ +/** + * 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 "gpg/GpgContext.h" + +/** + * create a new uid in certain key pair + * @param key target key pair + * @param uid uid args + * @return if successful + */ +bool GpgME::GpgContext::addUID(const GpgKey &key, const GpgUID &uid) { + QString userid = QString("%1 (%3) <%2>").arg(uid.name, uid.email, uid.comment); + auto gpgmeError = gpgme_op_adduid(mCtx, key.key_refer, userid.toUtf8().constData(), 0); + if (gpgmeError == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } + +} + +/** + * Revoke(Delete) UID from certain key pair + * @param key target key pair + * @param uid target uid + * @return if successful + */ +bool GpgME::GpgContext::revUID(const GpgKey &key, const GpgUID &uid) { + auto gpgmeError = gpgme_op_revuid(mCtx, key.key_refer, uid.uid.toUtf8().constData(), 0); + if (gpgmeError == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } +} + +/** + * Set one of a uid of a key pair as primary + * @param key target key pair + * @param uid target uid + * @return if successful + */ +bool GpgME::GpgContext::setPrimaryUID(const GpgKey &key, const GpgUID &uid) { + auto gpgmeError = gpgme_op_set_uid_flag(mCtx, key.key_refer, + uid.uid.toUtf8().constData(), "primary", nullptr); + if (gpgmeError == GPG_ERR_NO_ERROR) { + emit signalKeyUpdated(key.id); + return true; + } else { + checkErr(gpgmeError); + return false; + } +} + diff --git a/src/gpg/result_analyse/DecryptResultAnalyse.cpp b/src/gpg/result_analyse/DecryptResultAnalyse.cpp index 7eda4027..b4d0b14f 100644 --- a/src/gpg/result_analyse/DecryptResultAnalyse.cpp +++ b/src/gpg/result_analyse/DecryptResultAnalyse.cpp @@ -1,59 +1,86 @@ -// -// Created by eric on 2021/6/9. -// +/** + * 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 "gpg/result_analyse/DecryptResultAnalyse.h" DecryptResultAnalyse::DecryptResultAnalyse(GpgME::GpgContext *ctx, gpgme_error_t error, gpgme_decrypt_result_t result) : mCtx(ctx) { - stream << "Decrypt Report: " << Qt::endl << "-----" << Qt::endl; + stream << tr("[#] Decrypt Operation "); if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) { - stream << "Status: Success" << Qt::endl; + stream << tr("[Success]") << Qt::endl; } else { + stream << tr("[Failed] ") << gpgme_strerror(error) << Qt::endl; setStatus(-1); - stream << "Status: " << gpgme_strerror(error) << Qt::endl; - - if (result != nullptr && result->unsupported_algorithm != nullptr) - stream << "Unsupported algo: " << result->unsupported_algorithm << Qt::endl; + if (result != nullptr && result->unsupported_algorithm != nullptr) { + stream << "------------>" << Qt::endl; + stream << tr("Unsupported Algo: ") << result->unsupported_algorithm << Qt::endl; + } } - if(result != nullptr) { - if (result->file_name != nullptr) - stream << "File name: " << result->file_name << Qt::endl; - stream << Qt::endl; + if (result != nullptr && result->recipients != nullptr) { + stream << "------------>" << Qt::endl; + if (result->file_name != nullptr) { + stream << tr("File Name: ") << result->file_name << Qt::endl; + stream << Qt::endl; + } auto reci = result->recipients; if (reci != nullptr) - stream << "Recipient(s): " << Qt::endl; + stream << tr("Recipient(s): ") << Qt::endl; while (reci != nullptr) { printReci(stream, reci); reci = reci->next; } + stream << "<------------" << Qt::endl; } - stream << "-----" << Qt::endl << Qt::endl; + stream << Qt::endl; } bool DecryptResultAnalyse::printReci(QTextStream &stream, gpgme_recipient_t reci) { bool keyFound = true; - stream << QApplication::tr(">Recipient: "); + stream << QApplication::tr(" {>} Recipient: "); - try { - auto key = mCtx->getKeyById(reci->keyid); + auto key = mCtx->getKeyById(reci->keyid); + if(key.good) { stream << key.name; if (!key.email.isEmpty()) { stream << "<" << key.email << ">"; } - } catch(std::runtime_error &ignored) { + } else { stream << "<Unknown>"; setStatus(0); keyFound = false; } + stream << Qt::endl; - stream << "Public algo: " << gpgme_pubkey_algo_name(reci->pubkey_algo) << Qt::endl << Qt::endl; + stream << tr(" Keu ID: ") << reci->keyid << Qt::endl; + stream << tr(" Public Algo: ") << gpgme_pubkey_algo_name(reci->pubkey_algo) << Qt::endl; + return keyFound; } diff --git a/src/gpg/result_analyse/EncryptResultAnalyse.cpp b/src/gpg/result_analyse/EncryptResultAnalyse.cpp index dfcad1ac..1ba685c9 100644 --- a/src/gpg/result_analyse/EncryptResultAnalyse.cpp +++ b/src/gpg/result_analyse/EncryptResultAnalyse.cpp @@ -1,33 +1,58 @@ -// -// Created by eric on 2021/6/9. -// +/** + * 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 "gpg/result_analyse/EncryptResultAnalyse.h" EncryptResultAnalyse::EncryptResultAnalyse(gpgme_error_t error, gpgme_encrypt_result_t result) { - stream << "# Encrypt Report: " << Qt::endl << "-----" << Qt::endl; + qDebug() << "Start Encrypt Result Analyse"; - if(gpgme_err_code(error) == GPG_ERR_NO_ERROR) { - stream << "Status: Encrypt Success" << Qt::endl; - } + stream << "[#] Encrypt Operation "; + + if(gpgme_err_code(error) == GPG_ERR_NO_ERROR) + stream << "[Success]" << Qt::endl; else { - stream << "Status:" << gpgme_strerror(error) << Qt::endl; + stream << "[Failed] " << gpgme_strerror(error) << Qt::endl; setStatus(-1); + } + + if(!~status) { + stream << "------------>" << Qt::endl; if (result != nullptr) { - stream << "Invalid Recipients: " << Qt::endl; + stream << tr("Invalid Recipients: ") << Qt::endl; auto inv_reci = result->invalid_recipients; while (inv_reci != nullptr) { - stream << "Fingerprint: " << inv_reci->fpr << Qt::endl; - stream << "Reason: " << gpgme_strerror(inv_reci->reason) << Qt::endl; + stream << tr("Fingerprint: ") << inv_reci->fpr << Qt::endl; + stream << tr("Reason: ") << gpgme_strerror(inv_reci->reason) << Qt::endl; stream << Qt::endl; inv_reci = inv_reci->next; } } + stream << "<------------" << Qt::endl; } - stream << "-----" << Qt::endl; stream << Qt::endl; } diff --git a/src/gpg/result_analyse/ResultAnalyse.cpp b/src/gpg/result_analyse/ResultAnalyse.cpp index 960c24cd..7186d21a 100644 --- a/src/gpg/result_analyse/ResultAnalyse.cpp +++ b/src/gpg/result_analyse/ResultAnalyse.cpp @@ -1,6 +1,26 @@ -// -// Created by eric on 2021/6/8. -// +/** + * 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 "gpg/result_analyse/ResultAnalyse.h" diff --git a/src/gpg/result_analyse/SignResultAnalyse.cpp b/src/gpg/result_analyse/SignResultAnalyse.cpp index fac598d5..10d76678 100644 --- a/src/gpg/result_analyse/SignResultAnalyse.cpp +++ b/src/gpg/result_analyse/SignResultAnalyse.cpp @@ -24,58 +24,73 @@ #include "gpg/result_analyse/SignResultAnalyse.h" -SignResultAnalyse::SignResultAnalyse(gpgme_error_t error, gpgme_sign_result_t result) { +SignResultAnalyse::SignResultAnalyse(GpgME::GpgContext *ctx, gpgme_error_t error, gpgme_sign_result_t result) { - stream << "# Sign Report: " << Qt::endl - << "-----" << Qt::endl; - stream << "Status: " << gpgme_strerror(error) << Qt::endl << Qt::endl; + qDebug() << "Start Sign Result Analyse"; - if(gpg_err_code(error) != GPG_ERR_NO_ERROR) { + stream << tr("[#] Sign Operation "); + + if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) + stream << tr("[Success]") << Qt::endl; + else { + stream << tr("[Failed] ") << gpgme_strerror(error) << Qt::endl; setStatus(-1); } - if(result != nullptr) { + if (result != nullptr && (result->signatures != nullptr || result->invalid_signers != nullptr)) { + qDebug() << "Sign Result Analyse Getting Result"; + stream << "------------>" << Qt::endl; auto new_sign = result->signatures; while (new_sign != nullptr) { - stream << "> A New Signature: " << Qt::endl; + stream << tr("[>] New Signature: ") << Qt::endl; + + qDebug() << "Signers Fingerprint: " << new_sign->fpr; - stream << "Sign mode: "; + stream << tr(" Sign Mode: "); if (new_sign->type == GPGME_SIG_MODE_NORMAL) - stream << "Normal"; + stream << tr("Normal"); else if (new_sign->type == GPGME_SIG_MODE_CLEAR) - stream << "Clear"; + stream << tr("Clear"); else if (new_sign->type == GPGME_SIG_MODE_DETACH) - stream << "Detach"; + stream << tr("Detach"); stream << Qt::endl; - stream << "Public key algo: " << gpgme_pubkey_algo_name(new_sign->pubkey_algo) << Qt::endl; - stream << "Hash algo: " << gpgme_hash_algo_name(new_sign->hash_algo) << Qt::endl; - stream << "Date of signature: " << QDateTime::fromTime_t(new_sign->timestamp).toString() << Qt::endl; + GpgKey singerKey = ctx->getKeyByFpr(new_sign->fpr); + if(singerKey.good) { + stream << tr(" Signer: ") << singerKey.uids.first().uid << Qt::endl; + } else { + stream << tr(" Signer: ") << tr("<unknown>") << Qt::endl; + } + stream << tr(" Public Key Algo: ") << gpgme_pubkey_algo_name(new_sign->pubkey_algo) << Qt::endl; + stream << tr(" Hash Algo: ") << gpgme_hash_algo_name(new_sign->hash_algo) << Qt::endl; + stream << tr(" Date & Time: ") << QDateTime::fromTime_t(new_sign->timestamp).toString() << Qt::endl; stream << Qt::endl; new_sign = new_sign->next; } + qDebug() << "Sign Result Analyse Getting Invalid Signer"; + auto invalid_signer = result->invalid_signers; if (invalid_signer != nullptr) - stream << "Invalid Signers: " << Qt::endl; + stream << tr("Invalid Signers: ") << Qt::endl; while (invalid_signer != nullptr) { setStatus(0); - stream << "Fingerprint: " << invalid_signer->fpr << Qt::endl; - stream << "Reason: " << gpgme_strerror(invalid_signer->reason) << Qt::endl; + stream << tr("[>] Signer: ") << Qt::endl; + stream << tr(" Fingerprint: ") << invalid_signer->fpr << Qt::endl; + stream << tr(" Reason: ") << gpgme_strerror(invalid_signer->reason) << Qt::endl; stream << Qt::endl; invalid_signer = invalid_signer->next; } + stream << "<------------" << Qt::endl; } - stream << "-----" << Qt::endl << Qt::endl; - } diff --git a/src/gpg/result_analyse/VerifyResultAnalyse.cpp b/src/gpg/result_analyse/VerifyResultAnalyse.cpp index 59c7e3ef..75e07d33 100644 --- a/src/gpg/result_analyse/VerifyResultAnalyse.cpp +++ b/src/gpg/result_analyse/VerifyResultAnalyse.cpp @@ -1,6 +1,26 @@ -// -// Created by eric on 2021/6/7. -// +/** + * 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 "GpgFrontend.h" #include "gpg/result_analyse/VerifyResultAnalyse.h" @@ -8,23 +28,31 @@ VerifyResultAnalyse::VerifyResultAnalyse(GpgME::GpgContext *ctx, gpgme_error_t error, gpgme_verify_result_t result) : mCtx(ctx) { - stream << "# Verify Report: " << Qt::endl << "-----" << Qt::endl; - stream << "Status: " << gpgme_strerror(error) << Qt::endl; + qDebug() << "Verify Result Analyse Started"; - if(result != nullptr) { + stream << tr("[#] Verify Operation "); + if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) + stream << tr("[Success]") << Qt::endl; + else { + stream << tr("[Failed] ") << gpgme_strerror(error) << Qt::endl; + setStatus(-1); + } + + + if (result != nullptr && result->signatures) { + stream << "------------>" << Qt::endl; auto sign = result->signatures; if (sign == nullptr) { - stream << "> Not Signature Found" << Qt::endl; + stream << "[>] Not Signature Found" << Qt::endl; setStatus(0); return; } + stream << "[>] Signed On " << QDateTime::fromTime_t(sign->timestamp).toString() << Qt::endl; - stream << "> It was Signed ON " << QDateTime::fromTime_t(sign->timestamp).toString() << Qt::endl; - - stream << Qt::endl << "> It Contains:" << Qt::endl; + stream << Qt::endl << "[>] Signatures:" << Qt::endl; bool canContinue = true; @@ -63,13 +91,11 @@ VerifyResultAnalyse::VerifyResultAnalyse(GpgME::GpgContext *ctx, gpgme_error_t e if (sign->summary & GPGME_SIGSUM_VALID) { stream << QApplication::tr("Signature Fully Valid.") << Qt::endl; } else { - stream << QApplication::tr("Signature NOT Fully Valid.") << Qt::endl; + stream << QApplication::tr("Signature Not Fully Valid.") << Qt::endl; } if (!(sign->status & GPGME_SIGSUM_KEY_MISSING)) { - if (!printSigner(stream, sign)) { - setStatus(0); - } + if (!printSigner(stream, sign)) setStatus(0); } else { stream << QApplication::tr("Key is NOT present with ID 0x") << QString(sign->fpr) << Qt::endl; } @@ -117,26 +143,26 @@ VerifyResultAnalyse::VerifyResultAnalyse(GpgME::GpgContext *ctx, gpgme_error_t e stream << Qt::endl; sign = sign->next; } + stream << "<------------" << Qt::endl; } - - stream << "-----" << Qt::endl; - stream << Qt::endl; } bool VerifyResultAnalyse::printSigner(QTextStream &stream, gpgme_signature_t sign) { bool keyFound = true; - stream << QApplication::tr("Signed By: "); auto key = mCtx->getKeyByFpr(sign->fpr); - if(!key.good) { - stream << "<Unknown>"; + + key = mCtx->getKeyByFpr(sign->fpr); + + if (!key.good) { + stream << tr(" Signed By: ") << tr("<unknown>") << Qt::endl; setStatus(0); keyFound = false; + } else { + stream << tr(" Signed By: ") << key.uids.first().uid << Qt::endl; } - stream << key.name; - if (!key.email.isEmpty()) { - stream << "<" << key.email << ">"; - } + stream << tr(" Public Key Algo: ") << gpgme_pubkey_algo_name(sign->pubkey_algo) << Qt::endl; + stream << tr(" Hash Algo: ") << gpgme_hash_algo_name(sign->hash_algo) << Qt::endl; + stream << tr(" Date & Time: ") << QDateTime::fromTime_t(sign->timestamp).toString() << Qt::endl; stream << Qt::endl; return keyFound; - }
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ff3b0bb4..1ec264e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,12 +43,14 @@ int main(int argc, char *argv[]) { // unicode in source QTextCodec::setCodecForLocale(QTextCodec::codecForName("utf-8")); - // css +#if OS_PLATFORM == WINDOWS + // load css file QFile file(RESOURCE_DIR(qApp->applicationDirPath()) + "/css/default.qss"); file.open(QFile::ReadOnly); QString styleSheet = QLatin1String(file.readAll()); qApp->setStyleSheet(styleSheet); file.close(); +#endif /** * internationalisation. loop to restart mainwindow diff --git a/src/server/BaseAPI.cpp b/src/server/BaseAPI.cpp new file mode 100644 index 00000000..be388df9 --- /dev/null +++ b/src/server/BaseAPI.cpp @@ -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]> starting on May 12, 2021. + * + */ + +#include "server/BaseAPI.h" +#include "rapidjson/writer.h" + +BaseAPI::BaseAPI(ComUtils::ServiceType serviceType) : utils(new ComUtils(nullptr)), + reqUrl(utils->getUrl(serviceType)), request(reqUrl) { + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); +} + +BaseAPI::~BaseAPI() { + utils->deleteLater(); +} + +QNetworkReply *BaseAPI::send_json_data() { + rapidjson::StringBuffer sb; + rapidjson::Writer<rapidjson::StringBuffer> writer(sb); + document.Accept(writer); + + QByteArray postData(sb.GetString()); + qDebug() << "postData" << QString::fromUtf8(postData); + + auto reply = utils->getNetworkManager().post(request, postData); + + while (reply->isRunning()) QApplication::processEvents(); + + QByteArray replyData = reply->readAll().constData(); + if (utils->checkServerReply(replyData)) { + good = true, deal_reply(); + } + + return reply; +} + +void BaseAPI::start() { + construct_json(); + send_json_data()->deleteLater(); +} + +void BaseAPI::refresh() { + document.Clear(); + utils->clear(); + good = false; +} + +bool BaseAPI::result() const { + return good; +} diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 00000000..423e9d1e --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,7 @@ +aux_source_directory(./api SERVER_SOURCE) +aux_source_directory(. SERVER_SOURCE) + +add_library(server STATIC ${SERVER_SOURCE}) + +target_link_libraries(server + Qt5::Network Qt5::Widgets Qt5::Core) diff --git a/src/server/ComUtils.cpp b/src/server/ComUtils.cpp new file mode 100644 index 00000000..01be4ea7 --- /dev/null +++ b/src/server/ComUtils.cpp @@ -0,0 +1,197 @@ +/** + * 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 "server/ComUtils.h" + +/** + * check server reply if it can parse into a json object + * @param reply reply data in byte array + * @return if successful + */ +bool ComUtils::checkServerReply(const QByteArray &reply) { + + if(reply.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("Nothing Reply. Please check the Internet connection.")); + return false; + } + + qDebug() << "Reply" << reply; + + /** + * Server Reply Format(Except Timeout) + * { + * "status": 200, + * "msg": "OK", + * "timestamp": 1628652783895 + * "data" : { + * ... + * } + * } + */ + + // check if reply is a json object + if (replyDoc.Parse(reply).HasParseError() || !replyDoc.IsObject()) { + QMessageBox::critical(this, tr("Error"), tr("Unknown Error")); + return false; + } + + // check status(int) and message(string) + if (replyDoc.HasMember("status") && replyDoc.HasMember("msg") && replyDoc.HasMember("timestamp") && + replyDoc.HasMember("data") + && replyDoc["status"].IsNumber() && replyDoc["msg"].IsString() && replyDoc["timestamp"].IsNumber() && + replyDoc["data"].IsObject()) { + + int status = replyDoc["status"].GetInt(); + QDateTime time; + time.setMSecsSinceEpoch(replyDoc["timestamp"].GetInt64()); + auto message = replyDoc["msg"].GetString(); + dataVal = replyDoc["data"].GetObject(); + + qDebug() << "Reply Date & Time" << time; + + // check reply timestamp + if (time < QDateTime::currentDateTime().addSecs(-10)) { + QMessageBox::critical(this, tr("Network Error"), tr("Outdated Reply")); + return false; + } + + // check status code if successful (200-299) + // check data object + if (status / 100 == 2) { + is_good = true; + return true; + } else { + if (dataVal.HasMember("exceptionMessage") && dataVal["exceptionMessage"].IsString()) + QMessageBox::critical(this, message, dataVal["exceptionMessage"].GetString()); + else QMessageBox::critical(this, message, tr("Unknown Reason")); + } + + } else QMessageBox::critical(this, tr("Network Error"), tr("Unknown Reply Format")); + + return false; +} + +/** + * get value in data + * @param key key of value + * @return value in string format + */ +QString ComUtils::getDataValueStr(const QString &key) const { + if (is_good) { + auto k_byte_array = key.toUtf8(); + if (dataVal.HasMember(k_byte_array.data())) { + return dataVal[k_byte_array.data()].GetString(); + } else return {}; + } else return {}; +} + +/** + * Get eventually url by service type + * @param type service which server provides + * @return url + */ +QString ComUtils::getUrl(ComUtils::ServiceType type) const { + auto host = settings.value("general/currentGpgfrontendServer", + "service.gpgfrontend.pub").toString(); + + auto protocol = QString(); + // Localhost Debug Server + if (host == "localhost") protocol = "http://"; + else protocol = "https://"; + + auto url = protocol + host + ":9049/"; + + switch (type) { + case GetServiceToken: + url += "/user"; + break; + case ShortenCryptText: + url += "/text/new"; + break; + case GetFullCryptText: + url += "/text/get"; + break; + case UploadPubkey: + url += "/key/upload"; + break; + case GetPubkey: + url += "/key/get"; + break; + } + + qDebug() << "ComUtils getUrl" << url; + + return url; +} + +bool ComUtils::checkDataValueStr(const QString &key) const { + auto key_byte_array_data = key.toUtf8().constData(); + if (is_good) { + return dataVal.HasMember(key_byte_array_data) && dataVal[key_byte_array_data].IsString(); + } else return false; +} + +bool ComUtils::checkServiceTokenFormat(const QString &uuid) const { + return re_uuid.match(uuid).hasMatch(); +} + +QByteArray ComUtils::getSignStringBase64(GpgME::GpgContext *ctx, const QString &str, const GpgKey &key) { + QVector<GpgKey> keys{key}; + QByteArray outSignText; + auto signData = str.toUtf8(); + + // The use of multi-threading brings an improvement in UI smoothness + gpgme_error_t error; + auto thread = QThread::create([&]() { + error = ctx->sign(keys, signData, &outSignText, GPGME_SIG_MODE_NORMAL, nullptr, false); + }); + thread->start(); + while (thread->isRunning()) QApplication::processEvents(); + thread->deleteLater(); + + return outSignText.toBase64(); +} + +const rapidjson::Value &ComUtils::getDataValue(const QString &key) const { + if (is_good) { + auto k_byte_array = key.toUtf8(); + if (dataVal.HasMember(k_byte_array.data())) { + return dataVal[k_byte_array.data()]; + } + } + throw std::runtime_error("Inner Error"); +} + +bool ComUtils::checkDataValue(const QString &key) const{ + auto key_byte_array_data = key.toUtf8().constData(); + if (is_good) { + return dataVal.HasMember(key_byte_array_data); + } else return false; +} + +void ComUtils::clear() { + this->dataVal.Clear(); + this->replyDoc.Clear(); + is_good = false; +}
\ No newline at end of file diff --git a/src/server/api/PubkeyGetter.cpp b/src/server/api/PubkeyGetter.cpp new file mode 100644 index 00000000..2ba55d11 --- /dev/null +++ b/src/server/api/PubkeyGetter.cpp @@ -0,0 +1,96 @@ +/** + * 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 "server/api/PubkeyGetter.h" + +PubkeyGetter::PubkeyGetter(GpgME::GpgContext *ctx, const QVector<QString> &fprs) : BaseAPI(ComUtils::GetPubkey), + mCtx(ctx), mFprs(fprs) { +} + +void PubkeyGetter::construct_json() { + document.SetArray(); + QStringList keyIds; + + rapidjson::Document::AllocatorType &allocator = document.GetAllocator(); + + for (const auto &fprStr : mFprs) { + rapidjson::Value fpr; + + auto fprByteArray = fprStr.toUtf8(); + fpr.SetString(fprByteArray.constData(), fprByteArray.count()); + + document.PushBack(fpr, allocator); + keyIds.clear(); + } +} + +void PubkeyGetter::deal_reply() { + + const auto &utils = getUtils(); + + /** + * { + * "pubkeys" : [ + * { + * "publicKey" : ..., + * "fpr" : ..., + * "sha" : ... + * }, + * ... + * ] + * } + */ + + if (!utils.checkDataValue("pubkeys")) { + QMessageBox::critical(nullptr, tr("Error"), + tr("The communication content with the server does not meet the requirements")); + } else { + auto &pubkeys = utils.getDataValue("pubkeys"); + qDebug() << "Pubkey Getter" << pubkeys.IsArray() << pubkeys.GetArray().Size(); + if (pubkeys.IsArray()) { + for (const auto &pubkey : pubkeys.GetArray()) { + if (pubkey.IsObject() + && pubkey.HasMember("publicKey") && pubkey.HasMember("fpr") && pubkey.HasMember("sha") + && pubkey["publicKey"].IsString() && pubkey["fpr"].IsString() && pubkey["sha"].IsString()) { + + auto pubkeyData = QString(pubkey["publicKey"].GetString()); + + QCryptographicHash shaGen(QCryptographicHash::Sha256); + shaGen.addData(pubkeyData.toUtf8()); + + if (shaGen.result().toHex() == pubkey["sha"].GetString()) { + mCtx->importKey(pubkeyData.toUtf8()); + } + + } + } + + } else { + QMessageBox::critical(nullptr, tr("Error"), + tr("The communication content with the server does not meet the requirements")); + } + } +} + + diff --git a/src/server/api/PubkeyUploader.cpp b/src/server/api/PubkeyUploader.cpp new file mode 100644 index 00000000..c6101c65 --- /dev/null +++ b/src/server/api/PubkeyUploader.cpp @@ -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]> starting on May 12, 2021. + * + */ + +#include "server/api/PubkeyUploader.h" + +PubkeyUploader::PubkeyUploader(GpgME::GpgContext *ctx, const QVector<GpgKey> &keys) : BaseAPI(ComUtils::UploadPubkey), + mCtx(ctx), + mKeys(keys) { +} + +void PubkeyUploader::construct_json() { + document.SetArray(); + QStringList keyIds; + QCryptographicHash shaGen(QCryptographicHash::Sha256); + + auto &allocator = document.GetAllocator(); + + QVector<QByteArray> keysData; + for (const auto &key : mKeys) { + QByteArray keyDataBuf; + keyIds << key.id; + + // The use of multi-threading brings an improvement in UI smoothness + gpgme_error_t error; + auto thread = QThread::create([&]() { + error = mCtx->exportKeys(&keyIds, &keyDataBuf); + }); + thread->start(); + while (thread->isRunning()) QApplication::processEvents(); + thread->deleteLater(); + keysData.push_back(keyDataBuf); + keyIds.clear(); + } + + int index = 0; + for (const auto &keyData : keysData) { + rapidjson::Value publicKeyObj, pubkey, sha, signedFpr; + + shaGen.addData(keyData); + auto shaStr = shaGen.result().toHex(); + shaGen.reset(); + + auto signFprStr = ComUtils::getSignStringBase64(mCtx, mKeys[index].fpr, mKeys[index]); + qDebug() << "signFprStr" << signFprStr; + + pubkey.SetString(keyData.data(), keyData.count(), allocator); + sha.SetString(shaStr.data(), shaStr.count(), allocator); + signedFpr.SetString(signFprStr.data(), signFprStr.count(), allocator); + + publicKeyObj.SetObject(); + publicKeyObj.AddMember("publicKey", pubkey, allocator); + publicKeyObj.AddMember("sha", sha, allocator); + publicKeyObj.AddMember("signedFpr", signedFpr, allocator); + + document.PushBack(publicKeyObj, allocator); + index++; + } +} + +void PubkeyUploader::deal_reply() { + + const auto &utils = getUtils(); + + /** + * { + * "strings" : [ + * "...", + * "..." + * ] + * } + */ + + if (!utils.checkDataValue("strings")) { + QMessageBox::critical(nullptr, tr("Error"), + tr("The communication content with the server does not meet the requirements")); + } else { + auto &strings = utils.getDataValue("strings"); + qDebug() << "Pubkey Uploader" << strings.IsArray() << strings.GetArray().Size(); + if (strings.IsArray() && strings.GetArray().Size() == mKeys.size()) { + good = true; + } else { + good = false; + } + } +} diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 618b5d28..b6552227 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -4,6 +4,7 @@ aux_source_directory(./widgets UI_SOURCE) aux_source_directory(./keygen UI_SOURCE) aux_source_directory(./main_window UI_SOURCE) aux_source_directory(./help UI_SOURCE) +aux_source_directory(./settings UI_SOURCE) add_library(gpgfrontend-ui STATIC ${UI_SOURCE}) diff --git a/src/ui/FileEncryptionDialog.cpp b/src/ui/FileEncryptionDialog.cpp index e92dfc90..23ec0b2a 100755 --- a/src/ui/FileEncryptionDialog.cpp +++ b/src/ui/FileEncryptionDialog.cpp @@ -229,7 +229,7 @@ void FileEncryptionDialog::slotExecuteAction() { if (mAction == Sign) { qDebug() << "Action Sign"; - gpgme_error_t err = mCtx->sign(keys, inBuffer, outBuffer, true); + gpgme_error_t err = mCtx->sign(keys, inBuffer, outBuffer, GPGME_SIG_MODE_DETACH, nullptr); if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { qDebug() << "Error" << gpgme_strerror(err); QMessageBox::warning(this, tr("Error"), diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index ce5343bf..77f3b760 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -26,9 +26,9 @@ #include <utility> -KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : - QMainWindow(parent), appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) -{ +KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent) : + QMainWindow(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { mCtx = ctx; /* the list of Keys available*/ @@ -36,14 +36,14 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : mKeyList->setColumnWidth(2, 250); mKeyList->setColumnWidth(3, 250); setCentralWidget(mKeyList); - mKeyList->setDoubleClickedAction([this] (const GpgKey &key, QWidget *parent) { + mKeyList->setDoubleClickedAction([this](const GpgKey &key, QWidget *parent) { new KeyDetailsDialog(mCtx, key, parent); }); createActions(); createMenus(); createToolBars(); - connect(this,SIGNAL(signalStatusBarChanged(QString)),this->parent(),SLOT(slotSetStatusBarText(QString))); + connect(this, SIGNAL(signalStatusBarChanged(QString)), this->parent(), SLOT(slotSetStatusBarText(QString))); /* Restore the iconstyle */ this->settings.sync(); @@ -51,7 +51,8 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : QSize iconSize = settings.value("toolbar/iconsize", QSize(24, 24)).toSize(); settings.setValue("toolbar/iconsize", iconSize); - Qt::ToolButtonStyle buttonStyle = static_cast<Qt::ToolButtonStyle>(settings.value("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon).toUInt()); + Qt::ToolButtonStyle buttonStyle = static_cast<Qt::ToolButtonStyle>(settings.value("toolbar/iconstyle", + Qt::ToolButtonTextUnderIcon).toUInt()); this->setIconSize(iconSize); this->setToolButtonStyle(buttonStyle); @@ -83,8 +84,7 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : mKeyList->addMenuAction(showKeyDetailsAct); } -void KeyMgmt::createActions() -{ +void KeyMgmt::createActions() { openKeyFileAct = new QAction(tr("&Open"), this); openKeyFileAct->setShortcut(tr("Ctrl+O")); openKeyFileAct->setToolTip(tr("Open Key File")); @@ -147,8 +147,7 @@ void KeyMgmt::createActions() connect(showKeyDetailsAct, SIGNAL(triggered()), this, SLOT(slotShowKeyDetails())); } -void KeyMgmt::createMenus() -{ +void KeyMgmt::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(openKeyFileAct); fileMenu->addAction(closeAct); @@ -168,13 +167,12 @@ void KeyMgmt::createMenus() keyMenu->addAction(deleteCheckedKeysAct); } -void KeyMgmt::createToolBars() -{ +void KeyMgmt::createToolBars() { QToolBar *keyToolBar = addToolBar(tr("Key")); keyToolBar->setObjectName("keytoolbar"); // add button with popup menu for import - auto* generateToolButton = new QToolButton(this); + auto *generateToolButton = new QToolButton(this); generateToolButton->setMenu(generateKeyMenu); generateToolButton->setPopupMode(QToolButton::InstantPopup); generateToolButton->setIcon(QIcon(":key_generate.png")); @@ -184,7 +182,7 @@ void KeyMgmt::createToolBars() keyToolBar->addWidget(generateToolButton); // add button with popup menu for import - auto* toolButton = new QToolButton(this); + auto *toolButton = new QToolButton(this); toolButton->setMenu(importKeyMenu); toolButton->setPopupMode(QToolButton::InstantPopup); toolButton->setIcon(QIcon(":key_import.png")); @@ -201,17 +199,17 @@ void KeyMgmt::createToolBars() } -void KeyMgmt::slotImportKeys(QByteArray inBuffer) -{ +void KeyMgmt::slotImportKeys(QByteArray inBuffer) { GpgImportInformation result = mCtx->importKey(std::move(inBuffer)); new KeyImportDetailDialog(mCtx, result, false, this); } -void KeyMgmt::slotImportKeyFromFile() -{ - QString fileName = QFileDialog::getOpenFileName(this, tr("Open Key"), "", tr("Key Files") + " (*.asc *.txt);;"+tr("Keyring files")+" (*.gpg);;All Files (*)"); - if (! fileName.isNull()) { +void KeyMgmt::slotImportKeyFromFile() { + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Key"), "", + tr("Key Files") + " (*.asc *.txt);;" + tr("Keyring files") + + " (*.gpg);;All Files (*)"); + if (!fileName.isNull()) { QFile file; file.setFileName(fileName); if (!file.open(QIODevice::ReadOnly)) { @@ -224,30 +222,25 @@ void KeyMgmt::slotImportKeyFromFile() } } -void KeyMgmt::slotImportKeyFromKeyServer() -{ +void KeyMgmt::slotImportKeyFromKeyServer() { importDialog = new KeyServerImportDialog(mCtx, mKeyList, false, this); importDialog->show(); } -void KeyMgmt::slotImportKeyFromClipboard() -{ +void KeyMgmt::slotImportKeyFromClipboard() { QClipboard *cb = QApplication::clipboard(); slotImportKeys(cb->text(QClipboard::Clipboard).toUtf8()); } -void KeyMgmt::slotDeleteSelectedKeys() -{ +void KeyMgmt::slotDeleteSelectedKeys() { deleteKeysWithWarning(mKeyList->getSelected()); } -void KeyMgmt::slotDeleteCheckedKeys() -{ +void KeyMgmt::slotDeleteCheckedKeys() { deleteKeysWithWarning(mKeyList->getChecked()); } -void KeyMgmt::deleteKeysWithWarning(QStringList *uidList) -{ +void KeyMgmt::deleteKeysWithWarning(QStringList *uidList) { /** * TODO: Different Messages for private/public key, check if * more than one selected... compare to seahorse "delete-dialog" @@ -258,7 +251,8 @@ void KeyMgmt::deleteKeysWithWarning(QStringList *uidList) } QString keynames; for (const auto &uid : *uidList) { - auto &key = mCtx->getKeyById(uid); + auto key = mCtx->getKeyById(uid); + if (!key.good) continue; keynames.append(key.name); keynames.append("<i> <"); keynames.append(key.email); @@ -266,8 +260,9 @@ void KeyMgmt::deleteKeysWithWarning(QStringList *uidList) } int ret = QMessageBox::warning(this, tr("Deleting Keys"), - "<b>"+tr("Are you sure that you want to delete the following keys?")+"</b><br/><br/>"+keynames+ - +"<br/>"+tr("The action can not be undone."), + "<b>" + tr("Are you sure that you want to delete the following keys?") + + "</b><br/><br/>" + keynames + + +"<br/>" + tr("The action can not be undone."), QMessageBox::No | QMessageBox::Yes); if (ret == QMessageBox::Yes) { @@ -275,28 +270,36 @@ void KeyMgmt::deleteKeysWithWarning(QStringList *uidList) } } -void KeyMgmt::slotShowKeyDetails() -{ +void KeyMgmt::slotShowKeyDetails() { if (mKeyList->getSelected()->isEmpty()) { return; } - auto &key = mCtx->getKeyById(mKeyList->getSelected()->first()); + auto key = mCtx->getKeyById(mKeyList->getSelected()->first()); + + if (!key.good) { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); + return; + } new KeyDetailsDialog(mCtx, key); } -void KeyMgmt::slotExportKeyToFile() -{ +void KeyMgmt::slotExportKeyToFile() { auto *keyArray = new QByteArray(); if (!mCtx->exportKeys(mKeyList->getChecked(), keyArray)) { delete keyArray; return; } - auto &key = mCtx->getKeyById(mKeyList->getSelected()->first()); - QString fileString = key.name + " " + key.email+ "(" + key.id+ ")_pub.asc"; + auto key = mCtx->getKeyById(mKeyList->getSelected()->first()); + if (!key.good) { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); + return; + } + QString fileString = key.name + " " + key.email + "(" + key.id + ")_pub.asc"; - QString fileName = QFileDialog::getSaveFileName(this, tr("Export Key To File"), fileString, tr("Key Files") + " (*.asc *.txt);;All Files (*)"); + QString fileName = QFileDialog::getSaveFileName(this, tr("Export Key To File"), fileString, + tr("Key Files") + " (*.asc *.txt);;All Files (*)"); QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { delete keyArray; @@ -309,8 +312,7 @@ void KeyMgmt::slotExportKeyToFile() emit signalStatusBarChanged(QString(tr("key(s) exported"))); } -void KeyMgmt::slotExportKeyToClipboard() -{ +void KeyMgmt::slotExportKeyToClipboard() { auto *keyArray = new QByteArray(); QClipboard *cb = QApplication::clipboard(); if (!mCtx->exportKeys(mKeyList->getChecked(), keyArray)) { @@ -320,27 +322,29 @@ void KeyMgmt::slotExportKeyToClipboard() delete keyArray; } -void KeyMgmt::slotGenerateKeyDialog() -{ - auto *keyGenDialog = new KeyGenDialog(mCtx,this); +void KeyMgmt::slotGenerateKeyDialog() { + auto *keyGenDialog = new KeyGenDialog(mCtx, this); keyGenDialog->show(); } -void KeyMgmt::closeEvent(QCloseEvent *event) -{ +void KeyMgmt::closeEvent(QCloseEvent *event) { QMainWindow::closeEvent(event); } void KeyMgmt::slotGenerateSubKey() { auto selectedList = mKeyList->getSelected(); - if(selectedList->empty()) { + if (selectedList->empty()) { QMessageBox::information(nullptr, tr("Invalid Operation"), tr("Please select one KeyPair before doing this operation.")); return; } - const auto &key = mCtx->getKeyById(selectedList->first()); - if(!key.is_private_key) { + const auto key = mCtx->getKeyById(selectedList->first()); + if (!key.good) { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); + return; + } + if (!key.is_private_key) { QMessageBox::critical(nullptr, tr("Invalid Operation"), tr("If a key pair does not have a private key then it will not be able to generate sub-keys.")); diff --git a/src/ui/KeyUploadDialog.cpp b/src/ui/KeyUploadDialog.cpp index faf892f0..ab38569f 100644 --- a/src/ui/KeyUploadDialog.cpp +++ b/src/ui/KeyUploadDialog.cpp @@ -24,8 +24,6 @@ #include "ui/KeyUploadDialog.h" -#include <utility> - KeyUploadDialog::KeyUploadDialog(GpgME::GpgContext *ctx, const QVector<GpgKey> &keys, QWidget *parent) : appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat), diff --git a/src/ui/SettingsDialog.cpp b/src/ui/SettingsDialog.cpp deleted file mode 100755 index 1732d718..00000000 --- a/src/ui/SettingsDialog.cpp +++ /dev/null @@ -1,777 +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/SettingsDialog.h" -#include "smtp/SmtpMime" -#include "ui/WaitingDialog.h" - -SettingsDialog::SettingsDialog(GpgME::GpgContext *ctx, QWidget *parent) - : QDialog(parent) { - mCtx = ctx; - tabWidget = new QTabWidget; - generalTab = new GeneralTab(mCtx); - appearanceTab = new AppearanceTab; - sendMailTab = new SendMailTab; - keyserverTab = new KeyserverTab; - advancedTab = new AdvancedTab; - gpgPathsTab = new GpgPathsTab; - - tabWidget->addTab(generalTab, tr("General")); - tabWidget->addTab(appearanceTab, tr("Appearance")); - tabWidget->addTab(sendMailTab, tr("Send Mail")); - tabWidget->addTab(keyserverTab, tr("Key Server")); - // tabWidget->addTab(gpgPathsTab, tr("Gpg paths")); - tabWidget->addTab(advancedTab, tr("Advanced")); - - buttonBox = - new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - - connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotAccept())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(tabWidget); - mainLayout->addWidget(buttonBox); - setLayout(mainLayout); - - setWindowTitle(tr("Settings")); - - // slots for handling the restartneeded member - this->slotSetRestartNeeded(false); - connect(generalTab, SIGNAL(signalRestartNeeded(bool)), this, - SLOT(slotSetRestartNeeded(bool))); - connect(appearanceTab, SIGNAL(signalRestartNeeded(bool)), this, - SLOT(slotSetRestartNeeded(bool))); - connect(sendMailTab, SIGNAL(signalRestartNeeded(bool)), this, - SLOT(slotSetRestartNeeded(bool))); - connect(keyserverTab, SIGNAL(signalRestartNeeded(bool)), this, - SLOT(slotSetRestartNeeded(bool))); - connect(advancedTab, SIGNAL(signalRestartNeeded(bool)), this, - SLOT(slotSetRestartNeeded(bool))); - - connect(this, SIGNAL(signalRestartNeeded(bool)), parent, - SLOT(slotSetRestartNeeded(bool))); - - this->show(); -} - -bool SettingsDialog::getRestartNeeded() const { return this->restartNeeded; } - -void SettingsDialog::slotSetRestartNeeded(bool needed) { - this->restartNeeded = needed; -} - -void SettingsDialog::slotAccept() { - generalTab->applySettings(); - sendMailTab->applySettings(); - appearanceTab->applySettings(); - keyserverTab->applySettings(); - advancedTab->applySettings(); - gpgPathsTab->applySettings(); - if (getRestartNeeded()) { - emit signalRestartNeeded(true); - } - close(); -} - -// http://www.informit.com/articles/article.aspx?p=1405555&seqNum=3 -// http://developer.qt.nokia.com/wiki/How_to_create_a_multi_language_application -QHash<QString, QString> SettingsDialog::listLanguages() { - QHash<QString, QString> languages; - - languages.insert("", tr("System Default")); - - QString appPath = qApp->applicationDirPath(); - QDir qmDir = QDir(RESOURCE_DIR(appPath) + "/ts/"); - QStringList fileNames = qmDir.entryList(QStringList("gpgfrontend_*.qm")); - - for (int i = 0; i < fileNames.size(); ++i) { - QString locale = fileNames[i]; - locale.truncate(locale.lastIndexOf('.')); - locale.remove(0, locale.indexOf('_') + 1); - - // this works in qt 4.8 - QLocale qloc(locale); -#if QT_VERSION < 0x040800 - QString language = - QLocale::languageToString(qloc.language()) + " (" + locale + - ")"; //+ " (" + QLocale::languageToString(qloc.language()) + ")"; -#else - QString language = qloc.nativeLanguageName() + " (" + locale + ")"; -#endif - languages.insert(locale, language); - } - return languages; -} - -GeneralTab::GeneralTab(GpgME::GpgContext *ctx, QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - mCtx = ctx; - - /***************************************** - * remember Password-Box - *****************************************/ - auto *rememberPasswordBox = new QGroupBox(tr("Remember Password")); - auto *rememberPasswordBoxLayout = new QHBoxLayout(); - rememberPasswordCheckBox = - new QCheckBox(tr("Remember password until closing gpg4usb"), this); - rememberPasswordBoxLayout->addWidget(rememberPasswordCheckBox); - rememberPasswordBox->setLayout(rememberPasswordBoxLayout); - - /***************************************** - * Save-Checked-Keys-Box - *****************************************/ - auto *saveCheckedKeysBox = new QGroupBox(tr("Save Checked Keys")); - auto *saveCheckedKeysBoxLayout = new QHBoxLayout(); - saveCheckedKeysCheckBox = new QCheckBox( - tr("Save checked private keys on exit and restore them on next start."), - this); - saveCheckedKeysBoxLayout->addWidget(saveCheckedKeysCheckBox); - saveCheckedKeysBox->setLayout(saveCheckedKeysBoxLayout); - - /***************************************** - * Key-Impport-Confirmation Box - *****************************************/ - auto *importConfirmationBox = - new QGroupBox(tr("Confirm drag'n'drop key import")); - auto *importConfirmationBoxLayout = new QHBoxLayout(); - importConfirmationCheckBox = new QCheckBox( - tr("Import files dropped on the keylist without confirmation."), this); - importConfirmationBoxLayout->addWidget(importConfirmationCheckBox); - importConfirmationBox->setLayout(importConfirmationBoxLayout); - - /***************************************** - * Language Select Box - *****************************************/ - auto *langBox = new QGroupBox(tr("Language")); - auto *langBoxLayout = new QVBoxLayout(); - langSelectBox = new QComboBox; - lang = SettingsDialog::listLanguages(); - - foreach (QString l, lang) { langSelectBox->addItem(l); } - - langBoxLayout->addWidget(langSelectBox); - langBoxLayout->addWidget( - new QLabel(tr("<b>NOTE: </b> GpgFrontend will restart automatically if " - "you change the language!"))); - langBox->setLayout(langBoxLayout); - connect(langSelectBox, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotLanguageChanged())); - - /***************************************** - * Own Key Select Box - *****************************************/ - auto *ownKeyBox = new QGroupBox(tr("Own key")); - auto *ownKeyBoxLayout = new QVBoxLayout(); - ownKeySelectBox = new QComboBox; - - ownKeyBox->setLayout(ownKeyBoxLayout); - mKeyList = new KeyList(mCtx); - - // Fill the keyid hashmap - keyIds.insert("", tr("<none>")); - - for (const auto &keyid : *mKeyList->getAllPrivateKeys()) { - auto &key = mCtx->getKeyById(keyid); - keyIds.insert(key.id, key.uids.first().uid); - } - for (const auto &k : keyIds.keys()) { - ownKeySelectBox->addItem(keyIds.find(k).value()); - keyIdsList.append(k); - } - connect(ownKeySelectBox, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotOwnKeyIdChanged())); - - ownKeyBoxLayout->addWidget(new QLabel( - tr("Key pair for synchronization and identity authentication"))); - ownKeyBoxLayout->addWidget(ownKeySelectBox); - - /***************************************** - * Mainlayout - *****************************************/ - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(rememberPasswordBox); - mainLayout->addWidget(saveCheckedKeysBox); - mainLayout->addWidget(importConfirmationBox); - mainLayout->addWidget(langBox); - mainLayout->addWidget(ownKeyBox); - - setSettings(); - mainLayout->addStretch(1); - setLayout(mainLayout); -} - -/********************************** - * Read the settings from config - * and set the buttons and checkboxes - * appropriately - **********************************/ -void GeneralTab::setSettings() { - // Keysaving - if (settings.value("keys/saveKeyChecked").toBool()) { - saveCheckedKeysCheckBox->setCheckState(Qt::Checked); - } - - // Language setting - QString langKey = settings.value("int/lang").toString(); - QString langValue = lang.value(langKey); - if (langKey != "") { - langSelectBox->setCurrentIndex(langSelectBox->findText(langValue)); - } - - QString ownKeyId = settings.value("general/ownKeyId").toString(); - qDebug() << "OwnKeyId" << ownKeyId; - if (ownKeyId.isEmpty()) { - ownKeySelectBox->setCurrentText("<none>"); - } else { - const auto text = keyIds.find(ownKeyId).value(); - qDebug() << "OwnKey" << ownKeyId << text; - ownKeySelectBox->setCurrentText(text); - } - - // Get own key information from keydb/gpg.conf (if contained) - if (settings.value("general/confirmImportKeys", Qt::Checked).toBool()) { - importConfirmationCheckBox->setCheckState(Qt::Checked); - } -} - -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ -void GeneralTab::applySettings() { - settings.setValue("keys/saveKeyChecked", - saveCheckedKeysCheckBox->isChecked()); - // TODO: clear passwordCache instantly on unset rememberPassword - settings.setValue("general/rememberPassword", - rememberPasswordCheckBox->isChecked()); - settings.setValue("int/lang", lang.key(langSelectBox->currentText())); - settings.setValue("general/ownKeyId", - keyIdsList[ownKeySelectBox->currentIndex()]); - settings.setValue("general/confirmImportKeys", - importConfirmationCheckBox->isChecked()); -} - -void GeneralTab::slotLanguageChanged() { emit signalRestartNeeded(true); } - -void GeneralTab::slotOwnKeyIdChanged() { - // Set ownKeyId to currently selected -} - -SendMailTab::SendMailTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - - enableCheckBox = new QCheckBox(tr("Enable")); - enableCheckBox->setTristate(false); - - smtpAddress = new QLineEdit(); - username = new QLineEdit(); - password = new QLineEdit(); - password->setEchoMode(QLineEdit::Password); - - portSpin = new QSpinBox(); - portSpin->setMinimum(1); - portSpin->setMaximum(65535); - connectionTypeComboBox = new QComboBox(); - connectionTypeComboBox->addItem("None"); - connectionTypeComboBox->addItem("SSL"); - connectionTypeComboBox->addItem("TLS"); - connectionTypeComboBox->addItem("STARTTLS"); - - defaultSender = new QLineEdit();; - checkConnectionButton = new QPushButton("Check Connection"); - - auto layout = new QGridLayout(); - layout->addWidget(enableCheckBox, 0, 0); - layout->addWidget(new QLabel(tr("SMTP Address")), 1, 0); - layout->addWidget(smtpAddress, 1, 1, 1, 4); - layout->addWidget(new QLabel(tr("Username")), 2, 0); - layout->addWidget(username, 2, 1, 1, 4); - layout->addWidget(new QLabel(tr("Password")), 3, 0); - layout->addWidget(password, 3, 1, 1, 4); - layout->addWidget(new QLabel(tr("Port")), 4, 0); - layout->addWidget(portSpin, 4, 1, 1, 1); - layout->addWidget(new QLabel(tr("Connection Security")), 5, 0); - layout->addWidget(connectionTypeComboBox, 5, 1, 1, 1); - - layout->addWidget(new QLabel(tr("Default Sender")), 6, 0); - layout->addWidget(defaultSender, 6, 1, 1, 4); - layout->addWidget(checkConnectionButton, 7, 0); - - connect(enableCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotCheckBoxSetEnableDisable(int))); - connect(checkConnectionButton, SIGNAL(clicked(bool)), this, SLOT(slotCheckConnection())); - - - this->setLayout(layout); - setSettings(); -} - -/********************************** - * Read the settings from config - * and set the buttons and checkboxes - * appropriately - **********************************/ -void SendMailTab::setSettings() { - - if (settings.value("sendMail/enable", false).toBool()) - enableCheckBox->setCheckState(Qt::Checked); - else { - enableCheckBox->setCheckState(Qt::Unchecked); - smtpAddress->setDisabled(true); - username->setDisabled(true); - password->setDisabled(true); - portSpin->setDisabled(true); - connectionTypeComboBox->setDisabled(true); - defaultSender->setDisabled(true); - checkConnectionButton->setDisabled(true); - } - - smtpAddress->setText(settings.value("sendMail/smtpAddress", QString()).toString()); - username->setText(settings.value("sendMail/username", QString()).toString()); - password->setText(settings.value("sendMail/password", QString()).toString()); - portSpin->setValue(settings.value("sendMail/port", 25).toInt()); - connectionTypeComboBox->setCurrentText(settings.value("sendMail/connectionType", "None").toString()); - defaultSender->setText(settings.value("sendMail/defaultSender", QString()).toString()); - -} - -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ -void SendMailTab::applySettings() { - - settings.setValue("sendMail/smtpAddress", smtpAddress->text()); - settings.setValue("sendMail/username", username->text()); - settings.setValue("sendMail/password", password->text()); - settings.setValue("sendMail/port", portSpin->value()); - settings.setValue("sendMail/connectionType", connectionTypeComboBox->currentText()); - settings.setValue("sendMail/defaultSender", defaultSender->text()); - - settings.setValue("sendMail/enable", enableCheckBox->isChecked()); -} - -void SendMailTab::slotCheckConnection() { - - SmtpClient::ConnectionType connectionType = SmtpClient::ConnectionType::TcpConnection; - - if (connectionTypeComboBox->currentText() == "SSL") { - connectionType = SmtpClient::ConnectionType::SslConnection; - } else if (connectionTypeComboBox->currentText() == "TLS") { - connectionType = SmtpClient::ConnectionType::TlsConnection; - } else if (connectionTypeComboBox->currentText() == "STARTTLS") { - connectionType = SmtpClient::ConnectionType::TlsConnection; - } else { - connectionType = SmtpClient::ConnectionType::TcpConnection; - } - - SmtpClient smtp(smtpAddress->text(), portSpin->value(), connectionType); - - // We need to set the username (your email address) and the password - // for smtp authentification. - - smtp.setUser(username->text()); - smtp.setPassword(password->text()); - - bool if_success = true; - - if (!smtp.connectToHost()) { - QMessageBox::critical(this, tr("Fail"), tr("Fail to Connect SMTP Server")); - if_success = false; - } - if (if_success && !smtp.login()) { - QMessageBox::critical(this, tr("Fail"), tr("Fail to Login")); - if_success = false; - } - - if (if_success) - QMessageBox::information(this, tr("Success"), tr("Succeed in connecting and login")); - -} - -void SendMailTab::slotCheckBoxSetEnableDisable(int state) { - if (state == Qt::Checked) { - smtpAddress->setEnabled(true); - username->setEnabled(true); - password->setEnabled(true); - portSpin->setEnabled(true); - connectionTypeComboBox->setEnabled(true); - defaultSender->setEnabled(true); - checkConnectionButton->setEnabled(true); - } else { - smtpAddress->setDisabled(true); - username->setDisabled(true); - password->setDisabled(true); - portSpin->setDisabled(true); - connectionTypeComboBox->setDisabled(true); - defaultSender->setDisabled(true); - checkConnectionButton->setDisabled(true); - } -} - -AppearanceTab::AppearanceTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - /***************************************** - * Icon-Size-Box - *****************************************/ - auto *iconSizeBox = new QGroupBox(tr("Iconsize")); - iconSizeGroup = new QButtonGroup(); - iconSizeSmall = new QRadioButton(tr("small")); - iconSizeMedium = new QRadioButton(tr("medium")); - iconSizeLarge = new QRadioButton(tr("large")); - - iconSizeGroup->addButton(iconSizeSmall, 1); - iconSizeGroup->addButton(iconSizeMedium, 2); - iconSizeGroup->addButton(iconSizeLarge, 3); - - auto *iconSizeBoxLayout = new QHBoxLayout(); - iconSizeBoxLayout->addWidget(iconSizeSmall); - iconSizeBoxLayout->addWidget(iconSizeMedium); - iconSizeBoxLayout->addWidget(iconSizeLarge); - - iconSizeBox->setLayout(iconSizeBoxLayout); - - /***************************************** - * Icon-Style-Box - *****************************************/ - auto *iconStyleBox = new QGroupBox(tr("Iconstyle")); - iconStyleGroup = new QButtonGroup(); - iconTextButton = new QRadioButton(tr("just text")); - iconIconsButton = new QRadioButton(tr("just icons")); - iconAllButton = new QRadioButton(tr("text and icons")); - - iconStyleGroup->addButton(iconTextButton, 1); - iconStyleGroup->addButton(iconIconsButton, 2); - iconStyleGroup->addButton(iconAllButton, 3); - - auto *iconStyleBoxLayout = new QHBoxLayout(); - iconStyleBoxLayout->addWidget(iconTextButton); - iconStyleBoxLayout->addWidget(iconIconsButton); - iconStyleBoxLayout->addWidget(iconAllButton); - - iconStyleBox->setLayout(iconStyleBoxLayout); - - /***************************************** - * Window-Size-Box - *****************************************/ - auto *windowSizeBox = new QGroupBox(tr("Windowstate")); - auto *windowSizeBoxLayout = new QHBoxLayout(); - windowSizeCheckBox = - new QCheckBox(tr("Save window size and position on exit."), this); - windowSizeBoxLayout->addWidget(windowSizeCheckBox); - windowSizeBox->setLayout(windowSizeBoxLayout); - - /***************************************** - * Info-Board-Font-Size-Box - *****************************************/ - - auto *infoBoardBox = new QGroupBox(tr("Information Board")); - auto *infoBoardLayout = new QHBoxLayout(); - infoBoardFontSizeSpin = new QSpinBox(); - infoBoardFontSizeSpin->setRange(9, 18); - infoBoardFontSizeSpin->setValue(10); - infoBoardFontSizeSpin->setSingleStep(1); - infoBoardLayout->addWidget(new QLabel(tr(" Front Size"))); - infoBoardLayout->addWidget(infoBoardFontSizeSpin); - infoBoardBox->setLayout(infoBoardLayout); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(iconSizeBox); - mainLayout->addWidget(iconStyleBox); - mainLayout->addWidget(windowSizeBox); - mainLayout->addWidget(infoBoardBox); - mainLayout->addStretch(1); - setSettings(); - setLayout(mainLayout); -} - -/********************************** - * Read the settings from config - * and set the buttons and checkboxes - * appropriately - **********************************/ -void AppearanceTab::setSettings() { - - // Iconsize - QSize iconSize = settings.value("toolbar/iconsize", QSize(24, 24)).toSize(); - switch (iconSize.height()) { - case 12: - iconSizeSmall->setChecked(true); - break; - case 24: - iconSizeMedium->setChecked(true); - break; - case 32: - iconSizeLarge->setChecked(true); - break; - } - // Iconstyle - Qt::ToolButtonStyle iconStyle = static_cast<Qt::ToolButtonStyle>( - settings.value("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon) - .toUInt()); - switch (iconStyle) { - case Qt::ToolButtonTextOnly: - iconTextButton->setChecked(true); - break; - case Qt::ToolButtonIconOnly: - iconIconsButton->setChecked(true); - break; - case Qt::ToolButtonTextUnderIcon: - iconAllButton->setChecked(true); - break; - default: - break; - } - - // Window Save and Position - if (settings.value("window/windowSave").toBool()) - windowSizeCheckBox->setCheckState(Qt::Checked); - - // infoBoardFontSize - auto infoBoardFontSize = settings.value("informationBoard/fontSize", 10).toInt(); - if (infoBoardFontSize < 9 || infoBoardFontSize > 18) - infoBoardFontSize = 10; - infoBoardFontSizeSpin->setValue(infoBoardFontSize); -} - -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ -void AppearanceTab::applySettings() { - switch (iconSizeGroup->checkedId()) { - case 1: - settings.setValue("toolbar/iconsize", QSize(12, 12)); - break; - case 2: - settings.setValue("toolbar/iconsize", QSize(24, 24)); - break; - case 3: - settings.setValue("toolbar/iconsize", QSize(32, 32)); - break; - } - - switch (iconStyleGroup->checkedId()) { - case 1: - settings.setValue("toolbar/iconstyle", Qt::ToolButtonTextOnly); - break; - case 2: - settings.setValue("toolbar/iconstyle", Qt::ToolButtonIconOnly); - break; - case 3: - settings.setValue("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon); - break; - } - - settings.setValue("window/windowSave", windowSizeCheckBox->isChecked()); - - settings.setValue("informationBoard/fontSize", infoBoardFontSizeSpin->value()); -} - -KeyserverTab::KeyserverTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - - auto keyServerList = settings.value("keyserver/keyServerList").toStringList(); - - auto *mainLayout = new QVBoxLayout(this); - - auto *label = new QLabel(tr("Default Key Server for import:")); - comboBox = new QComboBox; - comboBox->setEditable(false); - comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - - for (const auto &keyServer : keyServerList) { - comboBox->addItem(keyServer); - qDebug() << "KeyserverTab Get ListItemText" << keyServer; - } - - comboBox->setCurrentText( - settings.value("keyserver/defaultKeyServer").toString()); - - auto *addKeyServerBox = new QWidget(this); - auto *addKeyServerLayout = new QHBoxLayout(addKeyServerBox); - auto *http = new QLabel("URL: "); - newKeyServerEdit = new QLineEdit(this); - auto *newKeyServerButton = new QPushButton(tr("Add"), this); - connect(newKeyServerButton, SIGNAL(clicked()), this, SLOT(addKeyServer())); - addKeyServerLayout->addWidget(http); - addKeyServerLayout->addWidget(newKeyServerEdit); - addKeyServerLayout->addWidget(newKeyServerButton); - - mainLayout->addWidget(label); - mainLayout->addWidget(comboBox); - mainLayout->addWidget(addKeyServerBox); - mainLayout->addStretch(1); - - // Read keylist from ini-file and fill it into combobox - setSettings(); -} - -/********************************** - * Read the settings from config - * and set the buttons and checkboxes - * appropriately - **********************************/ -void KeyserverTab::setSettings() { - auto *keyServerList = new QStringList(); - for (int i = 0; i < comboBox->count(); i++) { - keyServerList->append(comboBox->itemText(i)); - qDebug() << "KeyserverTab ListItemText" << comboBox->itemText(i); - } - settings.setValue("keyserver/keyServerList", *keyServerList); - settings.setValue("keyserver/defaultKeyServer", comboBox->currentText()); -} - -void KeyserverTab::addKeyServer() { - if (newKeyServerEdit->text().startsWith("http://") || - newKeyServerEdit->text().startsWith("https://")) { - comboBox->addItem(newKeyServerEdit->text()); - } else { - comboBox->addItem("http://" + newKeyServerEdit->text()); - } - comboBox->setCurrentIndex(comboBox->count() - 1); -} - -/*********************************** - * get the values of the buttons and - * write them to settings-file - *************************************/ -void KeyserverTab::applySettings() { - settings.setValue("keyserver/defaultKeyServer", comboBox->currentText()); -} - -AdvancedTab::AdvancedTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - /***************************************** - * Steganography Box - *****************************************/ - auto *steganoBox = new QGroupBox(tr("Show Steganography Options [Advanced]")); - auto *steganoBoxLayout = new QHBoxLayout(); - steganoCheckBox = new QCheckBox(tr("Show Steganographic Options."), this); - steganoBoxLayout->addWidget(steganoCheckBox); - steganoBox->setLayout(steganoBoxLayout); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(steganoBox); - setSettings(); - mainLayout->addStretch(1); - setLayout(mainLayout); -} - -void AdvancedTab::setSettings() { - if (settings.value("advanced/steganography").toBool()) { - steganoCheckBox->setCheckState(Qt::Checked); - } -} - -void AdvancedTab::applySettings() { - settings.setValue("advanced/steganography", steganoCheckBox->isChecked()); -} - -GpgPathsTab::GpgPathsTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - setSettings(); - - /***************************************** - * Keydb Box - *****************************************/ - auto *keydbBox = new QGroupBox(tr("Relative path to keydb")); - auto *keydbBoxLayout = new QGridLayout(); - - // Label containing the current keydbpath relative to default keydb path - keydbLabel = new QLabel(accKeydbPath, this); - - auto *keydbButton = new QPushButton("Change keydb path", this); - connect(keydbButton, SIGNAL(clicked()), this, SLOT(chooseKeydbDir())); - auto *keydbDefaultButton = new QPushButton("Set keydb to default path", this); - connect(keydbDefaultButton, SIGNAL(clicked()), this, - SLOT(setKeydbPathToDefault())); - - keydbBox->setLayout(keydbBoxLayout); - keydbBoxLayout->addWidget(new QLabel(tr("Current keydb path: ")), 1, 1); - keydbBoxLayout->addWidget(keydbLabel, 1, 2); - keydbBoxLayout->addWidget(keydbButton, 1, 3); - keydbBoxLayout->addWidget(keydbDefaultButton, 2, 3); - keydbBoxLayout->addWidget( - new QLabel(tr("<b>NOTE: </b> Gpg4usb will restart automatically if you " - "change the keydb path!")), - 3, 1, 1, 3); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(keydbBox); - mainLayout->addStretch(1); - setLayout(mainLayout); -} - -QString GpgPathsTab::getRelativePath(const QString &dir1, const QString &dir2) { - QDir dir(dir1); - QString s; - - s = dir.relativeFilePath(dir2); - qDebug() << "relative path: " << s; - if (s.isEmpty()) { - s = "."; - } - return s; -} - -void GpgPathsTab::setKeydbPathToDefault() { - accKeydbPath = "."; - keydbLabel->setText("."); -} - -QString GpgPathsTab::chooseKeydbDir() { - QString dir = QFileDialog::getExistingDirectory( - this, tr("Choose keydb directory"), accKeydbPath, - QFileDialog::ShowDirsOnly); - - accKeydbPath = getRelativePath(defKeydbPath, dir); - keydbLabel->setText(accKeydbPath); - return ""; -} - -void GpgPathsTab::setSettings() { - defKeydbPath = qApp->applicationDirPath() + "/keydb"; - - accKeydbPath = settings.value("gpgpaths/keydbpath").toString(); - if (accKeydbPath.isEmpty()) { - accKeydbPath = "."; - } -} - -void GpgPathsTab::applySettings() { - settings.setValue("gpgpaths/keydbpath", accKeydbPath); -} diff --git a/src/ui/ShowCopyDialog.cpp b/src/ui/ShowCopyDialog.cpp new file mode 100644 index 00000000..3286f6c4 --- /dev/null +++ b/src/ui/ShowCopyDialog.cpp @@ -0,0 +1,53 @@ +/** + * 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/ShowCopyDialog.h" + +ShowCopyDialog::ShowCopyDialog(const QString &text, const QString &info, QWidget *parent) : QDialog(parent) { + textEdit = new QTextEdit(); + textEdit->setReadOnly(true); + textEdit->setLineWrapMode(QTextEdit::WidgetWidth); + textEdit->setText(text); + copyButton = new QPushButton("Copy"); + connect(copyButton, SIGNAL(clicked(bool)), this, SLOT(slotCopyText())); + + infoLabel = new QLabel(); + infoLabel->setText(info); + infoLabel->setWordWrap(true); + + auto *layout = new QVBoxLayout(); + layout->addWidget(textEdit); + layout->addWidget(copyButton); + layout->addWidget(infoLabel); + + this->setWindowTitle("Short Ciphertext"); + this->resize(320, 120); + this->setModal(true); + this->setLayout(layout); +} + +void ShowCopyDialog::slotCopyText() { + QClipboard *cb = QApplication::clipboard(); + cb->setText(textEdit->toPlainText()); +} diff --git a/src/ui/Wizard.cpp b/src/ui/Wizard.cpp index b0486cc9..8b482675 100644 --- a/src/ui/Wizard.cpp +++ b/src/ui/Wizard.cpp @@ -24,10 +24,6 @@ #include "ui/Wizard.h" -#ifdef Q_OS_WIN -#include "windows.h" -#endif - Wizard::Wizard(GpgME::GpgContext *ctx, KeyMgmt *keyMgmt, QWidget *parent) : QWizard(parent), appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { @@ -36,8 +32,6 @@ Wizard::Wizard(GpgME::GpgContext *ctx, KeyMgmt *keyMgmt, QWidget *parent) setPage(Page_Intro, new IntroPage(this)); setPage(Page_Choose, new ChoosePage(this)); - setPage(Page_ImportFromGpg4usb, new ImportFromGpg4usbPage(mCtx, mKeyMgmt, this)); - setPage(Page_ImportFromGnupg, new ImportFromGnupgPage(mCtx, mKeyMgmt, this)); setPage(Page_GenKey, new KeyGenPage(mCtx, this)); setPage(Page_Conclusion, new ConclusionPage(this)); #ifndef Q_WS_MAC @@ -54,7 +48,6 @@ Wizard::Wizard(GpgME::GpgContext *ctx, KeyMgmt *keyMgmt, QWidget *parent) settings.remove("wizard/nextPage"); connect(this, SIGNAL(accepted()), this, SLOT(slotWizardAccepted())); - // connect(this, SIGNAL(signalOpenHelp(QString)), parentWidget(), SLOT(signalOpenHelp(QString))); } @@ -67,57 +60,20 @@ void Wizard::slotWizardAccepted() { } } -bool Wizard::importPubAndSecKeysFromDir(const QString &dir, KeyMgmt *keyMgmt) { - QFile secRingFile(dir + "/secring.gpg"); - QFile pubRingFile(dir + "/pubring.gpg"); - - // Return, if no keyrings are found in subdir of chosen dir - if (!(pubRingFile.exists() or secRingFile.exists())) { - QMessageBox::critical(0, tr("Import Error"), tr("Couldn't locate any keyring file in %1").arg(dir)); - return false; - } - - QByteArray inBuffer; - if (secRingFile.exists()) { - // write content of secringfile to inBuffer - if (!secRingFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(nullptr, tr("Import error"), - tr("Couldn't open private keyringfile: %1").arg(secRingFile.fileName())); - return false; - } - inBuffer = secRingFile.readAll(); - secRingFile.close(); - } - - if (pubRingFile.exists()) { - // try to import public keys - if (!pubRingFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(nullptr, tr("Import error"), - tr("Couldn't open public keyringfile: %1").arg(pubRingFile.fileName())); - return false; - } - inBuffer.append(pubRingFile.readAll()); - pubRingFile.close(); - } - keyMgmt->slotImportKeys(inBuffer); - inBuffer.clear(); - - return true; -} - IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent), appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { setTitle(tr("Getting Started...")); setSubTitle(tr("... with GPGFrontend")); - auto *topLabel = new QLabel(tr("Welcome to use GPGFrontend for decrypting and signing text or file!")+ - " <br><br><a href='https://github.com/saturneric/GpgFrontend'>GpgFrontend</a> " + - tr("is a Powerful, Easy-to-Use, Compact, Cross-Platform, and Installation-Free OpenPGP Crypto Tool.") + - tr("For brief information have a look at the") + "<a href='https://saturneric.github.io/GpgFrontend/index.html#/overview'>"+ - tr("Overview") +"</a> (" + - tr("by clicking the link, the page will open in the web browser") + - "). <br>"); + auto *topLabel = new QLabel(tr("Welcome to use GPGFrontend for decrypting and signing text or file!") + + " <br><br><a href='https://gpgfrontend.pub'>GpgFrontend</a> " + + tr("is a Powerful, Easy-to-Use, Compact, Cross-Platform, and Installation-Free OpenPGP Crypto Tool.") + + tr("For brief information have a look at the") + + " <a href='https://gpgfrontend.pub/index.html#/overview'>" + + tr("Overview") + "</a> (" + + tr("by clicking the link, the page will open in the web browser") + + "). <br>"); topLabel->setTextFormat(Qt::RichText); topLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); topLabel->setOpenExternalLinks(true); @@ -130,7 +86,7 @@ IntroPage::IntroPage(QWidget *parent) languages = SettingsDialog::listLanguages(); auto *langSelectBox = new QComboBox(); - for(const auto &l : languages) { + for (const auto &l : languages) { langSelectBox->addItem(l); } // selected entry from config @@ -166,19 +122,21 @@ ChoosePage::ChoosePage(QWidget *parent) setSubTitle(tr("...by clicking on the appropriate link.")); auto *keygenLabel = new QLabel(tr("If you have never used GPGFrontend before and also don't own a gpg key yet you " - "may possibly want to read how to") + " <a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/generate-key\">" + "may possibly want to read how to") + + " <a href=\"https://gpgfrontend.pub/index.html#/manual/generate-key\">" + tr("Generate Key") + "</a><hr>"); keygenLabel->setTextFormat(Qt::RichText); keygenLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); keygenLabel->setOpenExternalLinks(true); keygenLabel->setWordWrap(true); - auto *encrDecyTextLabel = new QLabel(tr("If you want to learn how to encrypt, decrypt, sign and verify text, you can read ") - + "<a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/encrypt-decrypt-text\">" - + tr("Encrypt & Decrypt Text") + "</a> " + tr("or") - + " <a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/sign-verify-text\">" - + tr("Sign & Verify Text") - +"</a><hr>"); + auto *encrDecyTextLabel = new QLabel( + tr("If you want to learn how to encrypt, decrypt, sign and verify text, you can read ") + + "<a href=\"https://gpgfrontend.pub/index.html#/manual/encrypt-decrypt-text\">" + + tr("Encrypt & Decrypt Text") + "</a> " + tr("or") + + " <a href=\"https://gpgfrontend.pub/index.html#/manual/sign-verify-text\">" + + tr("Sign & Verify Text") + + "</a><hr>"); encrDecyTextLabel->setTextFormat(Qt::RichText); encrDecyTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); @@ -186,11 +144,13 @@ ChoosePage::ChoosePage(QWidget *parent) encrDecyTextLabel->setWordWrap(true); auto *signVerifyTextLabel = new QLabel(tr("If you want to operate file, you can read ") - + "<a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/encrypt-decrypt-file\">" - + tr("Encrypt & Sign File") + "</a> " + tr("or") - + " <a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/sign-verify-file\">" - + tr("Sign & Verify File") - +"</a><hr>"); + + + "<a href=\"https://gpgfrontend.pub/index.html#/manual/encrypt-decrypt-file\">" + + tr("Encrypt & Sign File") + "</a> " + tr("or") + + + " <a href=\"https://gpgfrontend.pub/index.html#/manual/sign-verify-file\">" + + tr("Sign & Verify File") + + "</a><hr>"); signVerifyTextLabel->setTextFormat(Qt::RichText); signVerifyTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); signVerifyTextLabel->setOpenExternalLinks(true); @@ -217,149 +177,6 @@ void ChoosePage::slotJumpPage(const QString &page) { wizard()->next(); } -ImportFromGpg4usbPage::ImportFromGpg4usbPage(GpgME::GpgContext *ctx, KeyMgmt *keyMgmt, QWidget *parent) - : QWizardPage(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { - mCtx = ctx; - mKeyMgmt = keyMgmt; - setTitle(tr("Import from...")); - setSubTitle(tr("...existing GPGFrontend")); - - auto *topLabel = new QLabel(tr("You can import keys and/or settings from existing GPGFrontend. <br><br>" - "Just check what you want to import, click the import button and choose " - "the directory of your other GPGFrontend in the appearing file dialog."), this); - topLabel->setWordWrap(true); - - gpg4usbKeyCheckBox = new QCheckBox(); - gpg4usbKeyCheckBox->setChecked(true); - auto *keyLabel = new QLabel(tr("Keys")); - - gpg4usbConfigCheckBox = new QCheckBox(); - gpg4usbConfigCheckBox->setChecked(true); - auto *configLabel = new QLabel(tr("Configuration")); - - auto *importFromGpg4usbButton = new QPushButton(tr("Import from GPGFrontend")); - connect(importFromGpg4usbButton, SIGNAL(clicked()), this, SLOT(slotImportFromOlderGpg4usb())); - - auto *gpg4usbLayout = new QGridLayout(); - gpg4usbLayout->addWidget(topLabel, 1, 1, 1, 2); - gpg4usbLayout->addWidget(gpg4usbKeyCheckBox, 2, 1, Qt::AlignRight); - gpg4usbLayout->addWidget(keyLabel, 2, 2); - gpg4usbLayout->addWidget(gpg4usbConfigCheckBox, 3, 1, Qt::AlignRight); - gpg4usbLayout->addWidget(configLabel, 3, 2); - gpg4usbLayout->addWidget(importFromGpg4usbButton, 4, 2); - - this->setLayout(gpg4usbLayout); -} - -void ImportFromGpg4usbPage::slotImportFromOlderGpg4usb() { - QString dir = QFileDialog::getExistingDirectory(this, tr("Other GPGFrontend directory")); - - // Return, if cancel was hit - if (dir.isEmpty()) { - return; - } - - // try to import keys, if appropriate box is checked, return, if import was unsuccessful - if (gpg4usbKeyCheckBox->isChecked()) { - if (!Wizard::importPubAndSecKeysFromDir(dir + "/keydb", mKeyMgmt)) { - return; - } - } - - // try to import config, if appropriate box is checked - if (gpg4usbConfigCheckBox->isChecked()) { - slotImportConfFromGpg4usb(dir); - - settings.setValue("wizard/nextPage", this->nextId()); - QMessageBox::information(nullptr, tr("Configuration Imported"), - tr("Imported Configuration from old GPGFrontend.<br>" - "Will now restart to activate the configuration.")); - // TODO: edit->maybesave? - qApp->exit(RESTART_CODE); - } - wizard()->next(); -} - -bool ImportFromGpg4usbPage::slotImportConfFromGpg4usb(const QString &dir) { - QString path = dir + "/conf/gpgfrontend.ini"; - QSettings oldconf(path, QSettings::IniFormat, this); - QSettings actualConf; - foreach(QString key, oldconf.allKeys()) { - actualConf.setValue(key, oldconf.value(key)); - } - return true; -} - -int ImportFromGpg4usbPage::nextId() const { - return Wizard::Page_Conclusion; -} - -ImportFromGnupgPage::ImportFromGnupgPage(GpgME::GpgContext *ctx, KeyMgmt *keyMgmt, QWidget *parent) - : QWizardPage(parent) { - mCtx = ctx; - mKeyMgmt = keyMgmt; - setTitle(tr("Import keys...")); - setSubTitle(tr("...from existing GnuPG installation")); - - auto *gnupgLabel = new QLabel(tr("You can import keys from a locally installed GnuPG.<br><br> The location is read " - "from registry in Windows or assumed to be the .gnupg folder in the your home directory in Linux.<br>")); - gnupgLabel->setWordWrap(true); - - importFromGnupgButton = new QPushButton(tr("Import keys from GnuPG")); - connect(importFromGnupgButton, SIGNAL(clicked()), this, SLOT(slotrImportKeysFromGnupg())); - - auto *layout = new QGridLayout(); - layout->addWidget(gnupgLabel); - layout->addWidget(importFromGnupgButton); - - this->setLayout(layout); -} - -void ImportFromGnupgPage::slotrImportKeysFromGnupg() { - // first get gnupghomedir and check, if it exists - QString gnuPGHome = getGnuPGHome(); - if (gnuPGHome == nullptr) { - QMessageBox::critical(0, tr("Import Error"), tr("Couldn't locate GnuPG home directory")); - return; - } - - // Try to import the keyring files and return the return value of the method - Wizard::importPubAndSecKeysFromDir(gnuPGHome, mKeyMgmt); - wizard()->next(); -} - -QString ImportFromGnupgPage::getGnuPGHome() { - QString gnuPGHome = ""; -#ifdef _WIN32 - bool existsAndSuccess = false; - - HKEY hKey; - - existsAndSuccess = (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\GNU\\GNUPG", 0, KEY_READ, &hKey) == ERROR_SUCCESS); - - if (existsAndSuccess) { - QSettings gnuPGsettings("HKEY_CURRENT_USER\\Software\\GNU\\GNUPG", QSettings::NativeFormat); - if (gnuPGsettings.contains("HomeDir")) { - gnuPGHome = gnuPGsettings.value("HomeDir").toString(); - } else { - return NULL; - } - } -#else - gnuPGHome = QDir::homePath() + "/.gnupg"; - if (!QFile(gnuPGHome).exists()) { - return nullptr; - } -#endif - - return gnuPGHome; -} - -int ImportFromGnupgPage::nextId() const { - return Wizard::Page_Conclusion; -} - KeyGenPage::KeyGenPage(GpgME::GpgContext *ctx, QWidget *parent) : QWizardPage(parent) { mCtx = ctx; @@ -407,10 +224,11 @@ ConclusionPage::ConclusionPage(QWidget *parent) setTitle(tr("Ready.")); setSubTitle(tr("Have fun with GPGFrontend!")); - auto *bottomLabel = new QLabel(tr("You are ready to use GPGFrontend now.<br><br>")+ - "<a href=\"https://saturneric.github.io/GpgFrontend/index.html#/overview\">" - + tr("The Online Document") + "</a>" - + tr(" will get you started with GPGFrontend. It will open in the main window.<br>")); + auto *bottomLabel = new QLabel(tr("You are ready to use GPGFrontend now.<br><br>") + + "<a href=\"https://saturneric.github.io/GpgFrontend/index.html#/overview\">" + + tr("The Online Document") + "</a>" + + + tr(" will get you started with GPGFrontend. It will open in the main window.<br>")); bottomLabel->setTextFormat(Qt::RichText); bottomLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); diff --git a/src/ui/help/AboutDialog.cpp b/src/ui/help/AboutDialog.cpp index 807c509d..8fb504db 100644 --- a/src/ui/help/AboutDialog.cpp +++ b/src/ui/help/AboutDialog.cpp @@ -37,7 +37,7 @@ AboutDialog::AboutDialog(int defaultIndex, QWidget *parent) auto *tabWidget = new QTabWidget; auto *infoTab = new InfoTab(); auto *translatorsTab = new TranslatorsTab(); - auto *updateTab = new UpdateTab(); + updateTab = new UpdateTab(); tabWidget->addTab(infoTab, tr("General")); tabWidget->addTab(translatorsTab, tr("Translators")); @@ -45,9 +45,6 @@ AboutDialog::AboutDialog(int defaultIndex, QWidget *parent) connect(tabWidget, &QTabWidget::currentChanged, this, [&](int index) { qDebug() << "Current Index" << index; - if(index == 2) { - updateTab->getLatestVersion(); - } }); if(defaultIndex < tabWidget->count() && defaultIndex >= 0) { @@ -62,7 +59,14 @@ AboutDialog::AboutDialog(int defaultIndex, QWidget *parent) mainLayout->addWidget(buttonBox); setLayout(mainLayout); - this->exec(); + this->resize(320, 580); + + this->show(); +} + +void AboutDialog::showEvent(QShowEvent *ev) { + QDialog::showEvent(ev); + updateTab->getLatestVersion(); } InfoTab::InfoTab(QWidget *parent) @@ -157,6 +161,8 @@ UpdateTab::UpdateTab(QWidget *parent) { layout->addItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), 2, 1, 1, 1); + connect(this, SIGNAL(replyFromUpdateServer(QByteArray)), this, SLOT(processReplyDataFromUpdateServer(QByteArray))); + setLayout(layout); } @@ -172,44 +178,42 @@ void UpdateTab::getLatestVersion() { QNetworkRequest request; request.setUrl(QUrl(baseUrl)); - QNetworkReply *replay = manager->get(request); + auto thread = QThread::create([replay, this]() { + while(replay->isRunning()) QApplication::processEvents(); + emit replyFromUpdateServer(replay->readAll()); + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); - while(replay->isRunning()) { - QApplication::processEvents(); - } +} + +void UpdateTab::processReplyDataFromUpdateServer(const QByteArray& data) { - this->pb->setHidden(true); + qDebug() << "Try to Process Reply Data From Update Server"; - if(replay->error() != QNetworkReply::NoError) { + this->pb->setHidden(true); + + Document d; + if (d.Parse(data.constData()).HasParseError() || !d.IsObject()) { qDebug() << "VersionCheckThread Found Network Error"; auto latestVersion = "Unknown"; latestVersionLabel->setText("<center><b>" + tr("Latest Version From Github: ") + latestVersion + "</b></center>"); return; } - QByteArray bytes = replay->readAll(); - - Document d; - d.Parse(bytes.constData()); - QString latestVersion = d["tag_name"].GetString(); qDebug() << "Latest Version From Github" << latestVersion; - QRegularExpression re("^[vV](\\d+\\.)?(\\d+\\.)?(\\*|\\d+)"); + QRegularExpression re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); QRegularExpressionMatch match = re.match(latestVersion); if (match.hasMatch()) { - latestVersion = match.captured(0); // matched == "23 def" + latestVersion = match.captured(0); qDebug() << "Latest Version Matched" << latestVersion; - } else { - latestVersion = "Unknown"; - } + } else latestVersion = "Unknown"; latestVersionLabel->setText("<center><b>" + tr("Latest Version From Github: ") + latestVersion + "</b></center>"); - if(latestVersion > currentVersion) { - upgradeLabel->setHidden(false); - } - + if(latestVersion > currentVersion) upgradeLabel->setHidden(false); } diff --git a/src/ui/help/VersionCheckThread.cpp b/src/ui/help/VersionCheckThread.cpp index c7c77d1c..bf1bbeda 100644 --- a/src/ui/help/VersionCheckThread.cpp +++ b/src/ui/help/VersionCheckThread.cpp @@ -1,6 +1,26 @@ -// -// Created by Administrator on 2021/7/12. -// +/** + * 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/help/VersionCheckThread.h" #include "GpgFrontendBuildInfo.h" diff --git a/src/ui/keygen/KeygenDialog.cpp b/src/ui/keygen/KeygenDialog.cpp index 7991ddd1..98216dc6 100644 --- a/src/ui/keygen/KeygenDialog.cpp +++ b/src/ui/keygen/KeygenDialog.cpp @@ -23,6 +23,7 @@ */ #include "ui/keygen/KeygenDialog.h" +#include "ui/WaitingDialog.h" KeyGenDialog::KeyGenDialog(GpgME::GpgContext *ctx, QWidget *parent) : QDialog(parent), mCtx(ctx) { @@ -92,34 +93,28 @@ void KeyGenDialog::slotKeyGenAccept() { genKeyInfo.setExpired(dateEdit->dateTime()); } - kg = new KeyGenThread(&genKeyInfo, mCtx); - connect(kg, SIGNAL(signalKeyGenerated(bool)), this, SLOT(slotKeyGenResult(bool))); - kg->start(); - - this->accept(); - - auto *dialog = new QDialog(this, Qt::CustomizeWindowHint | Qt::WindowTitleHint); - dialog->setModal(true); - dialog->setWindowTitle(tr("Generating Key...")); - - auto *waitMessage = new QLabel( - tr("Collecting random data for key generation.\n This may take a while.\n To speed up the process use your computer\n (e.g. browse the net, listen to music,...)")); - auto *pb = new QProgressBar(); - pb->setRange(0, 0); - - auto *layout = new QVBoxLayout(dialog); - layout->addWidget(waitMessage); - layout->addWidget(pb); - dialog->setLayout(layout); + gpgme_error_t error = false; + auto thread = QThread::create([&]() { + error = mCtx->generateKey(&genKeyInfo); + }); + thread->start(); + auto *dialog = new WaitingDialog("Generating", this); dialog->show(); - while (kg->isRunning()) { + while (thread->isRunning()) { QCoreApplication::processEvents(); } dialog->close(); + if(gpgme_err_code(error) == GPG_ERR_NO_ERROR) { + QMessageBox::information(this, tr("Success"), tr("The new key pair has been generated.")); + this->close(); + } + else + QMessageBox::critical(this, tr("Failure"), tr(gpgme_strerror(error))); + } else { /** * create error message @@ -307,13 +302,6 @@ bool KeyGenDialog::check_email_address(const QString &str) { return re_email.match(str).hasMatch(); } -void KeyGenDialog::slotKeyGenResult(bool success) { - if(success) - QMessageBox::information(nullptr, tr("Success"), tr("The new key pair has been generated.")); - else - QMessageBox::critical(nullptr, tr("Failure"), tr("An error occurred during key generation.")); -} - QGroupBox *KeyGenDialog::create_basic_info_group_box() { errorLabel = new QLabel(tr("")); diff --git a/src/ui/keygen/SubkeyGenerateDialog.cpp b/src/ui/keygen/SubkeyGenerateDialog.cpp index c545381f..3d709d81 100644 --- a/src/ui/keygen/SubkeyGenerateDialog.cpp +++ b/src/ui/keygen/SubkeyGenerateDialog.cpp @@ -23,6 +23,7 @@ */ #include "ui/keygen/SubkeyGenerateDialog.h" +#include "ui/WaitingDialog.h" SubkeyGenerateDialog::SubkeyGenerateDialog(GpgME::GpgContext *ctx, const GpgKey &key, QWidget *parent) : genKeyInfo(true), mCtx(ctx), mKey(key), QDialog(parent) { @@ -224,34 +225,27 @@ void SubkeyGenerateDialog::slotKeyGenAccept() { genKeyInfo.setExpired(dateEdit->dateTime()); } - kg = new SubkeyGenerateThread(mKey ,&genKeyInfo, mCtx); - connect(kg, SIGNAL(signalKeyGenerated(bool)), this, SLOT(slotKeyGenResult(bool))); - kg->start(); - - this->accept(); - - auto *dialog = new QDialog(this, Qt::CustomizeWindowHint | Qt::WindowTitleHint); - dialog->setModal(true); - dialog->setWindowTitle(tr("Generating Subkey...")); - - auto *waitMessage = new QLabel( - tr("Collecting random data for subkey generation.\n This may take a while.\n To speed up the process use your computer\n (e.g. browse the net, listen to music,...)")); - auto *pb = new QProgressBar(); - pb->setRange(0, 0); - - auto *layout = new QVBoxLayout(dialog); - layout->addWidget(waitMessage); - layout->addWidget(pb); - dialog->setLayout(layout); + gpgme_error_t error = false; + auto thread = QThread::create([&]() { + error = mCtx->generateSubkey(mKey, &genKeyInfo); + }); + thread->start(); + auto *dialog = new WaitingDialog("Generating", this); dialog->show(); - while (!kg->isFinished() && kg->isRunning()) { + while (thread->isRunning()) { QCoreApplication::processEvents(); } dialog->close(); + if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) { + QMessageBox::information(nullptr, tr("Success"), tr("The new subkey has been generated.")); + this->close(); + } else + QMessageBox::critical(this, tr("Failure"), tr(gpgme_strerror(error))); + } else { /** * create error message @@ -303,10 +297,3 @@ void SubkeyGenerateDialog::slotActivatedKeyType(int index) { genKeyInfo.setAlgo(this->keyTypeComboBox->itemText(index)); refresh_widgets_state(); } - -void SubkeyGenerateDialog::slotKeyGenResult(bool success) { - if(success) - QMessageBox::information(nullptr, tr("Success"), tr("The new subkey has been generated.")); - else - QMessageBox::critical(nullptr, tr("Failure"), tr("An error occurred during subkey generation.")); -} diff --git a/src/ui/keygen/SubkeyGenerateThread.cpp b/src/ui/keygen/SubkeyGenerateThread.cpp deleted file mode 100644 index 125f35f8..00000000 --- a/src/ui/keygen/SubkeyGenerateThread.cpp +++ /dev/null @@ -1,36 +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]><[email protected]> starting on May 12, 2021. - * - */ - -#include "ui/keygen/SubkeyGenerateThread.h" - -SubkeyGenerateThread::SubkeyGenerateThread(GpgKey key, GenKeyInfo *keyGenParams, GpgME::GpgContext *ctx) - : mKey(std::move(key)), keyGenParams(keyGenParams) , mCtx(ctx) { - connect(this, &SubkeyGenerateThread::finished, this, &SubkeyGenerateThread::deleteLater); -} - -void SubkeyGenerateThread::run() { - bool success = mCtx->generateSubkey(mKey, keyGenParams); - emit signalKeyGenerated(success); - emit finished({}); -} diff --git a/src/ui/keypair_details/EditSubKeyDialog.cpp b/src/ui/keypair_details/EditSubKeyDialog.cpp index 5e26a098..e44c987f 100644 --- a/src/ui/keypair_details/EditSubKeyDialog.cpp +++ b/src/ui/keypair_details/EditSubKeyDialog.cpp @@ -1,5 +1,25 @@ -// -// Created by eric on 2021/6/2. -// +/** + * 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/keypair_details/EditSubKeyDialog.h" diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index 9ca4e37e..c0a2df99 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -147,7 +147,6 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q keyServerOperaButton->setStyleSheet("text-align:center;"); auto *revokeCertGenButton = new QPushButton(tr("Generate Revoke Certificate")); - revokeCertGenButton->setDisabled(true); connect(revokeCertGenButton, SIGNAL(clicked()), this, SLOT(slotGenRevokeCert())); hBoxLayout->addWidget(keyServerOperaButton); @@ -215,7 +214,11 @@ void KeyPairDetailTab::slotExportPrivateKey() { return; } - auto &key = mCtx->getKeyById(*keyid); + auto key = mCtx->getKeyById(*keyid); + if (!key.good) { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); + return; + } QString fileString = key.name + " " + key.email + "(" + key.id + ")_secret.asc"; QString fileName = QFileDialog::getSaveFileName(this, tr("Export Key To File"), fileString, @@ -342,15 +345,8 @@ void KeyPairDetailTab::slotGenRevokeCert() { QStringLiteral("%1 (*.rev)").arg( tr("Revocation Certificates"))); - auto process = mCtx->generateRevokeCert(mKey, mOutputFileName); - - auto *dialog = new WaitingDialog("Generating", this); - - while (process->state() == QProcess::Running) { - QApplication::processEvents(); - } - - dialog->close(); + if (!mOutputFileName.isEmpty()) + mCtx->generateRevokeCert(mKey, mOutputFileName); } diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp index 74b52284..6a924394 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp @@ -132,9 +132,8 @@ void KeyPairSubkeyTab::slotRefreshSubkeyList() { this->buffered_subkeys.clear(); for(const auto &subkeys : mKey.subKeys) { - if(subkeys.disabled || subkeys.revoked) { + if(subkeys.disabled || subkeys.revoked) continue; - } this->buffered_subkeys.push_back(&subkeys); } diff --git a/src/ui/keypair_details/KeyUIDSignDialog.cpp b/src/ui/keypair_details/KeyUIDSignDialog.cpp index f112ea25..9232cfce 100644 --- a/src/ui/keypair_details/KeyUIDSignDialog.cpp +++ b/src/ui/keypair_details/KeyUIDSignDialog.cpp @@ -89,13 +89,12 @@ void KeyUIDSignDialog::slotSignKey(bool clicked) { // Set Signers QVector<GpgKey> keys; mKeyList->getCheckedKeys(keys); - mCtx->setSigners(keys); const auto expires = expiresEdit->dateTime(); for(const auto &uid : mUids) { // Sign For mKey - if (!mCtx->signKey(mKey, uid.uid, &expires)) { + if (!mCtx->signKey(mKey, keys, uid.uid, &expires)) { QMessageBox::critical(nullptr, tr("Unsuccessful Operation"), QString(tr("Signature operation failed for UID ") + "%1") diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp new file mode 100644 index 00000000..e391c666 --- /dev/null +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -0,0 +1,610 @@ +/** + * 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 "MainWindow.h" + +void MainWindow::slotFileEncrypt() { + + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + QFileInfo pathInfo(fileInfo.absolutePath()); + + if (!fileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); + return; + } + if (!pathInfo.isWritable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); + return; + } + if (QFile::exists(path + ".asc")) { + auto ret = QMessageBox::warning(this, + tr("Warning"), + tr("The target file already exists, do you need to overwrite it?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + } + + QVector<GpgKey> keys; + + mKeyList->getCheckedKeys(keys); + + if (keys.empty()) { + QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); + return; + } + + for (const auto &key : keys) { + if (!GpgME::GpgContext::checkIfKeyCanEncr(key)) { + QMessageBox::information(this, + tr("Invalid Operation"), + tr("The selected key contains a key that does not actually have a encrypt usage.<br/>") + + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); + return; + + } + } + + gpgme_encrypt_result_t result; + + gpgme_error_t error; + bool if_error = false; + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::encryptFile(mCtx, keys, path, &result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog(tr("Encrypting"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + + dialog->close(); + if (!if_error) { + auto resultAnalyse = new EncryptResultAnalyse(error, result); + auto &reportText = resultAnalyse->getResultReport(); + infoBoard->associateTabWidget(edit->tabWidget); + infoBoard->associateFileTreeView(edit->curFilePage()); + + if (resultAnalyse->getStatus() < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (resultAnalyse->getStatus() > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + delete resultAnalyse; + + fileTreeView->update(); + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } +} + +void MainWindow::slotFileDecrypt() { + + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + QFileInfo pathInfo(fileInfo.absolutePath()); + if (!fileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); + return; + } + if (!pathInfo.isWritable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); + return; + } + + QString outFileName, fileExtension = fileInfo.suffix(); + + if (fileExtension == "asc" || fileExtension == "gpg") { + int pos = path.lastIndexOf(QChar('.')); + outFileName = path.left(pos); + } else { + outFileName = path + ".out"; + } + + if (QFile::exists(outFileName)) { + auto ret = QMessageBox::warning(this, + tr("Warning"), + tr("The target file already exists, do you need to overwrite it?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + } + + gpgme_decrypt_result_t result; + gpgme_error_t error; + bool if_error = false; + + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::decryptFile(mCtx, path, &result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog("Decrypting", this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + + dialog->close(); + + if (!if_error) { + auto resultAnalyse = new DecryptResultAnalyse(mCtx, error, result); + auto &reportText = resultAnalyse->getResultReport(); + infoBoard->associateTabWidget(edit->tabWidget); + infoBoard->associateFileTreeView(edit->curFilePage()); + + if (resultAnalyse->getStatus() < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (resultAnalyse->getStatus() > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + delete resultAnalyse; + + fileTreeView->update(); + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } + + +} + +void MainWindow::slotFileSign() { + + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + QFileInfo pathInfo(fileInfo.absolutePath()); + + if (!fileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); + return; + } + if (!pathInfo.isWritable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); + return; + } + + if (QFile::exists(path + ".sig")) { + auto ret = QMessageBox::warning(this, + tr("Warning"), + tr("The target file already exists, do you need to overwrite it?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + } + + QVector<GpgKey> keys; + + mKeyList->getCheckedKeys(keys); + + if (keys.empty()) { + QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); + return; + } + + for (const auto &key : keys) { + if (!GpgME::GpgContext::checkIfKeyCanEncr(key)) { + QMessageBox::information(this, + tr("Invalid Operation"), + tr("The selected key contains a key that does not actually have a encrypt usage.<br/>") + + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); + return; + + } + } + + gpgme_sign_result_t result; + gpgme_error_t error; + bool if_error = false; + + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::signFile(mCtx, keys, path, &result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog(tr("Signing"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + + dialog->close(); + + if (!if_error) { + + auto resultAnalyse = new SignResultAnalyse(mCtx, error, result); + auto &reportText = resultAnalyse->getResultReport(); + infoBoard->associateTabWidget(edit->tabWidget); + infoBoard->associateFileTreeView(edit->curFilePage()); + + if (resultAnalyse->getStatus() < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (resultAnalyse->getStatus() > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + delete resultAnalyse; + + fileTreeView->update(); + + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } + + fileTreeView->update(); + +} + +void MainWindow::slotFileVerify() { + + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + + QString signFilePath, dataFilePath; + + 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"; + } + + QFileInfo dataFileInfo(dataFilePath), signFileInfo(signFilePath); + + if (!dataFileInfo.isFile() || !signFileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), + tr("Please select the appropriate target file or signature file. Ensure that both are in this directory.")); + return; + } + if (!dataFileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read target file.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read signature file.")); + return; + } + + gpgme_verify_result_t result; + + gpgme_error_t error; + bool if_error = false; + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::verifyFile(mCtx, dataFilePath, &result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog(tr("Verifying"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + dialog->close(); + + if (!if_error) { + auto resultAnalyse = new VerifyResultAnalyse(mCtx, error, result); + auto &reportText = resultAnalyse->getResultReport(); + infoBoard->associateTabWidget(edit->tabWidget); + infoBoard->associateFileTreeView(edit->curFilePage()); + + if (resultAnalyse->getStatus() < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (resultAnalyse->getStatus() > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + if (resultAnalyse->getStatus() >= 0) { + infoBoard->resetOptionActionsMenu(); + infoBoard->addOptionalAction("Show Verify Details", [this, error, result]() { + VerifyDetailsDialog(this, mCtx, mKeyList, error, result); + }); + } + + delete resultAnalyse; + + fileTreeView->update(); + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } +} + +void MainWindow::slotFileEncryptSign() { + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + QFileInfo pathInfo(fileInfo.absolutePath()); + + if (!fileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); + return; + } + if (!pathInfo.isWritable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); + return; + } + if (QFile::exists(path + ".gpg")) { + auto ret = QMessageBox::warning(this, + tr("Warning"), + tr("The target file already exists, do you need to overwrite it?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + } + + QVector<GpgKey> keys; + + mKeyList->getCheckedKeys(keys); + + if (keys.empty()) { + QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); + return; + } + + bool can_sign = false, can_encr = false; + + for (const auto &key : keys) { + bool key_can_sign = GpgME::GpgContext::checkIfKeyCanSign(key); + bool key_can_encr = GpgME::GpgContext::checkIfKeyCanEncr(key); + + if (!key_can_sign && !key_can_encr) { + QMessageBox::critical(nullptr, + tr("Invalid KeyPair"), + tr("The selected keypair cannot be used for signing and encryption at the same time.<br/>") + + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); + return; + } + + if (key_can_sign) can_sign = true; + if (key_can_encr) can_encr = true; + } + + if (!can_encr) { + QMessageBox::critical(nullptr, + tr("Incomplete Operation"), + tr("None of the selected key pairs can provide the encryption function.")); + return; + } + + if (!can_sign) { + QMessageBox::warning(nullptr, + tr("Incomplete Operation"), + tr("None of the selected key pairs can provide the signature function.")); + } + + gpgme_encrypt_result_t encr_result = nullptr; + gpgme_sign_result_t sign_result = nullptr; + + gpgme_error_t error; + bool if_error = false; + + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::encryptSignFile(mCtx, keys, path, &encr_result, &sign_result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog(tr("Encrypting and Signing"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + dialog->close(); + + if (!if_error) { + + auto resultAnalyseEncr = new EncryptResultAnalyse(error, encr_result); + auto resultAnalyseSign = new SignResultAnalyse(mCtx, error, sign_result); + int status = std::min(resultAnalyseEncr->getStatus(), resultAnalyseSign->getStatus()); + auto reportText = resultAnalyseEncr->getResultReport() + resultAnalyseSign->getResultReport(); + + infoBoard->associateFileTreeView(edit->curFilePage()); + + if (status < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (status > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + delete resultAnalyseEncr; + delete resultAnalyseSign; + + fileTreeView->update(); + + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } +} + +void MainWindow::slotFileDecryptVerify() { + auto fileTreeView = edit->slotCurPageFileTreeView(); + auto path = fileTreeView->getSelected(); + + QFileInfo fileInfo(path); + QFileInfo pathInfo(fileInfo.absolutePath()); + if (!fileInfo.isFile()) { + QMessageBox::critical(this, tr("Error"), tr("Select a file(.gpg/.asc) before doing it.")); + return; + } + if (!fileInfo.isReadable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); + return; + } + if (!pathInfo.isWritable()) { + QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); + return; + } + + QString outFileName, fileExtension = fileInfo.suffix(); + + if (fileExtension == "asc" || fileExtension == "gpg") { + int pos = path.lastIndexOf(QChar('.')); + outFileName = path.left(pos); + } else { + outFileName = path + ".out"; + } + + gpgme_decrypt_result_t d_result = nullptr; + gpgme_verify_result_t v_result = nullptr; + + gpgme_error_t error; + bool if_error = false; + + auto thread = QThread::create([&]() { + try { + error = GpgFileOpera::decryptVerifyFile(mCtx, path, &d_result, &v_result); + } catch (const std::runtime_error &e) { + if_error = true; + } + }); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + + + auto *dialog = new WaitingDialog(tr("Decrypting and Verifying"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + dialog->close(); + + if (!if_error) { + infoBoard->associateFileTreeView(edit->curFilePage()); + + auto resultAnalyseDecrypt = new DecryptResultAnalyse(mCtx, error, d_result); + auto resultAnalyseVerify = new VerifyResultAnalyse(mCtx, error, v_result); + + int status = std::min(resultAnalyseDecrypt->getStatus(), resultAnalyseVerify->getStatus()); + auto &reportText = resultAnalyseDecrypt->getResultReport() + resultAnalyseVerify->getResultReport(); + if (status < 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); + else if (status > 0) + infoBoard->slotRefresh(reportText, INFO_ERROR_OK); + else + infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + + if (resultAnalyseVerify->getStatus() >= 0) { + infoBoard->resetOptionActionsMenu(); + infoBoard->addOptionalAction("Show Verify Details", [this, error, v_result]() { + VerifyDetailsDialog(this, mCtx, mKeyList, error, v_result); + }); + } + delete resultAnalyseDecrypt; + delete resultAnalyseVerify; + + fileTreeView->update(); + } else { + QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; + } +} + +void MainWindow::slotFileEncryptCustom() { + QStringList *keyList; + keyList = mKeyList->getChecked(); + new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Encrypt, this); +} + +void MainWindow::slotFileDecryptCustom() { + QStringList *keyList; + keyList = mKeyList->getChecked(); + new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Decrypt, this); +} + +void MainWindow::slotFileSignCustom() { + QStringList *keyList; + keyList = mKeyList->getChecked(); + new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Sign, this); +} + +void MainWindow::slotFileVerifyCustom() { + QStringList *keyList; + keyList = mKeyList->getChecked(); + new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Verify, this); +} diff --git a/src/ui/main_window/MainWindowServerSlotFunction.cpp b/src/ui/main_window/MainWindowServerSlotFunction.cpp new file mode 100644 index 00000000..3a7e9f71 --- /dev/null +++ b/src/ui/main_window/MainWindowServerSlotFunction.cpp @@ -0,0 +1,238 @@ +/** + * 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 "MainWindow.h" +#include "server/ComUtils.h" +#include "ui/ShowCopyDialog.h" + +#include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" + +/** + * get full size crypt text from server using short crypto text + * @param shortenCryptoText short crypto text([GpgFrontend_ShortCrypto]://) + * @return + */ +QString MainWindow::getCryptText(const QString &shortenCryptoText) { + + QString ownKeyId = settings.value("general/ownKeyId").toString(); + + GpgKey key = mCtx->getKeyById(ownKeyId); + if (!key.good) { + QMessageBox::critical(this, tr("Invalid Own Key"), + tr("Own Key can not be use to do any operation. " + "Please go to the setting interface to select an OwnKey and get a ServiceToken.")); + return {}; + } + + auto utils = new ComUtils(this); + + QString serviceToken = settings.value("general/serviceToken").toString(); + if (serviceToken.isEmpty() || !utils->checkServiceTokenFormat(serviceToken)) { + QMessageBox::critical(this, tr("Error"), + tr("Please obtain a Service Token from the server in the settings.")); + return {}; + } + + QUrl reqUrl(utils->getUrl(ComUtils::GetFullCryptText)); + QNetworkRequest request(reqUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + // Sign Shorten Text + auto outSignTextBase64 = ComUtils::getSignStringBase64(mCtx, shortenCryptoText, key); + + rapidjson::Document doc; + doc.SetObject(); + + rapidjson::Value s, t; + + // Signature + s.SetString(outSignTextBase64.constData(), outSignTextBase64.count()); + // Service Token + const auto t_byte_array = serviceToken.toUtf8(); + t.SetString(t_byte_array.constData(), t_byte_array.count()); + + rapidjson::Document::AllocatorType &allocator = doc.GetAllocator(); + + doc.AddMember("signature", s, allocator); + doc.AddMember("serviceToken", t, allocator); + + rapidjson::StringBuffer sb; + rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb); + doc.Accept(writer); + + QByteArray postData(sb.GetString()); + qDebug() << "postData" << QString::fromUtf8(postData); + + QNetworkReply *reply = utils->getNetworkManager().post(request, postData); + + auto dialog = new WaitingDialog(tr("Getting Cpt From Server"), this); + dialog->show(); + + while (reply->isRunning()) QApplication::processEvents(); + + dialog->close(); + + QByteArray replyData = reply->readAll().constData(); + if (utils->checkServerReply(replyData)) { + /** + * { + * "cryptoText" : ... + * "sha": ... + * "serviceToken": ... + * "date": ... + * } + */ + + if (!utils->checkDataValueStr("cryptoText") + || !utils->checkDataValueStr("sha") + || !utils->checkDataValueStr("serviceToken")) { + QMessageBox::critical(this, tr("Error"), + tr("The communication content with the server does not meet the requirements")); + return {}; + } + + auto cryptoText = utils->getDataValueStr("cryptoText"); + auto sha = utils->getDataValueStr("sha"); + auto serviceTokenFromServer = utils->getDataValueStr("serviceToken"); + + QCryptographicHash sha_generator(QCryptographicHash::Sha256); + sha_generator.addData(cryptoText.toUtf8()); + + if (sha_generator.result().toHex() == sha && serviceToken == serviceTokenFromServer) { + return cryptoText; + } else QMessageBox::critical(this, tr("Error"), tr("Invalid short ciphertext")); + + return {}; + } + + return {}; +} + +void MainWindow::shortenCryptText() { + + // gather information + QString serviceToken = settings.value("general/serviceToken").toString(); + QString ownKeyId = settings.value("general/ownKeyId").toString(); + QByteArray cryptoText = edit->curTextPage()->toPlainText().toUtf8(); + + auto utils = new ComUtils(this); + + if (serviceToken.isEmpty() || !utils->checkServiceTokenFormat(serviceToken)) { + QMessageBox::critical(this, tr("Invalid Service Token"), + tr("Please go to the setting interface to get a ServiceToken.")); + return; + } + + QUrl reqUrl(utils->getUrl(ComUtils::ShortenCryptText)); + QNetworkRequest request(reqUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + GpgKey key = mCtx->getKeyById(ownKeyId); + if (!key.good) { + QMessageBox::critical(this, tr("Invalid Own Key"), tr("Own Key can not be use to do any operation.")); + return; + } + + QCryptographicHash ch(QCryptographicHash::Md5); + ch.addData(cryptoText); + QString md5 = ch.result().toHex(); + + qDebug() << "md5" << md5; + + QByteArray signText = QString("[%1][%2]").arg(serviceToken, md5).toUtf8(); + + QCryptographicHash sha(QCryptographicHash::Sha256); + sha.addData(signText); + QString shaText = sha.result().toHex(); + + qDebug() << "shaText" << shaText; + + QByteArray outSignTextBase64 = ComUtils::getSignStringBase64(mCtx, signText, key); + + rapidjson::Value c, s, m, t; + + rapidjson::Document doc; + doc.SetObject(); + + c.SetString(cryptoText.constData(), cryptoText.count()); + auto m_byte_array = shaText.toUtf8(); + m.SetString(m_byte_array.constData(), m_byte_array.count()); + s.SetString(outSignTextBase64.constData(), outSignTextBase64.count()); + auto t_byte_array = serviceToken.toUtf8(); + t.SetString(t_byte_array.constData(), t_byte_array.count()); + + rapidjson::Document::AllocatorType &allocator = doc.GetAllocator(); + + doc.AddMember("cryptoText", c, allocator); + doc.AddMember("sha", m, allocator); + doc.AddMember("sign", s, allocator); + doc.AddMember("serviceToken", t, allocator); + + rapidjson::StringBuffer sb; + rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb); + doc.Accept(writer); + + QByteArray postData(sb.GetString()); + qDebug() << "postData" << QString::fromUtf8(postData); + + QNetworkReply *reply = networkAccessManager->post(request, postData); + + auto *dialog = new WaitingDialog(tr("Getting Scpt From Server"), this); + dialog->show(); + while (reply->isRunning()) QApplication::processEvents(); + dialog->close(); + + if (utils->checkServerReply(reply->readAll().constData())) { + + /** + * { + * "shortenText" : ... + * "md5": ... + * } + */ + + if (!utils->checkDataValueStr("shortenText") || !utils->checkDataValueStr("md5")) { + QMessageBox::critical(this, tr("Error"), + tr("The communication content with the server does not meet the requirements")); + return; + } + + QString shortenText = utils->getDataValueStr("shortenText"); + + QCryptographicHash md5_generator(QCryptographicHash::Md5); + md5_generator.addData(shortenText.toUtf8()); + if (md5_generator.result().toHex() == utils->getDataValueStr("md5")) { + auto *dialog = new ShowCopyDialog(shortenText, + tr("Notice: Use Decrypt & Verify operation to decrypt this short crypto text."), + this); + dialog->show(); + } else { + QMessageBox::critical(this, tr("Error"), tr("There is a problem with the communication with the server")); + return; + } + } + +} + diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 4bcee080..501418d6 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -24,7 +24,14 @@ #include "MainWindow.h" #include "ui/SendMailDialog.h" +#include "ui/widgets/SignersPicker.h" +#include "server/api/PubkeyUploader.h" +#include "advance/UnknownSignersChecker.h" + +/** + * Encrypt Entry(Text & File) + */ void MainWindow::slotEncrypt() { if (edit->tabCount() == 0) return; @@ -50,33 +57,33 @@ void MainWindow::slotEncrypt() { } } - auto *tmp = new QByteArray(); + auto tmp = QByteArray(); gpgme_encrypt_result_t result = nullptr; gpgme_error_t error; auto thread = QThread::create([&]() { - error = mCtx->encrypt(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, &result); + error = mCtx->encrypt(keys, edit->curTextPage()->toPlainText().toUtf8(), &tmp, &result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); auto *dialog = new WaitingDialog(tr("Encrypting"), this); - while (thread->isRunning()) { + while (thread->isRunning()) QApplication::processEvents(); - } dialog->close(); auto resultAnalyse = new EncryptResultAnalyse(error, result); auto &reportText = resultAnalyse->getResultReport(); - auto *tmp2 = new QString(*tmp); - edit->slotFillTextEditWithText(*tmp2); + auto tmp2 = QString(tmp); + edit->slotFillTextEditWithText(tmp2); infoBoard->associateTextEdit(edit->curTextPage()); + // check result analyse status if (resultAnalyse->getStatus() < 0) infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); else if (resultAnalyse->getStatus() > 0) @@ -84,10 +91,11 @@ void MainWindow::slotEncrypt() { else infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + // set optional actions if (resultAnalyse->getStatus() >= 0) { infoBoard->resetOptionActionsMenu(); infoBoard->addOptionalAction("Send Mail", [this]() { - if(settings.value("sendMail/enable", false).toBool()) + if (settings.value("sendMail/enable", false).toBool()) new SendMailDialog(edit->curTextPage()->toPlainText(), this); else { QMessageBox::warning(nullptr, @@ -128,15 +136,15 @@ void MainWindow::slotSign() { } } - auto *tmp = new QByteArray(); + auto tmp = QByteArray(); gpgme_sign_result_t result = nullptr; gpgme_error_t error; auto thread = QThread::create([&]() { - error = mCtx->sign(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, false, &result); + error = mCtx->sign(keys, edit->curTextPage()->toPlainText().toUtf8(), &tmp, GPGME_SIG_MODE_CLEAR, &result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); auto *dialog = new WaitingDialog(tr("Signing"), this); @@ -146,9 +154,9 @@ void MainWindow::slotSign() { dialog->close(); infoBoard->associateTextEdit(edit->curTextPage()); - edit->slotFillTextEditWithText(QString::fromUtf8(*tmp)); + edit->slotFillTextEditWithText(QString::fromUtf8(tmp)); - auto resultAnalyse = new SignResultAnalyse(error, result); + auto resultAnalyse = new SignResultAnalyse(mCtx, error, result); auto &reportText = resultAnalyse->getResultReport(); if (resultAnalyse->getStatus() < 0) @@ -169,18 +177,23 @@ void MainWindow::slotDecrypt() { if (edit->slotCurPageTextEdit() != nullptr) { - auto *decrypted = new QByteArray(); + auto decrypted = QByteArray(); QByteArray text = edit->curTextPage()->toPlainText().toUtf8(); GpgME::GpgContext::preventNoDataErr(&text); + if (text.trimmed().startsWith(GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD)) { + QMessageBox::critical(this, tr("Notice"), tr("Short Crypto Text only supports Decrypt & Verify.")); + return; + } + gpgme_decrypt_result_t result = nullptr; gpgme_error_t error; auto thread = QThread::create([&]() { // try decrypt, if fail do nothing, especially don't replace text - error = mCtx->decrypt(text, decrypted, &result); + error = mCtx->decrypt(text, &decrypted, &result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); auto *dialog = new WaitingDialog(tr("Decrypting"), this); @@ -193,7 +206,7 @@ void MainWindow::slotDecrypt() { infoBoard->associateTextEdit(edit->curTextPage()); if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) - edit->slotFillTextEditWithText(QString::fromUtf8(*decrypted)); + edit->slotFillTextEditWithText(QString::fromUtf8(decrypted)); auto resultAnalyse = new DecryptResultAnalyse(mCtx, error, result); @@ -233,20 +246,18 @@ void MainWindow::slotVerify() { QByteArray text = edit->curTextPage()->toPlainText().toUtf8(); GpgME::GpgContext::preventNoDataErr(&text); - gpgme_verify_result_t result; gpgme_error_t error; auto thread = QThread::create([&]() { error = mCtx->verify(&text, nullptr, &result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); auto *dialog = new WaitingDialog(tr("Verifying"), this); - while (thread->isRunning()) { + while (thread->isRunning()) QApplication::processEvents(); - } dialog->close(); auto resultAnalyse = new VerifyResultAnalyse(mCtx, error, result); @@ -274,7 +285,6 @@ void MainWindow::slotVerify() { void MainWindow::slotEncryptSign() { - if (edit->tabCount() == 0) return; if (edit->slotCurPageTextEdit() != nullptr) { @@ -287,49 +297,49 @@ void MainWindow::slotEncryptSign() { return; } - bool can_sign = false, can_encr = false; - for (const auto &key : keys) { - bool key_can_sign = GpgME::GpgContext::checkIfKeyCanSign(key); bool key_can_encr = GpgME::GpgContext::checkIfKeyCanEncr(key); - if (!key_can_sign && !key_can_encr) { + if (!key_can_encr) { QMessageBox::critical(nullptr, tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for signing and encryption at the same time.<br/>") + tr("The selected keypair cannot be used for encryption.<br/>") + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); return; } - if (key_can_sign) can_sign = true; - if (key_can_encr) can_encr = true; } - if (!can_encr) { - QMessageBox::critical(nullptr, - tr("Incomplete Operation"), - tr("None of the selected key pairs can provide the encryption function.")); - return; + QVector<GpgKey> signerKeys; + + auto signersPicker = new SignersPicker(mCtx, this); + + QEventLoop loop; + connect(signersPicker, SIGNAL(finished(int)), &loop, SLOT(quit())); + loop.exec(); + + signersPicker->getCheckedSigners(signerKeys); + + for (const auto &key : keys) { + qDebug() << "Keys " << key.email; } - if (!can_sign) { - QMessageBox::warning(nullptr, - tr("Incomplete Operation"), - tr("None of the selected key pairs can provide the signature function.")); + for (const auto &signer : signerKeys) { + qDebug() << "Signers " << signer.email; } - auto *tmp = new QByteArray(); + + auto tmp = QByteArray(); gpgme_encrypt_result_t encr_result = nullptr; gpgme_sign_result_t sign_result = nullptr; - gpgme_decrypt_result_t result = nullptr; - gpgme_error_t error; auto thread = QThread::create([&]() { - error = mCtx->encryptSign(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, &encr_result, + error = mCtx->encryptSign(keys, signerKeys, edit->curTextPage()->toPlainText().toUtf8(), &tmp, + &encr_result, &sign_result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); auto *dialog = new WaitingDialog(tr("Encrypting and Signing"), this); @@ -337,15 +347,29 @@ void MainWindow::slotEncryptSign() { QApplication::processEvents(); } + if (settings.value("advanced/autoPubkeyExchange").toBool()) { + PubkeyUploader pubkeyUploader(mCtx, signerKeys); + pubkeyUploader.start(); + if (!pubkeyUploader.result()) { + QMessageBox::warning(nullptr, + tr("Automatic Key Exchange Warning"), + tr("Part of the automatic key exchange failed, which may be related to your key.") + + + tr("If possible, try to use the RSA algorithm compatible with the server for signing.")); + } + } + dialog->close(); if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) { - auto *tmp2 = new QString(*tmp); - edit->slotFillTextEditWithText(*tmp2); + auto tmp2 = QString(tmp); + edit->slotFillTextEditWithText(tmp2); } + qDebug() << "Start Analyse Result"; + auto resultAnalyseEncr = new EncryptResultAnalyse(error, encr_result); - auto resultAnalyseSign = new SignResultAnalyse(error, sign_result); + auto resultAnalyseSign = new SignResultAnalyse(mCtx, error, sign_result); int status = std::min(resultAnalyseEncr->getStatus(), resultAnalyseSign->getStatus()); auto reportText = resultAnalyseEncr->getResultReport() + resultAnalyseSign->getResultReport(); @@ -358,10 +382,12 @@ void MainWindow::slotEncryptSign() { else infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + qDebug() << "End Analyse Result"; + if (status >= 0) { infoBoard->resetOptionActionsMenu(); infoBoard->addOptionalAction("Send Mail", [this]() { - if(settings.value("sendMail/enable", false).toBool()) + if (settings.value("sendMail/enable", false).toBool()) new SendMailDialog(edit->curTextPage()->toPlainText(), this); else { QMessageBox::warning(nullptr, @@ -369,6 +395,15 @@ void MainWindow::slotEncryptSign() { tr("Please go to the settings interface to enable and configure this function.")); } }); + infoBoard->addOptionalAction("Shorten Ciphertext", [this]() { + if (settings.value("general/serviceToken").toString().isEmpty()) + QMessageBox::warning(nullptr, + tr("Service Token Empty"), + tr("Please go to the settings interface to set Own Key and get Service Token.")); + else { + shortenCryptText(); + } + }); } delete resultAnalyseEncr; @@ -384,31 +419,56 @@ void MainWindow::slotDecryptVerify() { if (edit->slotCurPageTextEdit() != nullptr) { - auto *decrypted = new QByteArray(); - QByteArray text = edit->curTextPage()->toPlainText().toUtf8(); + auto decrypted = QByteArray(); + QString plainText = edit->curTextPage()->toPlainText(); + + + if (plainText.trimmed().startsWith(GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD)) { + auto cryptoText = getCryptText(plainText); + if (!cryptoText.isEmpty()) { + plainText = cryptoText; + } + } + + QByteArray text = plainText.toUtf8(); + GpgME::GpgContext::preventNoDataErr(&text); gpgme_decrypt_result_t d_result = nullptr; gpgme_verify_result_t v_result = nullptr; + auto *dialog = new WaitingDialog(tr("Decrypting and Verifying"), this); + + // Automatically import public keys that are not stored locally + if (settings.value("advanced/autoPubkeyExchange").toBool()) { + gpgme_verify_result_t tmp_v_result = nullptr; + auto thread = QThread::create([&]() { + mCtx->verify(&text, nullptr, &tmp_v_result); + }); + thread->start(); + while (thread->isRunning()) QApplication::processEvents(); + auto *checker = new UnknownSignersChecker(mCtx, tmp_v_result); + checker->start(); + checker->deleteLater(); + } + gpgme_error_t error; auto thread = QThread::create([&]() { - error = mCtx->decryptVerify(text, decrypted, &d_result, &v_result); + error = mCtx->decryptVerify(text, &decrypted, &d_result, &v_result); }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); - auto *dialog = new WaitingDialog(tr("Decrypting and Verifying"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } + while (thread->isRunning()) QApplication::processEvents(); dialog->close(); + qDebug() << "Start Analyse Result"; + infoBoard->associateTextEdit(edit->curTextPage()); if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) - edit->slotFillTextEditWithText(QString::fromUtf8(*decrypted)); + edit->slotFillTextEditWithText(QString::fromUtf8(decrypted)); auto resultAnalyseDecrypt = new DecryptResultAnalyse(mCtx, error, d_result); auto resultAnalyseVerify = new VerifyResultAnalyse(mCtx, error, v_result); @@ -430,11 +490,15 @@ void MainWindow::slotDecryptVerify() { } delete resultAnalyseDecrypt; delete resultAnalyseVerify; + + qDebug() << "End Analyse Result"; + } else if (edit->slotCurPageFileTreeView() != nullptr) { this->slotFileDecryptVerify(); } } + /* * Append the selected (not checked!) Key(s) To Textedit */ @@ -452,7 +516,11 @@ void MainWindow::slotCopyMailAddressToClipboard() { if (mKeyList->getSelected()->isEmpty()) { return; } - auto &key = mCtx->getKeyById(mKeyList->getSelected()->first()); + auto key = mCtx->getKeyById(mKeyList->getSelected()->first()); + if (!key.good) { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); + return; + } QClipboard *cb = QApplication::clipboard(); QString mail = key.email; cb->setText(mail); @@ -462,9 +530,11 @@ void MainWindow::slotShowKeyDetails() { if (mKeyList->getSelected()->isEmpty()) { return; } - auto &key = mCtx->getKeyById(mKeyList->getSelected()->first()); + auto key = mCtx->getKeyById(mKeyList->getSelected()->first()); if (key.good) { new KeyDetailsDialog(mCtx, key, this); + } else { + QMessageBox::critical(nullptr, tr("Error"), tr("Key Not Found.")); } } @@ -487,604 +557,20 @@ void MainWindow::uploadKeyToServer() { dialog->slotUpload(); } -void MainWindow::slotFileEncrypt() { - - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - QFileInfo pathInfo(fileInfo.absolutePath()); - - if (!fileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); - return; - } - if (!pathInfo.isWritable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); - return; - } - if (QFile::exists(path + ".asc")) { - auto ret = QMessageBox::warning(this, - tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) - return; - } - - QVector<GpgKey> keys; - - mKeyList->getCheckedKeys(keys); - - if (keys.empty()) { - QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); - return; - } - - for (const auto &key : keys) { - if (!GpgME::GpgContext::checkIfKeyCanEncr(key)) { - QMessageBox::information(this, - tr("Invalid Operation"), - tr("The selected key contains a key that does not actually have a encrypt usage.<br/>") - + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); - return; - - } - } - - gpgme_encrypt_result_t result; - - gpgme_error_t error; - bool if_error = false; - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::encryptFile(mCtx, keys, path, &result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - auto *dialog = new WaitingDialog(tr("Encrypting"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - - dialog->close(); - if (!if_error) { - auto resultAnalyse = new EncryptResultAnalyse(error, result); - auto &reportText = resultAnalyse->getResultReport(); - infoBoard->associateTabWidget(edit->tabWidget); - infoBoard->associateFileTreeView(edit->curFilePage()); - - if (resultAnalyse->getStatus() < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (resultAnalyse->getStatus() > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - delete resultAnalyse; - - fileTreeView->update(); - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } -} - -void MainWindow::slotFileDecrypt() { - - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - QFileInfo pathInfo(fileInfo.absolutePath()); - if (!fileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); - return; - } - if (!pathInfo.isWritable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); - return; - } - - QString outFileName, fileExtension = fileInfo.suffix(); - - if (fileExtension == "asc" || fileExtension == "gpg") { - int pos = path.lastIndexOf(QChar('.')); - outFileName = path.left(pos); - } else { - outFileName = path + ".out"; - } - - if (QFile::exists(outFileName)) { - auto ret = QMessageBox::warning(this, - tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) - return; - } - - gpgme_decrypt_result_t result; - gpgme_error_t error; - bool if_error = false; - - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::decryptFile(mCtx, path, &result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - auto *dialog = new WaitingDialog("Decrypting", this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - - dialog->close(); - - if (!if_error) { - auto resultAnalyse = new DecryptResultAnalyse(mCtx, error, result); - auto &reportText = resultAnalyse->getResultReport(); - infoBoard->associateTabWidget(edit->tabWidget); - infoBoard->associateFileTreeView(edit->curFilePage()); - - if (resultAnalyse->getStatus() < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (resultAnalyse->getStatus() > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - delete resultAnalyse; - - fileTreeView->update(); - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } - - -} - -void MainWindow::slotFileSign() { - - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - QFileInfo pathInfo(fileInfo.absolutePath()); - - if (!fileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); - return; - } - if (!pathInfo.isWritable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); - return; - } - - if (QFile::exists(path + ".sig")) { - auto ret = QMessageBox::warning(this, - tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) - return; - } - - QVector<GpgKey> keys; - - mKeyList->getCheckedKeys(keys); - - if (keys.empty()) { - QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); - return; - } - - for (const auto &key : keys) { - if (!GpgME::GpgContext::checkIfKeyCanEncr(key)) { - QMessageBox::information(this, - tr("Invalid Operation"), - tr("The selected key contains a key that does not actually have a encrypt usage.<br/>") - + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); - return; - - } - } - - gpgme_sign_result_t result; - gpgme_error_t error; - bool if_error = false; - - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::signFile(mCtx, keys, path, &result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - auto *dialog = new WaitingDialog(tr("Signing"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - - dialog->close(); - - if (!if_error) { - - auto resultAnalyse = new SignResultAnalyse(error, result); - auto &reportText = resultAnalyse->getResultReport(); - infoBoard->associateTabWidget(edit->tabWidget); - infoBoard->associateFileTreeView(edit->curFilePage()); - - if (resultAnalyse->getStatus() < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (resultAnalyse->getStatus() > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - delete resultAnalyse; - - fileTreeView->update(); - - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } - - fileTreeView->update(); - -} - -void MainWindow::slotFileVerify() { - - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - - QString signFilePath, dataFilePath; - - 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"; - } - - QFileInfo dataFileInfo(dataFilePath), signFileInfo(signFilePath); - - if (!dataFileInfo.isFile() || !signFileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), - tr("Please select the appropriate target file or signature file. Ensure that both are in this directory.")); - return; - } - if (!dataFileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read target file.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read signature file.")); - return; - } - - gpgme_verify_result_t result; - - gpgme_error_t error; - bool if_error = false; - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::verifyFile(mCtx, dataFilePath, &result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - auto *dialog = new WaitingDialog(tr("Verifying"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - dialog->close(); - - if (!if_error) { - auto resultAnalyse = new VerifyResultAnalyse(mCtx, error, result); - auto &reportText = resultAnalyse->getResultReport(); - infoBoard->associateTabWidget(edit->tabWidget); - infoBoard->associateFileTreeView(edit->curFilePage()); - - if (resultAnalyse->getStatus() < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (resultAnalyse->getStatus() > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - if (resultAnalyse->getStatus() >= 0) { - infoBoard->resetOptionActionsMenu(); - infoBoard->addOptionalAction("Show Verify Details", [this, error, result]() { - VerifyDetailsDialog(this, mCtx, mKeyList, error, result); - }); - } - - delete resultAnalyse; - - fileTreeView->update(); - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } -} - -void MainWindow::slotFileEncryptSign() { - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - QFileInfo pathInfo(fileInfo.absolutePath()); - - if (!fileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), tr("Select a file before doing it.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); - return; - } - if (!pathInfo.isWritable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); - return; - } - if (QFile::exists(path + ".gpg")) { - auto ret = QMessageBox::warning(this, - tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) - return; - } - - QVector<GpgKey> keys; - - mKeyList->getCheckedKeys(keys); - - if (keys.empty()) { - QMessageBox::critical(this, tr("No Key Selected"), tr("No Key Selected")); - return; - } - - bool can_sign = false, can_encr = false; - - for (const auto &key : keys) { - bool key_can_sign = GpgME::GpgContext::checkIfKeyCanSign(key); - bool key_can_encr = GpgME::GpgContext::checkIfKeyCanEncr(key); - - if (!key_can_sign && !key_can_encr) { - QMessageBox::critical(nullptr, - tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for signing and encryption at the same time.<br/>") - + tr("<br/>For example the Following Key: <br/>") + key.uids.first().uid); - return; - } - - if (key_can_sign) can_sign = true; - if (key_can_encr) can_encr = true; - } - - if (!can_encr) { - QMessageBox::critical(nullptr, - tr("Incomplete Operation"), - tr("None of the selected key pairs can provide the encryption function.")); - return; - } - - if (!can_sign) { - QMessageBox::warning(nullptr, - tr("Incomplete Operation"), - tr("None of the selected key pairs can provide the signature function.")); - } - - gpgme_encrypt_result_t encr_result = nullptr; - gpgme_sign_result_t sign_result = nullptr; - - gpgme_error_t error; - bool if_error = false; - - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::encryptSignFile(mCtx, keys, path, &encr_result, &sign_result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - WaitingDialog *dialog = new WaitingDialog(tr("Encrypting and Signing"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - dialog->close(); - - if (!if_error) { - - auto resultAnalyseEncr = new EncryptResultAnalyse(error, encr_result); - auto resultAnalyseSign = new SignResultAnalyse(error, sign_result); - int status = std::min(resultAnalyseEncr->getStatus(), resultAnalyseSign->getStatus()); - auto reportText = resultAnalyseEncr->getResultReport() + resultAnalyseSign->getResultReport(); - - infoBoard->associateFileTreeView(edit->curFilePage()); - - if (status < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (status > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - delete resultAnalyseEncr; - delete resultAnalyseSign; - - fileTreeView->update(); - - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } -} - -void MainWindow::slotFileDecryptVerify() { - auto fileTreeView = edit->slotCurPageFileTreeView(); - auto path = fileTreeView->getSelected(); - - QFileInfo fileInfo(path); - QFileInfo pathInfo(fileInfo.absolutePath()); - if (!fileInfo.isFile()) { - QMessageBox::critical(this, tr("Error"), tr("Select a file(.gpg/.asc) before doing it.")); - return; - } - if (!fileInfo.isReadable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to read this file.")); - return; - } - if (!pathInfo.isWritable()) { - QMessageBox::critical(this, tr("Error"), tr("No permission to create file.")); - return; - } - - QString outFileName, fileExtension = fileInfo.suffix(); - - if (fileExtension == "asc" || fileExtension == "gpg") { - int pos = path.lastIndexOf(QChar('.')); - outFileName = path.left(pos); - } else { - outFileName = path + ".out"; - } - - gpgme_decrypt_result_t d_result = nullptr; - gpgme_verify_result_t v_result = nullptr; - - gpgme_error_t error; - bool if_error = false; - - auto thread = QThread::create([&]() { - try { - error = GpgFileOpera::decryptVerifyFile(mCtx, path, &d_result, &v_result); - } catch (const std::runtime_error &e) { - if_error = true; - } - }); - connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); - thread->start(); - - - auto *dialog = new WaitingDialog(tr("Decrypting and Verifying"), this); - while (thread->isRunning()) { - QApplication::processEvents(); - } - dialog->close(); - - if (!if_error) { - infoBoard->associateFileTreeView(edit->curFilePage()); - - auto resultAnalyseDecrypt = new DecryptResultAnalyse(mCtx, error, d_result); - auto resultAnalyseVerify = new VerifyResultAnalyse(mCtx, error, v_result); - - int status = std::min(resultAnalyseDecrypt->getStatus(), resultAnalyseVerify->getStatus()); - auto &reportText = resultAnalyseDecrypt->getResultReport() + resultAnalyseVerify->getResultReport(); - if (status < 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_CRITICAL); - else if (status > 0) - infoBoard->slotRefresh(reportText, INFO_ERROR_OK); - else - infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); - - if (resultAnalyseVerify->getStatus() >= 0) { - infoBoard->resetOptionActionsMenu(); - infoBoard->addOptionalAction("Show Verify Details", [this, error, v_result]() { - VerifyDetailsDialog(this, mCtx, mKeyList, error, v_result); - }); - } - delete resultAnalyseDecrypt; - delete resultAnalyseVerify; - - fileTreeView->update(); - } else { - QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); - return; - } -} - -void MainWindow::slotFileEncryptCustom() { - QStringList *keyList; - keyList = mKeyList->getChecked(); - new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Encrypt, this); -} - -void MainWindow::slotFileDecryptCustom() { - QStringList *keyList; - keyList = mKeyList->getChecked(); - new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Decrypt, this); -} - -void MainWindow::slotFileSignCustom() { - QStringList *keyList; - keyList = mKeyList->getChecked(); - new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Sign, this); -} - -void MainWindow::slotFileVerifyCustom() { - QStringList *keyList; - keyList = mKeyList->getChecked(); - new FileEncryptionDialog(mCtx, *keyList, FileEncryptionDialog::Verify, this); -} void MainWindow::slotOpenFile(QString &path) { edit->slotOpenFile(path); } void MainWindow::slotVersionUpgrade(const QString ¤tVersion, const QString &latestVersion) { - if(currentVersion < latestVersion) { + if (currentVersion < latestVersion) { QMessageBox::warning(this, tr("Outdated Version"), tr("This version(%1) is out of date, please update the latest version in time. ").arg( currentVersion) + tr("You can download the latest version(%1) on Github Releases Page.<br/>").arg( latestVersion)); - } else if(currentVersion > latestVersion) { + } else if (currentVersion > latestVersion) { QMessageBox::warning(this, tr("Unreleased Version"), tr("This version(%1) has not been officially released and is not recommended for use in a production environment. <br/>").arg( diff --git a/src/ui/settings/SettingsAdvanced.cpp b/src/ui/settings/SettingsAdvanced.cpp new file mode 100644 index 00000000..30414250 --- /dev/null +++ b/src/ui/settings/SettingsAdvanced.cpp @@ -0,0 +1,67 @@ +/** + * 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/SettingsDialog.h" + +AdvancedTab::AdvancedTab(QWidget *parent) +: QWidget(parent), appPath(qApp->applicationDirPath()), +settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + /***************************************** + * Steganography Box + *****************************************/ + auto *steganoBox = new QGroupBox(tr("Show Steganography Options")); + auto *steganoBoxLayout = new QHBoxLayout(); + steganoCheckBox = new QCheckBox(tr("Show Steganographic Options."), this); + steganoBoxLayout->addWidget(steganoCheckBox); + steganoBox->setLayout(steganoBoxLayout); + + auto *pubkeyExchangeBox = new QGroupBox(tr("Pubkey Exchange")); + auto *pubkeyExchangeBoxLayout = new QHBoxLayout(); + autoPubkeyExchangeCheckBox = new QCheckBox(tr("Auto Pubkey Exchange"), this); + pubkeyExchangeBoxLayout->addWidget(autoPubkeyExchangeCheckBox); + pubkeyExchangeBox->setLayout(pubkeyExchangeBoxLayout); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(steganoBox); + mainLayout->addWidget(pubkeyExchangeBox); + setSettings(); + mainLayout->addStretch(1); + setLayout(mainLayout); +} + +void AdvancedTab::setSettings() { + if (settings.value("advanced/steganography").toBool()) { + steganoCheckBox->setCheckState(Qt::Checked); + } + if (settings.value("advanced/autoPubkeyExchange").toBool()) { + autoPubkeyExchangeCheckBox->setCheckState(Qt::Checked); + } +} + +void AdvancedTab::applySettings() { + settings.setValue("advanced/steganography", steganoCheckBox->isChecked()); + settings.setValue("advanced/autoPubkeyExchange", autoPubkeyExchangeCheckBox->isChecked()); +} + diff --git a/src/ui/settings/SettingsAppearance.cpp b/src/ui/settings/SettingsAppearance.cpp new file mode 100644 index 00000000..aeb7ed70 --- /dev/null +++ b/src/ui/settings/SettingsAppearance.cpp @@ -0,0 +1,187 @@ +/** + * 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/SettingsDialog.h" + +AppearanceTab::AppearanceTab(QWidget *parent) +: QWidget(parent), appPath(qApp->applicationDirPath()), +settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + /***************************************** + * Icon-Size-Box + *****************************************/ + auto *iconSizeBox = new QGroupBox(tr("Iconsize")); + iconSizeGroup = new QButtonGroup(); + iconSizeSmall = new QRadioButton(tr("small")); + iconSizeMedium = new QRadioButton(tr("medium")); + iconSizeLarge = new QRadioButton(tr("large")); + + iconSizeGroup->addButton(iconSizeSmall, 1); + iconSizeGroup->addButton(iconSizeMedium, 2); + iconSizeGroup->addButton(iconSizeLarge, 3); + + auto *iconSizeBoxLayout = new QHBoxLayout(); + iconSizeBoxLayout->addWidget(iconSizeSmall); + iconSizeBoxLayout->addWidget(iconSizeMedium); + iconSizeBoxLayout->addWidget(iconSizeLarge); + + iconSizeBox->setLayout(iconSizeBoxLayout); + + /***************************************** + * Icon-Style-Box + *****************************************/ + auto *iconStyleBox = new QGroupBox(tr("Iconstyle")); + iconStyleGroup = new QButtonGroup(); + iconTextButton = new QRadioButton(tr("just text")); + iconIconsButton = new QRadioButton(tr("just icons")); + iconAllButton = new QRadioButton(tr("text and icons")); + + iconStyleGroup->addButton(iconTextButton, 1); + iconStyleGroup->addButton(iconIconsButton, 2); + iconStyleGroup->addButton(iconAllButton, 3); + + auto *iconStyleBoxLayout = new QHBoxLayout(); + iconStyleBoxLayout->addWidget(iconTextButton); + iconStyleBoxLayout->addWidget(iconIconsButton); + iconStyleBoxLayout->addWidget(iconAllButton); + + iconStyleBox->setLayout(iconStyleBoxLayout); + + /***************************************** + * Window-Size-Box + *****************************************/ + auto *windowSizeBox = new QGroupBox(tr("Windowstate")); + auto *windowSizeBoxLayout = new QHBoxLayout(); + windowSizeCheckBox = + new QCheckBox(tr("Save window size and position on exit."), this); + windowSizeBoxLayout->addWidget(windowSizeCheckBox); + windowSizeBox->setLayout(windowSizeBoxLayout); + + /***************************************** + * Info-Board-Font-Size-Box + *****************************************/ + + auto *infoBoardBox = new QGroupBox(tr("Information Board")); + auto *infoBoardLayout = new QHBoxLayout(); + infoBoardFontSizeSpin = new QSpinBox(); + infoBoardFontSizeSpin->setRange(9, 18); + infoBoardFontSizeSpin->setValue(10); + infoBoardFontSizeSpin->setSingleStep(1); + infoBoardLayout->addWidget(new QLabel(tr(" Front Size"))); + infoBoardLayout->addWidget(infoBoardFontSizeSpin); + infoBoardBox->setLayout(infoBoardLayout); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(iconSizeBox); + mainLayout->addWidget(iconStyleBox); + mainLayout->addWidget(windowSizeBox); + mainLayout->addWidget(infoBoardBox); + mainLayout->addStretch(1); + setSettings(); + setLayout(mainLayout); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void AppearanceTab::setSettings() { + + // Iconsize + QSize iconSize = settings.value("toolbar/iconsize", QSize(24, 24)).toSize(); + switch (iconSize.height()) { + case 12: + iconSizeSmall->setChecked(true); + break; + case 24: + iconSizeMedium->setChecked(true); + break; + case 32: + iconSizeLarge->setChecked(true); + break; + } + // Iconstyle + Qt::ToolButtonStyle iconStyle = static_cast<Qt::ToolButtonStyle>( + settings.value("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon) + .toUInt()); + switch (iconStyle) { + case Qt::ToolButtonTextOnly: + iconTextButton->setChecked(true); + break; + case Qt::ToolButtonIconOnly: + iconIconsButton->setChecked(true); + break; + case Qt::ToolButtonTextUnderIcon: + iconAllButton->setChecked(true); + break; + default: + break; + } + + // Window Save and Position + if (settings.value("window/windowSave").toBool()) + windowSizeCheckBox->setCheckState(Qt::Checked); + + // infoBoardFontSize + auto infoBoardFontSize = settings.value("informationBoard/fontSize", 10).toInt(); + if (infoBoardFontSize < 9 || infoBoardFontSize > 18) + infoBoardFontSize = 10; + infoBoardFontSizeSpin->setValue(infoBoardFontSize); +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void AppearanceTab::applySettings() { + switch (iconSizeGroup->checkedId()) { + case 1: + settings.setValue("toolbar/iconsize", QSize(12, 12)); + break; + case 2: + settings.setValue("toolbar/iconsize", QSize(24, 24)); + break; + case 3: + settings.setValue("toolbar/iconsize", QSize(32, 32)); + break; + } + + switch (iconStyleGroup->checkedId()) { + case 1: + settings.setValue("toolbar/iconstyle", Qt::ToolButtonTextOnly); + break; + case 2: + settings.setValue("toolbar/iconstyle", Qt::ToolButtonIconOnly); + break; + case 3: + settings.setValue("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon); + break; + } + + settings.setValue("window/windowSave", windowSizeCheckBox->isChecked()); + + settings.setValue("informationBoard/fontSize", infoBoardFontSizeSpin->value()); +} + diff --git a/src/ui/settings/SettingsDialog.cpp b/src/ui/settings/SettingsDialog.cpp new file mode 100644 index 00000000..0ca188f7 --- /dev/null +++ b/src/ui/settings/SettingsDialog.cpp @@ -0,0 +1,205 @@ +/** + * 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/SettingsDialog.h" +#include "ui/WaitingDialog.h" + +SettingsDialog::SettingsDialog(GpgME::GpgContext *ctx, QWidget *parent) + : QDialog(parent) { + mCtx = ctx; + tabWidget = new QTabWidget; + generalTab = new GeneralTab(mCtx); + appearanceTab = new AppearanceTab; + sendMailTab = new SendMailTab; + keyserverTab = new KeyserverTab; + advancedTab = new AdvancedTab; + gpgPathsTab = new GpgPathsTab; + + tabWidget->addTab(generalTab, tr("General")); + tabWidget->addTab(appearanceTab, tr("Appearance")); + tabWidget->addTab(sendMailTab, tr("Send Mail")); + tabWidget->addTab(keyserverTab, tr("Key Server")); + // tabWidget->addTab(gpgPathsTab, tr("Gpg paths")); + tabWidget->addTab(advancedTab, tr("Advanced")); + + buttonBox = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotAccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(tabWidget); + mainLayout->stretch(0); + mainLayout->addWidget(buttonBox); + mainLayout->stretch(0); + setLayout(mainLayout); + + setWindowTitle(tr("Settings")); + + // slots for handling the restartneeded member + this->slotSetRestartNeeded(false); + connect(generalTab, SIGNAL(signalRestartNeeded(bool)), this, + SLOT(slotSetRestartNeeded(bool))); + connect(appearanceTab, SIGNAL(signalRestartNeeded(bool)), this, + SLOT(slotSetRestartNeeded(bool))); + connect(sendMailTab, SIGNAL(signalRestartNeeded(bool)), this, + SLOT(slotSetRestartNeeded(bool))); + connect(keyserverTab, SIGNAL(signalRestartNeeded(bool)), this, + SLOT(slotSetRestartNeeded(bool))); + connect(advancedTab, SIGNAL(signalRestartNeeded(bool)), this, + SLOT(slotSetRestartNeeded(bool))); + + connect(this, SIGNAL(signalRestartNeeded(bool)), parent, + SLOT(slotSetRestartNeeded(bool))); + + this->resize(480, 640); + this->show(); +} + +bool SettingsDialog::getRestartNeeded() const { return this->restartNeeded; } + +void SettingsDialog::slotSetRestartNeeded(bool needed) { + this->restartNeeded = needed; +} + +void SettingsDialog::slotAccept() { + generalTab->applySettings(); + sendMailTab->applySettings(); + appearanceTab->applySettings(); + keyserverTab->applySettings(); + advancedTab->applySettings(); + gpgPathsTab->applySettings(); + if (getRestartNeeded()) { + emit signalRestartNeeded(true); + } + close(); +} + +// http://www.informit.com/articles/article.aspx?p=1405555&seqNum=3 +// http://developer.qt.nokia.com/wiki/How_to_create_a_multi_language_application +QHash<QString, QString> SettingsDialog::listLanguages() { + QHash<QString, QString> languages; + + languages.insert("", tr("System Default")); + + QString appPath = qApp->applicationDirPath(); + QDir qmDir = QDir(RESOURCE_DIR(appPath) + "/ts/"); + QStringList fileNames = qmDir.entryList(QStringList("gpgfrontend_*.qm")); + + for (int i = 0; i < fileNames.size(); ++i) { + QString locale = fileNames[i]; + locale.truncate(locale.lastIndexOf('.')); + locale.remove(0, locale.indexOf('_') + 1); + + // this works in qt 4.8 + QLocale qloc(locale); +#if QT_VERSION < 0x040800 + QString language = + QLocale::languageToString(qloc.language()) + " (" + locale + + ")"; //+ " (" + QLocale::languageToString(qloc.language()) + ")"; +#else + QString language = qloc.nativeLanguageName() + " (" + locale + ")"; +#endif + languages.insert(locale, language); + } + return languages; +} + +GpgPathsTab::GpgPathsTab(QWidget *parent) + : QWidget(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + setSettings(); + + /***************************************** + * Keydb Box + *****************************************/ + auto *keydbBox = new QGroupBox(tr("Relative path to keydb")); + auto *keydbBoxLayout = new QGridLayout(); + + // Label containing the current keydbpath relative to default keydb path + keydbLabel = new QLabel(accKeydbPath, this); + + auto *keydbButton = new QPushButton("Change keydb path", this); + connect(keydbButton, SIGNAL(clicked()), this, SLOT(chooseKeydbDir())); + auto *keydbDefaultButton = new QPushButton("Set keydb to default path", this); + connect(keydbDefaultButton, SIGNAL(clicked()), this, + SLOT(setKeydbPathToDefault())); + + keydbBox->setLayout(keydbBoxLayout); + keydbBoxLayout->addWidget(new QLabel(tr("Current keydb path: ")), 1, 1); + keydbBoxLayout->addWidget(keydbLabel, 1, 2); + keydbBoxLayout->addWidget(keydbButton, 1, 3); + keydbBoxLayout->addWidget(keydbDefaultButton, 2, 3); + keydbBoxLayout->addWidget( + new QLabel(tr("<b>NOTE: </b> Gpg4usb will restart automatically if you " + "change the keydb path!")), + 3, 1, 1, 3); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(keydbBox); + mainLayout->addStretch(1); + setLayout(mainLayout); +} + +QString GpgPathsTab::getRelativePath(const QString &dir1, const QString &dir2) { + QDir dir(dir1); + QString s; + + s = dir.relativeFilePath(dir2); + qDebug() << "relative path: " << s; + if (s.isEmpty()) { + s = "."; + } + return s; +} + +void GpgPathsTab::setKeydbPathToDefault() { + accKeydbPath = "."; + keydbLabel->setText("."); +} + +QString GpgPathsTab::chooseKeydbDir() { + QString dir = QFileDialog::getExistingDirectory( + this, tr("Choose keydb directory"), accKeydbPath, + QFileDialog::ShowDirsOnly); + + accKeydbPath = getRelativePath(defKeydbPath, dir); + keydbLabel->setText(accKeydbPath); + return ""; +} + +void GpgPathsTab::setSettings() { + defKeydbPath = qApp->applicationDirPath() + "/keydb"; + + accKeydbPath = settings.value("gpgpaths/keydbpath").toString(); + if (accKeydbPath.isEmpty()) { + accKeydbPath = "."; + } +} + +void GpgPathsTab::applySettings() { + settings.setValue("gpgpaths/keydbpath", accKeydbPath); +} diff --git a/src/ui/settings/SettingsGeneral.cpp b/src/ui/settings/SettingsGeneral.cpp new file mode 100644 index 00000000..bec66154 --- /dev/null +++ b/src/ui/settings/SettingsGeneral.cpp @@ -0,0 +1,358 @@ +/** + * 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/SettingsDialog.h" +#include "ui/WaitingDialog.h" +#include "server/ComUtils.h" + +#include "rapidjson/prettywriter.h" + +GeneralTab::GeneralTab(GpgME::GpgContext *ctx, QWidget *parent) + : QWidget(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + mCtx = ctx; + + /***************************************** + * GpgFrontend Server + *****************************************/ + auto *serverBox = new QGroupBox(tr("GpgFrontend Server")); + auto *serverBoxLayout = new QVBoxLayout(); + serverSelectBox = new QComboBox(); + serverBoxLayout->addWidget(serverSelectBox); + serverBoxLayout->addWidget(new QLabel( + tr("Server that provides short key and key exchange services"))); + + serverBox->setLayout(serverBoxLayout); + + /***************************************** + * Save-Checked-Keys-Box + *****************************************/ + auto *saveCheckedKeysBox = new QGroupBox(tr("Save Checked Keys")); + auto *saveCheckedKeysBoxLayout = new QHBoxLayout(); + saveCheckedKeysCheckBox = new QCheckBox( + tr("Save checked private keys on exit and restore them on next start."), + this); + saveCheckedKeysBoxLayout->addWidget(saveCheckedKeysCheckBox); + saveCheckedKeysBox->setLayout(saveCheckedKeysBoxLayout); + + /***************************************** + * Key-Impport-Confirmation Box + *****************************************/ + auto *importConfirmationBox = + new QGroupBox(tr("Confirm drag'n'drop key import")); + auto *importConfirmationBoxLayout = new QHBoxLayout(); + importConfirmationCheckBox = new QCheckBox( + tr("Import files dropped on the keylist without confirmation."), this); + importConfirmationBoxLayout->addWidget(importConfirmationCheckBox); + importConfirmationBox->setLayout(importConfirmationBoxLayout); + + /***************************************** + * Language Select Box + *****************************************/ + auto *langBox = new QGroupBox(tr("Language")); + auto *langBoxLayout = new QVBoxLayout(); + langSelectBox = new QComboBox; + lang = SettingsDialog::listLanguages(); + + for (const auto &l: lang) { langSelectBox->addItem(l); } + + langBoxLayout->addWidget(langSelectBox); + langBoxLayout->addWidget( + new QLabel(tr("<b>NOTE: </b> GpgFrontend will restart automatically if " + "you change the language!"))); + langBox->setLayout(langBoxLayout); + connect(langSelectBox, SIGNAL(currentIndexChanged(int)), this, + SLOT(slotLanguageChanged())); + + /***************************************** + * Own Key Select Box + *****************************************/ + auto *ownKeyBox = new QGroupBox(tr("Own key")); + auto *ownKeyBoxLayout = new QVBoxLayout(); + auto *ownKeyServiceTokenLayout = new QHBoxLayout(); + ownKeySelectBox = new QComboBox; + getServiceTokenButton = new QPushButton(tr("Get Service Token")); + serviceTokenLabel = new QLabel(tr("No Service Token Found")); + serviceTokenLabel->setAlignment(Qt::AlignCenter); + + ownKeyBox->setLayout(ownKeyBoxLayout); + mKeyList = new KeyList(mCtx); + + // Fill the keyid hashmap + keyIds.insert("", tr("<none>")); + + for (const auto &keyid : *mKeyList->getAllPrivateKeys()) { + auto key = mCtx->getKeyById(keyid); + if (!key.good) continue; + keyIds.insert(key.id, key.uids.first().uid); + } + for (const auto &k : keyIds.keys()) { + ownKeySelectBox->addItem(keyIds.find(k).value()); + keyIdsList.append(k); + } + connect(ownKeySelectBox, SIGNAL(currentIndexChanged(int)), this, + SLOT(slotOwnKeyIdChanged())); + connect(getServiceTokenButton, SIGNAL(clicked(bool)), this, + SLOT(slotGetServiceToken())); + + ownKeyBoxLayout->addWidget(new QLabel( + tr("Key pair for synchronization and identity authentication"))); + ownKeyBoxLayout->addWidget(ownKeySelectBox); + ownKeyBoxLayout->addLayout(ownKeyServiceTokenLayout); + ownKeyServiceTokenLayout->addWidget(getServiceTokenButton); + ownKeyServiceTokenLayout->addWidget(serviceTokenLabel); + ownKeyServiceTokenLayout->stretch(0); + + /***************************************** + * Mainlayout + *****************************************/ + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(serverBox); + mainLayout->addWidget(saveCheckedKeysBox); + mainLayout->addWidget(importConfirmationBox); + mainLayout->addWidget(langBox); + mainLayout->addWidget(ownKeyBox); + + setSettings(); + mainLayout->addStretch(1); + setLayout(mainLayout); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void GeneralTab::setSettings() { + // Keysaving + if (settings.value("keys/saveKeyChecked").toBool()) { + saveCheckedKeysCheckBox->setCheckState(Qt::Checked); + } + + 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 ¤t) -> void { + settings.setValue("general/currentGpgfrontendServer", current); + }); + + // Language setting + QString langKey = settings.value("int/lang").toString(); + QString langValue = lang.value(langKey); + if (langKey != "") { + langSelectBox->setCurrentIndex(langSelectBox->findText(langValue)); + } + + QString own_key_id = settings.value("general/ownKeyId").toString(); + qDebug() << "OwnKeyId" << own_key_id; + if (own_key_id.isEmpty()) { + ownKeySelectBox->setCurrentText("<none>"); + } else { + const auto text = keyIds.find(own_key_id).value(); + qDebug() << "OwnKey" << own_key_id << text; + ownKeySelectBox->setCurrentText(text); + } + + serviceToken = settings.value("general/serviceToken").toString(); + qDebug() << "Load Service Token" << serviceToken; + if (!serviceToken.isEmpty()) { + serviceTokenLabel->setText(serviceToken); + } + + // Get own key information from keydb/gpg.conf (if contained) + if (settings.value("general/confirmImportKeys", Qt::Checked).toBool()) { + importConfirmationCheckBox->setCheckState(Qt::Checked); + } +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void GeneralTab::applySettings() { + settings.setValue("keys/saveKeyChecked", + saveCheckedKeysCheckBox->isChecked()); + + 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; + + settings.setValue("int/lang", lang.key(langSelectBox->currentText())); + + settings.setValue("general/ownKeyId", + keyIdsList[ownKeySelectBox->currentIndex()]); + + settings.setValue("general/serviceToken", + serviceToken); + + settings.setValue("general/confirmImportKeys", + importConfirmationCheckBox->isChecked()); +} + +void GeneralTab::slotLanguageChanged() { emit signalRestartNeeded(true); } + +void GeneralTab::slotOwnKeyIdChanged() { + // Set ownKeyId to currently selected + this->serviceTokenLabel->setText(tr("No Service Token Found")); + serviceToken.clear(); +} + +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, tr("Invalid Operation"), + tr("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->getKeyById(keyId); + + if (!key.good) { + QMessageBox::critical(this, tr("Error"), + tr("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, tr("Error"), + tr("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->getKeyByFpr(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, tr("Notice"), + tr("Succeed in getting service token")); + } else { + QMessageBox::critical(this, tr("Error"), tr("There is a problem with the communication with the server")); + } + } + +} diff --git a/src/ui/settings/SettingsKeyServer.cpp b/src/ui/settings/SettingsKeyServer.cpp new file mode 100644 index 00000000..b49eb9e0 --- /dev/null +++ b/src/ui/settings/SettingsKeyServer.cpp @@ -0,0 +1,148 @@ +/** + * 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/SettingsDialog.h" + +KeyserverTab::KeyserverTab(QWidget *parent) + : QWidget(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + + auto generalGroupBox = new QGroupBox(tr("General")); + auto generalLayout = new QVBoxLayout(); + + keyServerTable = new QTableWidget(); + keyServerTable->setColumnCount(3); + keyServerTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + keyServerTable->horizontalHeader()->setStretchLastSection(false); + keyServerTable->verticalHeader()->hide(); + keyServerTable->setShowGrid(false); + keyServerTable->sortByColumn(0, Qt::AscendingOrder); + keyServerTable->setSelectionBehavior(QAbstractItemView::SelectRows); + keyServerTable->setSelectionMode(QAbstractItemView::SingleSelection); + + // tableitems not editable + keyServerTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + // no focus (rectangle around tableitems) + // may be it should focus on whole row + keyServerTable->setFocusPolicy(Qt::NoFocus); + keyServerTable->setAlternatingRowColors(true); + + QStringList labels; + labels << tr("No.") << tr("Address") << tr("Available"); + keyServerTable->setHorizontalHeaderLabels(labels); + + auto *mainLayout = new QVBoxLayout(this); + auto *label = new QLabel(tr("Default Key Server for Import:")); + + comboBox = new QComboBox; + comboBox->setEditable(false); + comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + auto *addKeyServerBox = new QWidget(this); + auto *addKeyServerLayout = new QHBoxLayout(addKeyServerBox); + auto *http = new QLabel("URL: "); + newKeyServerEdit = new QLineEdit(this); + auto *newKeyServerButton = new QPushButton(tr("Add"), this); + connect(newKeyServerButton, SIGNAL(clicked()), this, SLOT(addKeyServer())); + addKeyServerLayout->addWidget(http); + addKeyServerLayout->addWidget(newKeyServerEdit); + addKeyServerLayout->addWidget(newKeyServerButton); + + generalLayout->addWidget(label); + generalLayout->addWidget(comboBox); + generalLayout->addWidget(keyServerTable); + generalLayout->addWidget(addKeyServerBox); + generalLayout->addStretch(0); + + generalGroupBox->setLayout(generalLayout); + mainLayout->addWidget(generalGroupBox); + mainLayout->addStretch(0); + + setLayout(mainLayout); + // Read keylist from ini-file and fill it into combobox + setSettings(); + refreshTable(); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void KeyserverTab::setSettings() { + + keyServerStrList = settings.value("keyserver/keyServerList").toStringList(); + + for (const auto &keyServer : keyServerStrList) { + comboBox->addItem(keyServer); + qDebug() << "KeyserverTab Get ListItemText" << keyServer; + } + + comboBox->setCurrentText( + settings.value("keyserver/defaultKeyServer").toString()); + +} + +void KeyserverTab::addKeyServer() { + QString targetUrl; + if (newKeyServerEdit->text().startsWith("http://") || + newKeyServerEdit->text().startsWith("https://")) + targetUrl = newKeyServerEdit->text(); + else + targetUrl = "http://" + newKeyServerEdit->text(); + keyServerStrList.append(targetUrl); + comboBox->addItem(targetUrl); + refreshTable(); +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void KeyserverTab::applySettings() { + settings.setValue("keyserver/keyServerList", keyServerStrList); + settings.setValue("keyserver/defaultKeyServer", comboBox->currentText()); +} + +void KeyserverTab::refreshTable() { + + qDebug() << "Start Refreshing Key Server Table"; + + keyServerTable->setRowCount(keyServerStrList.size()); + + int index = 0; + for (const auto &server : keyServerStrList) { + auto *tmp1 = new QTableWidgetItem(QString::number(index)); + tmp1->setTextAlignment(Qt::AlignCenter); + keyServerTable->setItem(index, 0, tmp1); + auto *tmp2 = new QTableWidgetItem(server); + tmp2->setTextAlignment(Qt::AlignCenter); + keyServerTable->setItem(index, 1, tmp2); + auto *tmp3 = new QTableWidgetItem(""); + tmp3->setTextAlignment(Qt::AlignCenter); + keyServerTable->setItem(index, 2, tmp3); + index++; + } +} diff --git a/src/ui/settings/SettingsSendMail.cpp b/src/ui/settings/SettingsSendMail.cpp new file mode 100644 index 00000000..a5e3274f --- /dev/null +++ b/src/ui/settings/SettingsSendMail.cpp @@ -0,0 +1,193 @@ +/** + * 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/SettingsDialog.h" +#include "smtp/SmtpMime" + +SendMailTab::SendMailTab(QWidget *parent) + : QWidget(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + + enableCheckBox = new QCheckBox(tr("Enable")); + enableCheckBox->setTristate(false); + + smtpAddress = new QLineEdit(); + username = new QLineEdit(); + password = new QLineEdit(); + password->setEchoMode(QLineEdit::Password); + + portSpin = new QSpinBox(); + portSpin->setMinimum(1); + portSpin->setMaximum(65535); + connectionTypeComboBox = new QComboBox(); + connectionTypeComboBox->addItem("None"); + connectionTypeComboBox->addItem("SSL"); + connectionTypeComboBox->addItem("TLS"); + connectionTypeComboBox->addItem("STARTTLS"); + + defaultSender = new QLineEdit();; + checkConnectionButton = new QPushButton(tr("Check Connection")); + + auto generalGroupBox = new QGroupBox(tr("General")); + auto connectGroupBox = new QGroupBox(tr("Connection")); + auto preferenceGroupBox = new QGroupBox(tr("Preference")); + + auto generalLayout = new QGridLayout(); + generalLayout->addWidget(enableCheckBox); + + auto connectLayout = new QGridLayout(); + connectLayout->addWidget(new QLabel(tr("SMTP Address")), 1, 0); + connectLayout->addWidget(smtpAddress, 1, 1, 1, 4); + connectLayout->addWidget(new QLabel(tr("Username")), 2, 0); + connectLayout->addWidget(username, 2, 1, 1, 4); + connectLayout->addWidget(new QLabel(tr("Password")), 3, 0); + connectLayout->addWidget(password, 3, 1, 1, 4); + connectLayout->addWidget(new QLabel(tr("Port")), 4, 0); + connectLayout->addWidget(portSpin, 4, 1, 1, 1); + connectLayout->addWidget(new QLabel(tr("Connection Security")), 5, 0); + connectLayout->addWidget(connectionTypeComboBox, 5, 1, 1, 1); + connectLayout->addWidget(checkConnectionButton, 6, 0); + + auto preferenceLayout = new QGridLayout(); + + preferenceLayout->addWidget(new QLabel(tr("Default Sender")), 0, 0); + preferenceLayout->addWidget(defaultSender, 0, 1, 1, 4); + + generalGroupBox->setLayout(generalLayout); + connectGroupBox->setLayout(connectLayout); + preferenceGroupBox->setLayout(preferenceLayout); + + auto vBox = new QVBoxLayout(); + vBox->addWidget(generalGroupBox); + vBox->addWidget(connectGroupBox); + vBox->addWidget(preferenceGroupBox); + vBox->addStretch(0); + + connect(enableCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotCheckBoxSetEnableDisable(int))); + connect(checkConnectionButton, SIGNAL(clicked(bool)), this, SLOT(slotCheckConnection())); + + + this->setLayout(vBox); + setSettings(); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void SendMailTab::setSettings() { + + if (settings.value("sendMail/enable", false).toBool()) + enableCheckBox->setCheckState(Qt::Checked); + else { + enableCheckBox->setCheckState(Qt::Unchecked); + smtpAddress->setDisabled(true); + username->setDisabled(true); + password->setDisabled(true); + portSpin->setDisabled(true); + connectionTypeComboBox->setDisabled(true); + defaultSender->setDisabled(true); + checkConnectionButton->setDisabled(true); + } + + smtpAddress->setText(settings.value("sendMail/smtpAddress", QString()).toString()); + username->setText(settings.value("sendMail/username", QString()).toString()); + password->setText(settings.value("sendMail/password", QString()).toString()); + portSpin->setValue(settings.value("sendMail/port", 25).toInt()); + connectionTypeComboBox->setCurrentText(settings.value("sendMail/connectionType", "None").toString()); + defaultSender->setText(settings.value("sendMail/defaultSender", QString()).toString()); + +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void SendMailTab::applySettings() { + + settings.setValue("sendMail/smtpAddress", smtpAddress->text()); + settings.setValue("sendMail/username", username->text()); + settings.setValue("sendMail/password", password->text()); + settings.setValue("sendMail/port", portSpin->value()); + settings.setValue("sendMail/connectionType", connectionTypeComboBox->currentText()); + settings.setValue("sendMail/defaultSender", defaultSender->text()); + + settings.setValue("sendMail/enable", enableCheckBox->isChecked()); +} + +void SendMailTab::slotCheckConnection() { + + SmtpClient::ConnectionType connectionType; + const auto selectedConnType = connectionTypeComboBox->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(smtpAddress->text(), portSpin->value(), connectionType); + + smtp.setUser(username->text()); + smtp.setPassword(password->text()); + + bool if_success = true; + + if (!smtp.connectToHost()) { + QMessageBox::critical(this, tr("Fail"), tr("Fail to Connect SMTP Server")); + if_success = false; + } + if (if_success && !smtp.login()) { + QMessageBox::critical(this, tr("Fail"), tr("Fail to Login")); + if_success = false; + } + + if (if_success) + QMessageBox::information(this, tr("Success"), tr("Succeed in connecting and login")); + +} + +void SendMailTab::slotCheckBoxSetEnableDisable(int state) { + if (state == Qt::Checked) { + smtpAddress->setEnabled(true); + username->setEnabled(true); + password->setEnabled(true); + portSpin->setEnabled(true); + connectionTypeComboBox->setEnabled(true); + defaultSender->setEnabled(true); + checkConnectionButton->setEnabled(true); + } else { + smtpAddress->setDisabled(true); + username->setDisabled(true); + password->setDisabled(true); + portSpin->setDisabled(true); + connectionTypeComboBox->setDisabled(true); + defaultSender->setDisabled(true); + checkConnectionButton->setDisabled(true); + } +} + diff --git a/src/ui/widgets/EditorPage.cpp b/src/ui/widgets/EditorPage.cpp index 05d5cbea..beb37b96 100644 --- a/src/ui/widgets/EditorPage.cpp +++ b/src/ui/widgets/EditorPage.cpp @@ -44,8 +44,6 @@ EditorPage::EditorPage(QString filePath, QWidget *parent) : QWidget(parent), // Front in same width this->setFont({"Courier"}); - - connect(textPage, SIGNAL(textChanged()), this, SLOT(formatGpgHeader())); } const QString &EditorPage::getFilePath() const { diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index f26917a4..b9372c59 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -50,11 +50,11 @@ InfoBoardWidget::InfoBoardWidget(QWidget *parent, GpgME::GpgContext *ctx, KeyLis actionButtonMenu->setFixedHeight(36); actionButtonLayout = new QHBoxLayout(); - actionButtonLayout->setContentsMargins(0, 0, 0, 0); + actionButtonLayout->setContentsMargins(5, 5, 5, 5); actionButtonLayout->setSpacing(0); actionButtonMenu->setLayout(actionButtonLayout); - auto label = new QLabel(tr("Optional Actions Menu")); + auto label = new QLabel(tr("Optional Actions")); label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); label->setContentsMargins(0, 0, 0, 0); @@ -150,25 +150,44 @@ void InfoBoardWidget::associateTabWidget(QTabWidget *tab) { connect(tab, SIGNAL(tabCloseRequested(int)), this, SLOT(slotReset())); } + void InfoBoardWidget::addOptionalAction(const QString &name, const std::function<void()> &action) { auto actionButton = new QPushButton(name); + auto layout = new QHBoxLayout(); + layout->setContentsMargins(5, 0, 5, 0); infoBoard->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - actionButtonLayout->addWidget(actionButton); + // set margin from surroundings + layout->addWidget(actionButton); + actionButtonLayout->addLayout(layout); connect(actionButton, &QPushButton::clicked, this, [=]() { action(); }); } +/** + * Delete All item in actionButtonLayout + */ void InfoBoardWidget::resetOptionActionsMenu() { - QLayoutItem *item; - while ((item = actionButtonLayout->layout()->takeAt(2)) != nullptr) { - actionButtonLayout->removeItem(item); - delete item->widget(); - delete item; - } + deleteWidgetsInLayout(actionButtonLayout, 2); } void InfoBoardWidget::slotReset() { this->infoBoard->clear(); resetOptionActionsMenu(); } + +/** + * Try Delete all widget from target layout + * @param layout target layout + */ +void InfoBoardWidget::deleteWidgetsInLayout(QLayout *layout, int start_index) { + QLayoutItem *item; + while ((item = layout->layout()->takeAt(start_index)) != nullptr) { + layout->removeItem(item); + if (item->layout() != nullptr) + deleteWidgetsInLayout(item->layout()); + else if (item->widget() != nullptr) + delete item->widget(); + delete item; + } +} diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 3929868e..9c9c6763 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -31,8 +31,7 @@ KeyList::KeyList(GpgME::GpgContext *ctx, KeyListColumn::InfoType infoType, QWidget *parent) : QWidget(parent), mSelectType(selectType), mInfoType(infoType), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) -{ + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { mCtx = ctx; @@ -43,7 +42,7 @@ KeyList::KeyList(GpgME::GpgContext *ctx, mKeyList->setShowGrid(false); mKeyList->sortByColumn(2, Qt::AscendingOrder); mKeyList->setSelectionBehavior(QAbstractItemView::SelectRows); - mKeyList->setSelectionMode( QAbstractItemView::SingleSelection ); + mKeyList->setSelectionMode(QAbstractItemView::SingleSelection); // tableitems not editable mKeyList->setEditTriggers(QAbstractItemView::NoEditTriggers); @@ -54,22 +53,22 @@ KeyList::KeyList(GpgME::GpgContext *ctx, mKeyList->setAlternatingRowColors(true); // Hidden Column For Purpose - if(!(mInfoType & KeyListColumn::TYPE)) { + if (!(mInfoType & KeyListColumn::TYPE)) { mKeyList->setColumnHidden(1, true); } - if(!(mInfoType & KeyListColumn::NAME)) { + if (!(mInfoType & KeyListColumn::NAME)) { mKeyList->setColumnHidden(2, true); } - if(!(mInfoType & KeyListColumn::EmailAddress)) { + if (!(mInfoType & KeyListColumn::EmailAddress)) { mKeyList->setColumnHidden(3, true); } - if(!(mInfoType & KeyListColumn::Usage)) { + if (!(mInfoType & KeyListColumn::Usage)) { mKeyList->setColumnHidden(4, true); } - if(!(mInfoType & KeyListColumn::Validity)) { + if (!(mInfoType & KeyListColumn::Validity)) { mKeyList->setColumnHidden(5, true); } - if(!(mInfoType & KeyListColumn::FingerPrint)) { + if (!(mInfoType & KeyListColumn::FingerPrint)) { mKeyList->setColumnHidden(6, true); } @@ -95,8 +94,7 @@ KeyList::KeyList(GpgME::GpgContext *ctx, slotRefresh(); } -void KeyList::slotRefresh() -{ +void KeyList::slotRefresh() { QStringList *keyList; keyList = getChecked(); // while filling the table, sort enabled causes errors @@ -110,21 +108,21 @@ void KeyList::slotRefresh() int row_count = 0; while (it != keys.end()) { - if(mFilter != nullptr) { - if(!mFilter(*it)) { + if (mFilter != nullptr) { + if (!mFilter(*it)) { it = keys.erase(it); continue; } } - if(!excluded_key_ids.isEmpty()){ + if (!excluded_key_ids.isEmpty()) { auto iterator = std::find_if(excluded_key_ids.begin(), excluded_key_ids.end(), - [it] (const auto &key_id) -> bool { - if(it->id == key_id) return true; - else return false; - }); + [it](const auto &key_id) -> bool { + if (it->id == key_id) return true; + else return false; + }); - if(iterator != excluded_key_ids.end()) { + if (iterator != excluded_key_ids.end()) { it = keys.erase(it); continue; } @@ -161,10 +159,10 @@ void KeyList::slotRefresh() type_steam << "pub"; } - if(it->is_private_key && !it->has_master_key) { + if (it->is_private_key && !it->has_master_key) { type_steam << "#"; } - auto* tmp1 = new QTableWidgetItem(type_str); + auto *tmp1 = new QTableWidgetItem(type_str); mKeyList->setItem(row_index, 1, tmp1); auto *tmp2 = new QTableWidgetItem(it->name); @@ -177,13 +175,13 @@ void KeyList::slotRefresh() QString usage; QTextStream usage_steam(&usage); - if(GpgME::GpgContext::checkIfKeyCanCert(*it)) + if (GpgME::GpgContext::checkIfKeyCanCert(*it)) usage_steam << "C"; - if(GpgME::GpgContext::checkIfKeyCanEncr(*it)) + if (GpgME::GpgContext::checkIfKeyCanEncr(*it)) usage_steam << "E"; - if(GpgME::GpgContext::checkIfKeyCanSign(*it)) + if (GpgME::GpgContext::checkIfKeyCanSign(*it)) usage_steam << "S"; - if(GpgME::GpgContext::checkIfKeyCanAuth(*it)) + if (GpgME::GpgContext::checkIfKeyCanAuth(*it)) usage_steam << "A"; auto *temp_usage = new QTableWidgetItem(usage); @@ -199,7 +197,7 @@ void KeyList::slotRefresh() mKeyList->setItem(row_index, 6, temp_fpr); // strike out expired keys - if(it->expired || it->revoked) { + if (it->expired || it->revoked) { QFont strike = tmp2->font(); strike.setStrikeOut(true); tmp0->setFont(strike); @@ -218,8 +216,7 @@ void KeyList::slotRefresh() setChecked(keyList); } -QStringList *KeyList::getChecked() -{ +QStringList *KeyList::getChecked() { auto *ret = new QStringList(); for (int i = 0; i < mKeyList->rowCount(); i++) { if (mKeyList->item(i, 0)->checkState() == Qt::Checked) { @@ -229,19 +226,17 @@ QStringList *KeyList::getChecked() return ret; } -QStringList *KeyList::getAllPrivateKeys() -{ +QStringList *KeyList::getAllPrivateKeys() { auto *ret = new QStringList(); for (int i = 0; i < mKeyList->rowCount(); i++) { - if (mKeyList->item(i, 1)) { + if (mKeyList->item(i, 1) && buffered_keys[i].is_private_key) { *ret << buffered_keys[i].id; } } return ret; } -QStringList *KeyList::getPrivateChecked() -{ +QStringList *KeyList::getPrivateChecked() { auto *ret = new QStringList(); for (int i = 0; i < mKeyList->rowCount(); i++) { if ((mKeyList->item(i, 0)->checkState() == Qt::Checked) && (mKeyList->item(i, 1))) { @@ -251,19 +246,17 @@ QStringList *KeyList::getPrivateChecked() return ret; } -void KeyList::setChecked(QStringList *keyIds) -{ +void KeyList::setChecked(QStringList *keyIds) { if (!keyIds->isEmpty()) { for (int i = 0; i < mKeyList->rowCount(); i++) { - if (keyIds->contains(buffered_keys[i].id)) { + if (keyIds->contains(buffered_keys[i].id)) { mKeyList->item(i, 0)->setCheckState(Qt::Checked); } } } } -QStringList *KeyList::getSelected() -{ +QStringList *KeyList::getSelected() { auto *ret = new QStringList(); for (int i = 0; i < mKeyList->rowCount(); i++) { @@ -274,47 +267,42 @@ QStringList *KeyList::getSelected() return ret; } -[[maybe_unused]] bool KeyList::containsPrivateKeys() -{ +[[maybe_unused]] bool KeyList::containsPrivateKeys() { for (int i = 0; i < mKeyList->rowCount(); i++) { if (mKeyList->item(i, 1)) { - return true; + return true; } } return false; } -void KeyList::setColumnWidth(int row, int size) -{ +void KeyList::setColumnWidth(int row, int size) { mKeyList->setColumnWidth(row, size); } -void KeyList::contextMenuEvent(QContextMenuEvent *event) -{ +void KeyList::contextMenuEvent(QContextMenuEvent *event) { if (mKeyList->selectedItems().length() > 0) { popupMenu->exec(event->globalPos()); } } -void KeyList::addSeparator() -{ +void KeyList::addSeparator() { popupMenu->addSeparator(); } -void KeyList::addMenuAction(QAction *act) -{ +void KeyList::addMenuAction(QAction *act) { popupMenu->addAction(act); } -void KeyList::dropEvent(QDropEvent* event) -{ +void KeyList::dropEvent(QDropEvent *event) { auto *dialog = new QDialog(); dialog->setWindowTitle(tr("Import Keys")); QLabel *label; - label = new QLabel(tr("You've dropped something on the table.\n GpgFrontend will now try to import key(s).")+"\n"); + label = new QLabel( + tr("You've dropped something on the table.\n GpgFrontend will now try to import key(s).") + "\n"); // "always import keys"-CheckBox auto *checkBox = new QCheckBox(tr("Always import without bothering.")); @@ -332,56 +320,50 @@ void KeyList::dropEvent(QDropEvent* event) dialog->setLayout(vbox); - if (settings.value("general/confirmImportKeys",Qt::Checked).toBool()) - { + if (settings.value("general/confirmImportKeys", Qt::Checked).toBool()) { dialog->exec(); if (dialog->result() == QDialog::Rejected) { return; } - if (checkBox->isChecked()){ - settings.setValue("general/confirmImportKeys", false); + if (checkBox->isChecked()) { + settings.setValue("general/confirmImportKeys", false); } else { settings.setValue("general/confirmImportKeys", true); } } - if (event->mimeData()->hasUrls()) - { - for (const QUrl& tmp : event->mimeData()->urls()) - { - QFile file; - file.setFileName(tmp.toLocalFile()); - if (!file.open(QIODevice::ReadOnly)) { - qDebug() << tr("Couldn't Open File: ") + tmp.toString(); - } - QByteArray inBuffer = file.readAll(); - this->importKeys(inBuffer); - file.close(); - } - } else { - QByteArray inBuffer(event->mimeData()->text().toUtf8()); + if (event->mimeData()->hasUrls()) { + for (const QUrl &tmp : event->mimeData()->urls()) { + QFile file; + file.setFileName(tmp.toLocalFile()); + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << tr("Couldn't Open File: ") + tmp.toString(); + } + QByteArray inBuffer = file.readAll(); this->importKeys(inBuffer); - } + file.close(); + } + } else { + QByteArray inBuffer(event->mimeData()->text().toUtf8()); + this->importKeys(inBuffer); + } } -void KeyList::dragEnterEvent(QDragEnterEvent *event) -{ +void KeyList::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); } /** set background color for Keys and put them to top * */ -[[maybe_unused]] void KeyList::markKeys(QStringList *keyIds) -{ - foreach(QString id, *keyIds) { - qDebug() << "marked: " << id; - } +[[maybe_unused]] void KeyList::markKeys(QStringList *keyIds) { + foreach(QString id, *keyIds) { + qDebug() << "marked: " << id; + } } -void KeyList::importKeys(QByteArray inBuffer) -{ +void KeyList::importKeys(QByteArray inBuffer) { GpgImportInformation result = mCtx->importKey(std::move(inBuffer)); new KeyImportDetailDialog(mCtx, result, false, this); } @@ -397,7 +379,7 @@ void KeyList::getCheckedKeys(QVector<GpgKey> &keys) { void KeyList::setExcludeKeys(std::initializer_list<QString> key_ids) { excluded_key_ids.clear(); - for(auto &key_id : key_ids) { + for (auto &key_id : key_ids) { excluded_key_ids.push_back(key_id); } } @@ -407,8 +389,8 @@ void KeyList::setFilter(std::function<bool(const GpgKey &)> filter) { } void KeyList::slotDoubleClicked(const QModelIndex &index) { - if(mAction != nullptr) { - const auto &key = mCtx->getKeyById(buffered_keys[index.row()].id); + if (mAction != nullptr) { + const auto key = mCtx->getKeyById(buffered_keys[index.row()].id); mAction(key, this); } diff --git a/src/ui/widgets/SignersPicker.cpp b/src/ui/widgets/SignersPicker.cpp new file mode 100644 index 00000000..00df5fdd --- /dev/null +++ b/src/ui/widgets/SignersPicker.cpp @@ -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. + * + */ + +#include "ui/widgets/SignersPicker.h" + +SignersPicker::SignersPicker(GpgME::GpgContext *ctx, QWidget *parent) : mCtx(ctx), QDialog(parent) { + auto confirmButton = new QPushButton(tr("Confirm")); + connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(accept())); + + /*Setup KeyList*/ + mKeyList = new KeyList(mCtx, KeyListRow::ONLY_SECRET_KEY, + KeyListColumn::NAME | KeyListColumn::EmailAddress | KeyListColumn::Usage); + + mKeyList->setFilter([](const GpgKey &key) -> bool { + if (!GpgME::GpgContext::checkIfKeyCanSign(key)) return false; + else return true; + }); + + mKeyList->slotRefresh(); + + auto *vbox2 = new QVBoxLayout(); + vbox2->addWidget(new QLabel("Select Signer(s): ")); + vbox2->addWidget(mKeyList); + vbox2->addWidget(confirmButton); + vbox2->addStretch(0); + setLayout(vbox2); + + this->setModal(true); + this->setWindowTitle("Signers Picker"); + this->setMinimumWidth(480); + this->show(); + + +} + +void SignersPicker::getCheckedSigners(QVector<GpgKey> &keys) { + mKeyList->getPrivateCheckedKeys(keys); +} |