diff --git a/src/kleo/keyresolver.h b/src/kleo/keyresolver.h index c4aaac11c..ceb656f8c 100644 --- a/src/kleo/keyresolver.h +++ b/src/kleo/keyresolver.h @@ -1,255 +1,255 @@ /* -*- c++ -*- keyresolver.h This file is part of libkleopatra, the KDE keymanagement library Copyright (c) 2018 Intevation GmbH Libkleopatra 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 2 of the License, or (at your option) any later version. Libkleopatra 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KLEO_KEYRESOLVER_H__ #define __KLEO_KEYRESOLVER_H__ #include "kleo_export.h" #include #include #include #include #include #include #include class QStringList; namespace Kleo { /** * Class to find Keys for E-Mail encryption. * * The KeyResolver uses the Keycache to find keys for signing * or encryption. * * Overrides can be provided for address book integration if the * format is not Auto overrides will only be respected if they * match the format provided in the constructor. * * If no override key is provided for an address the key * with a uid that matches the address and has the highest * validity is used. If both keys have the same validity * the newest subkey is used. * * The KeyResolver also supports groups so the number of * encryption keys / hidden encryption keys does not necessarily * need to match the amount of sender addresses. For this reason * maps are used heavily to map: * * CryptoFormat * - Addresses * -- For each Address a List of Keys * * As a caller you should iterate over the CryptoFormats and * send one message for each format for the recipients and signed * by the signing keys. * - * If the CryptoMessageFormat is Auto the minmum number + * If the CryptoMessageFormat is Auto the minimum number * of CryptoMessageFormats is returned that respects all overrides. * * ----- * Planned: * * As the central place to manage mail encryption / signing keys * the Keyresolver will also show various warning / nagging messages * and offer solutions if nagging is not explicitly turned off. * These include: * * - If own keys or subkeys are about to expire: * Offer to extend their expiration date. * * - (S/MIME) If they are about to expire: Offer * to generate a new CSR. * * - If a user has not marked a key as backed up and * uses it for encryption for several mails. * * - If a user has multiple keys for a sender address and they * are not cross signed. Offer to cross sign / publish. */ class KLEO_EXPORT KeyResolver : public QObject { Q_OBJECT public: /** Creates a new key resolver object. * * @param encrypt: Should encryption keys be selected. * @param sign: Should signing keys be selected. * @param format: A specific format for selection. Default Auto. * @param allowMixed: Specify if multiple message formats may be resolved. **/ explicit KeyResolver(bool encrypt, bool sign, CryptoMessageFormat format = AutoFormat, bool allowMixed = true); ~KeyResolver() {} /** * Set the list of (To/CC) recipient addresses. Also looks * up possible keys, but doesn't interact with the user. * * @param addresses: A list of unnormalized addresses */ void setRecipients(const QStringList &addresses); /** * Set the senders address. * * Sender address will be added to encryption keys and used * for signing key resolution if the signing keys are not * explicitly set through setSigningKeys. * * @param sender: The sender of this message. */ void setSender(const QString &sender); /** * Set the list of hidden (BCC) recipient addresses. Also looks * up possible keys, but doesn't interact with the user. * * @param addresses: A list of unnormalized addresses. */ void setHiddenRecipients(const QStringList &addresses); /** * Set up possible override keys for recipients / sender * addresses. The keys for the fingerprints are looked * up and used when found. Does not interact with the user. * * @param overrides: A map of \ -> (\ \) */ void setOverrideKeys(const QMap > &overrides); /** * Set explicit signing keys. If this was set for a * protocol the sender address will be only used as an additional encryption * recipient for that protocol. */ void setSigningKeys(const QStringList &fingerprints); /** * Turn Nagging messages off or on, default on. * See class description about nagging. * * @param value: Turn nagging on or off. */ void enableNagging(bool value); /** * Set the minimum user id validity for autoresolution. * * The default value is marginal * * @param validity int representation of a GpgME::UserID::Validity. */ void setMinimumValidity(int validity); /** * Get the encryption keys after resolution. * * @return the resolved sender / key pairs for encryption by format. */ QMap > > encryptionKeys() const; /** * Get the secondary encryption keys after resolution. * The Map will only contain values if hidden recipients * were set. * * @return the resolved resolved sender / key pairs for encryption * by format. */ QMap > > hiddenKeys() const; /** * Get the signing keys to use after resolution. * * @return the resolved resolved sender / key pairs for signing * by format. */ QMap > signingKeys() const; /** * Starts the key resolving procedure. Emits keysResolved on success or * error. * * @param showApproval: If set to true a dialog listing the keys * will always be shown. * @param parentWidget: Optional, a Widget to use as parent for dialogs. */ void start(bool showApproval, QWidget *parentWidget = nullptr); /** * Access possibly updated Override Keys * * @return A map of email's with new overrides and the according * cryptoformat / fingerprint. Should be saved somehow. */ QMap > overrideKeys() const; /** * Set window flags for a possible dialog. */ void setDialogWindowFlags(Qt::WindowFlags flags); /** * Set the protocol that is preferred to be displayed first when * it is not clear from the keys. E.g. if both OpenPGP and S/MIME * can be resolved. */ void setPreferredProtocol(GpgME::Protocol proto); Q_SIGNALS: /** * Emitted when key resolution finished. * * @param success: The general result. If true continue sending, * if false abort. * @param sendUnencrypted: If there could be no key found for one of * the recipients the user was queried if the * mail should be sent out unencrypted. * sendUnencrypted is true if the user agreed * to this.*/ void keysResolved(bool success, bool sendUnencrypted); private: class Private; friend class Private; std::shared_ptr d; }; } // namespace Kleo #endif // __KLEO_KEYRESOLVER_H__ diff --git a/src/ui/cryptoconfigmodule.cpp b/src/ui/cryptoconfigmodule.cpp index 11c86cd34..1492b9054 100644 --- a/src/ui/cryptoconfigmodule.cpp +++ b/src/ui/cryptoconfigmodule.cpp @@ -1,978 +1,978 @@ /* cryptoconfigmodule.cpp This file is part of kgpgcertmanager Copyright (c) 2004 Klar�vdalens Datakonsult AB Libkleopatra 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 2 of the License, or (at your option) any later version. Libkleopatra 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "cryptoconfigmodule.h" #include "cryptoconfigmodule_p.h" #include "directoryserviceswidget.h" #include "kdhorizontalline.h" #include "filenamerequester.h" #include #include #include #include "kleo_ui_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; namespace { class ScrollArea : public QScrollArea { public: explicit ScrollArea(QWidget *p) : QScrollArea(p) {} QSize sizeHint() const override { const QSize wsz = widget() ? widget()->sizeHint() : QSize(); return QSize(wsz.width() + style()->pixelMetric(QStyle::PM_ScrollBarExtent), QScrollArea::sizeHint().height()); } }; } inline QIcon loadIcon(const QString &s) { QString ss = s; return QIcon::fromTheme(ss.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QStringLiteral("-"))); } static unsigned int num_components_with_options(const QGpgME::CryptoConfig *config) { if (!config) { return 0; } const QStringList components = config->componentList(); unsigned int result = 0; for (QStringList::const_iterator it = components.begin(); it != components.end(); ++it) if (const QGpgME::CryptoConfigComponent *const comp = config->component(*it)) if (!comp->groupList().empty()) { ++result; } return result; } static KPageView::FaceType determineJanusFace(const QGpgME::CryptoConfig *config, Kleo::CryptoConfigModule::Layout layout, bool &ok) { ok = true; if (num_components_with_options(config) < 2) { ok = false; return KPageView::Plain; } return layout == CryptoConfigModule::LinearizedLayout ? KPageView::Plain : layout == CryptoConfigModule::TabbedLayout ? KPageView::Tabbed : /* else */ KPageView::List; } Kleo::CryptoConfigModule::CryptoConfigModule(QGpgME::CryptoConfig *config, QWidget *parent) : KPageWidget(parent), mConfig(config) { init(IconListLayout); } Kleo::CryptoConfigModule::CryptoConfigModule(QGpgME::CryptoConfig *config, Layout layout, QWidget *parent) : KPageWidget(parent), mConfig(config) { init(layout); } void Kleo::CryptoConfigModule::init(Layout layout) { if (QLayout *l = this->layout()) { l->setContentsMargins(0, 0, 0, 0); } QGpgME::CryptoConfig *const config = mConfig; bool configOK = false; const KPageView::FaceType type = determineJanusFace(config, layout, configOK); setFaceType(type); QVBoxLayout *vlay = nullptr; QWidget *vbox = nullptr; if (type == Plain) { QWidget *w = new QWidget(this); QVBoxLayout *l = new QVBoxLayout(w); l->setContentsMargins(0, 0, 0, 0); QScrollArea *s = new QScrollArea(w); s->setFrameStyle(QFrame::NoFrame); s->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); s->setWidgetResizable(true); l->addWidget(s); vbox = new QWidget(s->viewport()); vlay = new QVBoxLayout(vbox); vlay->setContentsMargins(0, 0, 0, 0); s->setWidget(vbox); addPage(w, configOK ? QString() : i18n("GpgConf Error")); } const QStringList components = config->componentList(); for (QStringList::const_iterator it = components.begin(); it != components.end(); ++it) { //qCDebug(KLEO_UI_LOG) <<"Component" << (*it).toLocal8Bit() <<":"; QGpgME::CryptoConfigComponent *comp = config->component(*it); Q_ASSERT(comp); if (comp->groupList().empty()) { continue; } std::unique_ptr compGUI(new CryptoConfigComponentGUI(this, comp)); compGUI->setObjectName(*it); // KJanusWidget doesn't seem to have iterators, so we store a copy... mComponentGUIs.append(compGUI.get()); if (type == Plain) { QGroupBox *gb = new QGroupBox(comp->description(), vbox); (new QVBoxLayout(gb))->addWidget(compGUI.release()); vlay->addWidget(gb); } else { vbox = new QWidget(this); vlay = new QVBoxLayout(vbox); vlay->setContentsMargins(0, 0, 0, 0); KPageWidgetItem *pageItem = new KPageWidgetItem(vbox, comp->description()); if (type != Tabbed) { pageItem->setIcon(loadIcon(comp->iconName())); } addPage(pageItem); QScrollArea *scrollArea = type == Tabbed ? new QScrollArea(vbox) : new ScrollArea(vbox); scrollArea->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); scrollArea->setWidgetResizable(true); vlay->addWidget(scrollArea); const QSize compGUISize = compGUI->sizeHint(); scrollArea->setWidget(compGUI.release()); // Set a nice startup size const int deskHeight = QApplication::desktop()->height(); int dialogHeight; if (deskHeight > 1000) { // very big desktop ? dialogHeight = 800; } else if (deskHeight > 650) { // big desktop ? dialogHeight = 500; } else { // small (800x600, 640x480) desktop dialogHeight = 400; } Q_ASSERT(scrollArea->widget()); if (type != Tabbed) { scrollArea->setMinimumHeight(qMin(compGUISize.height(), dialogHeight)); } } } if (mComponentGUIs.empty()) { const QString msg = i18n("The gpgconf tool used to provide the information " "for this dialog does not seem to be installed " "properly. It did not return any components. " "Try running \"%1\" on the command line for more " "information.", components.empty() ? QLatin1String("gpgconf --list-components") : QLatin1String("gpgconf --list-options gpg")); QLabel *label = new QLabel(msg, vbox); label->setWordWrap(true); label->setMinimumHeight(fontMetrics().lineSpacing() * 5); vlay->addWidget(label); } } bool Kleo::CryptoConfigModule::hasError() const { return mComponentGUIs.empty(); } void Kleo::CryptoConfigModule::save() { bool changed = false; QList::Iterator it = mComponentGUIs.begin(); for (; it != mComponentGUIs.end(); ++it) { if ((*it)->save()) { changed = true; } } if (changed) { mConfig->sync(true /*runtime*/); } } void Kleo::CryptoConfigModule::reset() { QList::Iterator it = mComponentGUIs.begin(); for (; it != mComponentGUIs.end(); ++it) { (*it)->load(); } } void Kleo::CryptoConfigModule::defaults() { QList::Iterator it = mComponentGUIs.begin(); for (; it != mComponentGUIs.end(); ++it) { (*it)->defaults(); } } void Kleo::CryptoConfigModule::cancel() { mConfig->clear(); } //// Kleo::CryptoConfigComponentGUI::CryptoConfigComponentGUI( CryptoConfigModule *module, QGpgME::CryptoConfigComponent *component, QWidget *parent) : QWidget(parent), mComponent(component) { QGridLayout *glay = new QGridLayout(this); const QStringList groups = mComponent->groupList(); if (groups.size() > 1) { glay->setColumnMinimumWidth(0, KDHorizontalLine::indentHint()); for (QStringList::const_iterator it = groups.begin(), end = groups.end(); it != end; ++it) { QGpgME::CryptoConfigGroup *group = mComponent->group(*it); Q_ASSERT(group); if (!group) { continue; } const QString title = group->description(); KDHorizontalLine *hl = new KDHorizontalLine(title.isEmpty() ? *it : title, this); const int row = glay->rowCount(); glay->addWidget(hl, row, 0, 1, 3); mGroupGUIs.append(new CryptoConfigGroupGUI(module, group, glay, this)); } } else if (!groups.empty()) { mGroupGUIs.append(new CryptoConfigGroupGUI(module, mComponent->group(groups.front()), glay, this)); } glay->setRowStretch(glay->rowCount(), 1); } bool Kleo::CryptoConfigComponentGUI::save() { bool changed = false; QList::Iterator it = mGroupGUIs.begin(); for (; it != mGroupGUIs.end(); ++it) { if ((*it)->save()) { changed = true; } } return changed; } void Kleo::CryptoConfigComponentGUI::load() { QList::Iterator it = mGroupGUIs.begin(); for (; it != mGroupGUIs.end(); ++it) { (*it)->load(); } } void Kleo::CryptoConfigComponentGUI::defaults() { QList::Iterator it = mGroupGUIs.begin(); for (; it != mGroupGUIs.end(); ++it) { (*it)->defaults(); } } //// Kleo::CryptoConfigGroupGUI::CryptoConfigGroupGUI( CryptoConfigModule *module, QGpgME::CryptoConfigGroup *group, QGridLayout *glay, QWidget *widget) : QObject(module), mGroup(group) { const bool de_vs = Kleo::Formatting::complianceMode() == QLatin1String("de-vs"); const int startRow = glay->rowCount(); const QStringList entries = mGroup->entryList(); for (QStringList::const_iterator it = entries.begin(), end = entries.end(); it != end; ++it) { QGpgME::CryptoConfigEntry *entry = group->entry(*it); Q_ASSERT(entry); /* Skip "dangerous" options if we are running in CO_DE_VS. */ if (de_vs && entry->level() > QGpgME::CryptoConfigEntry::Level_Advanced) { qCDebug(KLEO_UI_LOG) << "entry" << *it << "too advanced, skipping"; continue; } CryptoConfigEntryGUI *entryGUI = CryptoConfigEntryGUIFactory::createEntryGUI(module, entry, *it, glay, widget); if (entryGUI) { mEntryGUIs.append(entryGUI); entryGUI->load(); } } const int endRow = glay->rowCount() - 1; if (endRow < startRow) { return; } const QString iconName = group->iconName(); if (iconName.isEmpty()) { return; } QLabel *l = new QLabel(widget); l->setPixmap(loadIcon(iconName).pixmap(32, 32)); glay->addWidget(l, startRow, 0, endRow - startRow + 1, 1, Qt::AlignTop); } bool Kleo::CryptoConfigGroupGUI::save() { bool changed = false; QList::Iterator it = mEntryGUIs.begin(); for (; it != mEntryGUIs.end(); ++it) { if ((*it)->isChanged()) { (*it)->save(); changed = true; } } return changed; } void Kleo::CryptoConfigGroupGUI::load() { QList::Iterator it = mEntryGUIs.begin(); for (; it != mEntryGUIs.end(); ++it) { (*it)->load(); } } void Kleo::CryptoConfigGroupGUI::defaults() { QList::Iterator it = mEntryGUIs.begin(); for (; it != mEntryGUIs.end(); ++it) { (*it)->resetToDefault(); } } //// typedef CryptoConfigEntryGUI *(*constructor)(CryptoConfigModule *, QGpgME::CryptoConfigEntry *, const QString &, QGridLayout *, QWidget *); namespace { template CryptoConfigEntryGUI *_create(CryptoConfigModule *m, QGpgME::CryptoConfigEntry *e, const QString &n, QGridLayout *l, QWidget *p) { return new T_Widget(m, e, n, l, p); } } static const struct WidgetsByEntryName { const char *entryGlob; constructor create; } widgetsByEntryName[] = { { "*/*/debug-level", &_create }, { "gpg/*/keyserver", &_create } }; static const unsigned int numWidgetsByEntryName = sizeof widgetsByEntryName / sizeof * widgetsByEntryName; static const constructor listWidgets[QGpgME::CryptoConfigEntry::NumArgType] = { // None: A list of options with no arguments (e.g. -v -v -v) is shown as a spinbox &_create, nullptr, // String // Int/UInt: Let people type list of numbers (1,2,3....). Untested. &_create, &_create, nullptr, // Path nullptr, // Formerly URL &_create, nullptr, // DirPath }; static const constructor scalarWidgets[QGpgME::CryptoConfigEntry::NumArgType] = { &_create, // None &_create, // String &_create, // Int &_create, // UInt &_create, // Path nullptr, // Formerly URL nullptr, // LDAPURL &_create, // DirPath }; CryptoConfigEntryGUI *Kleo::CryptoConfigEntryGUIFactory::createEntryGUI(CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) { Q_ASSERT(entry); // try to lookup by path: const QString path = entry->path(); for (unsigned int i = 0; i < numWidgetsByEntryName; ++i) if (QRegExp(QLatin1String(widgetsByEntryName[i].entryGlob), Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(path)) { return widgetsByEntryName[i].create(module, entry, entryName, glay, widget); } // none found, so look up by type: const unsigned int argType = entry->argType(); Q_ASSERT(argType < QGpgME::CryptoConfigEntry::NumArgType); if (entry->isList()) if (const constructor create = listWidgets[argType]) { return create(module, entry, entryName, glay, widget); } else { qCWarning(KLEO_UI_LOG) << "No widget implemented for list of type" << entry->argType(); } else if (const constructor create = scalarWidgets[argType]) { return create(module, entry, entryName, glay, widget); } else { qCWarning(KLEO_UI_LOG) << "No widget implemented for type" << entry->argType(); } return nullptr; } //// Kleo::CryptoConfigEntryGUI::CryptoConfigEntryGUI( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName) : QObject(module), mEntry(entry), mName(entryName), mChanged(false) { connect(this, &CryptoConfigEntryGUI::changed, module, &CryptoConfigModule::changed); } QString Kleo::CryptoConfigEntryGUI::description() const { QString descr = mEntry->description(); if (descr.isEmpty()) { // happens for expert options // String does not need to be translated because the options itself // are also not translated return QStringLiteral("\"%1\"").arg(mName); } if (i18nc("Translate this to 'yes' or 'no' (use the English words!) " "depending on whether your language uses " - "Sentence style capitalisation in GUI labels (yes) or not (no). " + "Sentence style capitalization in GUI labels (yes) or not (no). " "Context: We get some backend strings in that have the wrong " - "capitalizaion (in English, at least) so we need to force the " + "capitalization (in English, at least) so we need to force the " "first character to upper-case. It is this behaviour you can " "control for your language with this translation.", "yes") == QLatin1String("yes")) { descr[0] = descr[0].toUpper(); } return descr; } void Kleo::CryptoConfigEntryGUI::resetToDefault() { mEntry->resetToDefault(); load(); } //// Kleo::CryptoConfigEntryLineEdit::CryptoConfigEntryLineEdit( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName) { const int row = glay->rowCount(); mLineEdit = new KLineEdit(widget); QLabel *label = new QLabel(description(), widget); label->setBuddy(mLineEdit); glay->addWidget(label, row, 1); glay->addWidget(mLineEdit, row, 2); if (entry->isReadOnly()) { label->setEnabled(false); mLineEdit->setEnabled(false); } else { connect(mLineEdit, &KLineEdit::textChanged, this, &CryptoConfigEntryLineEdit::slotChanged); } } void Kleo::CryptoConfigEntryLineEdit::doSave() { mEntry->setStringValue(mLineEdit->text()); } void Kleo::CryptoConfigEntryLineEdit::doLoad() { mLineEdit->setText(mEntry->stringValue()); } //// static const struct { const char *label; const char *name; } debugLevels[] = { { I18N_NOOP("0 - None"), "none"}, { I18N_NOOP("1 - Basic"), "basic"}, { I18N_NOOP("2 - Verbose"), "advanced"}, { I18N_NOOP("3 - More Verbose"), "expert"}, { I18N_NOOP("4 - All"), "guru"}, }; static const unsigned int numDebugLevels = sizeof debugLevels / sizeof * debugLevels; Kleo::CryptoConfigEntryDebugLevel::CryptoConfigEntryDebugLevel(CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName), mComboBox(new QComboBox(widget)) { QLabel *label = new QLabel(i18n("Set the debugging level to"), widget); label->setBuddy(mComboBox); for (unsigned int i = 0; i < numDebugLevels; ++i) { mComboBox->addItem(i18n(debugLevels[i].label)); } if (entry->isReadOnly()) { label->setEnabled(false); mComboBox->setEnabled(false); } else { connect(mComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &CryptoConfigEntryDebugLevel::slotChanged); } const int row = glay->rowCount(); glay->addWidget(label, row, 1); glay->addWidget(mComboBox, row, 2); } void Kleo::CryptoConfigEntryDebugLevel::doSave() { const unsigned int idx = mComboBox->currentIndex(); if (idx < numDebugLevels) { mEntry->setStringValue(QLatin1String(debugLevels[idx].name)); } else { mEntry->setStringValue(QString()); } } void Kleo::CryptoConfigEntryDebugLevel::doLoad() { const QString str = mEntry->stringValue(); for (unsigned int i = 0; i < numDebugLevels; ++i) if (str == QLatin1String(debugLevels[i].name)) { mComboBox->setCurrentIndex(i); return; } mComboBox->setCurrentIndex(0); } //// Kleo::CryptoConfigEntryPath::CryptoConfigEntryPath( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName), mFileNameRequester(nullptr) { const int row = glay->rowCount(); mFileNameRequester = new FileNameRequester(widget); mFileNameRequester->setExistingOnly(false); mFileNameRequester->setFilter(QDir::Files); QLabel *label = new QLabel(description(), widget); label->setBuddy(mFileNameRequester); glay->addWidget(label, row, 1); glay->addWidget(mFileNameRequester, row, 2); if (entry->isReadOnly()) { label->setEnabled(false); mFileNameRequester->setEnabled(false); } else { connect(mFileNameRequester, &FileNameRequester::fileNameChanged, this, &CryptoConfigEntryPath::slotChanged); } } void Kleo::CryptoConfigEntryPath::doSave() { mEntry->setURLValue(QUrl::fromLocalFile(mFileNameRequester->fileName())); } void Kleo::CryptoConfigEntryPath::doLoad() { if (mEntry->urlValue().isLocalFile()) { mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile()); } else { mFileNameRequester->setFileName(mEntry->urlValue().toString()); } } //// Kleo::CryptoConfigEntryDirPath::CryptoConfigEntryDirPath( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName), mFileNameRequester(nullptr) { const int row = glay->rowCount(); mFileNameRequester = new FileNameRequester(widget); mFileNameRequester->setExistingOnly(false); mFileNameRequester->setFilter(QDir::Dirs); QLabel *label = new QLabel(description(), widget); label->setBuddy(mFileNameRequester); glay->addWidget(label, row, 1); glay->addWidget(mFileNameRequester, row, 2); if (entry->isReadOnly()) { label->setEnabled(false); mFileNameRequester->setEnabled(false); } else { connect(mFileNameRequester, &FileNameRequester::fileNameChanged, this, &CryptoConfigEntryDirPath::slotChanged); } } void Kleo::CryptoConfigEntryDirPath::doSave() { mEntry->setURLValue(QUrl::fromLocalFile(mFileNameRequester->fileName())); } void Kleo::CryptoConfigEntryDirPath::doLoad() { mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile()); } //// Kleo::CryptoConfigEntrySpinBox::CryptoConfigEntrySpinBox( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName) { if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_None && entry->isList()) { mKind = ListOfNone; } else if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_UInt) { mKind = UInt; } else { Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int); mKind = Int; } const int row = glay->rowCount(); mNumInput = new QSpinBox(widget); QLabel *label = new QLabel(description(), widget); label->setBuddy(mNumInput); glay->addWidget(label, row, 1); glay->addWidget(mNumInput, row, 2); if (entry->isReadOnly()) { label->setEnabled(false); mNumInput->setEnabled(false); } else { mNumInput->setMinimum(mKind == Int ? std::numeric_limits::min() : 0); mNumInput->setMaximum(std::numeric_limits::max()); connect(mNumInput, QOverload::of(&QSpinBox::valueChanged), this, &CryptoConfigEntrySpinBox::slotChanged); } } void Kleo::CryptoConfigEntrySpinBox::doSave() { int value = mNumInput->value(); switch (mKind) { case ListOfNone: mEntry->setNumberOfTimesSet(value); break; case UInt: mEntry->setUIntValue(value); break; case Int: mEntry->setIntValue(value); break; } } void Kleo::CryptoConfigEntrySpinBox::doLoad() { int value = 0; switch (mKind) { case ListOfNone: value = mEntry->numberOfTimesSet(); break; case UInt: value = mEntry->uintValue(); break; case Int: value = mEntry->intValue(); break; } mNumInput->setValue(value); } //// Kleo::CryptoConfigEntryCheckBox::CryptoConfigEntryCheckBox( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName) { const int row = glay->rowCount(); mCheckBox = new QCheckBox(widget); glay->addWidget(mCheckBox, row, 1, 1, 2); mCheckBox->setText(description()); if (entry->isReadOnly()) { mCheckBox->setEnabled(false); } else { connect(mCheckBox, &QCheckBox::toggled, this, &CryptoConfigEntryCheckBox::slotChanged); } } void Kleo::CryptoConfigEntryCheckBox::doSave() { mEntry->setBoolValue(mCheckBox->isChecked()); } void Kleo::CryptoConfigEntryCheckBox::doLoad() { mCheckBox->setChecked(mEntry->boolValue()); } Kleo::CryptoConfigEntryLDAPURL::CryptoConfigEntryLDAPURL( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName) { mLabel = new QLabel(widget); mPushButton = new QPushButton(entry->isReadOnly() ? i18n("Show...") : i18n("Edit..."), widget); const int row = glay->rowCount(); QLabel *label = new QLabel(description(), widget); label->setBuddy(mPushButton); glay->addWidget(label, row, 1); QHBoxLayout *hlay = new QHBoxLayout; glay->addLayout(hlay, row, 2); hlay->addWidget(mLabel, 1); hlay->addWidget(mPushButton); if (entry->isReadOnly()) { mLabel->setEnabled(false); } connect(mPushButton, &QPushButton::clicked, this, &CryptoConfigEntryLDAPURL::slotOpenDialog); } void Kleo::CryptoConfigEntryLDAPURL::doLoad() { setURLList(mEntry->urlValueList()); } void Kleo::CryptoConfigEntryLDAPURL::doSave() { mEntry->setURLValueList(mURLList); } void prepareURLCfgDialog(QDialog *dialog, DirectoryServicesWidget *dirserv, bool readOnly) { QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok, dialog); if (!readOnly) { buttonBox->addButton(QDialogButtonBox::Cancel); buttonBox->addButton(QDialogButtonBox::RestoreDefaults); QPushButton *defaultsBtn = buttonBox->button(QDialogButtonBox::RestoreDefaults); QObject::connect(defaultsBtn, &QPushButton::clicked, dirserv, &DirectoryServicesWidget::clear); QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); } QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(dirserv); layout->addWidget(buttonBox); dialog->setLayout(layout); } void Kleo::CryptoConfigEntryLDAPURL::slotOpenDialog() { // I'm a bad boy and I do it all on the stack. Enough classes already :) // This is just a simple dialog around the directory-services-widget QDialog dialog(mPushButton->parentWidget()); dialog.setWindowTitle(i18n("Configure LDAP Servers")); DirectoryServicesWidget *dirserv = new DirectoryServicesWidget(&dialog); prepareURLCfgDialog(&dialog, dirserv, mEntry->isReadOnly()); dirserv->setX509ReadOnly(mEntry->isReadOnly()); dirserv->setAllowedSchemes(DirectoryServicesWidget::LDAP); dirserv->setAllowedProtocols(DirectoryServicesWidget::X509Protocol); dirserv->addX509Services(mURLList); if (dialog.exec()) { setURLList(dirserv->x509Services()); slotChanged(); } } void Kleo::CryptoConfigEntryLDAPURL::setURLList(const QList &urlList) { mURLList = urlList; if (mURLList.isEmpty()) { mLabel->setText(i18n("None configured")); } else { mLabel->setText(i18np("1 server configured", "%1 servers configured", mURLList.count())); } } Kleo::CryptoConfigEntryKeyserver::CryptoConfigEntryKeyserver( CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName, QGridLayout *glay, QWidget *widget) : CryptoConfigEntryGUI(module, entry, entryName) { mLabel = new QLabel(widget); mPushButton = new QPushButton(i18n("Edit..."), widget); const int row = glay->rowCount(); QLabel *label = new QLabel(i18n("Use keyserver at"), widget); label->setBuddy(mPushButton); glay->addWidget(label, row, 1); QHBoxLayout *hlay = new QHBoxLayout; glay->addLayout(hlay, row, 2); hlay->addWidget(mLabel, 1); hlay->addWidget(mPushButton); if (entry->isReadOnly()) { mLabel->setEnabled(false); mPushButton->hide(); } else { connect(mPushButton, &QPushButton::clicked, this, &CryptoConfigEntryKeyserver::slotOpenDialog); } } Kleo::ParsedKeyserver Kleo::parseKeyserver(const QString &str) { const QStringList list = str.split(QRegExp(QLatin1String("[\\s,]")), QString::SkipEmptyParts); if (list.empty()) { return Kleo::ParsedKeyserver(); } Kleo::ParsedKeyserver result; result.url = list.front(); Q_FOREACH (const QString &kvpair, list.mid(1)) { const int idx = kvpair.indexOf(QLatin1Char('=')); if (idx < 0) { result.options.push_back(qMakePair(kvpair, QString())); // null QString } else { const QString key = kvpair.left(idx); const QString value = kvpair.mid(idx + 1); if (value.isEmpty()) { result.options.push_back(qMakePair(key, QStringLiteral(""))); // make sure it's not a null QString, only an empty one } else { result.options.push_back(qMakePair(key, value)); } } } return result; } QString Kleo::assembleKeyserver(const ParsedKeyserver &keyserver) { if (keyserver.options.empty()) { return keyserver.url; } QString result = keyserver.url; typedef QPair Pair; for (const Pair &pair : qAsConst(keyserver.options)) if (pair.second.isNull()) { result += QLatin1Char(' ') + pair.first; } else { result += QLatin1Char(' ') + pair.first + QLatin1Char('=') + pair.second; } return result; } void Kleo::CryptoConfigEntryKeyserver::doLoad() { mParsedKeyserver = parseKeyserver(mEntry->stringValue()); mLabel->setText(mParsedKeyserver.url); } void Kleo::CryptoConfigEntryKeyserver::doSave() { mParsedKeyserver.url = mLabel->text(); mEntry->setStringValue(assembleKeyserver(mParsedKeyserver)); } static QList string2urls(const QString &str) { QList ret; if (str.isEmpty()) { return ret; } ret << QUrl::fromUserInput(str); return ret; } static QString urls2string(const QList &urls) { return urls.empty() ? QString() : urls.front().url(); } void Kleo::CryptoConfigEntryKeyserver::slotOpenDialog() { // I'm a bad boy and I do it all on the stack. Enough classes already :) // This is just a simple dialog around the directory-services-widget QDialog dialog(mPushButton->parentWidget()); dialog.setWindowTitle(i18n("Configure Keyservers")); DirectoryServicesWidget *dirserv = new DirectoryServicesWidget(&dialog); prepareURLCfgDialog(&dialog, dirserv, mEntry->isReadOnly()); dirserv->setOpenPGPReadOnly(mEntry->isReadOnly()); dirserv->setAllowedSchemes(DirectoryServicesWidget::AllSchemes); dirserv->setAllowedProtocols(DirectoryServicesWidget::OpenPGPProtocol); dirserv->addOpenPGPServices(string2urls(mLabel->text())); if (dialog.exec()) { mLabel->setText(urls2string(dirserv->openPGPServices())); slotChanged(); } } #include "moc_cryptoconfigmodule_p.cpp" diff --git a/src/ui/keyapprovaldialog.cpp b/src/ui/keyapprovaldialog.cpp index fbf54e8cb..968daba6b 100644 --- a/src/ui/keyapprovaldialog.cpp +++ b/src/ui/keyapprovaldialog.cpp @@ -1,222 +1,220 @@ /* -*- c++ -*- keyapprovaldialog.h This file is part of libkleopatra, the KDE keymanagement library Copyright (c) 2004 Klarälvdalens Datakonsult AB Based on kpgpui.h Copyright (C) 2001,2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details Libkleopatra 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 2 of the License, or (at your option) any later version. Libkleopatra 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "keyapprovaldialog.h" #include "keyrequester.h" -#include #include -#include +#include -#include -#include +#include #include -#include +#include +#include +#include +#include #include +#include +#include #include -#include #include #include -#include -#include -#include - static Kleo::EncryptionPreference cb2pref(int i) { switch (i) { default: case 0: return Kleo::UnknownPreference; case 1: return Kleo::NeverEncrypt; case 2: return Kleo::AlwaysEncrypt; case 3: return Kleo::AlwaysEncryptIfPossible; case 4: return Kleo::AlwaysAskForEncryption; case 5: return Kleo::AskWheneverPossible; } } static int pref2cb(Kleo::EncryptionPreference p) { switch (p) { default: return 0; case Kleo::NeverEncrypt: return 1; case Kleo::AlwaysEncrypt: return 2; case Kleo::AlwaysEncryptIfPossible: return 3; case Kleo::AlwaysAskForEncryption: return 4; case Kleo::AskWheneverPossible: return 5; } } static QStringList preferencesStrings() { return QStringList() << xi18n("none") << i18n("Never Encrypt with This Key") << i18n("Always Encrypt with This Key") << i18n("Encrypt Whenever Encryption is Possible") << i18n("Always Ask") << i18n("Ask Whenever Encryption is Possible"); } class Q_DECL_HIDDEN Kleo::KeyApprovalDialog::Private { public: Private() {} Kleo::KeyRequester *selfRequester = nullptr; QStringList addresses; std::vector requesters; std::vector preferences; bool prefsChanged = false; }; Kleo::KeyApprovalDialog::KeyApprovalDialog(const std::vector &recipients, const std::vector &sender, QWidget *parent) : QDialog(parent), d(new Private()) { setWindowTitle(i18n("Encryption Key Approval")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &KeyApprovalDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &KeyApprovalDialog::reject); okButton->setDefault(true); Q_ASSERT(!recipients.empty()); QFrame *page = new QFrame(this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); QVBoxLayout *vlay = new QVBoxLayout(page); vlay->setContentsMargins(0, 0, 0, 0); vlay->addWidget(new QLabel(i18n("The following keys will be used for encryption:"), page)); QScrollArea *sv = new QScrollArea(page); sv->setWidgetResizable(true); vlay->addWidget(sv); QWidget *view = new QWidget(sv->viewport()); QGridLayout *glay = new QGridLayout(view); glay->setColumnStretch(1, 1); sv->setWidget(view); int row = -1; if (!sender.empty()) { ++row; glay->addWidget(new QLabel(i18n("Your keys:"), view), row, 0); d->selfRequester = new EncryptionKeyRequester(true, EncryptionKeyRequester::AllProtocols, view); d->selfRequester->setKeys(sender); glay->addWidget(d->selfRequester, row, 1); ++row; glay->addWidget(new KSeparator(Qt::Horizontal, view), row, 0, 1, 2); } const QStringList prefs = preferencesStrings(); for (std::vector::const_iterator it = recipients.begin(); it != recipients.end(); ++it) { ++row; glay->addWidget(new QLabel(i18n("Recipient:"), view), row, 0); glay->addWidget(new QLabel(it->address, view), row, 1); d->addresses.push_back(it->address); ++row; glay->addWidget(new QLabel(i18n("Encryption keys:"), view), row, 0); KeyRequester *req = new EncryptionKeyRequester(true, EncryptionKeyRequester::AllProtocols, view); req->setKeys(it->keys); glay->addWidget(req, row, 1); d->requesters.push_back(req); ++row; glay->addWidget(new QLabel(i18n("Encryption preference:"), view), row, 0); QComboBox *cb = new QComboBox(view); cb->setEditable(false); cb->addItems(prefs); glay->addWidget(cb, row, 1); cb->setCurrentIndex(pref2cb(it->pref)); connect(cb, QOverload::of(&QComboBox::activated), this, &KeyApprovalDialog::slotPrefsChanged); d->preferences.push_back(cb); } QSize size = sizeHint(); // don't make the dialog too large const QRect desk = QApplication::desktop()->screenGeometry(this); resize(QSize(qMin(size.width(), 3 * desk.width() / 4), qMin(size.height(), 7 * desk.height() / 8))); } Kleo::KeyApprovalDialog::~KeyApprovalDialog() { delete d; } std::vector Kleo::KeyApprovalDialog::senderKeys() const { return d->selfRequester ? d->selfRequester->keys() : std::vector(); } std::vector Kleo::KeyApprovalDialog::items() const { Q_ASSERT(d->requesters.size() == static_cast(d->addresses.size())); Q_ASSERT(d->requesters.size() == d->preferences.size()); std::vector result; result.reserve(d->requesters.size()); QStringList::const_iterator ait = d->addresses.constBegin(); std::vector::iterator rit = d->requesters.begin(); std::vector::iterator cit = d->preferences.begin(); while (ait != d->addresses.constEnd()) { result.push_back(Item(*ait++, (*rit++)->keys(), cb2pref((*cit++)->currentIndex()))); } return result; } bool Kleo::KeyApprovalDialog::preferencesChanged() const { return d->prefsChanged; } void Kleo::KeyApprovalDialog::slotPrefsChanged() { d->prefsChanged = true; } diff --git a/src/ui/keyselectiondialog.cpp b/src/ui/keyselectiondialog.cpp index 52fd32973..838c9d231 100644 --- a/src/ui/keyselectiondialog.cpp +++ b/src/ui/keyselectiondialog.cpp @@ -1,985 +1,981 @@ /* -*- c++ -*- keyselectiondialog.cpp This file is part of libkleopatra, the KDE keymanagement library - Copyright (c) 2004 Klar�vdalens Datakonsult AB + Copyright (c) 2004 Klarälvdalens Datakonsult AB Based on kpgpui.cpp Copyright (C) 2001,2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details Libkleopatra 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 2 of the License, or (at your option) any later version. Libkleopatra 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "keyselectiondialog.h" #include "keylistview.h" #include "progressdialog.h" #include "libkleo/dn.h" #include "kleo_ui_debug.h" // gpgme++ #include #include #include #include // KDE +#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include // Qt +#include #include -#include -#include #include -#include - -#include -#include +#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include - #include -#include -#include -#include -#include static bool checkKeyUsage(const GpgME::Key &key, unsigned int keyUsage, QString *statusString = nullptr) { auto setStatusString = [statusString](const QString &status) { if (statusString) { *statusString = status; } }; if (keyUsage & Kleo::KeySelectionDialog::ValidKeys) { if (key.isInvalid()) { if (key.keyListMode() & GpgME::Validate) { qCDebug(KLEO_UI_LOG) << "key is invalid"; setStatusString(i18n("The key is not valid.")); return false; } else { qCDebug(KLEO_UI_LOG) << "key is invalid - ignoring"; } } if (key.isExpired()) { qCDebug(KLEO_UI_LOG) << "key is expired"; setStatusString(i18n("The key is expired.")); return false; } else if (key.isRevoked()) { qCDebug(KLEO_UI_LOG) << "key is revoked"; setStatusString(i18n("The key is revoked.")); return false; } else if (key.isDisabled()) { qCDebug(KLEO_UI_LOG) << "key is disabled"; setStatusString(i18n("The key is disabled.")); return false; } } if (keyUsage & Kleo::KeySelectionDialog::EncryptionKeys && !key.canEncrypt()) { qCDebug(KLEO_UI_LOG) << "key can't encrypt"; setStatusString(i18n("The key is not designated for encryption.")); return false; } if (keyUsage & Kleo::KeySelectionDialog::SigningKeys && !key.canSign()) { qCDebug(KLEO_UI_LOG) << "key can't sign"; setStatusString(i18n("The key is not designated for signing.")); return false; } if (keyUsage & Kleo::KeySelectionDialog::CertificationKeys && !key.canCertify()) { qCDebug(KLEO_UI_LOG) << "key can't certify"; setStatusString(i18n("The key is not designated for certifying.")); return false; } if (keyUsage & Kleo::KeySelectionDialog::AuthenticationKeys && !key.canAuthenticate()) { qCDebug(KLEO_UI_LOG) << "key can't authenticate"; setStatusString(i18n("The key is not designated for authentication.")); return false; } if (keyUsage & Kleo::KeySelectionDialog::SecretKeys && !(keyUsage & Kleo::KeySelectionDialog::PublicKeys) && !key.hasSecret()) { qCDebug(KLEO_UI_LOG) << "key isn't secret"; setStatusString(i18n("The key is not secret.")); return false; } if (keyUsage & Kleo::KeySelectionDialog::TrustedKeys && key.protocol() == GpgME::OpenPGP && // only check this for secret keys for now. // Seems validity isn't checked for secret keylistings... !key.hasSecret()) { std::vector uids = key.userIDs(); for (std::vector::const_iterator it = uids.begin(); it != uids.end(); ++it) if (!it->isRevoked() && it->validity() >= GpgME::UserID::Marginal) { return true; } qCDebug(KLEO_UI_LOG) << "key has no UIDs with validity >= Marginal"; setStatusString(i18n("The key is not trusted enough.")); return false; } // X.509 keys are always trusted, else they won't be the keybox. // PENDING(marc) check that this ^ is correct setStatusString(i18n("The key can be used.")); return true; } static bool checkKeyUsage(const std::vector &keys, unsigned int keyUsage) { for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ++it) if (!checkKeyUsage(*it, keyUsage)) { return false; } return true; } static inline QString time_t2string(time_t t) { QDateTime dt; dt.setTime_t(t); return dt.toString(); } namespace { class ColumnStrategy : public Kleo::KeyListView::ColumnStrategy { public: ColumnStrategy(unsigned int keyUsage); QString title(int col) const override; int width(int col, const QFontMetrics &fm) const override; QString text(const GpgME::Key &key, int col) const override; QString toolTip(const GpgME::Key &key, int col) const override; QIcon icon(const GpgME::Key &key, int col) const override; private: const QIcon mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix; const unsigned int mKeyUsage; }; static QString iconPath(const QString &name) { return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("libkleopatra/pics/") + name + QStringLiteral(".png")); } ColumnStrategy::ColumnStrategy(unsigned int keyUsage) : Kleo::KeyListView::ColumnStrategy(), mKeyGoodPix(iconPath(QStringLiteral("key_ok"))), mKeyBadPix(iconPath(QStringLiteral("key_bad"))), mKeyUnknownPix(iconPath(QStringLiteral("key_unknown"))), mKeyValidPix(iconPath(QStringLiteral("key"))), mKeyUsage(keyUsage) { if (keyUsage == 0) qCWarning(KLEO_UI_LOG) << "KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead."; } QString ColumnStrategy::title(int col) const { switch (col) { case 0: return i18n("Key ID"); case 1: return i18n("User ID"); default: return QString(); } } int ColumnStrategy::width(int col, const QFontMetrics &fm) const { if (col == 0) { static const char hexchars[] = "0123456789ABCDEF"; int maxWidth = 0; for (unsigned int i = 0; i < 16; ++i) { maxWidth = qMax(fm.boundingRect(QLatin1Char(hexchars[i])).width(), maxWidth); } return 8 * maxWidth + 2 * 16 /* KIconLoader::SizeSmall */; } return Kleo::KeyListView::ColumnStrategy::width(col, fm); } QString ColumnStrategy::text(const GpgME::Key &key, int col) const { switch (col) { case 0: { if (key.shortKeyID()) { return QString::fromUtf8(key.shortKeyID()); } else { return xi18n("unknown"); } } case 1: { const char *uid = key.userID(0).id(); if (key.protocol() == GpgME::OpenPGP) { return uid && *uid ? QString::fromUtf8(uid) : QString(); } else { // CMS return Kleo::DN(uid).prettyDN(); } } default: return QString(); } } QString ColumnStrategy::toolTip(const GpgME::Key &key, int) const { const char *uid = key.userID(0).id(); const char *fpr = key.primaryFingerprint(); const char *issuer = key.issuerName(); const GpgME::Subkey subkey = key.subkey(0); const QString expiry = subkey.neverExpires() ? i18n("never") : time_t2string(subkey.expirationTime()); const QString creation = time_t2string(subkey.creationTime()); QString keyStatusString; if (!checkKeyUsage(key, mKeyUsage, &keyStatusString)) { // Show the status in bold if there is a problem keyStatusString = QLatin1String("") % keyStatusString % QLatin1String(""); } QString html = QStringLiteral("

