diff --git a/src/gpgolconfig/gpgolconfig.cpp b/src/gpgolconfig/gpgolconfig.cpp index 756d364..779e860 100644 --- a/src/gpgolconfig/gpgolconfig.cpp +++ b/src/gpgolconfig/gpgolconfig.cpp @@ -1,124 +1,125 @@ /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "gpgolconfig.h" #include "w32-gettext.h" #include "w32-util.h" #include "w32-qt-util.h" #include "gpgolconfigpage.h" #include "gpgoldebugpage.h" #include "cryptoconfigpage.h" #include #include #include #include #include #include #include #include #include #include GpgOLConfig::GpgOLConfig(const QCommandLineParser &parser): KPageDialog(nullptr) { setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); setWindowTitle(_("Configure GpgOL")); setWindowIcon(QIcon(":/gpgol-icon.svg")); const auto hwnd = parser.value(QStringLiteral("hwnd")); if (!hwnd.isEmpty()) { bool ok; WId id = (WId) hwnd.toInt(&ok); if (!ok) { qDebug() << "invalid hwnd value"; } else { W32::setupForeignParent(id, this, true); setModal(true); } } if (parser.isSet("gpgol-version")) { mVersion = parser.value("gpgol-version"); } else { mVersion = QStringLiteral("unknown version"); } setupGUI(); resize(800, 500); } void GpgOLConfig::setupGUI() { setFaceType(KPageDialog::List); QDialogButtonBox *buttonBox = new QDialogButtonBox(); buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Cancel | QDialogButtonBox::Ok); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); setButtonBox(buttonBox); + bool unused; bool hideCryptoConf = strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, "hideCryptoConfig"), - false); + false, unused); auto cryptoConfWidget = hideCryptoConf ? nullptr : new CryptoConfigPage; auto gpgolConfWidget = new GpgOLConfigPage; auto gpgolDbgWidget = new GpgOLDebugPage; connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, [this, cryptoConfWidget, gpgolConfWidget, gpgolDbgWidget] () { if (cryptoConfWidget) { cryptoConfWidget->save(); } gpgolConfWidget->save(); gpgolDbgWidget->save(); close(); }); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, [this, cryptoConfWidget, gpgolConfWidget, gpgolDbgWidget] () { if (currentPage()->widget() == cryptoConfWidget) { cryptoConfWidget->defaults(); } else if (currentPage()->widget() == gpgolConfWidget){ gpgolConfWidget->defaults(); } else if (currentPage()->widget() == gpgolDbgWidget){ gpgolDbgWidget->defaults(); } }); connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, [this] () { close(); }); KPageWidgetItem *page = new KPageWidgetItem(gpgolConfWidget, _("GpgOL")); page->setHeader(QStringLiteral("%1 - %2%3").arg(_("Configure GpgOL")).arg( _("Version ")).arg(mVersion)); page->setIcon(QIcon(":/gpgol-icon.svg")); addPage(page); if (cryptoConfWidget) { page = new KPageWidgetItem(cryptoConfWidget, QStringLiteral("%1\n%2").arg(_("GnuPG System")).arg(_("(Technical)"))); page->setHeader(_("Configuration of GnuPG System options")); page->setIcon(QIcon::fromTheme("document-encrypt")); addPage(page); } page = new KPageWidgetItem(gpgolDbgWidget, _("Debug")); page->setHeader(_("Configuration of debug options")); page->setIcon(QIcon::fromTheme("tools-report-bug")); addPage(page); } diff --git a/src/gpgolconfig/gpgolconfigpage.cpp b/src/gpgolconfig/gpgolconfigpage.cpp index 15d663d..8f4829a 100644 --- a/src/gpgolconfig/gpgolconfigpage.cpp +++ b/src/gpgolconfig/gpgolconfigpage.cpp @@ -1,346 +1,368 @@ /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "gpgolconfigpage.h" #include "w32-gettext.h" #include "w32-util.h" #include #include #include #include #include #include #include #include #include #include class SecretKeyFilter: public Kleo::DefaultKeyFilter { public: SecretKeyFilter(): Kleo::DefaultKeyFilter() { setHasSecret(Kleo::DefaultKeyFilter::Set); setRevoked(Kleo::DefaultKeyFilter::NotSet); setDisabled(Kleo::DefaultKeyFilter::NotSet); setExpired(Kleo::DefaultKeyFilter::NotSet); setCanEncrypt(Kleo::DefaultKeyFilter::Set); } }; static auto s_secretKeyFilter = std::shared_ptr (new SecretKeyFilter); /* class ExplainingChkBox: public QWidget { Q_OBJECT public: explicit ExplainingChkBox(const QString &text, const QString &explanation): mChkBox(new QCheckBox(text)), mExplanation(explanation) { auto hBox = new QHBoxLayout(this); hBox->addWidget(mChkBox); auto infoBtn = new QPushButton; infoBtn->setIcon(QIcon::fromTheme("help-contextual")); hBox->addWidget(infoBtn); hBox->addStretch(1); connect(infoBtn, &QPushButton::clicked, this, [this, infoBtn] () { QToolTip::showText(infoBtn->mapToGlobal(QPoint()), mExplanation, infoBtn); }); } void setChecked(bool value) { mChkBox->setChecked(value); } private: QCheckBox *mChkBox; QString mExplanation; }; */ GpgOLConfigPage::GpgOLConfigPage(QWidget *parent): QWidget(parent) { setupGUI(); load(); } /* Helper to build an "About" style layout. static QLayout *buildAboutLayout(const QString &version) { auto hLay = new QHBoxLayout; auto vLay = new QVBoxLayout; hLay->addLayout(vLay); hLay->addStretch(1); auto iconLbl = new QLabel; iconLbl->setPixmap(QIcon(":/gpgol-logo.png").pixmap(128, 80)); auto versionLbl = new QLabel(QStringLiteral(" ") + QString::fromUtf8(_("Version ")) + version); vLay->addWidget(iconLbl); vLay->addWidget(versionLbl); return hLay; } */ void GpgOLConfigPage::setupGUI() { auto baseLay = new QVBoxLayout(this); mSMIMEGrp = new QGroupBox(_("Enable the S/MIME support")); mSMIMEGrp->setCheckable(true); mSMIMEGrp->setAlignment(Qt::AlignLeft); auto smimeLay = new QVBoxLayout(mSMIMEGrp); mPreferSMIMEChk = new QCheckBox(_("&Prefer S/MIME")); mPreferSMIMEChk->setToolTip(_("Prefer S/MIME over OpenPGP if both are possible.")); smimeLay->addWidget(mPreferSMIMEChk); mSearchSMIMEChk = new QCheckBox(_("Search and import &X509 certificates in the configured directory services")); mSearchSMIMEChk->setToolTip(_("Searches for X509 certificates automatically and imports them. This option searches in all configured services.")); mSearchSMIMEWarning = new QLabel(_("Warning: The configured services will receive information about whom you send Emails!")); smimeLay->addWidget(mSearchSMIMEChk); smimeLay->addWidget(mSearchSMIMEWarning); connect(mSearchSMIMEChk, &QCheckBox::toggled, [this] (bool on) { mSearchSMIMEWarning->setVisible(on); }); baseLay->addWidget(mSMIMEGrp); // The general group auto generalGrp = new QGroupBox(_("General")); auto generalLay = new QVBoxLayout(generalGrp); generalGrp->setAlignment(Qt::AlignLeft); mAlwaysSigChk = new QCheckBox(_("&Sign new messages by default")); mAlwaysSigChk->setToolTip(_("Toggles the sign option for all new mails.")); mAlwaysEncChk = new QCheckBox(_("&Encrypt new messages by default")); mAlwaysEncChk->setToolTip(_("Toggles the encrypt option for all new mails.")); mReplyCryptChk = new QCheckBox(_("S&elect crypto settings automatically " "for reply and forward")); mReplyCryptChk->setToolTip(_("Toggles sign, encrypt options if the original mail was signed or encrypted.")); mInlinePGPChk = new QCheckBox(_("&Send OpenPGP mails without attachments as PGP/Inline")); mInlinePGPChk->setToolTip(_("Instead of using the PGP/MIME format, " "which properly handles attachments and encoding, " "the deprecated PGP/Inline is used.\n" "This can be useful for compatibility but should generally not " "be used.")); mAlwaysShowApprovalChk = new QCheckBox(QString::fromUtf8(_("&Always show security approval dialog"))); mAlwaysShowApprovalChk->setToolTip(_("Always show the security approval and certificate selection dialog. " "This slows down the encryption / signing process, especially with large keyrings.")); generalLay->addWidget(mAlwaysSigChk); generalLay->addWidget(mAlwaysEncChk); generalLay->addWidget(mReplyCryptChk); generalLay->addWidget(mInlinePGPChk); generalLay->addWidget(mAlwaysShowApprovalChk); // The draft encryption part mDraftEncChk = new QCheckBox(QString::fromUtf8(_("Encrypt &drafts of secure mails to this key:"))); mDraftEncChk->setToolTip(_("Encrypt drafts and autosaved mails if the secure button is toggled.")); mDraftKey = new Kleo::KeySelectionCombo (false); mDraftKey->setKeyFilter(s_secretKeyFilter); auto draftLay = new QHBoxLayout; draftLay->addWidget(mDraftEncChk); draftLay->addWidget(mDraftKey); generalLay->addLayout(draftLay); baseLay->addWidget(generalGrp); // The automation checkboxes mAutomationGrp = new QGroupBox(_("Automation")); mAutomationGrp->setToolTip(_("Enable or disable any automated key handling.")); auto autoLayout = new QVBoxLayout(mAutomationGrp); mAutomationGrp->setCheckable(true); mAutoImportChk = new QCheckBox(_("&Import any keys included in mails")); mAutoImportChk->setToolTip(_("Import OpenPGP keys from mail attachments or from mail headers.")); autoLayout->addWidget(mAutoImportChk); mAutoResolveChk = new QCheckBox(_("&Resolve recipient keys automatically")); autoLayout->addWidget(mAutoResolveChk); mAutoSecureChk = new QCheckBox(_("Automatically secure &messages")); mAutoSecureChk->setToolTip(_("Automatically toggles secure if keys with at least level 1 trust were found for all recipients.")); mAutoEncryptUntrustedChk = new QCheckBox(_("Also &with untrusted keys")); mAutoEncryptUntrustedChk->setToolTip(_("Also automatically toggles secure if keys with level 0 trust were found.")); auto subLay = new QHBoxLayout; subLay->addSpacing(20); subLay->addWidget(mAutoSecureChk); subLay->addWidget(mAutoEncryptUntrustedChk); autoLayout->addLayout(subLay); mAutoTrustChk = new QCheckBox(QStringLiteral("%1 (%2)").arg(_("Include OpenPGP &trust based on communication history")).arg(_("experimental"))); mAutoTrustChk->setToolTip(_("This changes the trust model to \"tofu+pgp\" which tracks the history of key usage. " "Automated trust can never exceed level 2.")); /* Dsiabled for now */ mAutoTrustChk->setVisible(false); autoLayout->addWidget(mAutoTrustChk); baseLay->addWidget(mAutomationGrp); // baseLay->addLayout(buildAboutLayout(mVersion)); baseLay->addStretch(1); connect(mAutoResolveChk, &QCheckBox::toggled, [this] (bool on) { mAutoSecureChk->setEnabled(on); mAutoEncryptUntrustedChk->setEnabled(mAutoSecureChk->isChecked()); mSearchSMIMEChk->setEnabled(mSMIMEGrp->isChecked() && on); }); connect(mAutoSecureChk, &QCheckBox::toggled, [this] (bool on) { mAutoEncryptUntrustedChk->setEnabled(on); }); connect(mSMIMEGrp, &QGroupBox::toggled, [this] (bool on) { mSearchSMIMEChk->setEnabled(mAutoResolveChk->isChecked() && on); }); connect (mDraftEncChk, &QCheckBox::toggled, [this] (bool on) { mDraftKey->setEnabled(on); }); } -static bool loadBool(const char *name, bool defaultVal) +static std::pair loadBool(const char *name, bool defaultVal) { - return strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, name), defaultVal); + bool forced = false; + bool val = strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, name), defaultVal, forced); + return std::make_pair(val, forced); } /* Bump this if you remove a config value */ #define CONFIG_VERSION "1" -static const QMap defaultMap { - { QStringLiteral("enableSmime"), false }, - { QStringLiteral("encryptDefault"), false }, - { QStringLiteral("signDefault"), false }, - { QStringLiteral("inlinePGP"), false }, - { QStringLiteral("replyCrypt"), true }, - { QStringLiteral("preferSmime"), false }, - { QStringLiteral("debugGPGME"), false }, - { QStringLiteral("automation"), true }, - { QStringLiteral("autoresolve"), true }, - { QStringLiteral("autosecure"), true }, - { QStringLiteral("autotrust"), false }, - { QStringLiteral("automation"), true }, - { QStringLiteral("syncEnc"), false }, - { QStringLiteral("searchSmimeServers"), false }, - { QStringLiteral("autoimport"), false }, - { QStringLiteral("autoencryptUntrusted"), false }, - { QStringLiteral("draftEnc"), false }, - { QStringLiteral("alwaysShowApproval"), false }, +static const QMap > defaultMap { + { QStringLiteral("enableSmime"), std::make_pair(false, false) }, + { QStringLiteral("encryptDefault"), std::make_pair(false, false) }, + { QStringLiteral("signDefault"), std::make_pair(false, false) }, + { QStringLiteral("inlinePGP"), std::make_pair(false, false) }, + { QStringLiteral("replyCrypt"), std::make_pair(true, false) }, + { QStringLiteral("preferSmime"), std::make_pair(false, false) }, + { QStringLiteral("debugGPGME"), std::make_pair(false, false) }, + { QStringLiteral("automation"), std::make_pair(true, false) }, + { QStringLiteral("autoresolve"), std::make_pair(true, false) }, + { QStringLiteral("autosecure"), std::make_pair(true, false) }, + { QStringLiteral("autotrust"), std::make_pair(false, false) }, + { QStringLiteral("automation"), std::make_pair(true, false) }, + { QStringLiteral("syncEnc"), std::make_pair(false, false) }, + { QStringLiteral("searchSmimeServers"), std::make_pair(false, false) }, + { QStringLiteral("autoimport"), std::make_pair(false, false) }, + { QStringLiteral("autoencryptUntrusted"), std::make_pair(false, false) }, + { QStringLiteral("draftEnc"), std::make_pair(false, false) }, + { QStringLiteral("alwaysShowApproval"), std::make_pair(false, false) }, }; -void GpgOLConfigPage::updateGUI(const QMap &values) +void GpgOLConfigPage::updateGUI(const QMap > &values) { - bool smimeEnabled = values["enableSmime"]; + bool smimeEnabled = values["enableSmime"].first; mSMIMEGrp->setChecked(smimeEnabled); - mPreferSMIMEChk->setChecked(values["preferSmime"]); - mSearchSMIMEChk->setChecked(values["searchSmimeServers"]); + mSMIMEGrp->setEnabled(!values["enableSmime"].second); + mPreferSMIMEChk->setChecked(values["preferSmime"].first); + mPreferSMIMEChk->setEnabled(!values["preferSmime"].second); + mSearchSMIMEChk->setChecked(values["searchSmimeServers"].first); + mSearchSMIMEChk->setEnabled(!values["searchSmimeServers"].second); mSearchSMIMEWarning->setVisible(mSearchSMIMEChk->isChecked()); - mAlwaysEncChk->setChecked(values["encryptDefault"]); - mAlwaysSigChk->setChecked(values["signDefault"]); - mInlinePGPChk->setChecked(values["inlinePGP"]); - mReplyCryptChk->setChecked(values["replyCrypt"]); - mDraftEncChk->setChecked(values["draftEnc"]); - mAlwaysShowApprovalChk->setChecked(values["alwaysShowApproval"]); - - mAutomationGrp->setChecked(values["automation"]); - mAutoSecureChk->setChecked(values["autosecure"]); - mAutoTrustChk->setChecked(values["autotrust"]); - mAutoResolveChk->setChecked(values["autoresolve"]); - mAutoImportChk->setChecked(values["autoimport"]); - mAutoEncryptUntrustedChk->setChecked(values["autoencryptUntrusted"]); - - mAutoSecureChk->setEnabled(mAutoResolveChk->isChecked() && mAutomationGrp->isChecked()); - mAutoEncryptUntrustedChk->setEnabled(mAutoSecureChk->isChecked() && mAutomationGrp->isChecked()); - mSearchSMIMEChk->setEnabled(mAutoResolveChk->isChecked() && smimeEnabled); + mAlwaysEncChk->setChecked(values["encryptDefault"].first); + mAlwaysEncChk->setEnabled(!values["encryptDefault"].second); + mAlwaysSigChk->setChecked(values["signDefault"].first); + mAlwaysSigChk->setEnabled(!values["signDefault"].second); + mInlinePGPChk->setChecked(values["inlinePGP"].first); + mInlinePGPChk->setEnabled(!values["inlinePGP"].second); + mReplyCryptChk->setChecked(values["replyCrypt"].first); + mReplyCryptChk->setEnabled(!values["replyCrypt"].second); + mDraftEncChk->setChecked(values["draftEnc"].first); + mDraftEncChk->setEnabled(!values["draftEnc"].second); + mAlwaysShowApprovalChk->setChecked(values["alwaysShowApproval"].first); + mAlwaysShowApprovalChk->setEnabled(!values["alwaysShowApproval"].second); + + mAutomationGrp->setChecked(values["automation"].first); + mAutomationGrp->setEnabled(!values["automation"].second); + mAutoSecureChk->setChecked(values["autosecure"].first); + mAutoSecureChk->setEnabled(!values["autosecure"].second); + mAutoTrustChk->setChecked(values["autotrust"].first); + mAutoTrustChk->setEnabled(!values["autotrust"].second); + mAutoResolveChk->setChecked(values["autoresolve"].first); + mAutoResolveChk->setEnabled(!values["autoresolve"].second); + mAutoImportChk->setChecked(values["autoimport"].first); + mAutoImportChk->setEnabled(!values["autoimport"].second); + mAutoEncryptUntrustedChk->setChecked(values["autoencryptUntrusted"].first); + mAutoEncryptUntrustedChk->setEnabled(!values["autoencryptUntrusted"].second); + + mAutoSecureChk->setEnabled(mAutoResolveChk->isChecked() && mAutomationGrp->isChecked() && !values["autosecure"].second); + mAutoEncryptUntrustedChk->setEnabled(mAutoSecureChk->isChecked() && mAutomationGrp->isChecked() && !values["autoencryptUntrusted"].second); + mSearchSMIMEChk->setEnabled(mAutoResolveChk->isChecked() && smimeEnabled && !values["enableSmime"].second); } void GpgOLConfigPage::load() { - QMap confValues; + QMap > confValues; for (const auto &key: defaultMap.keys()) { - confValues[key] = loadBool(key.toLocal8Bit().constData(), defaultMap[key]); + confValues[key] = loadBool(key.toLocal8Bit().constData(), defaultMap[key].first); } updateGUI(confValues); const std::string version = W32::readRegStr(nullptr, GPGOL_REG_PATH, "config-version"); if (version != CONFIG_VERSION) { qDebug() << "Config update. Cleaning old values"; } - const std::string draftKeyFpr = W32::readRegStr(nullptr, GPGOL_REG_PATH, "draftKey"); + std::string draftKeyFpr = W32::readRegStr(nullptr, GPGOL_REG_PATH, "draftKey"); if (!draftKeyFpr.empty()) { + if (draftKeyFpr.compare(draftKeyFpr.size(), draftKeyFpr.size(), "!")) + { + draftKeyFpr.pop_back(); + mDraftKey->setEnabled(false); + } mDraftKey->setDefaultKey(QString::fromStdString(draftKeyFpr)); } } void GpgOLConfigPage::defaults() { updateGUI(defaultMap); } static void saveBool(const char *name, bool value) { const char *val = value ? "1" : "0"; if (!W32::writeRegStr(nullptr, GPGOL_REG_PATH, name, val)) { qWarning() << "Failed to write registry value for" << name; } } void GpgOLConfigPage::save() const { saveBool("enableSmime", mSMIMEGrp->isChecked()); saveBool("preferSmime", mPreferSMIMEChk->isChecked()); saveBool("searchSmimeServers", mSearchSMIMEChk->isChecked()); saveBool("encryptDefault", mAlwaysEncChk->isChecked()); saveBool("signDefault", mAlwaysSigChk->isChecked()); saveBool("inlinePGP", mInlinePGPChk->isChecked()); saveBool("replyCrypt", mReplyCryptChk->isChecked()); saveBool("draftEnc", mDraftEncChk->isChecked()); saveBool("alwaysShowApproval", mAlwaysShowApprovalChk->isChecked()); saveBool("automation", mAutomationGrp->isChecked()); saveBool("autosecure", mAutoSecureChk->isChecked()); saveBool("autotrust", mAutoTrustChk->isChecked()); saveBool("autoresolve", mAutoResolveChk->isChecked()); saveBool("autoencryptUntrusted", mAutoEncryptUntrustedChk->isChecked()); saveBool("autoimport", mAutoImportChk->isChecked()); W32::writeRegStr(nullptr, GPGOL_REG_PATH, "config-version", CONFIG_VERSION); const auto key = mDraftKey->currentKey(); if (!key.isNull()) { W32::writeRegStr(nullptr, GPGOL_REG_PATH, "draftKey", key.primaryFingerprint()); } } #include "gpgolconfigpage.moc" diff --git a/src/gpgolconfig/gpgolconfigpage.h b/src/gpgolconfig/gpgolconfigpage.h index 9416295..87ac2bf 100644 --- a/src/gpgolconfig/gpgolconfigpage.h +++ b/src/gpgolconfig/gpgolconfigpage.h @@ -1,58 +1,58 @@ #ifndef GPGOLCONFIGPAGE_H #define GPGOLCONFIGPAGE_H /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include #include #include class QGroupBox; class QCheckBox; class QLabel; class ExplainingChkBox; namespace Kleo { class KeySelectionCombo; } // namespace Kleo class GpgOLConfigPage: public QWidget { Q_OBJECT public: explicit GpgOLConfigPage(QWidget *parent = nullptr); void save() const; void load(); void defaults(); protected: void setupGUI(); - void updateGUI(const QMap &values); + void updateGUI(const QMap > &values); private: QGroupBox *mSMIMEGrp, *mAutomationGrp; QLabel *mSearchSMIMEWarning; QCheckBox *mPreferSMIMEChk, *mAutoSecureChk, *mAlwaysEncChk, *mAlwaysSigChk, *mInlinePGPChk, *mAutoTrustChk, *mAutoResolveChk, *mReplyCryptChk, *mSearchSMIMEChk, *mAutoEncryptUntrustedChk, *mAutoImportChk, *mDraftEncChk, *mAlwaysShowApprovalChk; Kleo::KeySelectionCombo *mDraftKey; }; #endif diff --git a/src/gpgolconfig/gpgoldebugpage.cpp b/src/gpgolconfig/gpgoldebugpage.cpp index cb9900e..dbd0e84 100644 --- a/src/gpgolconfig/gpgoldebugpage.cpp +++ b/src/gpgolconfig/gpgoldebugpage.cpp @@ -1,277 +1,278 @@ /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "gpgoldebugpage.h" #include "w32-gettext.h" #include "w32-util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* See gpgol/src/debug.h */ #define DBG_OOM (1<<1) #define DBG_MEMORY (1<<2) #define DBG_TRACE (1<<3) #define DBG_DATA (1<<4) GpgOLDebugPage::GpgOLDebugPage(QWidget *parent): QWidget(parent) { setupGUI(); load(); } class VersionLoader: public QThread { Q_OBJECT void run() override { const auto vers = Kleo::gpg4winVersion(); const auto desc = Kleo::gpg4winDescription(); const auto longDesc = Kleo::gpg4winLongDescription(); emit resultReady(vers, desc, longDesc); deleteLater(); } signals: void resultReady(const QString &vers, const QString &desc, const QString &ldesc); }; void GpgOLDebugPage::setupGUI() { auto baseLay = new QVBoxLayout(this); // The debugging group mDbgGrp = new QGroupBox(_("Enable Logging")); mDbgGrp->setCheckable(true); mDbgCombo = new QComboBox; mDbgCombo->addItem(_("Default"), 1); mDbgCombo->addItem(_("+Outlook API calls"), (DBG_OOM)); mDbgCombo->addItem(_("+Memory analysis"), (DBG_OOM | DBG_MEMORY)); mDbgCombo->addItem(_("+Call tracing"), (DBG_OOM | DBG_MEMORY | DBG_TRACE)); mDbgVerboseWarningLabel = new QLabel(_("Warning: Decreased performance. Huge logs!")); mDbgComboLabel = new QLabel(_("Log level:")); mDbgLogFileLabel = new QLabel(_("Log File (required):")); mDbgDataChk = new QCheckBox(_("Include Mail contents (decrypted!) and meta information.")); mDbgLogFileName = new QLineEdit; auto dbgLay = new QVBoxLayout(mDbgGrp); auto logFileLay = new QHBoxLayout; mDbgLogFileBtn = new QPushButton; mDbgLogFileBtn->setIcon(style()->standardIcon(QStyle::SP_FileDialogStart)); logFileLay->addWidget(mDbgLogFileLabel, 0); logFileLay->addWidget(mDbgLogFileName, 1); logFileLay->addWidget(mDbgLogFileBtn, 0); dbgLay->addLayout(logFileLay); auto dbgComboLay = new QHBoxLayout; dbgLay->addLayout(dbgComboLay); dbgComboLay->addWidget(mDbgComboLabel); dbgComboLay->addWidget(mDbgCombo); dbgComboLay->addWidget(mDbgVerboseWarningLabel); dbgComboLay->addStretch(1); dbgLay->addWidget(mDbgDataChk); baseLay->addWidget(mDbgGrp); connect(mDbgGrp, &QGroupBox::toggled, [this] (bool) { enableDisableDbgWidgets(); }); connect(mDbgCombo, &QComboBox::currentTextChanged, [this] (QString) { mDbgVerboseWarningLabel->setVisible((mDbgCombo->currentData().toInt() & DBG_TRACE)); }); connect(mDbgLogFileBtn, &QPushButton::clicked, [this] () { const auto fileName = QFileDialog::getSaveFileName(this, _("Select log file"), mDbgLogFileName->text(), "(*.txt)"); if (!fileName.isEmpty()) { mDbgLogFileName->setText(QDir::toNativeSeparators(fileName)); } }); enableDisableDbgWidgets(); // End debugging group auto othersGrp = new QGroupBox(_("Potential workarounds")); mSyncEncChk = new QCheckBox (_("Block Outlook during encrypt / sign")); mSyncDecChk = new QCheckBox (_("Block Outlook during decrypt / verify")); auto othersLay = new QVBoxLayout(othersGrp); othersLay->addWidget(mSyncDecChk); othersLay->addWidget(mSyncEncChk); baseLay->addWidget(othersGrp); auto aboutGroup = new QGroupBox(); auto aboutLay = new QVBoxLayout(aboutGroup); auto descLabel = new QLabel; auto desc2Label = new QLabel; descLabel->setOpenExternalLinks(true); aboutLay->addWidget(descLabel); aboutLay->addWidget(desc2Label); aboutGroup->setVisible(false); baseLay->addWidget(aboutGroup); auto bugReportLabel = new QLabel(QStringLiteral("%1").arg(_("How to report a problem?"))); bugReportLabel->setOpenExternalLinks(true); baseLay->addWidget(bugReportLabel); baseLay->addStretch(1); auto loader = new VersionLoader; connect(loader, &VersionLoader::resultReady, this, [descLabel, desc2Label, aboutGroup] (const QString &version, const QString &desc, const QString &desc2) { if (version.isEmpty()) { return; } aboutGroup->setTitle(_("About GpgOL") + QLatin1Char(' ') + version); qDebug() << "Verified version" << version; descLabel->setText(desc); desc2Label->setText(desc2); aboutGroup->setVisible(true); }); loader->start(); } static bool loadBool(const char *name, bool defaultVal) { - return strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, name), defaultVal); + bool forced; + return strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, name), defaultVal, forced); } static const QMap defaultMap { { QStringLiteral("syncEnc"), false }, { QStringLiteral("syncDec"), false }, }; void GpgOLDebugPage::updateGUI(const QMap &values) { mSyncDecChk->setChecked(values["syncDec"]); mSyncEncChk->setChecked(values["syncEnc"]); } void GpgOLDebugPage::load() { QMap confValues; for (const auto &key: defaultMap.keys()) { confValues[key] = loadBool(key.toLocal8Bit().constData(), defaultMap[key]); } updateGUI(confValues); const auto logFile = W32::readRegStr(nullptr, GPGOL_REG_PATH, "logFile"); mDbgLogFileName->setText(logFile.empty() ? QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/gpgol.txt") : QString::fromStdString(logFile)); const auto logLevelS = W32::readRegStr(nullptr, GPGOL_REG_PATH, "enableDebug"); bool ok; int logLevel = QString::fromStdString(logLevelS).toInt(&ok); if (!ok) { logLevel = 0; } mDbgGrp->setChecked(logLevel > 0); int idx = 0; if ((logLevel & DBG_OOM)) { idx++; } if ((logLevel & DBG_MEMORY)) { idx++; } if ((logLevel & DBG_TRACE)) { idx++; } mDbgCombo->setCurrentIndex(idx); mDbgDataChk->setChecked((logLevel & DBG_DATA)); } void GpgOLDebugPage::defaults() { updateGUI(defaultMap); mDbgGrp->setChecked(false); } static void saveBool(const char *name, bool value) { const char *val = value ? "1" : "0"; if (!W32::writeRegStr(nullptr, GPGOL_REG_PATH, name, val)) { qWarning() << "Failed to write registry value for" << name; } } static void saveInt(const char *name, int value) { const std::string val = std::to_string(value); if (!W32::writeRegStr(nullptr, GPGOL_REG_PATH, name, val.c_str())) { qWarning() << "Failed to write registry value for" << name; } } void GpgOLDebugPage::save() const { saveBool("syncEnc", mSyncEncChk->isChecked()); saveBool("syncDec", mSyncDecChk->isChecked()); int logLevel = 0; if (mDbgGrp->isChecked()) { logLevel = mDbgCombo->currentData().toInt(); logLevel |= mDbgDataChk->isChecked() ? DBG_DATA : 0; } saveInt("enableDebug", logLevel); W32::writeRegStr(nullptr, GPGOL_REG_PATH, "logFile", QDir::toNativeSeparators( mDbgLogFileName->text()).toLocal8Bit().constData()); } void GpgOLDebugPage::enableDisableDbgWidgets() { bool vis = mDbgGrp->isChecked(); mDbgDataChk->setVisible(vis); mDbgCombo->setVisible(vis); mDbgComboLabel->setVisible(vis); mDbgLogFileName->setVisible(vis); mDbgLogFileLabel->setVisible(vis); mDbgLogFileBtn->setVisible(vis); mDbgVerboseWarningLabel->setVisible(vis && (mDbgCombo->currentData().toInt() & DBG_TRACE)); } #include "gpgoldebugpage.moc" diff --git a/src/util/w32-util.cpp b/src/util/w32-util.cpp index a1628e0..e79ff0e 100644 --- a/src/util/w32-util.cpp +++ b/src/util/w32-util.cpp @@ -1,305 +1,328 @@ /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include #include "w32-util.h" #include #ifdef _WIN32 # include #endif #include #include #define SLDIR "\\share\\locale" namespace W32 { std::string getGpg4winLocaleDir() { const auto instdir = getGpg4winDir(); if (instdir.empty()) { return std::string(); } return instdir + SLDIR; } std::string getGpg4winDir() { const auto tmp = readRegStr(nullptr, GPG4WIN_REGKEY_3, "Install Directory"); if (tmp.empty()) { return std::string(); } if (!access(tmp.c_str(), R_OK)) { return tmp; } else { fprintf (stderr, "Failed to access: %s\n", tmp.c_str()); } return std::string(); } /* Helper for read_w32_registry_string(). */ #ifdef _WIN32 static HKEY get_root_key(const char *root) { HKEY root_key; if( !root ) root_key = HKEY_CURRENT_USER; else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) ) root_key = HKEY_CLASSES_ROOT; else if( !strcmp( root, "HKEY_CURRENT_USER" ) ) root_key = HKEY_CURRENT_USER; else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) ) root_key = HKEY_LOCAL_MACHINE; else if( !strcmp( root, "HKEY_USERS" ) ) root_key = HKEY_USERS; else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) ) root_key = HKEY_PERFORMANCE_DATA; else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) ) root_key = HKEY_CURRENT_CONFIG; else return nullptr; return root_key; } #endif #if defined(_WIN64) #define CROSS_ACCESS KEY_WOW64_32KEY #else #define CROSS_ACCESS KEY_WOW64_64KEY #endif #ifdef _WIN32 std::string _readRegStr (HKEY root_key, const char *dir, const char *name, bool alternate) { HKEY key_handle; DWORD n1, nbytes, type; std::string ret; DWORD flags = KEY_READ; if (alternate) { flags |= CROSS_ACCESS; } if (RegOpenKeyExA(root_key, dir, 0, flags, &key_handle)) { return ret; } nbytes = 1; if (RegQueryValueExA(key_handle, name, 0, nullptr, nullptr, &nbytes)) { RegCloseKey (key_handle); return ret; } n1 = nbytes+1; char result[n1]; if (RegQueryValueExA(key_handle, name, 0, &type, (LPBYTE)result, &n1)) { RegCloseKey(key_handle); return ret; } RegCloseKey(key_handle); result[nbytes] = 0; /* make sure it is really a string */ ret = result; if (type == REG_EXPAND_SZ && strchr (result, '%')) { n1 += 1000; char tmp[n1 +1]; nbytes = ExpandEnvironmentStringsA(ret.c_str(), tmp, n1); if (nbytes && nbytes > n1) { n1 = nbytes; char tmp2[n1 +1]; nbytes = ExpandEnvironmentStringsA(result, tmp2, n1); if (nbytes && nbytes > n1) { /* oops - truncated, better don't expand at all */ return ret; } tmp2[nbytes] = 0; ret = tmp2; } else if (nbytes) { /* okay, reduce the length */ tmp[nbytes] = 0; ret = tmp; } } return ret; } #endif std::string readRegStr (const char *root, const char *dir, const char *name) { #ifndef _WIN32 (void)root; (void)dir; (void)name; return std::string(); #else HKEY root_key; std::string ret; if (!(root_key = get_root_key(root))) { return ret; } + + if (root == nullptr) + { + /* Nullptr so we first look into HKLM if we have + an override */ + ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, false); + if (ret.empty()) { + // Try alternate as fallback + ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, true); + } + if (ret.size() && ret[ret.size() - 1] == '!') + { + // Using override reg value + qDebug() << "Using override for %s" << name; + return ret; + } + } ret = _readRegStr (root_key, dir, name, false); if (ret.empty()) { // Try local machine as fallback. qDebug() << "Fallback to HKLM for" << dir << name; ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, false); if (ret.empty()) { // Try alternative registry view as fallback qDebug() << "Fallback to HKLM alternative for" << dir << name; ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, true); } } qDebug() << "Returning:" << (ret.empty() ? "empty" : ret.c_str()); return ret; #endif } bool writeRegStr(const char *root, const char *path, const char *key, const char *val) { #ifndef _WIN32 (void) root; (void) path; (void) key; (void) val; return false; #else HKEY h, hk; int type; int ec; hk = get_root_key (root); if (!hk) { fprintf(stderr, "Failed to find root key.\n"); } DWORD flags = KEY_ALL_ACCESS; ec = RegCreateKeyExA(hk, path, 0, NULL, REG_OPTION_NON_VOLATILE, flags, NULL, &h, NULL); if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating/opening registry key `%s' failed\n", path); return false; } type = strchr (val, '%')? REG_EXPAND_SZ : REG_SZ; ec = RegSetValueExA(h, key, 0, type, (const BYTE*)val, strlen (val)); if (ec != ERROR_SUCCESS) { fprintf (stderr, "saving registry key `%s'->`%s' failed\n", path, key); RegCloseKey(h); return false; } RegCloseKey(h); return true; #endif } #ifdef _WIN32 static int has_high_integrity(HANDLE hToken) { PTOKEN_MANDATORY_LABEL integrity_label = NULL; DWORD integrity_level = 0, size = 0; if (hToken == NULL || hToken == INVALID_HANDLE_VALUE) { return 0; } /* Get the required size */ if (!GetTokenInformation (hToken, TokenIntegrityLevel, NULL, 0, &size)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return 0; } } integrity_label = (PTOKEN_MANDATORY_LABEL) LocalAlloc(0, size); if (integrity_label == NULL) { return 0; } if (!GetTokenInformation (hToken, TokenIntegrityLevel, integrity_label, size, &size)) { LocalFree(integrity_label); return 0; } /* Get the last integrity level */ integrity_level = *GetSidSubAuthority(integrity_label->Label.Sid, (DWORD)(UCHAR)(*GetSidSubAuthorityCount( integrity_label->Label.Sid) - 1)); LocalFree (integrity_label); return integrity_level >= SECURITY_MANDATORY_HIGH_RID; } #endif bool isElevated() { #ifdef _WIN32 int ret = 0; HANDLE hToken = NULL; if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)) { DWORD elevation; DWORD cbSize = sizeof (DWORD); if (GetTokenInformation (hToken, TokenElevation, &elevation, sizeof (TokenElevation), &cbSize)) { ret = elevation; } } /* Elevation will be true and ElevationType TokenElevationTypeFull even if the token is a user token created by SAFER so we additionally check the integrity level of the token which will only be high in the real elevated process and medium otherwise. */ ret = ret && has_high_integrity (hToken); if (hToken) CloseHandle (hToken); return ret; #else return false; #endif } }// namespace W32 -bool strToBool(const std::string &str, bool defaultVal) +bool strToBool(const std::string &str, bool defaultVal, bool &forced) { + forced = false; if (str.empty()) { return defaultVal; } - if (str == "1") { + if (str.size() && str[str.size() - 1] == '!') + { + forced = true; + } + + if (str[0] == '1') { return true; } - if (str == "0") { + if (str[0] == '0') { return false; } qDebug() << "Unknown bool val" << str.c_str(); return defaultVal; } diff --git a/src/util/w32-util.h b/src/util/w32-util.h index 1fbfa64..c84078e 100644 --- a/src/util/w32-util.h +++ b/src/util/w32-util.h @@ -1,59 +1,60 @@ /* Copyright (C) 2018 by Intevation GmbH * * This file is free software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include /* The Registry key used by Gpg4win. */ #ifdef _WIN64 # define GPG4WIN_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG" #else # define GPG4WIN_REGKEY_2 "Software\\GNU\\GnuPG" #endif #ifdef _WIN64 # define GPG4WIN_REGKEY_3 "Software\\Wow6432Node\\Gpg4win" #else # define GPG4WIN_REGKEY_3 "Software\\Gpg4win" #endif #define GPGOL_REG_PATH "Software\\GNU\\GpgOL" namespace W32 { /* Get the locale dir of Gpg4win. */ std::string getGpg4winLocaleDir(); /** Get the Gpg4win Install directory. * * Looks for the Gpg4win 3.x registry key. * And checks that the directory can be read. * * @returns an empty string if no dir could be found. * **/ std::string getGpg4winDir(); /** Read a registry string value. If root is null first * HKEY_CURRENT_USER is searched and then it falls back * to HKEY_LOCAL_MACHINE . */ std::string readRegStr(const char *root, const char *path, const char *key); bool writeRegStr(const char *root, const char *path, const char *key, const char *val); /** Call this to switch to the W64 registry. */ void setW64RegistryMode(bool value); bool isElevated(); } // namespace W32 -/** Helper to convert a string 0 / 1 to a bool with a default. */ -bool strToBool(const std::string &str, bool defaultVal = false); +/** Helper to convert a string 0 / 1 to a bool with a default. + * If the value ends with ! the parameter forced is set to true*/ +bool strToBool(const std::string &str, bool defaultVal, bool &forced);