diff options
author | Saturneric <[email protected]> | 2021-07-20 16:42:50 +0000 |
---|---|---|
committer | Saturneric <[email protected]> | 2021-07-20 16:42:50 +0000 |
commit | c57feb6a678e27140bb9ae7d132b641947514c31 (patch) | |
tree | 5ed7a7728c084214ce858323a4eccdcba98200b6 /src | |
parent | Update Workflow (diff) | |
parent | Fix problem that macos cannot use multi-languages. (diff) | |
download | GpgFrontend-c57feb6a678e27140bb9ae7d132b641947514c31.tar.gz GpgFrontend-c57feb6a678e27140bb9ae7d132b641947514c31.zip |
Merge branch 'develop' into develop-ci
Diffstat (limited to '')
45 files changed, 3147 insertions, 1687 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d1336ba..ad56423d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(ALL_SOURCE_FILE) add_subdirectory(gpg) add_subdirectory(ui) +add_subdirectory(smtp) aux_source_directory(. BASE_SOURCE) @@ -23,6 +24,8 @@ if(${CMAKE_BUILD_TYPE} STREQUAL "Release") elseif(LINUX) file(COPY ${CMAKE_SOURCE_DIR}/resource/gpgfrontend DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gpgfrontend/usr/share) + else() + set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() else() set(RESOURCE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) @@ -33,7 +36,9 @@ message(STATUS "RESOURCE_OUTPUT_DIRECTORY ${RESOURCE_OUTPUT_DIRECTORY}") file(GLOB_RECURSE ALL_SOURCE_FILES RELACTIVE ${CMAKE_SOURCE_DIR}/src/*.cpp) # Set Translation Files -set(QT_TS_FILES gpgfrontend_en_us.ts gpgfrontend_zh_chs.ts gpgfrontend_zh_cht.ts gpg_frontend_fr.ts gpg_frontend_ru.ts) +set(QT_TS_FILES + gpgfrontend_en_us.ts gpgfrontend_zh_cn.ts + gpgfrontend_fr.ts gpgfrontend_ru.ts gpgfrontend_es.ts) list(TRANSFORM QT_TS_FILES PREPEND ${CMAKE_SOURCE_DIR}/resource/ts/) message(STATUS "QT_TS_FILES ${QT_TS_FILES}") set(QT_QM_FILES_OUTPUT_DIR ${RESOURCE_OUTPUT_DIRECTORY}/ts) @@ -44,6 +49,7 @@ add_custom_target(translations DEPENDS ${QON_QM_FILES}) # Set Build Information configure_file(${CMAKE_SOURCE_DIR}/include/GpgFrontend.h.in ${CMAKE_SOURCE_DIR}/include/GpgFrontend.h @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/include/GpgFrontendBuildInfo.h.in ${CMAKE_SOURCE_DIR}/include/GpgFrontendBuildInfo.h @ONLY) # Copy Resource Files file(COPY ${CMAKE_SOURCE_DIR}/resource/css DESTINATION ${RESOURCE_OUTPUT_DIRECTORY}/ FOLLOW_SYMLINK_CHAIN) @@ -118,19 +124,19 @@ endif() IF (MINGW) message(STATUS "Link Application Static Library For MINGW") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl) elseif(APPLE) message(STATUS "Link Application Static Library For macOS") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl) else() message(STATUS "Link Application Static Library For UNIX") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl pthread) endif() diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index a59bd01d..eb8b96b1 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -23,208 +23,223 @@ */ #include "MainWindow.h" +#include "ui/help/VersionCheckThread.h" MainWindow::MainWindow() - : appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - - auto waitingDialog = new WaitingDialog(this); - - auto ctx_thread = QThread::create([&]() { mCtx = new GpgME::GpgContext(); }); - - ctx_thread->start(); - - while (ctx_thread->isRunning()) - QApplication::processEvents(); - - waitingDialog->close(); - - - if (!mCtx->isGood()) { - QMessageBox::critical( - nullptr, tr("ENV Loading Failed"), - tr("Gnupg is not installed correctly, please follow the ReadME " - "instructions to install gnupg and then open GPGFrontend.")); - QCoreApplication::quit(); - exit(0); - } - - /* get path were app was started */ - setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); - setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); - - qDebug() << "Main Window" << this; - - edit = new TextEdit(this); - setCentralWidget(edit); - - /* the list of Keys available*/ - mKeyList = new KeyList(mCtx, KeyListRow::SECRET_OR_PUBLIC_KEY, - KeyListColumn::TYPE | KeyListColumn::NAME | - KeyListColumn::EmailAddress | - KeyListColumn::Usage | KeyListColumn::Validity, - this); - mKeyList->setFilter([](const GpgKey &key) -> bool { - if (key.revoked || key.disabled || key.expired) - return false; - else - return true; - }); - mKeyList->slotRefresh(); - - infoBoard = new InfoBoardWidget(this, mCtx, mKeyList); - - /* List of binary Attachments */ - attachmentDockCreated = false; - - /* Variable containing if restart is needed */ - this->slotSetRestartNeeded(false); - - keyMgmt = new KeyMgmt(mCtx, this); - keyMgmt->hide(); - /* test attachmentdir for files alll 15s */ - auto *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(slotCheckAttachmentFolder())); - timer->start(5000); - - createActions(); - createMenus(); - createToolBars(); - createStatusBar(); - createDockWindows(); - - connect(edit->tabWidget, SIGNAL(currentChanged(int)), this, - SLOT(slotDisableTabActions(int))); - - mKeyList->addMenuAction(appendSelectedKeysAct); - mKeyList->addMenuAction(copyMailAddressToClipboardAct); - mKeyList->addMenuAction(showKeyDetailsAct); - mKeyList->addMenuAction(refreshKeysFromKeyserverAct); - mKeyList->addMenuAction(uploadKeyToServerAct); - - restoreSettings(); - - // open filename if provided as first command line parameter - QStringList args = qApp->arguments(); - if (args.size() > 1) { - if (!args[1].startsWith("-")) { - if (QFile::exists(args[1])) - edit->loadFile(args[1]); + : appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { + + networkAccessManager = new QNetworkAccessManager(this); + + auto waitingDialog = new WaitingDialog(tr("Loading Gnupg"), this); + + // Init Gnupg + auto ctx_thread = QThread::create([&]() { mCtx = new GpgME::GpgContext(); }); + ctx_thread->start(); + while (ctx_thread->isRunning()) + QApplication::processEvents(); + waitingDialog->close(); + ctx_thread->deleteLater(); + + QString baseUrl = "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; + + QNetworkRequest request; + request.setUrl(QUrl(baseUrl)); + + QNetworkReply *replay = networkAccessManager->get(request); + + auto version_thread = new VersionCheckThread(replay); + + connect(version_thread, SIGNAL(finished(QPrivateSignal)), version_thread, SLOT(deleteLater())); + connect(version_thread, SIGNAL(upgradeVersion(const QString &, const QString &)), this, SLOT(slotVersionUpgrade(const QString &, const QString &))); + + version_thread->start(); + + // Check Context Status + if (!mCtx->isGood()) { + QMessageBox::critical( + nullptr, tr("ENV Loading Failed"), + tr("Gnupg is not installed correctly, please follow the ReadME " + "instructions to install gnupg and then open GPGFrontend.")); + QCoreApplication::quit(); + exit(0); + } + + /* get path were app was started */ + setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); + + edit = new TextEdit(this); + setCentralWidget(edit); + + /* the list of Keys available*/ + mKeyList = new KeyList(mCtx, KeyListRow::SECRET_OR_PUBLIC_KEY, + KeyListColumn::TYPE | KeyListColumn::NAME | + KeyListColumn::EmailAddress | + KeyListColumn::Usage | KeyListColumn::Validity, + this); + mKeyList->setFilter([](const GpgKey &key) -> bool { + if (key.revoked || key.disabled || key.expired) + return false; + else + return true; + }); + mKeyList->slotRefresh(); + + infoBoard = new InfoBoardWidget(this, mCtx, mKeyList); + + /* List of binary Attachments */ + attachmentDockCreated = false; + + /* Variable containing if restart is needed */ + this->slotSetRestartNeeded(false); + + keyMgmt = new KeyMgmt(mCtx, this); + keyMgmt->hide(); + /* test attachmentdir for files alll 15s */ + auto *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(slotCheckAttachmentFolder())); + timer->start(5000); + + createActions(); + createMenus(); + createToolBars(); + createStatusBar(); + createDockWindows(); + + connect(edit->tabWidget, SIGNAL(currentChanged(int)), this, + SLOT(slotDisableTabActions(int))); + + mKeyList->addMenuAction(appendSelectedKeysAct); + mKeyList->addMenuAction(copyMailAddressToClipboardAct); + mKeyList->addMenuAction(showKeyDetailsAct); + mKeyList->addSeparator(); + mKeyList->addMenuAction(refreshKeysFromKeyserverAct); + mKeyList->addMenuAction(uploadKeyToServerAct); + + restoreSettings(); + + // open filename if provided as first command line parameter + QStringList args = qApp->arguments(); + if (args.size() > 1) { + if (!args[1].startsWith("-")) { + if (QFile::exists(args[1])) + edit->loadFile(args[1]); + } + } + edit->curTextPage()->setFocus(); + this->setMinimumSize(1200, 700); + this->setWindowTitle(qApp->applicationName()); + this->show(); + + // Show wizard, if the don't show wizard message box wasn't checked + // and keylist doesn't contain a private key + qDebug() << "wizard/showWizard" + << settings.value("wizard/showWizard", true).toBool(); + qDebug() << "wizard/nextPage" << settings.value("wizard/nextPage").isNull(); + if (settings.value("wizard/showWizard", true).toBool() || + !settings.value("wizard/nextPage").isNull()) { + slotStartWizard(); } - } - edit->curTextPage()->setFocus(); - this->setMinimumSize(1200, 700); - this->setWindowTitle(qApp->applicationName()); - this->show(); - - // Show wizard, if the don't show wizard message box wasn't checked - // and keylist doesn't contain a private key - qDebug() << "wizard/showWizard" - << settings.value("wizard/showWizard", true).toBool(); - qDebug() << "wizard/nextPage" << settings.value("wizard/nextPage").isNull(); - if (settings.value("wizard/showWizard", true).toBool() || - !settings.value("wizard/nextPage").isNull()) { - slotStartWizard(); - } } void MainWindow::restoreSettings() { - // state sets pos & size of dock-widgets - this->restoreState(settings.value("window/windowState").toByteArray()); - - // Restore window size & location - if (settings.value("window/windowSave").toBool()) { - QPoint pos = settings.value("window/pos", QPoint(100, 100)).toPoint(); - QSize size = settings.value("window/size", QSize(800, 450)).toSize(); - this->resize(size); - this->move(pos); - } else { - this->resize(QSize(800, 450)); - this->move(QPoint(100, 100)); - } - - // Iconsize - QSize iconSize = settings.value("toolbar/iconsize", QSize(24, 24)).toSize(); - this->setIconSize(iconSize); - - importButton->setIconSize(iconSize); - fileEncButton->setIconSize(iconSize); - // set list of keyserver if not defined - QStringList *keyServerDefaultList; - keyServerDefaultList = new QStringList("http://keys.gnupg.net"); - keyServerDefaultList->append("https://keyserver.ubuntu.com"); - keyServerDefaultList->append("http://pool.sks-keyservers.net"); - - QStringList keyServerList = - settings.value("keyserver/keyServerList", *keyServerDefaultList) - .toStringList(); - settings.setValue("keyserver/keyServerList", keyServerList); - - // set default keyserver, if it's not set - QString defaultKeyServer = settings - .value("keyserver/defaultKeyServer", - QString("https://keyserver.ubuntu.com")) - .toString(); - settings.setValue("keyserver/defaultKeyServer", defaultKeyServer); - - // Iconstyle - Qt::ToolButtonStyle buttonStyle = static_cast<Qt::ToolButtonStyle>( - settings.value("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon) - .toUInt()); - this->setToolButtonStyle(buttonStyle); - importButton->setToolButtonStyle(buttonStyle); - fileEncButton->setToolButtonStyle(buttonStyle); - - // Checked Keys - if (settings.value("keys/saveKeyChecked").toBool()) { - QStringList keyIds = - settings.value("keys/savedCheckedKeyList").toStringList(); - mKeyList->setChecked(&keyIds); - } + // state sets pos & size of dock-widgets + this->restoreState(settings.value("window/windowState").toByteArray()); + + // Restore window size & location + if (settings.value("window/windowSave").toBool()) { + QPoint pos = settings.value("window/pos", QPoint(100, 100)).toPoint(); + QSize size = settings.value("window/size", QSize(800, 450)).toSize(); + this->resize(size); + this->move(pos); + } else { + this->resize(QSize(800, 450)); + this->move(QPoint(100, 100)); + } + + // Iconsize + QSize iconSize = settings.value("toolbar/iconsize", QSize(24, 24)).toSize(); + this->setIconSize(iconSize); + + importButton->setIconSize(iconSize); + fileEncButton->setIconSize(iconSize); + // set list of keyserver if not defined + QStringList *keyServerDefaultList; + keyServerDefaultList = new QStringList("http://keys.gnupg.net"); + keyServerDefaultList->append("https://keyserver.ubuntu.com"); + keyServerDefaultList->append("http://pool.sks-keyservers.net"); + + QStringList keyServerList = + settings.value("keyserver/keyServerList", *keyServerDefaultList) + .toStringList(); + settings.setValue("keyserver/keyServerList", keyServerList); + + // set default keyserver, if it's not set + QString defaultKeyServer = settings + .value("keyserver/defaultKeyServer", + QString("https://keyserver.ubuntu.com")) + .toString(); + settings.setValue("keyserver/defaultKeyServer", defaultKeyServer); + + // Iconstyle + Qt::ToolButtonStyle buttonStyle = static_cast<Qt::ToolButtonStyle>( + settings.value("toolbar/iconstyle", Qt::ToolButtonTextUnderIcon) + .toUInt()); + this->setToolButtonStyle(buttonStyle); + importButton->setToolButtonStyle(buttonStyle); + fileEncButton->setToolButtonStyle(buttonStyle); + + // Checked Keys + if (settings.value("keys/saveKeyChecked").toBool()) { + QStringList keyIds = + settings.value("keys/savedCheckedKeyList").toStringList(); + mKeyList->setChecked(&keyIds); + } } void MainWindow::saveSettings() { - // window position and size - settings.setValue("window/windowState", saveState()); - settings.setValue("window/pos", pos()); - settings.setValue("window/size", size()); - - // keyid-list of private checked keys - if (settings.value("keys/saveKeyChecked").toBool()) { - QStringList *keyIds = mKeyList->getChecked(); - if (!keyIds->isEmpty()) { - settings.setValue("keys/savedCheckedKeyList", *keyIds); + // window position and size + settings.setValue("window/windowState", saveState()); + settings.setValue("window/pos", pos()); + settings.setValue("window/size", size()); + + // keyid-list of private checked keys + if (settings.value("keys/saveKeyChecked").toBool()) { + QStringList *keyIds = mKeyList->getChecked(); + if (!keyIds->isEmpty()) { + settings.setValue("keys/savedCheckedKeyList", *keyIds); + } else { + settings.setValue("keys/savedCheckedKeyList", ""); + } } else { - settings.setValue("keys/savedCheckedKeyList", ""); + settings.remove("keys/savedCheckedKeyList"); } - } else { - settings.remove("keys/savedCheckedKeyList"); - } } void MainWindow::closeAttachmentDock() { - if (!attachmentDockCreated) { - return; - } - attachmentDock->close(); - attachmentDock->deleteLater(); - attachmentDockCreated = false; + if (!attachmentDockCreated) { + return; + } + attachmentDock->close(); + attachmentDock->deleteLater(); + attachmentDockCreated = false; } void MainWindow::closeEvent(QCloseEvent *event) { - /* - * ask to save changes, if there are - * modified documents in any tab - */ - if (edit->maybeSaveAnyTab()) { - saveSettings(); - event->accept(); - } else { - event->ignore(); - } - - // clear password from memory - mCtx->clearPasswordCache(); + /* + * ask to save changes, if there are + * modified documents in any tab + */ + if (edit->maybeSaveAnyTab()) { + saveSettings(); + event->accept(); + } else { + event->ignore(); + } + + // clear password from memory + mCtx->clearPasswordCache(); } diff --git a/src/Mime.cpp b/src/Mime.cpp deleted file mode 100644 index 8e4c8d72..00000000 --- a/src/Mime.cpp +++ /dev/null @@ -1,233 +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. - * - */ - -/* TODO: proper import / copyright statement - * - */ - -#include "Mime.h" - -Mime::Mime(QByteArray *message) { - splitParts(message); - /* - mMessage = message; - int bStart = mMessage->indexOf("boundary=\"") + 10 ; - int bEnd = mMessage->indexOf("\"\n", bStart ); - - qDebug() << "bStart: " << bStart << " bEnd: " << bEnd; - mBoundary = new QByteArray(mMessage->mid(bStart, bEnd - bStart)); - qDebug() << "boundary: " << *mBoundary; - - Part *p1 = new Part(); - - int nb = mMessage->indexOf(*mBoundary, bEnd) + mBoundary->length() +1 ; - qDebug() << "nb: " << nb; - int eh = mMessage->indexOf("\n\n", nb); - qDebug() << "eh: " << eh; - QByteArray *header = new QByteArray(mMessage->mid(nb , eh - nb)); - qDebug() << "header:" << header; - - // split header at newlines - foreach(QByteArray tmp , header->split(* "\n")) { - // split lines at : - QList<QByteArray> tmp2 = tmp.split(* ":"); - p1->header.insert(QString(tmp2[0].trimmed()), QString(tmp2[1].trimmed())); - } - - QHashIterator<QString, QString> i(p1->header); - while (i.hasNext()) { - i.next(); - qDebug() << "found: " << i.key() << ":" << i.value() << endl; - } - - int nb2 = mMessage->indexOf(*mBoundary, eh); - - p1->body = mMessage->mid(eh , nb2 - eh); - - QTextCodec *codec = QTextCodec::codecForName("ISO-8859-15"); - QString qs = codec->toUnicode(p1->body); - qDebug() << "body: " << qs; - */ -} - -Mime::~Mime() -= default; - -void Mime::splitParts(QByteArray *message) { - int pos1, pos2, headEnd; - MimePart p_tmp; - - // find the boundary - pos1 = message->indexOf("boundary=\"") + 10; - pos2 = message->indexOf("\"\n", pos1); - QByteArray boundary = message->mid(pos1, pos2 - pos1); - //qDebug() << "boundary: " << boundary; - - while (pos2 > pos1) { - - pos1 = message->indexOf(boundary, pos2) + boundary.length() + 1; - headEnd = message->indexOf("\n\n", pos1); - if (headEnd < 0) - break; - QByteArray header = message->mid(pos1, headEnd - pos1); - - p_tmp.header = parseHeader(&header); - - pos2 = message->indexOf(boundary, headEnd); - p_tmp.body = message->mid(headEnd, pos2 - headEnd); - - mPartList.append(p_tmp); - } -} - -Header Mime::parseHeader(QByteArray *header) { - - QList<HeadElem> ret; - - /** http://www.aspnetmime.com/help/welcome/overviewmimeii.html : - * If a line starts with any white space, that line is said to be 'folded' and is actually - * part of the header above it. - */ - header->replace("\n ", " "); - - //split header at newlines - foreach(QByteArray line, header->split(*"\n")) { - HeadElem elem; - //split lines at : - QList<QByteArray> tmp2 = line.split(*":"); - elem.name = tmp2[0].trimmed(); - if (tmp2[1].contains(';')) { - // split lines at ; - // TODO: what if ; is inside "" - QList<QByteArray> tmp3 = tmp2[1].split(*";"); - elem.value = QString(tmp3.takeFirst().trimmed()); - foreach(QByteArray tmp4, tmp3) { - QList<QByteArray> tmp5 = tmp4.split(*"="); - elem.params.insert(QString(tmp5[0].trimmed()), QString(tmp5[1].trimmed())); - } - } else { - elem.value = tmp2[1].trimmed(); - } - ret.append(elem); - } - return Header(ret); -} - -Header Mime::getHeader(const QByteArray *message) { - int headEnd = message->indexOf("\n\n"); - QByteArray header = message->mid(0, headEnd); - return parseHeader(&header); -} - -bool Mime::isMultipart(QByteArray *message) { - return message->startsWith("Content-Type: multipart/mixed;"); -} - -/** - * if Content-Type is specified, it should be mime - * - */ -bool Mime::isMime(const QByteArray *message) { - return message->startsWith("Content-Type:"); -} - -/*** - * quotedPrintableDecode copied from KCodecs, where it is stated: - - The quoted-printable codec as described in RFC 2045, section 6.7. is by - Rik Hemsley (C) 2001. - - */ - -static const char hexChars[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' -}; - -/******************************** KCodecs ********************************/ -// strchr(3) for broken systems. -static int rikFindChar(const char *_s, const char c) { - const char *s = _s; - - while (true) { - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - } - - return static_cast<int>(s - _s); -} - -void Mime::quotedPrintableDecode(const QByteArray &in, QByteArray &out) { - // clear out the output buffer - out.resize(0); - if (in.isEmpty()) - return; - - char *cursor; - const char *data; - const size_t length = in.size(); - - data = in.data(); - out.resize(static_cast<int>(length)); - cursor = out.data(); - - for (unsigned int i = 0; i < length; i++) { - char c(in[i]); - - if ('=' == c) { - if (i < length - 2) { - char c1 = in[i + 1]; - char c2 = in[i + 2]; - - if (('\n' == c1) || ('\r' == c1 && '\n' == c2)) { - // Soft line break. No output. - if ('\r' == c1) - i += 2; // CRLF line breaks - else - i += 1; - } else { - // =XX encoded byte. - - int hexChar0 = rikFindChar(hexChars, c1); - int hexChar1 = rikFindChar(hexChars, c2); - - if (hexChar0 < 16 && hexChar1 < 16) { - *cursor++ = char((hexChar0 * 16) | hexChar1); - i += 2; - } - } - } - } else { - *cursor++ = c; - } - } - - out.truncate(static_cast<int>(cursor - out.data())); -} diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp index e94ce1ab..0462433d 100644 --- a/src/gpg/GpgContext.cpp +++ b/src/gpg/GpgContext.cpp @@ -24,8 +24,8 @@ #include "gpg/GpgContext.h" +#include <functional> #include <unistd.h> /* contains read/write */ -#include <Mime.h> #ifdef _WIN32 @@ -73,8 +73,10 @@ namespace GpgME { << 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) + 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) @@ -430,38 +432,6 @@ namespace GpgME { return err; } - - /** - * if this is mime, split text and attachments... - * message contains only text afterwards - */ - void parseMime(QByteArray *message) { - - QString pText; - bool show_ma_dock = false; - - Mime *mime = new Mime(message); - for (MimePart tmp : mime->parts()) { - if (tmp.header.getValue("Content-Type") == "text/plain" && - tmp.header.getValue("Content-Transfer-Encoding") != "base64") { - QByteArray body; - if (tmp.header.getValue("Content-Transfer-Encoding") == "quoted-printable") { - Mime::quotedPrintableDecode(tmp.body, body); - } else { - body = tmp.body; - } - pText.append(QString(body)); - } else { - // TODO - show_ma_dock = true; - } - } - *message = pText.toUtf8(); - if (show_ma_dock) { - // TODO - } - } - /** Decrypt QByteAarray, return QByteArray * mainly from http://basket.kde.org/ (kgpgme.cpp) */ @@ -496,28 +466,6 @@ namespace GpgME { gpgme_data_release(dataOut); } - /* - * 1) is it mime (content-type:) - * 2) parse header - * 2) choose action depending on content-type - */ - if (Mime::isMime(outBuffer)) { - Header header = Mime::getHeader(outBuffer); - // is it multipart, is multipart-parsing enabled - if (header.getValue("Content-Type") == "multipart/mixed" - && settings.value("mime/parseMime").toBool()) { - parseMime(outBuffer); - } else if (header.getValue("Content-Type") == "text/plain" - && settings.value("mime/parseQP").toBool()) { - if (header.getValue("Content-Transfer-Encoding") == "quoted-printable") { - auto *decoded = new QByteArray(); - Mime::quotedPrintableDecode(*outBuffer, *decoded); - //TODO: remove header - outBuffer = decoded; - } - } - } - if (result != nullptr) { *result = m_result; } @@ -659,16 +607,16 @@ namespace GpgME { bool GpgContext::exportSecretKey(const GpgKey &key, QByteArray *outBuffer) { qDebug() << "Export Secret Key" << key.id; gpgme_key_t target_key[2] = { - key.key_refer, - nullptr + 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); + 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) { + if (gpgme_err_code(error) != GPG_ERR_NO_ERROR) { checkErr(error); gpgme_data_release(dataOut); return false; @@ -680,20 +628,31 @@ namespace GpgME { } /** return type should be gpgme_error_t*/ - void GpgContext::executeGpgCommand(const QStringList &arguments, QByteArray *stdOut, QByteArray *stdErr) { + QProcess * GpgContext::executeGpgCommand(const QStringList &arguments, QByteArray *stdOut, QByteArray *stdErr, + const std::function<void(QProcess *)> &interactFunc) { QStringList args; - args << "--homedir" << gpgKeys << "--batch" << arguments; + 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); + }); - qDebug() << args; - QProcess gpg; - // qDebug() << "engine->file_name" << engine->file_name; + gpgProcess->start(gpgExec, args); - gpg.start(gpgBin, args); - gpg.waitForFinished(); + if (gpgProcess->waitForStarted()){ + qDebug() << "Gpg Process Started Success"; + } else { + qDebug() << "Gpg Process Started Failed"; + } - *stdOut = gpg.readAllStandardOutput(); - *stdErr = gpg.readAllStandardError(); - qDebug() << *stdOut; + return gpgProcess; } /*** @@ -734,26 +693,6 @@ namespace GpgME { return gpgmeError; } - /*** - * return type should contain: - * -> list of sigs - * -> valid - * -> decrypted message - */ - //void GpgContext::decryptVerify(QByteArray in) { - - /* gpgme_error_t err; - gpgme_data_t in, out; - - gpgme_decrypt_result_t decrypt_result; - gpgme_verify_result_t verify_result; - - err = gpgme_op_decrypt_verify (mCtx, in, out); - decrypt_result = gpgme_op_decrypt_result (mCtx); - - verify_result = gpgme_op_verify_result (mCtx); - */ - //} gpg_error_t GpgContext::sign(const QVector<GpgKey> &keys, const QByteArray &inBuffer, QByteArray *outBuffer, bool detached, gpgme_sign_result_t *result) { @@ -962,7 +901,7 @@ namespace GpgME { void GpgContext::setSigners(const QVector<GpgKey> &keys) { gpgme_signers_clear(mCtx); for (const auto &key : keys) { - if(checkIfKeyCanSign(key)) { + if (checkIfKeyCanSign(key)) { auto gpgmeError = gpgme_signers_add(mCtx, key.key_refer); checkErr(gpgmeError); } @@ -1180,7 +1119,7 @@ namespace GpgME { } } - if(gpgme_err_code(err) != GPG_ERR_NO_ERROR) + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) checkErr(err); if (dataIn) { @@ -1240,11 +1179,11 @@ namespace GpgME { return false; } - for (const auto& key : keys) { + for (const auto &key : keys) { err = gpgme_data_new(&dataOut); checkErr(err); - err = gpgme_op_export(mCtx,key.id.toUtf8().constData(), 0, dataOut); + err = gpgme_op_export(mCtx, key.id.toUtf8().constData(), 0, dataOut); checkErr(err); read_bytes = gpgme_data_seek(dataOut, 0, SEEK_END); @@ -1255,4 +1194,41 @@ namespace GpgME { } 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/main.cpp b/src/main.cpp index 00ab2738..ff3b0bb4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ */ #include "MainWindow.h" +#include "GpgFrontendBuildInfo.h" int main(int argc, char *argv[]) { @@ -61,7 +62,7 @@ int main(int argc, char *argv[]) { QTranslator translator, translator2; int return_from_event_loop_code; - qDebug() << settings.fileName(); + qDebug() << "Resource Directory" << RESOURCE_DIR(appPath); do { QApplication::removeTranslator(&translator); diff --git a/src/smtp/CMakeLists.txt b/src/smtp/CMakeLists.txt new file mode 100644 index 00000000..8e341f98 --- /dev/null +++ b/src/smtp/CMakeLists.txt @@ -0,0 +1,6 @@ +aux_source_directory(. SMTP_MIME_SOURCE) + +add_library(smtp STATIC ${SMTP_MIME_SOURCE}) + +target_link_libraries(smtp + Qt5::Network Qt5::Core) diff --git a/src/smtp/emailaddress.cpp b/src/smtp/emailaddress.cpp new file mode 100644 index 00000000..c0ecaa0d --- /dev/null +++ b/src/smtp/emailaddress.cpp @@ -0,0 +1,44 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/emailaddress.h" + +/* [1] Constructors and Destructors */ + +EmailAddress::EmailAddress(const QString &address, const QString &name) { + this->address = address; + this->name = name; +} + +EmailAddress::~EmailAddress() {} + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void EmailAddress::setName(const QString &name) { this->name = name; } + +void EmailAddress::setAddress(const QString &address) { + this->address = address; +} + +const QString &EmailAddress::getName() const { return name; } + +const QString &EmailAddress::getAddress() const { return address; } + +/* [2] --- */ diff --git a/src/smtp/mimeattachment.cpp b/src/smtp/mimeattachment.cpp new file mode 100644 index 00000000..033eecfb --- /dev/null +++ b/src/smtp/mimeattachment.cpp @@ -0,0 +1,43 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimeattachment.h" +#include <QFileInfo> + +/* [1] Constructors and Destructors */ + +MimeAttachment::MimeAttachment(QFile *file) : MimeFile(file) {} + +MimeAttachment::MimeAttachment(const QByteArray &stream, + const QString &fileName) + : MimeFile(stream, fileName) {} + +MimeAttachment::~MimeAttachment() = default; + +/* [1] --- */ + +/* [2] Protected methods */ + +void MimeAttachment::prepare() { + this->header += "Content-disposition: attachment\r\n"; + + /* !!! IMPORTANT !!! */ + MimeFile::prepare(); +} + +/* [2] --- */ diff --git a/src/smtp/mimecontentformatter.cpp b/src/smtp/mimecontentformatter.cpp new file mode 100644 index 00000000..8f538457 --- /dev/null +++ b/src/smtp/mimecontentformatter.cpp @@ -0,0 +1,59 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimecontentformatter.h" + +MimeContentFormatter::MimeContentFormatter(int max_length) + : max_length(max_length) {} + +QString MimeContentFormatter::format(const QString &content, + bool quotedPrintable) const { + + QString out; + + int chars = 0; + for (auto i : content) { + chars++; + if (!quotedPrintable) { + if (chars > max_length) { + out.append("\r\n"); + chars = 1; + } + } else { + if (i == '\n') { // new line + out.append(i); + chars = 0; + continue; + } + + if ((chars > max_length - 1) || + ((i == '=') && (chars > max_length - 3))) { + out.append('='); + out.append("\r\n"); + chars = 1; + } + } + out.append(i); + } + + return out; +} + +void MimeContentFormatter::setMaxLength(int l) { max_length = l; } + +int MimeContentFormatter::getMaxLength() const { return max_length; } diff --git a/src/smtp/mimefile.cpp b/src/smtp/mimefile.cpp new file mode 100644 index 00000000..4f095b84 --- /dev/null +++ b/src/smtp/mimefile.cpp @@ -0,0 +1,61 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimefile.h" +#include <QFileInfo> + +/* [1] Constructors and Destructors */ + +MimeFile::MimeFile(QFile *file) { + this->file = file; + this->cType = "application/octet-stream"; + this->cName = QFileInfo(*file).fileName(); + this->cEncoding = Base64; +} + +MimeFile::MimeFile(const QByteArray &stream, const QString &fileName) { + this->cEncoding = Base64; + this->cType = "application/octet-stream"; + this->file = nullptr; + this->cName = fileName; + this->content = stream; +} + +MimeFile::~MimeFile() { + delete file; +} + +/* [1] --- */ + +/* [2] Getters and setters */ + +/* [2] --- */ + +/* [3] Protected methods */ + +void MimeFile::prepare() { + if (this->file) { + file->open(QIODevice::ReadOnly); + this->content = file->readAll(); + file->close(); + } + /* !!! IMPORTANT !!!! */ + MimePart::prepare(); +} + +/* [3] --- */ diff --git a/src/smtp/mimehtml.cpp b/src/smtp/mimehtml.cpp new file mode 100644 index 00000000..9f3a53c3 --- /dev/null +++ b/src/smtp/mimehtml.cpp @@ -0,0 +1,46 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimehtml.h" + +/* [1] Constructors and Destructors */ + +MimeHtml::MimeHtml(const QString &html) : MimeText(html) { + this->cType = "text/html"; +} + +MimeHtml::~MimeHtml() = default; + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void MimeHtml::setHtml(const QString &html) { this->text = html; } + +const QString &MimeHtml::getHtml() const { return text; } + +/* [2] --- */ + +/* [3] Protected methods */ + +void MimeHtml::prepare() { + /* !!! IMPORTANT !!! */ + MimeText::prepare(); +} + +/* [3] --- */ diff --git a/src/smtp/mimeinlinefile.cpp b/src/smtp/mimeinlinefile.cpp new file mode 100644 index 00000000..a49167e8 --- /dev/null +++ b/src/smtp/mimeinlinefile.cpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimeinlinefile.h" + +/* [1] Constructors and Destructors */ + +MimeInlineFile::MimeInlineFile(QFile *f) : MimeFile(f) {} + +MimeInlineFile::~MimeInlineFile() = default; + +/* [1] --- */ + +/* [2] Getters and Setters */ + +/* [2] --- */ + +/* [3] Protected methods */ + +void MimeInlineFile::prepare() { + this->header += "Content-Disposition: inline\r\n"; + + /* !!! IMPORTANT !!! */ + MimeFile::prepare(); +} + +/* [3] --- */ diff --git a/src/smtp/mimemessage.cpp b/src/smtp/mimemessage.cpp new file mode 100644 index 00000000..cf653e0a --- /dev/null +++ b/src/smtp/mimemessage.cpp @@ -0,0 +1,308 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimemessage.h" +#include "smtp/quotedprintable.h" + +#include <QDateTime> +#include <QLocale> +#include <typeinfo> + +/* [1] Constructors and Destructors */ +MimeMessage::MimeMessage(bool createAutoMimeContent) + : replyTo(nullptr), hEncoding(MimePart::_8Bit) { + if (createAutoMimeContent) + this->content = new MimeMultiPart(); + + autoMimeContentCreated = createAutoMimeContent; +} + +MimeMessage::~MimeMessage() { + if (this->autoMimeContentCreated) { + this->autoMimeContentCreated = false; + delete (this->content); + } +} + +/* [1] --- */ + +/* [2] Getters and Setters */ +MimePart &MimeMessage::getContent() { return *content; } + +void MimeMessage::setContent(MimePart *content) { + if (this->autoMimeContentCreated) { + this->autoMimeContentCreated = false; + delete (this->content); + } + this->content = content; +} + +void MimeMessage::setReplyTo(EmailAddress *rto) { replyTo = rto; } + +void MimeMessage::setSender(EmailAddress *e) { + this->sender = e; + e->setParent(this); +} + +void MimeMessage::addRecipient(EmailAddress *rcpt, RecipientType type) { + switch (type) { + case To: + recipientsTo << rcpt; + break; + case Cc: + recipientsCc << rcpt; + break; + case Bcc: + recipientsBcc << rcpt; + break; + } + + rcpt->setParent(this); +} + +void MimeMessage::addTo(EmailAddress *rcpt) { this->recipientsTo << rcpt; } + +void MimeMessage::addCc(EmailAddress *rcpt) { this->recipientsCc << rcpt; } + +void MimeMessage::addBcc(EmailAddress *rcpt) { this->recipientsBcc << rcpt; } + +void MimeMessage::setSubject(const QString &subject) { + this->subject = subject; +} + +void MimeMessage::addPart(MimePart *part) { + if (typeid(*content) == typeid(MimeMultiPart)) { + ((MimeMultiPart *) content)->addPart(part); + }; +} + +void MimeMessage::setInReplyTo(const QString &inReplyTo) { + mInReplyTo = inReplyTo; +} + +void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc) { + this->hEncoding = hEnc; +} + +const EmailAddress &MimeMessage::getSender() const { return *sender; } + +const QList<EmailAddress *> & +MimeMessage::getRecipients(RecipientType type) const { + switch (type) { + default: + case To: + return recipientsTo; + case Cc: + return recipientsCc; + case Bcc: + return recipientsBcc; + } +} + +const EmailAddress *MimeMessage::getReplyTo() const { return replyTo; } + +const QString &MimeMessage::getSubject() const { return subject; } + +const QList<MimePart *> &MimeMessage::getParts() const { + if (typeid(*content) == typeid(MimeMultiPart)) { + return ((MimeMultiPart *) content)->getParts(); + } else { + auto *res = new QList<MimePart *>(); + res->append(content); + return *res; + } +} + +/* [2] --- */ + +/* [3] Public Methods */ + +QString MimeMessage::toString() { + QString mime; + + /* =========== MIME HEADER ============ */ + + /* ---------- Sender / From ----------- */ + mime = "From:"; + if (sender->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append(sender->getName().toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append(sender->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + sender->getName(); + } + } + mime += " <" + sender->getAddress() + ">\r\n"; + /* ---------------------------------- */ + + /* ------- Recipients / To ---------- */ + mime += "To:"; + QList<EmailAddress *>::iterator it; + int i; + for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) { + if (i != 0) { + mime += ","; + } + + if ((*it)->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append((*it)->getName().toUtf8()).toBase64() + + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append((*it)->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + mime += "\r\n"; + /* ---------------------------------- */ + + /* ------- Recipients / Cc ---------- */ + if (!recipientsCc.empty()) { + mime += "Cc:"; + } + for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i) { + if (i != 0) { + mime += ","; + } + + if ((*it)->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append((*it)->getName().toUtf8()).toBase64() + + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append((*it)->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + if (!recipientsCc.empty()) { + mime += "\r\n"; + } + /* ---------------------------------- */ + + /* ------------ Subject ------------- */ + mime += "Subject: "; + + switch (hEncoding) { + case MimePart::Base64: + mime += "=?utf-8?B?" + QByteArray().append(subject.toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += "=?utf-8?Q?" + + QuotedPrintable::encode(QByteArray().append(subject.toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += subject; + } + mime += "\r\n"; + /* ---------------------------------- */ + + /* ---------- Reply-To -------------- */ + if (replyTo) { + mime += "Reply-To: "; + if (replyTo->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append(replyTo->getName().toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode(QByteArray().append(replyTo->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + replyTo->getName(); + } + } + mime += " <" + replyTo->getAddress() + ">\r\n"; + } + + /* ---------------------------------- */ + + mime += "MIME-Version: 1.0\r\n"; + if (!mInReplyTo.isEmpty()) { + mime += "In-Reply-To: <" + mInReplyTo + ">\r\n"; + mime += "References: <" + mInReplyTo + ">\r\n"; + } + + QDateTime now = QDateTime::currentDateTime(); +#if QT_VERSION_MAJOR < 5 // Qt4 workaround since RFC2822Date isn't defined + QString shortDayName = + QLocale::c().dayName(now.date().dayOfWeek(), QLocale::ShortFormat); + QString shortMonthName = + QLocale::c().monthName(now.date().month(), QLocale::ShortFormat); + int utcOffset = now.secsTo(QDateTime(now.date(), now.time(), Qt::UTC)) / 60; + char timezoneSign = utcOffset >= 0 ? '+' : '-'; + utcOffset = utcOffset >= 0 ? utcOffset : -utcOffset; + QString timezone = QString("%1%2%3") + .arg(timezoneSign) + .arg(utcOffset / 60, 2, 10, QChar('0')) + .arg(utcOffset % 60, 2, 10, QChar('0')); + mime += QString("Date: %1\r\n") + .arg(now.toString("%1, dd %2 yyyy hh:mm:ss %3") + .arg(shortDayName) + .arg(shortMonthName) + .arg(timezone)); +#else // Qt5 supported + mime += QString("Date: %1\r\n").arg(now.toString(Qt::RFC2822Date)); +#endif // support RFC2822Date + + mime += content->toString(); + return mime; +} + +/* [3] --- */ diff --git a/src/smtp/mimemultipart.cpp b/src/smtp/mimemultipart.cpp new file mode 100644 index 00000000..14a813c2 --- /dev/null +++ b/src/smtp/mimemultipart.cpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimemultipart.h" +#include <QCryptographicHash> +#include <QRandomGenerator> +#include <QTime> + +const QString MULTI_PART_NAMES[] = { + "multipart/mixed", // Mixed + "multipart/digest", // Digest + "multipart/alternative", // Alternative + "multipart/related", // Related + "multipart/report", // Report + "multipart/signed", // Signed + "multipart/encrypted" // Encrypted +}; + +MimeMultiPart::MimeMultiPart(MultiPartType type) { + this->type = type; + this->cType = MULTI_PART_NAMES[this->type]; + this->cEncoding = _8Bit; + + QRandomGenerator generator; + + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(QByteArray().append((char) generator.generate())); + cBoundary = md5.result().toHex(); +} + +void MimeMultiPart::addPart(MimePart *part) { parts.append(part); } + +const QList<MimePart *> &MimeMultiPart::getParts() const { return parts; } + +void MimeMultiPart::prepare() { + QList<MimePart *>::iterator it; + + content = ""; + for (it = parts.begin(); it != parts.end(); it++) { + content += QString("--" + cBoundary + "\r\n").toUtf8(); + (*it)->prepare(); + content += (*it)->toString().toUtf8(); + }; + + content += QString("--" + cBoundary + "--\r\n").toUtf8(); + + MimePart::prepare(); +} + +void MimeMultiPart::setMimeType(const MultiPartType type) { + this->type = type; + this->cType = MULTI_PART_NAMES[type]; +} + +MimeMultiPart::MultiPartType MimeMultiPart::getMimeType() const { return type; } diff --git a/src/smtp/mimepart.cpp b/src/smtp/mimepart.cpp new file mode 100644 index 00000000..5d33884d --- /dev/null +++ b/src/smtp/mimepart.cpp @@ -0,0 +1,159 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimepart.h" +#include "smtp/quotedprintable.h" + +/* [1] Constructors and Destructors */ + +MimePart::MimePart() { + cEncoding = _7Bit; + prepared = false; + cBoundary = ""; +} + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void MimePart::setContent(const QByteArray &content) { + this->content = content; +} + +void MimePart::setHeader(const QString &header) { this->header = header; } + +void MimePart::addHeaderLine(const QString &line) { + this->header += line + "\r\n"; +} + +const QString &MimePart::getHeader() const { return header; } + +const QByteArray &MimePart::getContent() const { return content; } + +void MimePart::setContentId(const QString &cId) { this->cId = cId; } + +const QString &MimePart::getContentId() const { return this->cId; } + +void MimePart::setContentName(const QString &cName) { this->cName = cName; } + +const QString &MimePart::getContentName() const { return this->cName; } + +void MimePart::setContentType(const QString &cType) { this->cType = cType; } + +const QString &MimePart::getContentType() const { return this->cType; } + +void MimePart::setCharset(const QString &charset) { this->cCharset = charset; } + +const QString &MimePart::getCharset() const { return this->cCharset; } + +void MimePart::setEncoding(Encoding enc) { this->cEncoding = enc; } + +MimePart::Encoding MimePart::getEncoding() const { return this->cEncoding; } + +MimeContentFormatter &MimePart::getContentFormatter() { + return this->formatter; +} + +/* [2] --- */ + +/* [3] Public methods */ + +QString MimePart::toString() { + if (!prepared) + prepare(); + + return mimeString; +} + +/* [3] --- */ + +/* [4] Protected methods */ + +void MimePart::prepare() { + mimeString = QString(); + + /* === Header Prepare === */ + + /* Content-Type */ + mimeString.append("Content-Type: ").append(cType); + + if (cName != "") + mimeString.append("; name=\"").append(cName).append("\""); + + if (cCharset != "") + mimeString.append("; charset=").append(cCharset); + + if (cBoundary != "") + mimeString.append("; boundary=").append(cBoundary); + + mimeString.append("\r\n"); + /* ------------ */ + + /* Content-Transfer-Encoding */ + mimeString.append("Content-Transfer-Encoding: "); + switch (cEncoding) { + case _7Bit: + mimeString.append("7bit\r\n"); + break; + case _8Bit: + mimeString.append("8bit\r\n"); + break; + case Base64: + mimeString.append("base64\r\n"); + break; + case QuotedPrintable: + mimeString.append("quoted-printable\r\n"); + break; + } + /* ------------------------ */ + + /* Content-Id */ + if (cId != NULL) + mimeString.append("Content-ID: <").append(cId).append(">\r\n"); + /* ---------- */ + + /* Addition header lines */ + + mimeString.append(header).append("\r\n"); + + /* ------------------------- */ + + /* === End of Header Prepare === */ + + /* === Content === */ + switch (cEncoding) { + case _7Bit: + mimeString.append(QString(content).toLatin1()); + break; + case _8Bit: + mimeString.append(content); + break; + case Base64: + mimeString.append(formatter.format(content.toBase64())); + break; + case QuotedPrintable: + mimeString.append(formatter.format(QuotedPrintable::encode(content), true)); + break; + } + mimeString.append("\r\n"); + /* === End of Content === */ + + prepared = true; +} + +/* [4] --- */ diff --git a/src/smtp/mimetext.cpp b/src/smtp/mimetext.cpp new file mode 100644 index 00000000..3726647e --- /dev/null +++ b/src/smtp/mimetext.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/mimetext.h" + +/* [1] Constructors and Destructors */ + +MimeText::MimeText(const QString &txt) { + this->text = txt; + this->cType = "text/plain"; + this->cCharset = "utf-8"; + this->cEncoding = _8Bit; +} + +MimeText::~MimeText() = default; + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void MimeText::setText(const QString &text) { this->text = text; } + +const QString &MimeText::getText() const { return text; } + +/* [2] --- */ + +/* [3] Protected Methods */ + +void MimeText::prepare() { + this->content.clear(); + this->content.append(text.toUtf8()); + + /* !!! IMPORTANT !!! */ + MimePart::prepare(); +} + +/* [3] --- */ diff --git a/src/smtp/quotedprintable.cpp b/src/smtp/quotedprintable.cpp new file mode 100644 index 00000000..93e51122 --- /dev/null +++ b/src/smtp/quotedprintable.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/quotedprintable.h" + +QString QuotedPrintable::encode(const QByteArray &input) { + QString output; + + char byte; + const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + for (char i : input) { + byte = i; + + if ((byte == 0x20) || ((byte >= 33) && (byte <= 126) && (byte != 61))) { + output.append(byte); + } else { + output.append('='); + output.append(hex[((byte >> 4) & 0x0F)]); + output.append(hex[(byte & 0x0F)]); + } + } + + return output; +} + +QByteArray QuotedPrintable::decode(const QString &input) { + // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B + // C D E F + const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, + 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15}; + + QByteArray output; + + for (int i = 0; i < input.length(); ++i) { + if (input.at(i).toLatin1() == '=') { + output.append((hexVal[input.at(i + 1).toLatin1() - '0'] << 4) + + hexVal[input.at(i + 2).toLatin1() - '0']); + i += 2; + } else { + output.append(input.at(i).toLatin1()); + } + } + + return output; +} diff --git a/src/smtp/smtpclient.cpp b/src/smtp/smtpclient.cpp new file mode 100644 index 00000000..63202998 --- /dev/null +++ b/src/smtp/smtpclient.cpp @@ -0,0 +1,407 @@ +/* + Copyright (c) 2011-2012 - TÅ‘kés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/smtpclient.h" + +#include <QByteArray> +#include <QFileInfo> + +/* [1] Constructors and destructors */ + +SmtpClient::SmtpClient(const QString &host, int port, + ConnectionType connectionType) + : socket(NULL), name("localhost"), authMethod(AuthPlain), + connectionTimeout(5000), responseTimeout(5000), + sendMessageTimeout(60000) { + setConnectionType(connectionType); + + this->host = host; + this->port = port; + + connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, + SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, + SLOT(socketError(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); +} + +SmtpClient::~SmtpClient() { + if (socket) + delete socket; +} + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void SmtpClient::setUser(const QString &user) { this->user = user; } + +void SmtpClient::setPassword(const QString &password) { + this->password = password; +} + +void SmtpClient::setAuthMethod(AuthMethod method) { this->authMethod = method; } + +void SmtpClient::setHost(const QString &host) { this->host = host; } + +void SmtpClient::setPort(int port) { this->port = port; } + +void SmtpClient::setConnectionType(ConnectionType ct) { + this->connectionType = ct; + + if (socket) + delete socket; + + switch (connectionType) { + case TcpConnection: + socket = new QTcpSocket(this); + break; + case SslConnection: + case TlsConnection: + socket = new QSslSocket(this); + } +} + +const QString &SmtpClient::getHost() const { return this->host; } + +const QString &SmtpClient::getUser() const { return this->user; } + +const QString &SmtpClient::getPassword() const { return this->password; } + +SmtpClient::AuthMethod SmtpClient::getAuthMethod() const { + return this->authMethod; +} + +int SmtpClient::getPort() const { return this->port; } + +SmtpClient::ConnectionType SmtpClient::getConnectionType() const { + return connectionType; +} + +const QString &SmtpClient::getName() const { return this->name; } + +void SmtpClient::setName(const QString &name) { this->name = name; } + +const QString &SmtpClient::getResponseText() const { return responseText; } + +int SmtpClient::getResponseCode() const { return responseCode; } + +QTcpSocket *SmtpClient::getSocket() { return socket; } + +int SmtpClient::getConnectionTimeout() const { return connectionTimeout; } + +void SmtpClient::setConnectionTimeout(int msec) { connectionTimeout = msec; } + +int SmtpClient::getResponseTimeout() const { return responseTimeout; } + +void SmtpClient::setResponseTimeout(int msec) { responseTimeout = msec; } + +int SmtpClient::getSendMessageTimeout() const { return sendMessageTimeout; } + +void SmtpClient::setSendMessageTimeout(int msec) { sendMessageTimeout = msec; } + +/* [2] --- */ + +/* [3] Public methods */ + +bool SmtpClient::connectToHost() { + switch (connectionType) { + case TlsConnection: + case TcpConnection: + socket->connectToHost(host, port); + break; + case SslConnection: + ((QSslSocket *) socket)->connectToHostEncrypted(host, port); + break; + } + + // Tries to connect to server + if (!socket->waitForConnected(connectionTimeout)) { + emit smtpError(ConnectionTimeoutError); + return false; + } + + try { + // Wait for the server's response + waitForResponse(); + + // If the response code is not 220 (Service ready) + // means that is something wrong with the server + if (responseCode != 220) { + emit smtpError(ServerError); + return false; + } + + // Send a EHLO/HELO message to the server + // The client's first command must be EHLO/HELO + sendMessage("EHLO " + name); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 250. + if (responseCode != 250) { + emit smtpError(ServerError); + return false; + } + + if (connectionType == TlsConnection) { + // send a request to start TLS handshake + sendMessage("STARTTLS"); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 220. + if (responseCode != 220) { + emit smtpError(ServerError); + return false; + }; + + ((QSslSocket *) socket)->startClientEncryption(); + + if (!((QSslSocket *) socket)->waitForEncrypted(connectionTimeout)) { + qDebug() << ((QSslSocket *) socket)->errorString(); + emit smtpError(ConnectionTimeoutError); + return false; + } + + // Send ELHO one more time + sendMessage("EHLO " + name); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 250. + if (responseCode != 250) { + emit smtpError(ServerError); + return false; + } + } + } catch (ResponseTimeoutException) { + return false; + } catch (SendMessageTimeoutException) { + return false; + } + + // If no errors occured the function returns true. + return true; +} + +bool SmtpClient::login() { return login(user, password, authMethod); } + +bool SmtpClient::login(const QString &user, const QString &password, + AuthMethod method) { + try { + if (method == AuthPlain) { + // Sending command: AUTH PLAIN base64('\0' + username + '\0' + password) + sendMessage("AUTH PLAIN " + QByteArray() + .append((char) 0) + .append(user.toUtf8()) + .append((char) 0) + .append(password.toUtf8()) + .toBase64()); + + // Wait for the server's response + waitForResponse(); + + // If the response is not 235 then the authentication was faild + if (responseCode != 235) { + emit smtpError(AuthenticationFailedError); + return false; + } + } else if (method == AuthLogin) { + // Sending command: AUTH LOGIN + sendMessage("AUTH LOGIN"); + + // Wait for 334 response code + waitForResponse(); + if (responseCode != 334) { + emit smtpError(AuthenticationFailedError); + return false; + } + + // Send the username in base64 + sendMessage(QByteArray().append(user.toUtf8()).toBase64()); + + // Wait for 334 + waitForResponse(); + if (responseCode != 334) { + emit smtpError(AuthenticationFailedError); + return false; + } + + // Send the password in base64 + sendMessage(QByteArray().append(password.toUtf8()).toBase64()); + + // Wait for the server's responce + waitForResponse(); + + // If the response is not 235 then the authentication was faild + if (responseCode != 235) { + emit smtpError(AuthenticationFailedError); + return false; + } + } + } catch (ResponseTimeoutException) { + // Responce Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } catch (SendMessageTimeoutException) { + // Send Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } + + return true; +} + +bool SmtpClient::sendMail(MimeMessage &email) { + try { + // Send the MAIL command with the sender + sendMessage("MAIL FROM:<" + email.getSender().getAddress() + ">"); + + waitForResponse(); + + if (responseCode != 250) + return false; + + // Send RCPT command for each recipient + QList<EmailAddress *>::const_iterator it, itEnd; + // To (primary recipients) + for (it = email.getRecipients().begin(), + itEnd = email.getRecipients().end(); + it != itEnd; ++it) { + + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Cc (carbon copy) + for (it = email.getRecipients(MimeMessage::Cc).begin(), + itEnd = email.getRecipients(MimeMessage::Cc).end(); + it != itEnd; ++it) { + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Bcc (blind carbon copy) + for (it = email.getRecipients(MimeMessage::Bcc).begin(), + itEnd = email.getRecipients(MimeMessage::Bcc).end(); + it != itEnd; ++it) { + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Send DATA command + sendMessage("DATA"); + waitForResponse(); + + if (responseCode != 354) + return false; + + sendMessage(email.toString()); + + // Send \r\n.\r\n to end the mail data + sendMessage("."); + + waitForResponse(); + + if (responseCode != 250) + return false; + } catch (ResponseTimeoutException) { + return false; + } catch (SendMessageTimeoutException) { + return false; + } + + return true; +} + +void SmtpClient::quit() { + try { + sendMessage("QUIT"); + } catch (SmtpClient::SendMessageTimeoutException) { + // Manually close the connection to the smtp server if message "QUIT" wasn't + // received by the smtp server + if (socket->state() == QAbstractSocket::ConnectedState || + socket->state() == QAbstractSocket::ConnectingState || + socket->state() == QAbstractSocket::HostLookupState) + socket->disconnectFromHost(); + } +} + +/* [3] --- */ + +/* [4] Protected methods */ + +void SmtpClient::waitForResponse() { + do { + if (!socket->waitForReadyRead(responseTimeout)) { + emit smtpError(ResponseTimeoutError); + throw ResponseTimeoutException(); + } + + while (socket->canReadLine()) { + // Save the server's response + responseText = socket->readLine(); + + // Extract the respose code from the server's responce (first 3 digits) + responseCode = responseText.leftRef(3).toInt(); + + if (responseCode / 100 == 4) + emit smtpError(ServerError); + + if (responseCode / 100 == 5) + emit smtpError(ClientError); + + if (responseText[3] == ' ') { + return; + } + } + } while (true); +} + +void SmtpClient::sendMessage(const QString &text) { + socket->write(text.toUtf8() + "\r\n"); + if (!socket->waitForBytesWritten(sendMessageTimeout)) { + emit smtpError(SendDataTimeoutError); + throw SendMessageTimeoutException(); + } +} + +/* [4] --- */ + +/* [5] Slots for the socket's signals */ + +void SmtpClient::socketStateChanged(QAbstractSocket::SocketState /*state*/) {} + +void SmtpClient::socketError(QAbstractSocket::SocketError /*socketError*/) {} + +void SmtpClient::socketReadyRead() {} + +/* [5] --- */ diff --git a/src/ui/AboutDialog.cpp b/src/ui/AboutDialog.cpp deleted file mode 100644 index e51f225d..00000000 --- a/src/ui/AboutDialog.cpp +++ /dev/null @@ -1,94 +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/AboutDialog.h" - -AboutDialog::AboutDialog(QWidget *parent) - : QDialog(parent) { - this->setWindowTitle(tr("About ") + qApp->applicationName()); - - auto *tabWidget = new QTabWidget; - auto *infoTab = new InfoTab; - auto *translatorsTab = new TranslatorsTab; - - tabWidget->addTab(infoTab, tr("General")); - tabWidget->addTab(translatorsTab, tr("Translators")); - - auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(tabWidget); - mainLayout->addWidget(buttonBox); - setLayout(mainLayout); - - this->exec(); -} - -InfoTab::InfoTab(QWidget *parent) - : QWidget(parent) { - auto *pixmap = new QPixmap(":gpgfrontend-logo.png"); - auto *text = new QString("<center><h2>" + qApp->applicationName() + "</h2></center>" - + "<center><b>" + qApp->applicationVersion() + "</b></center>" - + "<center>" + GIT_VERSION + "</center>" - + tr("<br><center>GPGFrontend is an easy-to-use, compact, <br>" - "cross-platform, and installation-free gpg front-end tool.<br>" - "It visualizes most of the common operations of gpg commands.<br>" - "It's licensed under the GPL v3<br><br>" - "<b>Developer:</b><br>" - "Saturneric<br><br>" - "If you have any questions or suggestions, raise an issue<br/>" - "at <a href=\"https://github.com/saturneric/GpgFrontend\">GitHub</a> or send a mail to my mailing list at <a href=\"mailto:[email protected]\">[email protected]</a>.") + - tr("<br><br> Built with Qt ") + qVersion() - + tr(" and GPGME ") + GpgME::GpgContext::getGpgmeVersion() + - tr("<br>Built at ") + BUILD_TIMESTAMP + "</center>"); - - auto *layout = new QGridLayout(); - auto *pixmapLabel = new QLabel(); - pixmapLabel->setPixmap(*pixmap); - layout->addWidget(pixmapLabel, 0, 0, 1, -1, Qt::AlignCenter); - auto *aboutLabel = new QLabel(); - aboutLabel->setText(*text); - aboutLabel->setOpenExternalLinks(true); - layout->addWidget(aboutLabel, 1, 0, 1, -1); - layout->addItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, - QSizePolicy::Fixed), 2, 1, 1, 1); - - setLayout(layout); -} - -TranslatorsTab::TranslatorsTab(QWidget *parent) - : QWidget(parent) { - QFile translatorsFile; - translatorsFile.setFileName(qApp->applicationDirPath() + "/About"); - translatorsFile.open(QIODevice::ReadOnly); - QByteArray inBuffer = translatorsFile.readAll(); - - auto *label = new QLabel(inBuffer); - auto *mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(label); - - setLayout(mainLayout); -} - diff --git a/src/ui/AttachmentTableModel.cpp b/src/ui/AttachmentTableModel.cpp deleted file mode 100644 index 79cbdf9b..00000000 --- a/src/ui/AttachmentTableModel.cpp +++ /dev/null @@ -1,134 +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/AttachmentTableModel.h" - -#include <utility> - -/** compare with http://doc.qt.nokia.com/4.6/itemviews-addressbook.html - */ - -AttachmentTableModel::AttachmentTableModel(QObject *parent) : - QAbstractTableModel(parent) { -} - -AttachmentTableModel::AttachmentTableModel(QList<MimePart> mimeparts, QObject *parent) : - QAbstractTableModel(parent) { - listOfMimeparts = std::move(mimeparts); -} - - -void AttachmentTableModel::add(const MimePart &mp) { - listOfMimeparts.append(mp); - //QModelIndex changedIndex0 = createIndex(listOfMimeparts.size(), 0); - //QModelIndex changedIndex1 = createIndex(listOfMimeparts.size(), 1); - - //emit(dataChanged(changedIndex0, changedIndex1)); - // TODO: check the data-changed signal - // reset(); -} - -MimePart AttachmentTableModel::getSelectedMimePart(QModelIndex index) { - return listOfMimeparts.at(index.row()); -} - -MimePart AttachmentTableModel::getMimePart(int index) { - return listOfMimeparts.at(index); -} - -/*QList<MimePart> AttachmentTableModel::getSelectedMimeParts(QModelIndexList indexes){ - - foreach(QModelIndex index, indexes) { - qDebug() << "ir: "<< index.row(); - } - -}*/ - -int AttachmentTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return listOfMimeparts.size(); -} - -int AttachmentTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return 2; -} - -QVariant AttachmentTableModel::data(const QModelIndex &index, int role) const { - - //qDebug() << "called, index: " << index.column(); - - if (!index.isValid()) - return QVariant(); - - if (index.row() >= listOfMimeparts.size() || index.row() < 0) - return QVariant(); - - if (role == Qt::DisplayRole) { - MimePart mp = listOfMimeparts.at(index.row()); - - if (index.column() == 0) - return mp.header.getParam("Content-Type", "name"); - if (index.column() == 1) - return mp.header.getValue("Content-Type"); - - } - - // set icon - // TODO more generic matching, e.g. for audio - if (role == Qt::DecorationRole && index.column() == 0) { - MimePart mp = listOfMimeparts.at(index.row()); - QString icon; - if (mp.header.getValue("Content-Type").startsWith("image")) { - icon = ":mimetypes/image-x-generic.png"; - } else { - icon = mp.header.getValue("Content-Type").replace("/", "-"); - icon = ":mimetypes/" + icon + ".png"; - } - if (!QFile::exists(icon)) icon = ":mimetypes/unknown.png"; - return QIcon(icon); - } - - return QVariant(); -} - -QVariant AttachmentTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - //qDebug() << "called, section: " << section; - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) { - switch (section) { - case 0: - return tr("Filename"); - - case 1: - return tr("Contenttype"); - - default: - return QVariant(); - } - } - return QVariant(); -} diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 68f57b81..618b5d28 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -3,6 +3,7 @@ aux_source_directory(./keypair_details UI_SOURCE) 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) add_library(gpgfrontend-ui STATIC ${UI_SOURCE}) diff --git a/src/ui/FileEncryptionDialog.cpp b/src/ui/FileEncryptionDialog.cpp index 9cb7b00a..e92dfc90 100755 --- a/src/ui/FileEncryptionDialog.cpp +++ b/src/ui/FileEncryptionDialog.cpp @@ -203,27 +203,50 @@ void FileEncryptionDialog::slotExecuteAction() { QVector<GpgKey> keys; mKeyList->getCheckedKeys(keys); + qDebug() << "slotExecuteAction" << mAction; + if (mAction == Encrypt) { - if (!mCtx->encrypt(keys, inBuffer, outBuffer, nullptr)) return; + qDebug() << "Action Encrypt"; + gpgme_error_t err = mCtx->encrypt(keys, inBuffer, outBuffer, nullptr); + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { + qDebug() << "Error" << gpgme_strerror(err); + QMessageBox::warning(this, tr("Error"), + tr("Error Occurred During Encryption")); + return; + } } if (mAction == Decrypt) { - if (!mCtx->decrypt(inBuffer, outBuffer, nullptr)) return; + qDebug() << "Action Decrypt"; + gpgme_error_t err = mCtx->decrypt(inBuffer, outBuffer, nullptr); + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { + qDebug() << "Error" << gpgme_strerror(err); + QMessageBox::warning(this, tr("Error"), + tr("Error Occurred During Decryption")); + return; + } } if (mAction == Sign) { - if (gpgme_err_code(mCtx->sign(keys, inBuffer, outBuffer, true)) != GPG_ERR_NO_ERROR) return; + qDebug() << "Action Sign"; + gpgme_error_t err = mCtx->sign(keys, inBuffer, outBuffer, true); + if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) { + qDebug() << "Error" << gpgme_strerror(err); + QMessageBox::warning(this, tr("Error"), + tr("Error Occurred During Signature")); + return; + } } if (mAction == Verify) { - QFile signfile; - signfile.setFileName(signFileEdit->text()); - if (!signfile.open(QIODevice::ReadOnly)) { + QFile sign_file; + sign_file.setFileName(signFileEdit->text()); + if (!sign_file.open(QIODevice::ReadOnly)) { statusLabel->setText(tr("Couldn't open file")); signFileEdit->setStyleSheet("QLineEdit { background: yellow }"); return; } - auto signBuffer = signfile.readAll(); + auto signBuffer = sign_file.readAll(); gpgme_verify_result_t result; auto error = mCtx->verify(&inBuffer, &signBuffer, &result); new VerifyDetailsDialog(this, mCtx, mKeyList, error, result); diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index 0e2d9c9a..ce5343bf 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -78,7 +78,7 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : this->settings.setValue("keymgmt/setWindowSize", true); } - setWindowTitle(tr("KeyPairs Management")); + setWindowTitle(tr("Key Pair Management")); mKeyList->addMenuAction(deleteSelectedKeysAct); mKeyList->addMenuAction(showKeyDetailsAct); } diff --git a/src/ui/KeyServerImportDialog.cpp b/src/ui/KeyServerImportDialog.cpp index ec740691..a1355120 100644 --- a/src/ui/KeyServerImportDialog.cpp +++ b/src/ui/KeyServerImportDialog.cpp @@ -176,7 +176,7 @@ void KeyServerImportDialog::setMessage(const QString &text, bool error) { void KeyServerImportDialog::slotSearch() { if (searchLineEdit->text().isEmpty()) { - setMessage(tr("<h4>Text is empty.</h4>"), false); + setMessage("<h4>" + tr("Text is empty.") + "</h4>", false); return; } @@ -227,23 +227,23 @@ void KeyServerImportDialog::slotSearchFinished() { if (firstLine.contains("Error")) { QString text = QString(reply->readLine(1024)); if (text.contains("Too many responses")) { - setMessage(tr("<h4>CToo many responses from keyserver!</h4>"), true); + setMessage("<h4>" +tr("Too many responses from keyserver!") + "</h4>", true); return; } else if (text.contains("No keys found")) { // if string looks like hex string, search again with 0x prepended QRegExp rx("[0-9A-Fa-f]*"); QString query = searchLineEdit->text(); if (rx.exactMatch(query)) { - setMessage(tr("<h4>No keys found, input may be kexId, retrying search with 0x.</h4>"), true); + setMessage("<h4>" + tr("No keys found, input may be kexId, retrying search with 0x.") + "</h4>", true); searchLineEdit->setText(query.prepend("0x")); this->slotSearch(); return; } else { - setMessage(tr("<h4>No keys found containing the search string!</h4>"), true); + setMessage("<h4>" +tr("No keys found containing the search string!") + "</h4>", true); return; } } else if (text.contains("Insufficiently specific words")) { - setMessage(tr("<h4>Insufficiently specific search string!</h4>"), true); + setMessage("<h4>" + tr("Insufficiently specific search string!") + "</h4>", true); return; } else { setMessage(text, true); diff --git a/src/ui/KeyUploadDialog.cpp b/src/ui/KeyUploadDialog.cpp index e28b4230..faf892f0 100644 --- a/src/ui/KeyUploadDialog.cpp +++ b/src/ui/KeyUploadDialog.cpp @@ -36,9 +36,13 @@ KeyUploadDialog::KeyUploadDialog(GpgME::GpgContext *ctx, const QVector<GpgKey> & auto *pb = new QProgressBar(); pb->setRange(0, 0); + pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + pb->setTextVisible(false); auto *layout = new QVBoxLayout(); layout->addWidget(pb); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); this->setLayout(layout); this->setModal(true); diff --git a/src/ui/SendMailDialog.cpp b/src/ui/SendMailDialog.cpp new file mode 100644 index 00000000..5bea7cb2 --- /dev/null +++ b/src/ui/SendMailDialog.cpp @@ -0,0 +1,173 @@ +/** + * 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/SendMailDialog.h" + +#include <utility> +#include "smtp/SmtpMime" + +SendMailDialog::SendMailDialog(QString text, QWidget *parent) + : QDialog(parent), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat), mText(std::move(text)) { + + if (smtpAddress.isEmpty()) { + QMessageBox::critical(this, tr("Incomplete configuration"), + tr("The SMTP address is empty, please go to the setting interface to complete the configuration.")); + + deleteLater(); + return; + + } + + senderEdit = new QLineEdit(); + senderEdit->setText(defaultSender); + recipientEdit = new QTextEdit(); + recipientEdit->setPlaceholderText("One or more email addresses. Please use ; to separate."); + subjectEdit = new QLineEdit(); + + errorLabel = new QLabel(); + + qDebug() << "Send Mail Settings" << smtpAddress << username << password << defaultSender << connectionTypeSettings; + + confirmButton = new QPushButton("Confirm"); + + auto layout = new QGridLayout(); + layout->addWidget(new QLabel("Sender"), 0, 0); + layout->addWidget(senderEdit, 0, 1); + layout->addWidget(new QLabel("Recipient"), 1, 0); + layout->addWidget(recipientEdit, 1, 1); + layout->addWidget(new QLabel("Subject"), 2, 0); + layout->addWidget(subjectEdit, 2, 1); + layout->addWidget(confirmButton, 3, 1); + layout->addWidget(errorLabel, 4, 0, 1, 2); + + connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(slotConfirm())); + + this->setLayout(layout); + this->setWindowTitle("Send Mail"); + this->setModal(true); + this->setFixedWidth(320); + this->show(); +} + +bool SendMailDialog::check_email_address(const QString &str) { + return re_email.match(str).hasMatch(); +} + +void SendMailDialog::slotConfirm() { + + QString errString; + errorLabel->clear(); + + QStringList rcptStringList = recipientEdit->toPlainText().split(';'); + + if (rcptStringList.isEmpty()) { + errString.append(tr(" Recipient cannot be empty \n")); + } else { + for (const auto& reci : rcptStringList) { + qDebug() << "Receiver" << reci.trimmed(); + if (!check_email_address(reci.trimmed())) { + errString.append(tr(" One or more Recipient's Email Address is invalid \n")); + break; + } + } + } + if (senderEdit->text().isEmpty()) { + errString.append(tr(" Sender cannot be empty \n")); + } else if (!check_email_address(senderEdit->text())) { + errString.append(tr(" Sender's Email Address is invalid \n")); + } + + if (!errString.isEmpty()) { + errorLabel->setAutoFillBackground(true); + QPalette error = errorLabel->palette(); + error.setColor(QPalette::Window, "#ff8080"); + errorLabel->setPalette(error); + errorLabel->setText(errString); + return; + } + + SmtpClient::ConnectionType connectionType = SmtpClient::ConnectionType::TcpConnection; + + if (connectionTypeSettings == "SSL") { + connectionType = SmtpClient::ConnectionType::SslConnection; + } else if (connectionTypeSettings == "TLS") { + connectionType = SmtpClient::ConnectionType::TlsConnection; + } else if (connectionTypeSettings == "STARTTLS") { + connectionType = SmtpClient::ConnectionType::TlsConnection; + } else { + connectionType = SmtpClient::ConnectionType::TcpConnection; + } + + SmtpClient smtp(smtpAddress, port, connectionType); + + // We need to set the username (your email address) and the password + // for smtp authentification. + + smtp.setUser(username); + smtp.setPassword(password); + + // Now we create a MimeMessage object. This will be the email. + + MimeMessage message; + + message.setSender(new EmailAddress(senderEdit->text())); + for (const auto &reci : rcptStringList) { + if(!reci.isEmpty()) + message.addRecipient(new EmailAddress(reci.trimmed())); + } + message.setSubject(subjectEdit->text()); + + // Now add some text to the email. + // First we create a MimeText object. + + MimeText text; + + text.setText(mText); + + // Now add it to the mail + message.addPart(&text); + + // Now we can send the mail + if (!smtp.connectToHost()) { + qDebug() << "Connect to SMTP Server Failed"; + QMessageBox::critical(this, tr("Fail"), tr("Fail to Connect SMTP Server")); + return; + } + if (!smtp.login()) { + qDebug() << "Login to SMTP Server Failed"; + QMessageBox::critical(this, tr("Fail"), tr("Fail to Login into SMTP Server")); + return; + } + if (!smtp.sendMail(message)) { + qDebug() << "Send Mail to SMTP Server Failed"; + QMessageBox::critical(this, tr("Fail"), tr("Fail to Send Mail to SMTP Server")); + return; + } + smtp.quit(); + + // Close after sending email + QMessageBox::information(this, tr("Success"), tr("Succeed in Sending Mail to SMTP Server")); + deleteLater(); +} diff --git a/src/ui/SettingsDialog.cpp b/src/ui/SettingsDialog.cpp index 74c507e4..1732d718 100755 --- a/src/ui/SettingsDialog.cpp +++ b/src/ui/SettingsDialog.cpp @@ -23,203 +23,205 @@ */ #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; - mimeTab = new MimeTab; - keyserverTab = new KeyserverTab; - advancedTab = new AdvancedTab; - gpgPathsTab = new GpgPathsTab; - - tabWidget->addTab(generalTab, tr("General")); - tabWidget->addTab(appearanceTab, tr("Appearance")); - tabWidget->addTab(mimeTab, tr("PGP/Mime")); - 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(mimeTab, 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(); + : 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; + this->restartNeeded = needed; } void SettingsDialog::slotAccept() { - generalTab->applySettings(); - mimeTab->applySettings(); - appearanceTab->applySettings(); - keyserverTab->applySettings(); - advancedTab->applySettings(); - gpgPathsTab->applySettings(); - if (getRestartNeeded()) { - emit signalRestartNeeded(true); - } - close(); + 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; + QHash<QString, QString> languages; - languages.insert("", tr("System Default")); + languages.insert("", tr("System Default")); - QString appPath = qApp->applicationDirPath(); - QDir qmDir = QDir(RESOURCE_DIR(appPath) + "/ts/"); - QStringList fileNames = qmDir.entryList(QStringList("gpgfrontend_*.qm")); + 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); + 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); + // this works in qt 4.8 + QLocale qloc(locale); #if QT_VERSION < 0x040800 - QString language = - QLocale::languageToString(qloc.language()) + " (" + locale + - ")"; //+ " (" + QLocale::languageToString(qloc.language()) + ")"; + QString language = + QLocale::languageToString(qloc.language()) + " (" + locale + + ")"; //+ " (" + QLocale::languageToString(qloc.language()) + ")"; #else - QString language = qloc.nativeLanguageName() + " (" + locale + ")"; + QString language = qloc.nativeLanguageName() + " (" + locale + ")"; #endif - languages.insert(locale, language); - } - return languages; + 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); + : 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); } /********************************** @@ -228,32 +230,32 @@ GeneralTab::GeneralTab(GpgME::GpgContext *ctx, QWidget *parent) * 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); - } + // 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); + } } /*********************************** @@ -261,70 +263,72 @@ void GeneralTab::setSettings() { * 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()); + 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 + // Set ownKeyId to currently selected } -MimeTab::MimeTab(QWidget *parent) - : QWidget(parent), appPath(qApp->applicationDirPath()), - settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", - QSettings::IniFormat) { - /***************************************** - * MIME-Parsing-Box - *****************************************/ - auto *mimeQPBox = new QGroupBox(tr("Decode quoted printable")); - auto *mimeQPBoxLayout = new QVBoxLayout(); - mimeQPCheckBox = - new QCheckBox(tr("Try to recognize quoted printable."), this); - mimeQPBoxLayout->addWidget(mimeQPCheckBox); - mimeQPBox->setLayout(mimeQPBoxLayout); - - auto *mimeParseBox = new QGroupBox(tr("Parse PGP/MIME (Experimental)")); - auto *mimeParseBoxLayout = new QVBoxLayout(); - mimeParseCheckBox = new QCheckBox( - tr("Try to split attachments from PGP-MIME ecrypted messages."), this); - mimeParseBoxLayout->addWidget(mimeParseCheckBox); - mimeParseBox->setLayout(mimeParseBoxLayout); - - auto *mimeOpenAttachmentBox = - new QGroupBox(tr("Open with external application (Experimental)")); - auto *mimeOpenAttachmentBoxLayout = new QVBoxLayout(); - auto *mimeOpenAttachmentText = new QLabel(tr( - "Open attachments with default application for the filetype.<br> " - "There are at least two possible problems with this behaviour:" - "<ol><li>File needs to be saved unencrypted to attachments folder.<br> " - "Its your job to clean this folder.</li>" - "<li>The external application may have its own temp files.</li></ol>")); - - // mimeOpenAttachmentBox->setDisabled(true); - mimeOpenAttachmentCheckBox = - new QCheckBox(tr("Enable opening with external applications."), this); - - mimeOpenAttachmentBoxLayout->addWidget(mimeOpenAttachmentText); - mimeOpenAttachmentBoxLayout->addWidget(mimeOpenAttachmentCheckBox); - mimeOpenAttachmentBox->setLayout(mimeOpenAttachmentBoxLayout); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(mimeParseBox); - mainLayout->addWidget(mimeOpenAttachmentBox); - mainLayout->addWidget(mimeQPBox); - mainLayout->addStretch(1); - setLayout(mainLayout); - setSettings(); +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(); } /********************************** @@ -332,93 +336,180 @@ MimeTab::MimeTab(QWidget *parent) * and set the buttons and checkboxes * appropriately **********************************/ -void MimeTab::setSettings() { +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()); - // MIME-Parsing - if (settings.value("mime/parsemime").toBool()) - mimeParseCheckBox->setCheckState(Qt::Checked); - - // Qouted Printable - if (settings.value("mime/parseQP", true).toBool()) - mimeQPCheckBox->setCheckState(Qt::Checked); - - // Open Attachments with external app - if (settings.value("mime/openAttachment").toBool()) - mimeOpenAttachmentCheckBox->setCheckState(Qt::Checked); } /*********************************** * get the values of the buttons and * write them to settings-file *************************************/ -void MimeTab::applySettings() { - settings.setValue("mime/parsemime", mimeParseCheckBox->isChecked()); - settings.setValue("mime/parseQP", mimeQPCheckBox->isChecked()); - settings.setValue("mime/openAttachment", - mimeOpenAttachmentCheckBox->isChecked()); +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); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(iconSizeBox); - mainLayout->addWidget(iconStyleBox); - mainLayout->addWidget(windowSizeBox); - mainLayout->addStretch(1); - setSettings(); - setLayout(mainLayout); + : 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); } /********************************** @@ -428,40 +519,46 @@ AppearanceTab::AppearanceTab(QWidget *parent) **********************************/ 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); + // 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); } /*********************************** @@ -469,72 +566,74 @@ void AppearanceTab::setSettings() { * 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()); + 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 Keyserver 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(); + : 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(); } /********************************** @@ -543,23 +642,23 @@ KeyserverTab::KeyserverTab(QWidget *parent) * 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()); + 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); + if (newKeyServerEdit->text().startsWith("http://") || + newKeyServerEdit->text().startsWith("https://")) { + comboBox->addItem(newKeyServerEdit->text()); + } else { + comboBox->addItem("http://" + newKeyServerEdit->text()); + } + comboBox->setCurrentIndex(comboBox->count() - 1); } /*********************************** @@ -567,112 +666,112 @@ void KeyserverTab::addKeyServer() { * write them to settings-file *************************************/ void KeyserverTab::applySettings() { - settings.setValue("keyserver/defaultKeyServer", comboBox->currentText()); + 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); + : 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); - } + if (settings.value("advanced/steganography").toBool()) { + steganoCheckBox->setCheckState(Qt::Checked); + } } void AdvancedTab::applySettings() { - settings.setValue("advanced/steganography", steganoCheckBox->isChecked()); + 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); + : 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; + 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("."); + accKeydbPath = "."; + keydbLabel->setText("."); } QString GpgPathsTab::chooseKeydbDir() { - QString dir = QFileDialog::getExistingDirectory( - this, tr("Choose keydb directory"), accKeydbPath, - QFileDialog::ShowDirsOnly); + QString dir = QFileDialog::getExistingDirectory( + this, tr("Choose keydb directory"), accKeydbPath, + QFileDialog::ShowDirsOnly); - accKeydbPath = getRelativePath(defKeydbPath, dir); - keydbLabel->setText(accKeydbPath); - return ""; + accKeydbPath = getRelativePath(defKeydbPath, dir); + keydbLabel->setText(accKeydbPath); + return ""; } void GpgPathsTab::setSettings() { - defKeydbPath = qApp->applicationDirPath() + "/keydb"; + defKeydbPath = qApp->applicationDirPath() + "/keydb"; - accKeydbPath = settings.value("gpgpaths/keydbpath").toString(); - if (accKeydbPath.isEmpty()) { - accKeydbPath = "."; - } + accKeydbPath = settings.value("gpgpaths/keydbpath").toString(); + if (accKeydbPath.isEmpty()) { + accKeydbPath = "."; + } } void GpgPathsTab::applySettings() { - settings.setValue("gpgpaths/keydbpath", accKeydbPath); + settings.setValue("gpgpaths/keydbpath", accKeydbPath); } diff --git a/src/ui/WaitingDialog.cpp b/src/ui/WaitingDialog.cpp index 8281385a..daccc3ca 100644 --- a/src/ui/WaitingDialog.cpp +++ b/src/ui/WaitingDialog.cpp @@ -1,15 +1,20 @@ #include "ui/WaitingDialog.h" -WaitingDialog::WaitingDialog(QWidget *parent) : QDialog(parent) { +WaitingDialog::WaitingDialog(const QString &title, QWidget *parent) : QDialog(parent) { auto *pb = new QProgressBar(); pb->setRange(0, 0); + pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + pb->setTextVisible(false); auto *layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); layout->addWidget(pb); this->setLayout(layout); this->setModal(true); - this->setWindowTitle(tr("Processing")); + this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + this->setWindowTitle(title); this->setFixedSize(240, 42); this->show(); } diff --git a/src/ui/Wizard.cpp b/src/ui/Wizard.cpp index 456e8c52..b0486cc9 100644 --- a/src/ui/Wizard.cpp +++ b/src/ui/Wizard.cpp @@ -108,16 +108,20 @@ bool Wizard::importPubAndSecKeysFromDir(const QString &dir, KeyMgmt *keyMgmt) { IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent), appPath(qApp->applicationDirPath()), settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", QSettings::IniFormat) { - setTitle(tr("Getting started...")); + setTitle(tr("Getting Started...")); setSubTitle(tr("... with GPGFrontend")); - auto *topLabel = new QLabel(tr("To use GPGFrontend for decrypting and signing messages, you need a " - "private key. The next page will help you with " - "key generation or import.<br><br>" - "For more information have a look at the <a href='docu_concepts.html'>concepts</a> " - "(by clicking the link, the page will open in the main window). <br>")); + 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>"); + topLabel->setTextFormat(Qt::RichText); + topLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + topLabel->setOpenExternalLinks(true); topLabel->setWordWrap(true); - // connect(topLabel, SIGNAL(linkActivated(QString)), parentWidget()->parentWidget(), SLOT(openHelp(QString))); // QComboBox for language selection auto *langLabel = new QLabel(tr("Choose a Language")); @@ -126,9 +130,9 @@ IntroPage::IntroPage(QWidget *parent) languages = SettingsDialog::listLanguages(); auto *langSelectBox = new QComboBox(); - foreach(QString l, languages) { - langSelectBox->addItem(l); - } + for(const auto &l : languages) { + langSelectBox->addItem(l); + } // selected entry from config QString langKey = settings.value("int/lang").toString(); QString langValue = languages.value(langKey); @@ -162,25 +166,31 @@ 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\">" - + tr("create a new keypair") + "</a><hr>"); + "may possibly want to read how to") + " <a href=\"https://saturneric.github.io/GpgFrontend/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 and decrypt text, you can read ") + 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("this document") + "</a><hr>"); + + 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>"); encrDecyTextLabel->setTextFormat(Qt::RichText); encrDecyTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); encrDecyTextLabel->setOpenExternalLinks(true); encrDecyTextLabel->setWordWrap(true); - auto *signVerifyTextLabel = new QLabel(tr("If you want to sign and verify text, you can read ") - + "<a href=\"https://saturneric.github.io/GpgFrontend/index.html#/manual/sign-verify-text\">" - + tr("this document") + "</a>"); + 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>"); signVerifyTextLabel->setTextFormat(Qt::RichText); signVerifyTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); signVerifyTextLabel->setOpenExternalLinks(true); diff --git a/src/ui/help/AboutDialog.cpp b/src/ui/help/AboutDialog.cpp new file mode 100644 index 00000000..807c509d --- /dev/null +++ b/src/ui/help/AboutDialog.cpp @@ -0,0 +1,215 @@ +/** + * 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/AboutDialog.h" +#include "GpgFrontendBuildInfo.h" + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" + +using namespace rapidjson; + +AboutDialog::AboutDialog(int defaultIndex, QWidget *parent) + : QDialog(parent) { + this->setWindowTitle(tr("About ") + qApp->applicationName()); + + auto *tabWidget = new QTabWidget; + auto *infoTab = new InfoTab(); + auto *translatorsTab = new TranslatorsTab(); + auto *updateTab = new UpdateTab(); + + tabWidget->addTab(infoTab, tr("General")); + tabWidget->addTab(translatorsTab, tr("Translators")); + tabWidget->addTab(updateTab, tr("Update")); + + connect(tabWidget, &QTabWidget::currentChanged, this, [&](int index) { + qDebug() << "Current Index" << index; + if(index == 2) { + updateTab->getLatestVersion(); + } + }); + + if(defaultIndex < tabWidget->count() && defaultIndex >= 0) { + tabWidget->setCurrentIndex(defaultIndex); + } + + auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(tabWidget); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + this->exec(); +} + +InfoTab::InfoTab(QWidget *parent) + : QWidget(parent) { + auto *pixmap = new QPixmap(":gpgfrontend-logo.png"); + auto *text = new QString("<center><h2>" + qApp->applicationName() + "</h2></center>" + + "<center><b>" + qApp->applicationVersion() + "</b></center>" + + "<center>" + GIT_VERSION + "</center>" + + tr("<br><center>GPGFrontend is an easy-to-use, compact, cross-platform, <br>" + "and installation-free gpg front-end tool.<br>" + "It visualizes most of the common operations of gpg commands.<br>" + "It's licensed under the GPL v3<br><br>" + "<b>Developer:</b><br>" + "Saturneric<br><br>" + "If you have any questions or suggestions, raise an issue<br/>" + "at <a href=\"https://github.com/saturneric/GpgFrontend\">GitHub</a> or send a mail to my mailing list at <a href=\"mailto:[email protected]\">[email protected]</a>.") + + tr("<br><br> Built with Qt ") + qVersion() + + tr(" and GPGME ") + GpgME::GpgContext::getGpgmeVersion() + + tr("<br>Built at ") + BUILD_TIMESTAMP + "</center>"); + + auto *layout = new QGridLayout(); + auto *pixmapLabel = new QLabel(); + pixmapLabel->setPixmap(*pixmap); + layout->addWidget(pixmapLabel, 0, 0, 1, -1, Qt::AlignCenter); + auto *aboutLabel = new QLabel(); + aboutLabel->setText(*text); + aboutLabel->setOpenExternalLinks(true); + layout->addWidget(aboutLabel, 1, 0, 1, -1); + layout->addItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, + QSizePolicy::Fixed), 2, 1, 1, 1); + + setLayout(layout); +} + +TranslatorsTab::TranslatorsTab(QWidget *parent) + : QWidget(parent) { + QFile translatorsFile; + translatorsFile.setFileName(qApp->applicationDirPath() + "/About"); + translatorsFile.open(QIODevice::ReadOnly); + QByteArray inBuffer = translatorsFile.readAll(); + + auto *label = new QLabel(inBuffer); + auto *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(label); + + setLayout(mainLayout); +} + +UpdateTab::UpdateTab(QWidget *parent) { + auto *pixmap = new QPixmap(":gpgfrontend-logo.png"); + auto *layout = new QGridLayout(); + auto *pixmapLabel = new QLabel(); + pixmapLabel->setPixmap(*pixmap); + layout->addWidget(pixmapLabel, 0, 0, 1, -1, Qt::AlignCenter); + + currentVersion = "v" + QString::number(VERSION_MAJOR) + "." + + QString::number(VERSION_MINOR) + "." + + QString::number(VERSION_PATCH); + + auto tipsLabel = new QLabel(); + tipsLabel->setText("<center>" + + tr("It is recommended that you always check the version of GpgFrontend and upgrade to the latest version.") + + "</center><br><center>" + + tr("New versions not only represent new features, but also often represent functional and security fixes.") + "</center>"); + tipsLabel->setWordWrap(true); + + currentVersionLabel = new QLabel(); + currentVersionLabel->setText("<center>" + tr("Current Version: ") + "<b>" + currentVersion + "</b></center>"); + currentVersionLabel->setWordWrap(true); + + latestVersionLabel = new QLabel(); + latestVersionLabel->setWordWrap(true); + + upgradeLabel = new QLabel(); + upgradeLabel->setText("<center>" + + tr("The current version is inconsistent with the latest version on github.") + + "</center><br><center>" + + tr("Please click <a href=\"https://github.com/saturneric/GpgFrontend/releases\">here</a> to download the latest version.") + "</center>"); + upgradeLabel->setWordWrap(true); + upgradeLabel->setOpenExternalLinks(true); + upgradeLabel->setHidden(true); + + pb = new QProgressBar(); + pb->setRange(0, 0); + pb->setTextVisible(false); + + layout->addWidget(tipsLabel, 1, 0, 1, -1); + layout->addWidget(currentVersionLabel, 2, 0, 1, -1); + layout->addWidget(latestVersionLabel, 3, 0, 1, -1); + layout->addWidget(upgradeLabel, 4, 0, 1, -1); + layout->addWidget(pb, 5, 0, 1, -1); + layout->addItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, + QSizePolicy::Fixed), 2, 1, 1, 1); + + setLayout(layout); +} + +void UpdateTab::getLatestVersion() { + + this->pb->setHidden(false); + + qDebug() << "Try to get latest version"; + + QString baseUrl = "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; + + auto manager = new QNetworkAccessManager(this); + + QNetworkRequest request; + request.setUrl(QUrl(baseUrl)); + + QNetworkReply *replay = manager->get(request); + + while(replay->isRunning()) { + QApplication::processEvents(); + } + + this->pb->setHidden(true); + + if(replay->error() != QNetworkReply::NoError) { + 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+)"); + QRegularExpressionMatch match = re.match(latestVersion); + if (match.hasMatch()) { + latestVersion = match.captured(0); // matched == "23 def" + qDebug() << "Latest Version Matched" << latestVersion; + } else { + latestVersion = "Unknown"; + } + + latestVersionLabel->setText("<center><b>" + tr("Latest Version From Github: ") + latestVersion + "</b></center>"); + + if(latestVersion > currentVersion) { + upgradeLabel->setHidden(false); + } + +} diff --git a/src/ui/help/VersionCheckThread.cpp b/src/ui/help/VersionCheckThread.cpp new file mode 100644 index 00000000..c7c77d1c --- /dev/null +++ b/src/ui/help/VersionCheckThread.cpp @@ -0,0 +1,55 @@ +// +// Created by Administrator on 2021/7/12. +// + +#include "ui/help/VersionCheckThread.h" +#include "GpgFrontendBuildInfo.h" +#include "rapidjson/document.h" +#include "rapidjson/writer.h" + +using namespace rapidjson; + +void VersionCheckThread::run() { + qDebug() << "Start Version Thread to get latest version from Github"; + + auto currentVersion = "v" + QString::number(VERSION_MAJOR) + "." + + QString::number(VERSION_MINOR) + "." + + QString::number(VERSION_PATCH); + + while(mNetworkReply->isRunning()) { + QApplication::processEvents(); + } + + if(mNetworkReply->error() != QNetworkReply::NoError) { + qDebug() << "VersionCheckThread Found Network Error"; + return; + } + + QByteArray bytes = mNetworkReply->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+)"); + QRegularExpressionMatch match = re.match(latestVersion); + if (match.hasMatch()) { + latestVersion = match.captured(0); // matched == "23 def" + qDebug() << "Latest Version Matched" << latestVersion; + } else { + latestVersion = currentVersion; + qDebug() << "Latest Version Unknown" << latestVersion; + } + + if(latestVersion != currentVersion) { + emit upgradeVersion(currentVersion, latestVersion); + } + +} + +VersionCheckThread::VersionCheckThread(QNetworkReply *networkReply):mNetworkReply(networkReply) { + +} diff --git a/src/ui/keygen/KeygenDialog.cpp b/src/ui/keygen/KeygenDialog.cpp index b9fdae3f..7991ddd1 100644 --- a/src/ui/keygen/KeygenDialog.cpp +++ b/src/ui/keygen/KeygenDialog.cpp @@ -74,8 +74,7 @@ void KeyGenDialog::slotKeyGenAccept() { * primary keys should have a reasonable expiration date (no more than 2 years in the future) */ if(dateEdit->dateTime() > QDateTime::currentDateTime().addYears(2)) { - - errorString.append(tr(" Expiration time no more than 2 years. ")); + errorString.append(tr(" Expiration time no more than 2 years. \n")); } if (errorString.isEmpty()) { @@ -148,7 +147,7 @@ QGroupBox *KeyGenDialog::create_key_usage_group_box() { auto *groupBox = new QGroupBox(this); auto *grid = new QGridLayout(this); - groupBox->setTitle("Key Usage"); + groupBox->setTitle(tr("Key Usage")); auto* encrypt = new QCheckBox(tr("Encryption"), groupBox); encrypt->setTristate(false); diff --git a/src/ui/keygen/SubkeyGenerateThread.cpp b/src/ui/keygen/SubkeyGenerateThread.cpp index 4f19ac1f..125f35f8 100644 --- a/src/ui/keygen/SubkeyGenerateThread.cpp +++ b/src/ui/keygen/SubkeyGenerateThread.cpp @@ -24,8 +24,6 @@ #include "ui/keygen/SubkeyGenerateThread.h" -#include <utility> - SubkeyGenerateThread::SubkeyGenerateThread(GpgKey key, GenKeyInfo *keyGenParams, GpgME::GpgContext *ctx) : mKey(std::move(key)), keyGenParams(keyGenParams) , mCtx(ctx) { connect(this, &SubkeyGenerateThread::finished, this, &SubkeyGenerateThread::deleteLater); diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index 013d8e11..9ca4e37e 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -23,8 +23,10 @@ */ #include "ui/keypair_details/KeyPairDetailTab.h" +#include "ui/WaitingDialog.h" -KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, QWidget *parent) : mKey(mKey), QWidget(parent) { +KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, QWidget *parent) : mKey(mKey), + QWidget(parent) { mCtx = ctx; keyid = new QString(mKey.id); @@ -53,8 +55,8 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q algorithmVarLabel = new QLabel(); // Show the situation that master key not exists. - masterKeyExistVarLabel = new QLabel(mKey.has_master_key ? "Exists" : "Not Exists"); - if(!mKey.has_master_key){ + masterKeyExistVarLabel = new QLabel(mKey.has_master_key ? tr("Exists") : tr("Not Exists")); + if (!mKey.has_master_key) { auto paletteExpired = masterKeyExistVarLabel->palette(); paletteExpired.setColor(masterKeyExistVarLabel->foregroundRole(), Qt::red); masterKeyExistVarLabel->setPalette(paletteExpired); @@ -64,7 +66,7 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q masterKeyExistVarLabel->setPalette(paletteValid); } - if(mKey.expired){ + if (mKey.expired) { auto paletteExpired = expireVarLabel->palette(); paletteExpired.setColor(expireVarLabel->foregroundRole(), Qt::red); expireVarLabel->setPalette(paletteExpired); @@ -131,18 +133,27 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q auto *privKeyBox = new QGroupBox(tr("Operations")); auto *vboxPK = new QVBoxLayout(); - auto *exportButton = new QPushButton(tr("Export Private Key (Include Subkeys)")); + auto *exportButton = new QPushButton(tr("Export Private Key (Include Subkey)")); vboxPK->addWidget(exportButton); connect(exportButton, SIGNAL(clicked()), this, SLOT(slotExportPrivateKey())); - if(mKey.has_master_key) { + if (mKey.has_master_key) { auto *editExpiresButton = new QPushButton(tr("Modify Expiration Datetime (Master Key)")); vboxPK->addWidget(editExpiresButton); connect(editExpiresButton, SIGNAL(clicked()), this, SLOT(slotModifyEditDatetime())); + auto hBoxLayout = new QHBoxLayout(); auto *keyServerOperaButton = new QPushButton(tr("Key Server Operation (Pubkey)")); keyServerOperaButton->setStyleSheet("text-align:center;"); - vboxPK->addWidget(keyServerOperaButton); + + auto *revokeCertGenButton = new QPushButton(tr("Generate Revoke Certificate")); + revokeCertGenButton->setDisabled(true); + connect(revokeCertGenButton, SIGNAL(clicked()), this, SLOT(slotGenRevokeCert())); + + hBoxLayout->addWidget(keyServerOperaButton); + hBoxLayout->addWidget(revokeCertGenButton); + + vboxPK->addLayout(hBoxLayout); connect(keyServerOperaButton, SIGNAL(clicked()), this, SLOT(slotModifyEditDatetime())); // Set Menu @@ -152,8 +163,6 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q privKeyBox->setLayout(vboxPK); mvbox->addWidget(privKeyBox); - - } if ((mKey.expired) || (mKey.revoked)) { @@ -169,7 +178,7 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q expLabel->setText(tr("Warning: The Master Key has been revoked")); } - iconLabel->setPixmap(pixmap.scaled(24,24,Qt::KeepAspectRatio)); + iconLabel->setPixmap(pixmap.scaled(24, 24, Qt::KeepAspectRatio)); QFont font = expLabel->font(); font.setBold(true); expLabel->setFont(font); @@ -179,10 +188,11 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q mvbox->addLayout(expBox); } + mvbox->setContentsMargins(0, 0, 0, 0); + connect(mCtx, SIGNAL(signalKeyInfoChanged()), this, SLOT(slotRefreshKeyInfo())); slotRefreshKeyInfo(); - setAttribute(Qt::WA_DeleteOnClose, true); setLayout(mvbox); } @@ -190,22 +200,23 @@ KeyPairDetailTab::KeyPairDetailTab(GpgME::GpgContext *ctx, const GpgKey &mKey, Q void KeyPairDetailTab::slotExportPrivateKey() { // Show a information box with explanation about private key int ret = QMessageBox::information(this, tr("Exporting private Key"), - tr("<h3>You are about to export your <font color=\"red\">PRIVATE KEY</font>!</h3>\n" - "This is NOT your Public Key, so DON'T give it away.<br />" - "Do you REALLY want to export your PRIVATE KEY?"), + "<h3>" + tr("You are about to export your") + "<font color=\"red\">" + + tr("PRIVATE KEY") + "</font>!</h3>\n" + + tr("This is NOT your Public Key, so DON'T give it away.") + "<br />" + + tr("Do you REALLY want to export your PRIVATE KEY?"), QMessageBox::Cancel | QMessageBox::Ok); // export key, if ok was clicked if (ret == QMessageBox::Ok) { auto *keyArray = new QByteArray(); - if(!mCtx->exportSecretKey(mKey, keyArray)) { - QMessageBox::critical(this, "Error", "An error occurred during the export operation."); - return; - } + if (!mCtx->exportSecretKey(mKey, keyArray)) { + QMessageBox::critical(this, "Error", "An error occurred during the export operation."); + return; + } auto &key = mCtx->getKeyById(*keyid); - QString fileString = key.name + " " +key.email + "(" + + QString fileString = key.name + " " + key.email + "(" + key.id + ")_secret.asc"; QString fileName = QFileDialog::getSaveFileName(this, tr("Export Key To File"), fileString, tr("Key Files") + " (*.asc *.txt);;All Files (*)"); @@ -251,13 +262,13 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { QString usage; QTextStream usage_steam(&usage); - if(mKey.can_certify) + if (mKey.can_certify) usage_steam << "Cert "; - if(mKey.can_encrypt) + if (mKey.can_encrypt) usage_steam << "Encr "; - if(mKey.can_sign) + if (mKey.can_sign) usage_steam << "Sign "; - if(mKey.can_authenticate) + if (mKey.can_authenticate) usage_steam << "Auth "; usageVarLabel->setText(usage); @@ -265,13 +276,13 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { QString actualUsage; QTextStream actual_usage_steam(&actualUsage); - if(GpgME::GpgContext::checkIfKeyCanCert(mKey)) + if (GpgME::GpgContext::checkIfKeyCanCert(mKey)) actual_usage_steam << "Cert "; - if(GpgME::GpgContext::checkIfKeyCanEncr(mKey)) + if (GpgME::GpgContext::checkIfKeyCanEncr(mKey)) actual_usage_steam << "Encr "; - if(GpgME::GpgContext::checkIfKeyCanSign(mKey)) + if (GpgME::GpgContext::checkIfKeyCanSign(mKey)) actual_usage_steam << "Sign "; - if(GpgME::GpgContext::checkIfKeyCanAuth(mKey)) + if (GpgME::GpgContext::checkIfKeyCanAuth(mKey)) actual_usage_steam << "Auth "; actualUsageVarLabel->setText(actualUsage); @@ -301,7 +312,7 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { void KeyPairDetailTab::createKeyServerOperaMenu() { keyServerOperaMenu = new QMenu(this); - auto *uploadKeyPair = new QAction(tr("Upload Key Pair"), this); + auto *uploadKeyPair = new QAction(tr("Upload Key Pair to Key Server"), this); connect(uploadKeyPair, SIGNAL(triggered()), this, SLOT(slotUploadKeyToServer())); auto *updateKeyPair = new QAction(tr("Update Key Pair"), this); connect(updateKeyPair, SIGNAL(triggered()), this, SLOT(slotUpdateKeyToServer())); @@ -325,3 +336,21 @@ void KeyPairDetailTab::slotUpdateKeyToServer() { dialog->slotImportKey(keys); } +void KeyPairDetailTab::slotGenRevokeCert() { + auto mOutputFileName = QFileDialog::getSaveFileName(this, tr("Generate revocation certificate"), + QString(), + 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(); + +} + diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp index 70c7e4b8..74b52284 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp @@ -47,6 +47,7 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(GpgME::GpgContext *ctx, const GpgKey &key, QW auto subkeyListLayout = new QGridLayout(); subkeyListLayout->addWidget(subkeyList, 0, 0); subkeyListLayout->addLayout(uidButtonsLayout, 1, 0); + subkeyListLayout->setContentsMargins(0, 10, 0, 0); auto *subkeyDetailLayout = new QGridLayout(); @@ -79,6 +80,7 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(GpgME::GpgContext *ctx, const GpgKey &key, QW subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1); listBox->setLayout(subkeyListLayout); + listBox->setContentsMargins(0, 5, 0, 0); detailBox->setLayout(subkeyDetailLayout); baseLayout->addWidget(listBox); @@ -89,6 +91,8 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(GpgME::GpgContext *ctx, const GpgKey &key, QW connect(mCtx, SIGNAL(signalKeyInfoChanged()), this, SLOT(slotRefreshSubkeyList())); connect(subkeyList, SIGNAL(itemSelectionChanged()), this, SLOT(slotRefreshSubkeyDetail())); + baseLayout->setContentsMargins(0, 0, 0, 0); + setLayout(baseLayout); setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/src/ui/keypair_details/KeyPairUIDTab.cpp b/src/ui/keypair_details/KeyPairUIDTab.cpp index 51f188bd..2954aadb 100644 --- a/src/ui/keypair_details/KeyPairUIDTab.cpp +++ b/src/ui/keypair_details/KeyPairUIDTab.cpp @@ -52,21 +52,24 @@ KeyPairUIDTab::KeyPairUIDTab(GpgME::GpgContext *ctx, const GpgKey &key, QWidget gridLayout->addWidget(uidList, 0, 0); gridLayout->addLayout(uidButtonsLayout, 1, 0); + gridLayout->setContentsMargins(0, 10, 0, 0); auto uidGroupBox = new QGroupBox(); uidGroupBox->setLayout(gridLayout); - uidGroupBox->setTitle("UIDs"); + uidGroupBox->setTitle(tr("UIDs")); auto signGridLayout = new QGridLayout(); signGridLayout->addWidget(sigList, 0, 0); + signGridLayout->setContentsMargins(0, 10, 0, 0); auto signGroupBox = new QGroupBox(); signGroupBox->setLayout(signGridLayout); - signGroupBox->setTitle("Signature of Selected UID"); + signGroupBox->setTitle(tr("Signature of Selected UID")); auto vboxLayout = new QVBoxLayout(); vboxLayout->addWidget(uidGroupBox); vboxLayout->addWidget(signGroupBox); + vboxLayout->setContentsMargins(0, 0, 0, 0); connect(addUIDButton, SIGNAL(clicked(bool)), this, SLOT(slotAddUID())); connect(mCtx, SIGNAL(signalKeyInfoChanged()), this, SLOT(slotRefreshUIDList())); diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 36d8f363..4bcee080 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -23,6 +23,7 @@ */ #include "MainWindow.h" +#include "ui/SendMailDialog.h" void MainWindow::slotEncrypt() { @@ -58,12 +59,12 @@ void MainWindow::slotEncrypt() { auto thread = QThread::create([&]() { error = mCtx->encrypt(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, &result); }); - + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); thread->start(); - WaitingDialog *dialog = new WaitingDialog(this); + auto *dialog = new WaitingDialog(tr("Encrypting"), this); - while(thread->isRunning()) { + while (thread->isRunning()) { QApplication::processEvents(); } @@ -83,6 +84,19 @@ void MainWindow::slotEncrypt() { else infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + if (resultAnalyse->getStatus() >= 0) { + infoBoard->resetOptionActionsMenu(); + infoBoard->addOptionalAction("Send Mail", [this]() { + if(settings.value("sendMail/enable", false).toBool()) + new SendMailDialog(edit->curTextPage()->toPlainText(), this); + else { + QMessageBox::warning(nullptr, + tr("Function Disabled"), + tr("Please go to the settings interface to enable and configure this function.")); + } + }); + } + delete resultAnalyse; } else if (edit->slotCurPageFileTreeView() != nullptr) { this->slotFileEncrypt(); @@ -118,7 +132,19 @@ void MainWindow::slotSign() { gpgme_sign_result_t result = nullptr; - auto error = mCtx->sign(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, false, &result); + gpgme_error_t error; + auto thread = QThread::create([&]() { + error = mCtx->sign(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, false, &result); + }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + thread->start(); + + auto *dialog = new WaitingDialog(tr("Signing"), this); + while (thread->isRunning()) { + QApplication::processEvents(); + } + dialog->close(); + infoBoard->associateTextEdit(edit->curTextPage()); edit->slotFillTextEditWithText(QString::fromUtf8(*tmp)); @@ -154,10 +180,11 @@ void MainWindow::slotDecrypt() { // try decrypt, if fail do nothing, especially don't replace text error = mCtx->decrypt(text, decrypted, &result); }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); thread->start(); - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { + auto *dialog = new WaitingDialog(tr("Decrypting"), this); + while (thread->isRunning()) { QApplication::processEvents(); } @@ -213,13 +240,13 @@ void MainWindow::slotVerify() { auto thread = QThread::create([&]() { error = mCtx->verify(&text, nullptr, &result); }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); thread->start(); - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { + auto *dialog = new WaitingDialog(tr("Verifying"), this); + while (thread->isRunning()) { QApplication::processEvents(); } - dialog->close(); auto resultAnalyse = new VerifyResultAnalyse(mCtx, error, result); @@ -268,27 +295,27 @@ void MainWindow::slotEncryptSign() { 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); + 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 (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_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.")); + if (!can_sign) { + QMessageBox::warning(nullptr, + tr("Incomplete Operation"), + tr("None of the selected key pairs can provide the signature function.")); } auto *tmp = new QByteArray(); @@ -300,18 +327,19 @@ void MainWindow::slotEncryptSign() { gpgme_error_t error; auto thread = QThread::create([&]() { error = mCtx->encryptSign(keys, edit->curTextPage()->toPlainText().toUtf8(), tmp, &encr_result, - &sign_result); + &sign_result); }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); thread->start(); - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { + auto *dialog = new WaitingDialog(tr("Encrypting and Signing"), this); + while (thread->isRunning()) { QApplication::processEvents(); } dialog->close(); - if(gpgme_err_code(error) == GPG_ERR_NO_ERROR) { + if (gpgme_err_code(error) == GPG_ERR_NO_ERROR) { auto *tmp2 = new QString(*tmp); edit->slotFillTextEditWithText(*tmp2); } @@ -330,6 +358,19 @@ void MainWindow::slotEncryptSign() { else infoBoard->slotRefresh(reportText, INFO_ERROR_WARN); + if (status >= 0) { + infoBoard->resetOptionActionsMenu(); + infoBoard->addOptionalAction("Send Mail", [this]() { + if(settings.value("sendMail/enable", false).toBool()) + new SendMailDialog(edit->curTextPage()->toPlainText(), this); + else { + QMessageBox::warning(nullptr, + tr("Function Disabled"), + tr("Please go to the settings interface to enable and configure this function.")); + } + }); + } + delete resultAnalyseEncr; delete resultAnalyseSign; } else if (edit->slotCurPageFileTreeView() != nullptr) { @@ -354,10 +395,11 @@ void MainWindow::slotDecryptVerify() { auto thread = QThread::create([&]() { error = mCtx->decryptVerify(text, decrypted, &d_result, &v_result); }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); thread->start(); - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { + auto *dialog = new WaitingDialog(tr("Decrypting and Verifying"), this); + while (thread->isRunning()) { QApplication::processEvents(); } @@ -495,22 +537,27 @@ void MainWindow::slotFileEncrypt() { } } - try { - gpgme_encrypt_result_t result; + gpgme_encrypt_result_t result; - gpgme_error_t error; - auto thread = QThread::create([&]() { + gpgme_error_t error; + bool if_error = false; + auto thread = QThread::create([&]() { + try { error = GpgFileOpera::encryptFile(mCtx, keys, path, &result); - }); - thread->start(); - - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { - QApplication::processEvents(); + } catch (const std::runtime_error &e) { + if_error = true; } + }); + connect(thread, SIGNAL(finished(QPrivateSignal)), thread, SLOT(deleteLater())); + thread->start(); - dialog->close(); + 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); @@ -526,11 +573,10 @@ void MainWindow::slotFileEncrypt() { delete resultAnalyse; fileTreeView->update(); - - } catch (const std::runtime_error &e) { + } else { QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; } - } void MainWindow::slotFileDecrypt() { @@ -572,21 +618,28 @@ void MainWindow::slotFileDecrypt() { return; } - try { - gpgme_decrypt_result_t result; - gpgme_error_t error; - auto thread = QThread::create([&]() { - error = GpgFileOpera::decryptFile(mCtx, path, &result); - }); - thread->start(); + gpgme_decrypt_result_t result; + gpgme_error_t error; + bool if_error = false; - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { - QApplication::processEvents(); + 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(); - dialog->close(); + 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); @@ -602,7 +655,7 @@ void MainWindow::slotFileDecrypt() { delete resultAnalyse; fileTreeView->update(); - } catch (const std::runtime_error &e) { + } else { QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); return; } @@ -661,20 +714,28 @@ void MainWindow::slotFileSign() { } } - try { - gpgme_sign_result_t result; - gpgme_error_t error; - auto thread = QThread::create([&]() { - error = GpgFileOpera::signFile(mCtx, keys, path, &result); - }); - thread->start(); + gpgme_sign_result_t result; + gpgme_error_t error; + bool if_error = false; - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { - QApplication::processEvents(); + 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(); - dialog->close(); + 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(); @@ -692,8 +753,9 @@ void MainWindow::slotFileSign() { fileTreeView->update(); - } catch (const std::runtime_error &e) { + } else { QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; } fileTreeView->update(); @@ -709,11 +771,10 @@ void MainWindow::slotFileVerify() { QString signFilePath, dataFilePath; - if(fileInfo.suffix() == "gpg") { + if (fileInfo.suffix() == "gpg") { dataFilePath = path; signFilePath = path; - } - else if (fileInfo.suffix() == "sig") { + } else if (fileInfo.suffix() == "sig") { int pos = path.lastIndexOf(QChar('.')); dataFilePath = path.left(pos); signFilePath = path; @@ -743,21 +804,22 @@ void MainWindow::slotFileVerify() { gpgme_error_t error; bool if_error = false; auto thread = QThread::create([&]() { - try{ + 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(); - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { + auto *dialog = new WaitingDialog(tr("Verifying"), this); + while (thread->isRunning()) { QApplication::processEvents(); } dialog->close(); - if(!if_error) { + if (!if_error) { auto resultAnalyse = new VerifyResultAnalyse(mCtx, error, result); auto &reportText = resultAnalyse->getResultReport(); infoBoard->associateTabWidget(edit->tabWidget); @@ -832,45 +894,52 @@ void MainWindow::slotFileEncryptSign() { 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); + 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 (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_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.")); + if (!can_sign) { + QMessageBox::warning(nullptr, + tr("Incomplete Operation"), + tr("None of the selected key pairs can provide the signature function.")); } - try { + gpgme_encrypt_result_t encr_result = nullptr; + gpgme_sign_result_t sign_result = nullptr; - gpgme_encrypt_result_t encr_result = nullptr; - gpgme_sign_result_t sign_result = nullptr; + gpgme_error_t error; + bool if_error = false; - gpgme_error_t error; - auto thread = QThread::create([&]() { + auto thread = QThread::create([&]() { + try { error = GpgFileOpera::encryptSignFile(mCtx, keys, path, &encr_result, &sign_result); - }); - thread->start(); - - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { - QApplication::processEvents(); + } catch (const std::runtime_error &e) { + if_error = true; } - dialog->close(); + }); + 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); @@ -891,8 +960,9 @@ void MainWindow::slotFileEncryptSign() { fileTreeView->update(); - } catch (std::runtime_error &e) { + } else { QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); + return; } } @@ -924,22 +994,30 @@ void MainWindow::slotFileDecryptVerify() { outFileName = path + ".out"; } - try { + gpgme_decrypt_result_t d_result = nullptr; + gpgme_verify_result_t v_result = nullptr; - gpgme_decrypt_result_t d_result = nullptr; - gpgme_verify_result_t v_result = nullptr; + gpgme_error_t error; + bool if_error = false; - gpgme_error_t error; - auto thread = QThread::create([&]() { + auto thread = QThread::create([&]() { + try { error = GpgFileOpera::decryptVerifyFile(mCtx, path, &d_result, &v_result); - }); - thread->start(); - - WaitingDialog *dialog = new WaitingDialog(this); - while(thread->isRunning()) { - QApplication::processEvents(); + } catch (const std::runtime_error &e) { + if_error = true; } - dialog->close(); + }); + 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); @@ -964,7 +1042,7 @@ void MainWindow::slotFileDecryptVerify() { delete resultAnalyseVerify; fileTreeView->update(); - } catch (std::runtime_error &e) { + } else { QMessageBox::critical(this, tr("Error"), tr("An error occurred during operation.")); return; } @@ -997,3 +1075,21 @@ void MainWindow::slotFileVerifyCustom() { void MainWindow::slotOpenFile(QString &path) { edit->slotOpenFile(path); } + +void MainWindow::slotVersionUpgrade(const QString ¤tVersion, const QString &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) { + 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( + currentVersion) + + tr("You can download the latest version(%1) on Github Releases Page.<br/>").arg( + latestVersion)); + } +} diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index 189ead53..7065e41c 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -25,7 +25,11 @@ #include "MainWindow.h" void MainWindow::slotAbout() { - new AboutDialog(this); + new AboutDialog(0, this); +} + +void MainWindow::slotCheckUpdate() { + new AboutDialog(2, this); } void MainWindow::slotSetStatusBarText(const QString &text) { @@ -141,13 +145,6 @@ void MainWindow::slotOpenSettingsDialog() { importButton->setToolButtonStyle(buttonStyle); fileEncButton->setToolButtonStyle(buttonStyle); - // Mime-settings - if (settings.value("mime/parseMime").toBool()) { - createAttachmentDock(); - } else if (attachmentDockCreated) { - closeAttachmentDock(); - } - // restart mainwindow if necessary if (getRestartNeeded()) { if (edit->maybeSaveAnyTab()) { diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 233a0927..21f6e3b7 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -209,18 +209,27 @@ void MainWindow::createActions() { importKeyFromEditAct->setToolTip(tr("Import New Key From Editor")); connect(importKeyFromEditAct, SIGNAL(triggered()), this, SLOT(slotImportKeyFromEdit())); - openKeyManagementAct = new QAction(tr("Manage &keys"), this); + openKeyManagementAct = new QAction(tr("Manage &Keys"), this); openKeyManagementAct->setIcon(QIcon(":keymgmt.png")); openKeyManagementAct->setToolTip(tr("Open Keymanagement")); connect(openKeyManagementAct, SIGNAL(triggered()), this, SLOT(slotOpenKeyManagement())); - /* About Menu + /* + * About Menu */ aboutAct = new QAction(tr("&About"), this); aboutAct->setIcon(QIcon(":help.png")); aboutAct->setToolTip(tr("Show the application's About box")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(slotAbout())); + /* + * Check Update Menu + */ + checkUpdateAct = new QAction(tr("&Check for Updates"), this); + checkUpdateAct->setIcon(QIcon(":help.png")); + checkUpdateAct->setToolTip(tr("Check for updates")); + connect(checkUpdateAct, SIGNAL(triggered()), this, SLOT(slotCheckUpdate())); + startWizardAct = new QAction(tr("Open &Wizard"), this); startWizardAct->setToolTip(tr("Open the wizard")); connect(startWizardAct, SIGNAL(triggered()), this, SLOT(slotStartWizard())); @@ -231,8 +240,8 @@ void MainWindow::createActions() { appendSelectedKeysAct->setToolTip(tr("Append The Selected Keys To Text in Editor")); connect(appendSelectedKeysAct, SIGNAL(triggered()), this, SLOT(slotAppendSelectedKeys())); - copyMailAddressToClipboardAct = new QAction(tr("Copy EMail-address"), this); - copyMailAddressToClipboardAct->setToolTip(tr("Copy selected EMailaddress to clipboard")); + copyMailAddressToClipboardAct = new QAction(tr("Copy Email"), this); + copyMailAddressToClipboardAct->setToolTip(tr("Copy selected Email to clipboard")); connect(copyMailAddressToClipboardAct, SIGNAL(triggered()), this, SLOT(slotCopyMailAddressToClipboard())); // TODO: find central place for shared actions, to avoid code-duplication with keymgmt.cpp @@ -247,6 +256,7 @@ void MainWindow::createActions() { uploadKeyToServerAct = new QAction(tr("Upload Public Key(s) To Server"), this); uploadKeyToServerAct->setToolTip(tr("Upload The Selected Public Keys To Server")); connect(uploadKeyToServerAct, SIGNAL(triggered()), this, SLOT(uploadKeyToServer())); + /* Key-Shortcuts for Tab-Switchung-Action */ switchTabUpAct = new QAction(this); @@ -339,6 +349,7 @@ void MainWindow::createMenus() { helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(startWizardAct); helpMenu->addSeparator(); + helpMenu->addAction(checkUpdateAct); helpMenu->addAction(aboutAct); } @@ -436,26 +447,4 @@ void MainWindow::createDockWindows() { infoBoardDock->setWidget(infoBoard); infoBoardDock->widget()->layout()->setContentsMargins(0, 0, 0, 0); viewMenu->addAction(infoBoardDock->toggleViewAction()); - - /* Attachments-Dockwindow - */ - if (settings.value("mime/parseMime").toBool()) { - createAttachmentDock(); - } -} - -void MainWindow::createAttachmentDock() { - if (attachmentDockCreated) { - return; - } - mAttachments = new Attachments(); - attachmentDock = new QDockWidget(tr("Attached files:"), this); - attachmentDock->setObjectName("AttachmentDock"); - attachmentDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea); - addDockWidget(Qt::LeftDockWidgetArea, attachmentDock); - attachmentDock->setWidget(mAttachments); - // hide till attachment is decrypted - viewMenu->addAction(attachmentDock->toggleViewAction()); - attachmentDock->hide(); - attachmentDockCreated = true; } diff --git a/src/ui/widgets/Attachments.cpp b/src/ui/widgets/Attachments.cpp deleted file mode 100644 index 8f741f66..00000000 --- a/src/ui/widgets/Attachments.cpp +++ /dev/null @@ -1,180 +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. - * - */ - -/* TODO: - * - check content encoding (base64 / quoted-printable) and apply appropriate opperation (maybe already in mime.cpp) - * - check memory usage, use less copy operations / more references - * - possibility to clear attachment-view , e.g. with decryption or encrypting a new message - * - save all: like in thunderbird, one folder, all files go there - */ - -/* - * - save, delete (clear) all - * - each line save & clear button - * - attached files to view-menu - * - also an open button, whichs should save file to tmp-folder, and open with correct app (via QDesktopServices) - */ - - -#include "ui/widgets/Attachments.h" - -Attachments::Attachments(QWidget *parent) - : QWidget(parent) { - table = new AttachmentTableModel(this); - - tableView = new QTableView; - tableView->setModel(table); - tableView->setSelectionBehavior(QAbstractItemView::SelectRows); - // only one entry should be selected at time - tableView->setSelectionMode(QAbstractItemView::SingleSelection); - tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); - tableView->setFocusPolicy(Qt::NoFocus); - tableView->setAlternatingRowColors(true); - tableView->verticalHeader()->hide(); - tableView->setShowGrid(false); - tableView->setColumnWidth(0, 300); - tableView->horizontalHeader()->setStretchLastSection(true); - - auto *layout = new QVBoxLayout; - layout->addWidget(tableView); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); - createActions(); - -} - -void Attachments::contextMenuEvent(QContextMenuEvent *event) { - QMenu menu(this); - menu.addAction(saveFileAct); - // enable open with only if allowed by user - if (settings.value("mime/openAttachment").toBool()) - menu.addAction(openFileAct); - - menu.exec(event->globalPos()); -} - -void Attachments::createActions() { - saveFileAct = new QAction(tr("Save File"), this); - saveFileAct->setToolTip(tr("Save this file")); - saveFileAct->setIcon(QIcon(":filesave.png")); - connect(saveFileAct, SIGNAL(triggered()), this, SLOT(slotSaveFile())); - - openFileAct = new QAction(tr("Open File"), this); - openFileAct->setToolTip(tr("Open this file")); - openFileAct->setIcon(QIcon(":fileopen.png")); - connect(openFileAct, SIGNAL(triggered()), this, SLOT(slotOpenFile())); - -} - -void Attachments::slotSaveFile() { - - QModelIndexList indexes = tableView->selectionModel()->selection().indexes(); - - if (indexes.empty()) { - return; - } - - // only singe-selection possible now: TODO: foreach - MimePart mp = table->getMimePart(indexes.at(0).row()); - QString filename = mp.header.getParam("Content-Type", "name"); - // TODO: find out why filename is quoted - filename.chop(1); - filename.remove(0, 1); - // TODO: check if really base64 - saveByteArrayToFile(QByteArray::fromBase64(mp.body), filename); - -} - -void Attachments::saveByteArrayToFile(QByteArray outBuffer, QString filename) { - - //QString path=""; - QString path = std::move(filename); - QString outfileName = QFileDialog::getSaveFileName(this, tr("Save File"), path); - - if (outfileName.isEmpty()) return; - - QFile outfile(outfileName); - if (!outfile.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("File"), - tr("Cannot write file %1:\n%2.") - .arg(outfileName) - .arg(outfile.errorString())); - return; - } - - QDataStream out(&outfile); - out.writeRawData(outBuffer.data(), outBuffer.length()); - outfile.close(); -} - -/** - * WIP: TODO: - * - create attachments dir if not existing - * - ask for cleanup of dir on exit - * - remove code-duplication with saveByteArrayToFile - */ -void Attachments::slotOpenFile() { - - // TODO: make attachmentdir constant or configurable - QString attachmentDir = qApp->applicationDirPath() + "/attachments/"; - //QDir p = QDir(qApp->applicationDirPath() + "/attachments/"); - if (!QDir(attachmentDir).exists()) { - QDir().mkpath(attachmentDir); - } - - QModelIndexList indexes = tableView->selectionModel()->selection().indexes(); - MimePart mp = table->getMimePart(indexes.at(0).row()); - -// qDebug() << "mime: " << mp.header.getValue("Content-Type"); - - QString filename = mp.header.getParam("Content-Type", "name"); - // TODO: find out why filename is quoted -// qDebug() << "file: " << filename; - filename.chop(1); - filename.remove(0, 1); - filename.prepend(attachmentDir); - - // qDebug() << "file: " << filename; - QByteArray outBuffer = QByteArray::fromBase64(mp.body); - - - QFile outfile(filename); - if (!outfile.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("File"), - tr("Cannot write file %1:\n%2.") - .arg(filename) - .arg(outfile.errorString())); - return; - } - - QDataStream out(&outfile); - out.writeRawData(outBuffer.data(), outBuffer.length()); - outfile.close(); - QDesktopServices::openUrl(QUrl("file://" + filename, QUrl::TolerantMode)); -} - -void Attachments::addMimePart(MimePart *mp) { - table->add(*mp); -} - diff --git a/src/ui/widgets/EditorPage.cpp b/src/ui/widgets/EditorPage.cpp index 8a51b721..05d5cbea 100644 --- a/src/ui/widgets/EditorPage.cpp +++ b/src/ui/widgets/EditorPage.cpp @@ -42,6 +42,9 @@ EditorPage::EditorPage(QString filePath, QWidget *parent) : QWidget(parent), setAttribute(Qt::WA_DeleteOnClose); textPage->setFocus(); + // Front in same width + this->setFont({"Courier"}); + connect(textPage, SIGNAL(textChanged()), this, SLOT(formatGpgHeader())); } diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index bab6cf68..b9602d58 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -46,15 +46,33 @@ FilePage::FilePage(QWidget *parent) : QWidget(parent) { createPopupMenu(); - upLevelButton = new QPushButton("Up"); + + upLevelButton = new QPushButton(); connect(upLevelButton, SIGNAL(clicked(bool)), this, SLOT(slotUpLevel())); + QString buttonStyle = "QPushButton{border:none;background-color:rgba(255, 255, 255,100);}"; + + + auto upPixmap = QPixmap(":up.png"); + upPixmap = upPixmap.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QIcon upButtonIcon(upPixmap); + upLevelButton->setIcon(upButtonIcon); + upLevelButton->setIconSize(upPixmap.rect().size()); + upLevelButton->setStyleSheet(buttonStyle); + refreshButton = new QPushButton("Refresh"); connect(refreshButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); - goPathButton = new QPushButton("Go"); + goPathButton = new QPushButton(); connect(goPathButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); + auto updatePixmap = QPixmap(":refresh.png"); + updatePixmap = updatePixmap.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QIcon updateButtonIcon(updatePixmap); + goPathButton->setIcon(updateButtonIcon); + goPathButton->setIconSize(updatePixmap.rect().size()); + goPathButton->setStyleSheet(buttonStyle); + pathEdit = new QLineEdit(); pathEdit->setText(dirModel->rootPath()); @@ -62,7 +80,7 @@ FilePage::FilePage(QWidget *parent) : QWidget(parent) { menuLayout->addWidget(upLevelButton); menuLayout->setStretchFactor(upLevelButton, 1); menuLayout->addWidget(pathEdit); - menuLayout->setStretchFactor(pathEdit, 8); + menuLayout->setStretchFactor(pathEdit, 10); menuLayout->addWidget(goPathButton); menuLayout->setStretchFactor(goPathButton, 1); // menuLayout->addWidget(refreshButton); @@ -87,17 +105,22 @@ FilePage::FilePage(QWidget *parent) : QWidget(parent) { } void FilePage::fileTreeViewItemClicked(const QModelIndex &index) { - mPath = dirModel->fileInfo(index).absoluteFilePath(); - qDebug() << "mPath" << mPath; + selectedPath = dirModel->fileInfo(index).absoluteFilePath(); + qDebug() << "selectedPath" << selectedPath; } void FilePage::slotUpLevel() { QModelIndex currentRoot = dirTreeView->rootIndex(); - mPath = dirModel->fileInfo(currentRoot.parent()).absoluteFilePath(); - auto fileInfo = QFileInfo(mPath); + + mPath = dirModel->fileInfo(currentRoot).absoluteFilePath(); + QDir dir(mPath); + dir.makeAbsolute(); + dir.setPath(QDir::cleanPath(dir.filePath(QStringLiteral("..")))); + mPath = dir.absolutePath(); + auto fileInfo = QFileInfo(dir.absolutePath()); if(fileInfo.isDir() && fileInfo.isReadable() && fileInfo.isExecutable()) { - dirTreeView->setRootIndex(currentRoot.parent()); pathEdit->setText(mPath); + slotGoPath(); } qDebug() << "Current Root mPath" << mPath; emit pathChanged(mPath); @@ -106,8 +129,9 @@ void FilePage::slotUpLevel() { void FilePage::fileTreeViewItemDoubleClicked(const QModelIndex &index) { mPath = dirModel->fileInfo(index).absoluteFilePath(); auto fileInfo = QFileInfo(mPath); + auto targetModelIndex = dirTreeView->model()->index(index.row(), 0, index.parent()); if(fileInfo.isDir() && fileInfo.isReadable() && fileInfo.isExecutable()) { - dirTreeView->setRootIndex(index); + dirTreeView->setRootIndex(targetModelIndex); pathEdit->setText(mPath); } qDebug() << "Index mPath" << mPath; @@ -115,7 +139,7 @@ void FilePage::fileTreeViewItemDoubleClicked(const QModelIndex &index) { } QString FilePage::getSelected() const { - return mPath; + return selectedPath; } void FilePage::slotGoPath() { @@ -159,10 +183,10 @@ void FilePage::createPopupMenu() { void FilePage::onCustomContextMenu(const QPoint &point) { QModelIndex index = dirTreeView->indexAt(point); - mPath = dirModel->fileInfo(index).absoluteFilePath(); - qDebug() << "Right Click" << mPath; + selectedPath = dirModel->fileInfo(index).absoluteFilePath(); + qDebug() << "Right Click" << selectedPath; if (index.isValid()) { - QFileInfo info(mPath); + QFileInfo info(selectedPath); encryptItemAct->setEnabled(info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); decryptItemAct->setEnabled(info.isFile() && info.suffix() == "gpg"); signItemAct->setEnabled(info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); @@ -234,3 +258,10 @@ void FilePage::slotVerifyItem() { if(mainWindow != nullptr) mainWindow->slotFileVerify(); } + +void FilePage::keyPressEvent(QKeyEvent *event) { + qDebug() << "Key Press" << event->key(); + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + slotGoPath(); + } +} diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index 33517a8e..f26917a4 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -25,7 +25,9 @@ #include "ui/widgets/InfoBoardWidget.h" InfoBoardWidget::InfoBoardWidget(QWidget *parent, GpgME::GpgContext *ctx, KeyList *keyList) : - QWidget(parent), mCtx(ctx), mKeyList(keyList) { + QWidget(parent), mCtx(ctx), mKeyList(keyList), appPath(qApp->applicationDirPath()), + settings(RESOURCE_DIR(appPath) + "/conf/gpgfrontend.ini", + QSettings::IniFormat) { infoBoard = new QTextEdit(this); infoBoard->setReadOnly(true); @@ -42,7 +44,7 @@ InfoBoardWidget::InfoBoardWidget(QWidget *parent, GpgME::GpgContext *ctx, KeyLis detailMenu->addAction(importFromKeyserverAct); importFromKeyserverAct->setVisible(false); - QWidget *actionButtonMenu = new QWidget(); + auto *actionButtonMenu = new QWidget(); actionButtonMenu->setContentsMargins(0, 0, 0, 0); actionButtonMenu->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); actionButtonMenu->setFixedHeight(36); @@ -52,7 +54,7 @@ InfoBoardWidget::InfoBoardWidget(QWidget *parent, GpgME::GpgContext *ctx, KeyLis actionButtonLayout->setSpacing(0); actionButtonMenu->setLayout(actionButtonLayout); - auto label = new QLabel("Optional Actions Menu"); + auto label = new QLabel(tr("Optional Actions Menu")); label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); label->setContentsMargins(0, 0, 0, 0); @@ -107,7 +109,8 @@ void InfoBoardWidget::setInfoBoard(const QString &text, InfoBoardStatus verifyLa QPalette status = infoBoard->palette(); status.setColor(QPalette::Text, color); infoBoard->setPalette(status); - infoBoard->setFont(QFont("Times", 10)); + auto infoBoardFontSize = settings.value("informationBoard/fontSize", 10).toInt(); + infoBoard->setFont(QFont("Times", infoBoardFontSize)); } void InfoBoardWidget::slotRefresh(const QString &text, InfoBoardStatus status) { @@ -135,13 +138,16 @@ void InfoBoardWidget::associateTabWidget(QTabWidget *tab) { disconnect(mTextPage, SIGNAL(textChanged()), this, SLOT(slotReset())); // if (mFileTreeView != nullptr) // disconnect(mFileTreeView, &FilePage::pathChanged, this, &InfoBoardWidget::slotReset); - if (mTabWidget != nullptr) + if (mTabWidget != nullptr) { disconnect(mTabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); + connect(mTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(slotReset())); + } mTextPage = nullptr; mFileTreeView = nullptr; mTabWidget = tab; connect(tab, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); + connect(tab, SIGNAL(tabCloseRequested(int)), this, SLOT(slotReset())); } void InfoBoardWidget::addOptionalAction(const QString &name, const std::function<void()> &action) { diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index b044020a..3929868e 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -297,6 +297,11 @@ void KeyList::contextMenuEvent(QContextMenuEvent *event) } +void KeyList::addSeparator() +{ + popupMenu->addSeparator(); +} + void KeyList::addMenuAction(QAction *act) { popupMenu->addAction(act); diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 26cc3834..eab0f799 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -37,9 +37,6 @@ TextEdit::TextEdit(QWidget *parent) : QWidget(parent) { layout->setSpacing(0); setLayout(layout); - // Front in same width - this->setFont({"Courier"}); - connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(removeTab(int))); connect(this, &TextEdit::insertTargetTextPage, this, @@ -73,7 +70,7 @@ void TextEdit::slotNewHelpTab(const QString &title, const QString &path) const { void TextEdit::slotNewFileTab() const { auto *page = new FilePage(qobject_cast<QWidget *>(parent())); - tabWidget->addTab(page, "[File Browser]"); + tabWidget->addTab(page, "[Browser]"); tabWidget->setCurrentIndex(tabWidget->count() - 1); connect(page, SIGNAL(pathChanged(const QString &)), this, SLOT(slotFilePagePathChanged(const QString &))); @@ -132,6 +129,7 @@ void TextEdit::slotInsertTargetTextPage(const QString &pagePtr, auto *taregtTextPage = qobject_cast<EditorPage *>(it.value()); if (taregtTextPage != nullptr) { taregtTextPage->getTextPage()->insertPlainText(text); + taregtTextPage->getTextPage()->document()->setModified(false); } } } @@ -164,6 +162,7 @@ void TextEdit::slotReadTargetTextPageDone(const QString &pagePtr) { tabWidget->setTabText(index, strippedName(taregtTextPage->getFilePath())); } + taregtTextPage->getTextPage()->document()->setModified(false); connect(taregtTextPage->getTextPage()->document(), SIGNAL(modificationChanged(bool)), this, SLOT(slotShowModified())); @@ -638,13 +637,13 @@ void TextEdit::slotFilePagePathChanged(const QString &path) { int index = tabWidget->currentIndex(); QString mPath; QFileInfo fileInfo(path); - QString tPath = fileInfo.path(); + QString tPath = fileInfo.absoluteFilePath(); if (path.size() > 18) { mPath = tPath.mid(tPath.size() - 18, 18).prepend("..."); } else { mPath = tPath; } - mPath.prepend("[File Browser] "); + mPath.prepend("[Browser] "); mPath.append("/"); tabWidget->setTabText(index, mPath); } |