"); if (key.protocol() == GpgME::OpenPGP) { html += i18n("OpenPGP key for %1", uid ? QString::fromUtf8(uid) : i18n("unknown")); } else { html += i18n("S/MIME key for %1", uid ? Kleo::DN(uid).prettyDN() : i18n("unknown")); } html += QStringLiteral("

"); const auto addRow = [&html](const QString &name, const QString &value) { html += QStringLiteral("").arg(name, value); }; addRow(i18nc("Key creation date", "Created"), creation); addRow(i18nc("Key Expiration date", "Expiry"), expiry); addRow(i18nc("Key fingerprint", "Fingerprint"), fpr ? QString::fromLatin1(fpr) : i18n("unknown")); if (key.protocol() != GpgME::OpenPGP) { addRow(i18nc("Key issuer", "Issuer"), issuer ? Kleo::DN(issuer).prettyDN() : i18n("unknown")); } addRow(i18nc("Key status", "Status"), keyStatusString); html += QStringLiteral("
%1: %2
"); return html; } QIcon ColumnStrategy::icon(const GpgME::Key &key, int col) const { if (col != 0) { return QIcon(); } // this key did not undergo a validating keylisting yet: if (!(key.keyListMode() & GpgME::Validate)) { return mKeyUnknownPix; } if (!checkKeyUsage(key, mKeyUsage)) { return mKeyBadPix; } if (key.protocol() == GpgME::CMS) { return mKeyGoodPix; } switch (key.userID(0).validity()) { default: case GpgME::UserID::Unknown: case GpgME::UserID::Undefined: return mKeyUnknownPix; case GpgME::UserID::Never: return mKeyValidPix; case GpgME::UserID::Marginal: case GpgME::UserID::Full: case GpgME::UserID::Ultimate: return mKeyGoodPix; } } } static const int sCheckSelectionDelay = 250; Kleo::KeySelectionDialog::KeySelectionDialog(const QString &title, const QString &text, const std::vector &selectedKeys, unsigned int keyUsage, bool extendedSelection, bool rememberChoice, QWidget *parent, bool modal) : QDialog(parent), mOpenPGPBackend(nullptr), mSMIMEBackend(nullptr), mRememberCB(nullptr), mSelectedKeys(selectedKeys), mKeyUsage(keyUsage), mCurrentContextMenuItem(nullptr) { setWindowTitle(title); setModal(modal); init(rememberChoice, extendedSelection, text, QString()); } Kleo::KeySelectionDialog::KeySelectionDialog(const QString &title, const QString &text, const QString &initialQuery, const std::vector &selectedKeys, unsigned int keyUsage, bool extendedSelection, bool rememberChoice, QWidget *parent, bool modal) : QDialog(parent), mOpenPGPBackend(nullptr), mSMIMEBackend(nullptr), mRememberCB(nullptr), mSelectedKeys(selectedKeys), mKeyUsage(keyUsage), mSearchText(initialQuery), mInitialQuery(initialQuery), mCurrentContextMenuItem(nullptr) { setWindowTitle(title); setModal(modal); init(rememberChoice, extendedSelection, text, initialQuery); } Kleo::KeySelectionDialog::KeySelectionDialog(const QString &title, const QString &text, const QString &initialQuery, unsigned int keyUsage, bool extendedSelection, bool rememberChoice, QWidget *parent, bool modal) : QDialog(parent), mOpenPGPBackend(nullptr), mSMIMEBackend(nullptr), mRememberCB(nullptr), mKeyUsage(keyUsage), mSearchText(initialQuery), mInitialQuery(initialQuery), mCurrentContextMenuItem(nullptr) { setWindowTitle(title); setModal(modal); init(rememberChoice, extendedSelection, text, initialQuery); } void Kleo::KeySelectionDialog::init(bool rememberChoice, bool extendedSelection, const QString &text, const QString &initialQuery) { QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); mOkButton = buttonBox->button(QDialogButtonBox::Ok); mOkButton->setDefault(true); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); QPushButton *user1Button = new QPushButton; buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole); QPushButton *user2Button = new QPushButton; buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole); if (mKeyUsage & OpenPGPKeys) { mOpenPGPBackend = QGpgME::openpgp(); } if (mKeyUsage & SMIMEKeys) { mSMIMEBackend = QGpgME::smime(); } mCheckSelectionTimer = new QTimer(this); mStartSearchTimer = new QTimer(this); QFrame *page = new QFrame(this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); mTopLayout = new QVBoxLayout(page); mTopLayout->setContentsMargins(0, 0, 0, 0); if (!text.isEmpty()) { QLabel *textLabel = new QLabel(text, page); textLabel->setWordWrap(true); // Setting the size policy is necessary as a workaround for https://issues.kolab.org/issue4429 // and http://bugreports.qt.nokia.com/browse/QTBUG-8740 textLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); connect(textLabel, &QLabel::linkActivated, this, &KeySelectionDialog::slotStartCertificateManager); mTopLayout->addWidget(textLabel); } QPushButton *const searchExternalPB = new QPushButton(i18n("Search for &External Certificates"), page); mTopLayout->addWidget(searchExternalPB, 0, Qt::AlignLeft); connect(searchExternalPB, &QAbstractButton::clicked, this, &KeySelectionDialog::slotStartSearchForExternalCertificates); if (initialQuery.isEmpty()) { searchExternalPB->hide(); } QHBoxLayout *hlay = new QHBoxLayout(); mTopLayout->addLayout(hlay); QLineEdit *le = new QLineEdit(page); le->setClearButtonEnabled(true); le->setText(initialQuery); QLabel *lbSearchFor = new QLabel(i18n("&Search for:"), page); lbSearchFor->setBuddy(le); hlay->addWidget(lbSearchFor); hlay->addWidget(le, 1); le->setFocus(); connect(le, &QLineEdit::textChanged, this, static_cast(&KeySelectionDialog::slotSearch)); connect(mStartSearchTimer, &QTimer::timeout, this, &KeySelectionDialog::slotFilter); mKeyListView = new KeyListView(new ColumnStrategy(mKeyUsage), nullptr, page); mKeyListView->setObjectName(QStringLiteral("mKeyListView")); mKeyListView->header()->stretchLastSection(); mKeyListView->setRootIsDecorated(true); mKeyListView->setSortingEnabled(true); mKeyListView->header()->setSortIndicatorShown(true); mKeyListView->header()->setSortIndicator(1, Qt::AscendingOrder); // sort by User ID if (extendedSelection) { mKeyListView->setSelectionMode(QAbstractItemView::ExtendedSelection); } mTopLayout->addWidget(mKeyListView, 10); if (rememberChoice) { mRememberCB = new QCheckBox(i18n("&Remember choice"), page); mTopLayout->addWidget(mRememberCB); mRememberCB->setWhatsThis( i18n("

If you check this box your choice will " "be stored and you will not be asked again." "

")); } connect(mCheckSelectionTimer, &QTimer::timeout, this, static_cast(&KeySelectionDialog::slotCheckSelection)); connectSignals(); connect(mKeyListView, &Kleo::KeyListView::doubleClicked, this, &KeySelectionDialog::slotTryOk); connect(mKeyListView, &KeyListView::contextMenu, this, &KeySelectionDialog::slotRMB); user1Button->setText(i18n("&Reread Keys")); user2Button->setText(i18n("&Start Certificate Manager")); connect(user1Button, &QPushButton::clicked, this, &KeySelectionDialog::slotRereadKeys); connect(user2Button, &QPushButton::clicked, this, [this]() { slotStartCertificateManager(); }); connect(mOkButton, &QPushButton::clicked, this, &KeySelectionDialog::slotOk); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &KeySelectionDialog::slotCancel); slotRereadKeys(); mTopLayout->activate(); if (qApp) { QSize dialogSize(sizeHint()); KConfigGroup dialogConfig(KSharedConfig::openConfig(), "Key Selection Dialog"); dialogSize = dialogConfig.readEntry("Dialog size", dialogSize); const QByteArray headerState = dialogConfig.readEntry("header", QByteArray()); if (!headerState.isEmpty()) { mKeyListView->header()->restoreState(headerState); } resize(dialogSize); } } Kleo::KeySelectionDialog::~KeySelectionDialog() { disconnectSignals(); KConfigGroup dialogConfig(KSharedConfig::openConfig(), "Key Selection Dialog"); dialogConfig.writeEntry("Dialog size", size()); dialogConfig.writeEntry("header", mKeyListView->header()->saveState()); dialogConfig.sync(); } void Kleo::KeySelectionDialog::connectSignals() { if (mKeyListView->isMultiSelection()) connect(mKeyListView, &QTreeWidget::itemSelectionChanged, this, &KeySelectionDialog::slotSelectionChanged); else connect(mKeyListView, static_cast(&KeyListView::selectionChanged), this, static_cast(&KeySelectionDialog::slotCheckSelection)); } void Kleo::KeySelectionDialog::disconnectSignals() { if (mKeyListView->isMultiSelection()) disconnect(mKeyListView, &QTreeWidget::itemSelectionChanged, this, &KeySelectionDialog::slotSelectionChanged); else disconnect(mKeyListView, static_cast(&KeyListView::selectionChanged), this, static_cast(&KeySelectionDialog::slotCheckSelection)); } const GpgME::Key &Kleo::KeySelectionDialog::selectedKey() const { static const GpgME::Key null = GpgME::Key::null; if (mKeyListView->isMultiSelection() || !mKeyListView->selectedItem()) { return null; } return mKeyListView->selectedItem()->key(); } QString Kleo::KeySelectionDialog::fingerprint() const { return QLatin1String(selectedKey().primaryFingerprint()); } QStringList Kleo::KeySelectionDialog::fingerprints() const { QStringList result; for (std::vector::const_iterator it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) if (const char *fpr = it->primaryFingerprint()) { result.push_back(QLatin1String(fpr)); } return result; } QStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const { QStringList result; for (std::vector::const_iterator it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) if (it->protocol() == GpgME::OpenPGP) if (const char *fpr = it->primaryFingerprint()) { result.push_back(QLatin1String(fpr)); } return result; } QStringList Kleo::KeySelectionDialog::smimeFingerprints() const { QStringList result; for (std::vector::const_iterator it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) if (it->protocol() == GpgME::CMS) if (const char *fpr = it->primaryFingerprint()) { result.push_back(QLatin1String(fpr)); } return result; } void Kleo::KeySelectionDialog::slotRereadKeys() { mKeyListView->clear(); mListJobCount = 0; mTruncated = 0; mSavedOffsetY = mKeyListView->verticalScrollBar()->value(); disconnectSignals(); mKeyListView->setEnabled(false); // FIXME: save current selection if (mOpenPGPBackend) { startKeyListJobForBackend(mOpenPGPBackend, std::vector(), false /*non-validating*/); } if (mSMIMEBackend) { startKeyListJobForBackend(mSMIMEBackend, std::vector(), false /*non-validating*/); } if (mListJobCount == 0) { mKeyListView->setEnabled(true); KMessageBox::information(this, i18n("No backends found for listing keys. " "Check your installation."), i18n("Key Listing Failed")); connectSignals(); } } void Kleo::KeySelectionDialog::slotStartCertificateManager(const QString &query) { QStringList args; if (!query.isEmpty()) { args << QStringLiteral("--search") << query; } if (!QProcess::startDetached(QStringLiteral("kleopatra"), args)) KMessageBox::error(this, i18n("Could not start certificate manager; " "please check your installation."), i18n("Certificate Manager Error")); else { qCDebug(KLEO_UI_LOG) << "\nslotStartCertManager(): certificate manager started."; } } #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ static void showKeyListError(QWidget *parent, const GpgME::Error &err) { Q_ASSERT(err); const QString msg = i18n("

An error occurred while fetching " "the keys from the backend:

" "

%1

", QString::fromLocal8Bit(err.asString())); KMessageBox::error(parent, msg, i18n("Key Listing Failed")); } #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ namespace { struct ExtractFingerprint { QString operator()(const GpgME::Key &key) { return QLatin1String(key.primaryFingerprint()); } }; } void Kleo::KeySelectionDialog::startKeyListJobForBackend(const QGpgME::Protocol *backend, const std::vector &keys, bool validate) { Q_ASSERT(backend); QGpgME::KeyListJob *job = backend->keyListJob(false, false, validate); // local, w/o sigs, validation as givem if (!job) { return; } connect(job, &QGpgME::KeyListJob::result, this, &KeySelectionDialog::slotKeyListResult); if (validate) connect(job, &QGpgME::KeyListJob::nextKey, mKeyListView, &KeyListView::slotRefreshKey); else connect(job, &QGpgME::KeyListJob::nextKey, mKeyListView, &KeyListView::slotAddKey); QStringList fprs; std::transform(keys.begin(), keys.end(), std::back_inserter(fprs), ExtractFingerprint()); const GpgME::Error err = job->start(fprs, mKeyUsage & SecretKeys && !(mKeyUsage & PublicKeys)); if (err) { return showKeyListError(this, err); } #ifndef LIBKLEO_NO_PROGRESSDIALOG // FIXME: create a MultiProgressDialog: (void)new ProgressDialog(job, validate ? i18n("Checking selected keys...") : i18n("Fetching keys..."), this); #endif ++mListJobCount; } static void selectKeys(Kleo::KeyListView *klv, const std::vector &selectedKeys) { klv->clearSelection(); if (selectedKeys.empty()) { return; } for (std::vector::const_iterator it = selectedKeys.begin(); it != selectedKeys.end(); ++it) if (Kleo::KeyListViewItem *item = klv->itemByFingerprint(it->primaryFingerprint())) { item->setSelected(true); } } void Kleo::KeySelectionDialog::slotKeyListResult(const GpgME::KeyListResult &res) { if (res.error()) { showKeyListError(this, res.error()); } else if (res.isTruncated()) { ++mTruncated; } if (--mListJobCount > 0) { return; // not yet finished... } if (mTruncated > 0) KMessageBox::information(this, i18np("One backend returned truncated output.

" "Not all available keys are shown

", "%1 backends returned truncated output.

" "Not all available keys are shown

", mTruncated), i18n("Key List Result")); mKeyListView->flushKeys(); mKeyListView->setEnabled(true); mListJobCount = mTruncated = 0; mKeysToCheck.clear(); selectKeys(mKeyListView, mSelectedKeys); slotFilter(); connectSignals(); slotSelectionChanged(); // restore the saved position of the contents mKeyListView->verticalScrollBar()->setValue(mSavedOffsetY); mSavedOffsetY = 0; } void Kleo::KeySelectionDialog::slotSelectionChanged() { qCDebug(KLEO_UI_LOG) << "KeySelectionDialog::slotSelectionChanged()"; // (re)start the check selection timer. Checking the selection is delayed // because else drag-selection doesn't work very good (checking key trust // is slow). mCheckSelectionTimer->start(sCheckSelectionDelay); } namespace { struct AlreadyChecked { bool operator()(const GpgME::Key &key) const { return key.keyListMode() & GpgME::Validate; } }; } void Kleo::KeySelectionDialog::slotCheckSelection(KeyListViewItem *item) { qCDebug(KLEO_UI_LOG) << "KeySelectionDialog::slotCheckSelection()"; mCheckSelectionTimer->stop(); mSelectedKeys.clear(); if (!mKeyListView->isMultiSelection()) { if (item) { mSelectedKeys.push_back(item->key()); } } for (KeyListViewItem *it = mKeyListView->firstChild(); it; it = it->nextSibling()) if (it->isSelected()) { mSelectedKeys.push_back(it->key()); } mKeysToCheck.clear(); std::remove_copy_if(mSelectedKeys.begin(), mSelectedKeys.end(), std::back_inserter(mKeysToCheck), AlreadyChecked()); if (mKeysToCheck.empty()) { mOkButton->setEnabled(!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage)); return; } // performed all fast checks - now for validating key listing: startValidatingKeyListing(); } void Kleo::KeySelectionDialog::startValidatingKeyListing() { if (mKeysToCheck.empty()) { return; } mListJobCount = 0; mTruncated = 0; mSavedOffsetY = mKeyListView->verticalScrollBar()->value(); disconnectSignals(); mKeyListView->setEnabled(false); std::vector smime, openpgp; for (std::vector::const_iterator it = mKeysToCheck.begin(); it != mKeysToCheck.end(); ++it) if (it->protocol() == GpgME::OpenPGP) { openpgp.push_back(*it); } else { smime.push_back(*it); } if (!openpgp.empty()) { Q_ASSERT(mOpenPGPBackend); startKeyListJobForBackend(mOpenPGPBackend, openpgp, true /*validate*/); } if (!smime.empty()) { Q_ASSERT(mSMIMEBackend); startKeyListJobForBackend(mSMIMEBackend, smime, true /*validate*/); } Q_ASSERT(mListJobCount > 0); } bool Kleo::KeySelectionDialog::rememberSelection() const { return mRememberCB && mRememberCB->isChecked(); } void Kleo::KeySelectionDialog::slotRMB(Kleo::KeyListViewItem *item, const QPoint &p) { if (!item) { return; } mCurrentContextMenuItem = item; QMenu menu; menu.addAction(i18n("Recheck Key"), this, &KeySelectionDialog::slotRecheckKey); menu.exec(p); } void Kleo::KeySelectionDialog::slotRecheckKey() { if (!mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull()) { return; } mKeysToCheck.clear(); mKeysToCheck.push_back(mCurrentContextMenuItem->key()); } void Kleo::KeySelectionDialog::slotTryOk() { if (!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage)) { slotOk(); } } void Kleo::KeySelectionDialog::slotOk() { if (mCheckSelectionTimer->isActive()) { slotCheckSelection(); } #if 0 //Laurent I don't understand why we returns here. // button could be disabled again after checking the selected key1 if (!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage)) { return; } #endif mStartSearchTimer->stop(); accept(); } void Kleo::KeySelectionDialog::slotCancel() { mCheckSelectionTimer->stop(); mStartSearchTimer->stop(); reject(); } void Kleo::KeySelectionDialog::slotSearch(const QString &text) { mSearchText = text.trimmed().toUpper(); slotSearch(); } void Kleo::KeySelectionDialog::slotSearch() { mStartSearchTimer->setSingleShot(true); mStartSearchTimer->start(sCheckSelectionDelay); } void Kleo::KeySelectionDialog::slotFilter() { if (mSearchText.isEmpty()) { showAllItems(); return; } // OK, so we need to filter: QRegExp keyIdRegExp(QLatin1String("(?:0x)?[A-F0-9]{1,8}"), Qt::CaseInsensitive); if (keyIdRegExp.exactMatch(mSearchText)) { if (mSearchText.startsWith(QLatin1String("0X"))) // search for keyID only: { filterByKeyID(mSearchText.mid(2)); } else // search for UID and keyID: { filterByKeyIDOrUID(mSearchText); } } else { // search in UID: filterByUID(mSearchText); } } void Kleo::KeySelectionDialog::filterByKeyID(const QString &keyID) { Q_ASSERT(keyID.length() <= 8); Q_ASSERT(!keyID.isEmpty()); // regexp in slotFilter should prevent these if (keyID.isEmpty()) { showAllItems(); } else for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) { item->setHidden(!item->text(0).toUpper().startsWith(keyID)); } } static bool anyUIDMatches(const Kleo::KeyListViewItem *item, QRegExp &rx) { if (!item) { return false; } const std::vector uids = item->key().userIDs(); for (std::vector::const_iterator it = uids.begin(); it != uids.end(); ++it) if (it->id() && rx.indexIn(QString::fromUtf8(it->id())) >= 0) { return true; } return false; } void Kleo::KeySelectionDialog::filterByKeyIDOrUID(const QString &str) { Q_ASSERT(!str.isEmpty()); // match beginnings of words: QRegExp rx(QLatin1String("\\b") + QRegExp::escape(str), Qt::CaseInsensitive); for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) { item->setHidden(!item->text(0).toUpper().startsWith(str) && !anyUIDMatches(item, rx)); } } void Kleo::KeySelectionDialog::filterByUID(const QString &str) { Q_ASSERT(!str.isEmpty()); // match beginnings of words: QRegExp rx(QLatin1String("\\b") + QRegExp::escape(str), Qt::CaseInsensitive); for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) { item->setHidden(!anyUIDMatches(item, rx)); } } void Kleo::KeySelectionDialog::showAllItems() { for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) { item->setHidden(false); } } diff --git a/src/utils/classify.cpp b/src/utils/classify.cpp index 2634fad93..fe5375760 100644 --- a/src/utils/classify.cpp +++ b/src/utils/classify.cpp @@ -1,439 +1,439 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/classify.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra 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 2 of the License, or (at your option) any later version. Kleopatra 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "classify.h" #include "libkleo_debug.h" #include "kleo/checksumdefinition.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo::Class; namespace { const unsigned int ExamineContentHint = 0x8000; static const struct _classification { char extension[4]; unsigned int classification; } classifications[] = { // ordered by extension { "arl", CMS | Binary | CertificateRevocationList }, { "asc", OpenPGP | Ascii | OpaqueSignature | DetachedSignature | CipherText | AnyCertStoreType | ExamineContentHint }, { "cer", CMS | Binary | Certificate }, { "crl", CMS | Binary | CertificateRevocationList }, { "crt", CMS | Binary | Certificate }, { "der", CMS | Binary | Certificate | CertificateRevocationList }, { "gpg", OpenPGP | Binary | OpaqueSignature | CipherText | AnyCertStoreType | ExamineContentHint}, { "p10", CMS | Ascii | CertificateRequest }, { "p12", CMS | Binary | ExportedPSM }, { "p7c", CMS | Binary | Certificate }, { "p7m", CMS | AnyFormat | CipherText }, { "p7s", CMS | AnyFormat | AnySignature }, { "pem", CMS | Ascii | AnyType | ExamineContentHint }, { "pfx", CMS | Binary | Certificate }, { "pgp", OpenPGP | Binary | OpaqueSignature | CipherText | AnyCertStoreType | ExamineContentHint}, { "sig", OpenPGP | AnyFormat | DetachedSignature }, }; static const QMap gpgmeTypeMap { { GpgME::Data::PGPSigned, OpenPGP | OpaqueSignature }, /* PGPOther might be just an unencrypted unsigned pgp message. Decrypt * would yield the plaintext anyway so for us this is CipherText. */ { GpgME::Data::PGPOther, OpenPGP | CipherText }, { GpgME::Data::PGPKey, OpenPGP | Certificate }, { GpgME::Data::CMSSigned, CMS | AnySignature }, { GpgME::Data::CMSEncrypted, CMS | CipherText }, /* See PGPOther */ { GpgME::Data::CMSOther, CMS | CipherText }, { GpgME::Data::X509Cert, CMS | Certificate}, { GpgME::Data::PKCS12, CMS | Binary | ExportedPSM }, { GpgME::Data::PGPEncrypted, OpenPGP | CipherText }, { GpgME::Data::PGPSignature, OpenPGP | DetachedSignature }, }; static const unsigned int defaultClassification = NoClass; template