diff --git a/src/ui/dnattributeorderconfigwidget.cpp b/src/ui/dnattributeorderconfigwidget.cpp index 186c53325..2767dd201 100644 --- a/src/ui/dnattributeorderconfigwidget.cpp +++ b/src/ui/dnattributeorderconfigwidget.cpp @@ -1,378 +1,377 @@ /* -*- c++ -*- dnattributeorderconfigwidget.cpp This file is part of libkleopatra, the KDE keymanagement library 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 "dnattributeorderconfigwidget.h" #include "libkleo_debug.h" #include "libkleo/dn.h" #include -#include #include #include #include #include #include #include #include class Kleo::DNAttributeOrderConfigWidget::Private { public: enum { UUp = 0, Up = 1, Left = 2, Right = 3, Down = 4, DDown = 5 }; #ifndef QT_NO_TREEWIDGET QTreeWidget *availableLV = nullptr; QTreeWidget *currentLV = nullptr; #endif QToolButton *navTB[6]; #ifndef QT_NO_TREEWIDGET QTreeWidgetItem *placeHolderItem = nullptr; #endif Kleo::DNAttributeMapper *mapper = nullptr; }; #ifndef QT_NO_TREEWIDGET static void prepare(QTreeWidget *lv) { lv->setAllColumnsShowFocus(true); lv->header()->setStretchLastSection(true); lv->setHeaderLabels(QStringList() << QString() << i18n("Description")); } #endif Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidget(DNAttributeMapper *mapper, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private) { Q_ASSERT(mapper); d->mapper = mapper; QGridLayout *glay = new QGridLayout(this); glay->setContentsMargins(0, 0, 0, 0); glay->setColumnStretch(0, 1); glay->setColumnStretch(2, 1); int row = -1; ++row; glay->addWidget(new QLabel(i18n("Available attributes:"), this), row, 0); glay->addWidget(new QLabel(i18n("Current attribute order:"), this), row, 2); ++row; glay->setRowStretch(row, 1); #ifndef QT_NO_TREEWIDGET d->availableLV = new QTreeWidget(this); prepare(d->availableLV); d->availableLV->sortItems(0, Qt::AscendingOrder); glay->addWidget(d->availableLV, row, 0); d->currentLV = new QTreeWidget(this); prepare(d->currentLV); glay->addWidget(d->currentLV, row, 2); connect(d->availableLV, &QTreeWidget::itemClicked, this, &DNAttributeOrderConfigWidget::slotAvailableSelectionChanged); connect(d->currentLV, &QTreeWidget::itemClicked, this, &DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged); d->placeHolderItem = new QTreeWidgetItem(d->availableLV); d->placeHolderItem->setText(0, QStringLiteral("_X_")); d->placeHolderItem->setText(1, i18n("All others")); #endif // the up/down/left/right arrow cross: QGridLayout *xlay = new QGridLayout(); xlay->setSpacing(0); xlay->setObjectName(QStringLiteral("xlay")); xlay->setAlignment(Qt::AlignCenter); static const struct { const char *icon; int row, col; const char *tooltip; void(DNAttributeOrderConfigWidget::*slot)(); bool autorepeat; } navButtons[] = { { "go-top", 0, 1, I18N_NOOP("Move to top"), &DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked, false }, { "go-up", 1, 1, I18N_NOOP("Move one up"), &DNAttributeOrderConfigWidget::slotUpButtonClicked, true }, { "go-previous", 2, 0, I18N_NOOP("Remove from current attribute order"), &DNAttributeOrderConfigWidget::slotLeftButtonClicked, false }, { "go-next", 2, 2, I18N_NOOP("Add to current attribute order"), &DNAttributeOrderConfigWidget::slotRightButtonClicked, false }, { "go-down", 3, 1, I18N_NOOP("Move one down"), &DNAttributeOrderConfigWidget::slotDownButtonClicked, true }, { "go-bottom", 4, 1, I18N_NOOP("Move to bottom"), &DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked, false } }; for (unsigned int i = 0; i < sizeof navButtons / sizeof * navButtons; ++i) { QToolButton *tb = d->navTB[i] = new QToolButton(this); tb->setIcon(QIcon::fromTheme(QLatin1String(navButtons[i].icon))); tb->setEnabled(false); tb->setToolTip(i18n(navButtons[i].tooltip)); xlay->addWidget(tb, navButtons[i].row, navButtons[i].col); tb->setAutoRepeat(navButtons[i].autorepeat); connect(tb, &QToolButton::clicked, this, navButtons[i].slot); } glay->addLayout(xlay, row, 1); } Kleo::DNAttributeOrderConfigWidget::~DNAttributeOrderConfigWidget() { delete d; } void Kleo::DNAttributeOrderConfigWidget::load() { #ifndef QT_NO_TREEWIDGET // save the _X_ item: takePlaceHolderItem(); // clear the rest: d->availableLV->clear(); d->currentLV->clear(); const QStringList order = d->mapper->attributeOrder(); // fill the RHS listview: QTreeWidgetItem *last = nullptr; for (QStringList::const_iterator it = order.begin(); it != order.end(); ++it) { const QString attr = (*it).toUpper(); if (attr == QLatin1String("_X_")) { takePlaceHolderItem(); d->currentLV->insertTopLevelItem(d->currentLV->topLevelItemCount(), d->placeHolderItem); last = d->placeHolderItem; } else { last = new QTreeWidgetItem(d->currentLV, last); last->setText(0, attr); last->setText(1, d->mapper->name2label(attr)); } } // fill the LHS listview with what's left: const QStringList all = Kleo::DNAttributeMapper::instance()->names(); const QStringList::const_iterator end(all.end()); for (QStringList::const_iterator it = all.begin(); it != end; ++it) { if (!order.contains(*it)) { QTreeWidgetItem *item = new QTreeWidgetItem(d->availableLV); item->setText(0, *it); item->setText(1, d->mapper->name2label(*it)); } } if (!d->placeHolderItem->treeWidget()) { d->availableLV->addTopLevelItem(d->placeHolderItem); } #endif } void Kleo::DNAttributeOrderConfigWidget::takePlaceHolderItem() { #ifndef QT_NO_TREEWIDGET if (QTreeWidget *lv = d->placeHolderItem->treeWidget()) { lv->takeTopLevelItem(lv->indexOfTopLevelItem(d->placeHolderItem)); } #endif } void Kleo::DNAttributeOrderConfigWidget::save() const { #ifndef QT_NO_TREEWIDGET QStringList order; for (QTreeWidgetItemIterator it(d->currentLV); (*it); ++it) { order.push_back((*it)->text(0)); } d->mapper->setAttributeOrder(order); #endif } void Kleo::DNAttributeOrderConfigWidget::defaults() { qCDebug (LIBKLEO_LOG) << "Sorry, not implemented: Kleo::DNAttributeOrderConfigWidget::defaults()"; } void Kleo::DNAttributeOrderConfigWidget::slotAvailableSelectionChanged(QTreeWidgetItem *item) { d->navTB[Private::Right]->setEnabled(item); } void Kleo::DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged(QTreeWidgetItem *item) { enableDisableButtons(item); } void Kleo::DNAttributeOrderConfigWidget::enableDisableButtons(QTreeWidgetItem *item) { #ifndef QT_NO_TREEWIDGET d->navTB[Private::UUp ]->setEnabled(item && d->currentLV->itemAbove(item)); d->navTB[Private::Up ]->setEnabled(item && d->currentLV->itemAbove(item)); d->navTB[Private::Left ]->setEnabled(item); d->navTB[Private::Down ]->setEnabled(item && d->currentLV->itemBelow(item)); d->navTB[Private::DDown]->setEnabled(item && d->currentLV->itemBelow(item)); #endif } void Kleo::DNAttributeOrderConfigWidget::slotUpButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->currentLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *item = d->currentLV->selectedItems().first(); int itemIndex = d->currentLV->indexOfTopLevelItem(item); if (itemIndex <= 0) { return; } d->currentLV->takeTopLevelItem(itemIndex); d->currentLV->insertTopLevelItem(itemIndex - 1, item); d->currentLV->clearSelection(); item->setSelected(true); enableDisableButtons(item); Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->currentLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *item = d->currentLV->selectedItems().first(); int itemIndex = d->currentLV->indexOfTopLevelItem(item); if (itemIndex == 0) { return; } d->currentLV->takeTopLevelItem(itemIndex); d->currentLV->insertTopLevelItem(0, item); d->currentLV->clearSelection(); item->setSelected(true); enableDisableButtons(item); Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::slotDownButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->currentLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *item = d->currentLV->selectedItems().first(); int itemIndex = d->currentLV->indexOfTopLevelItem(item); if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) { return; } d->currentLV->takeTopLevelItem(itemIndex); d->currentLV->insertTopLevelItem(itemIndex + 1, item); d->currentLV->clearSelection(); item->setSelected(true); enableDisableButtons(item); Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->currentLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *item = d->currentLV->selectedItems().first(); const int itemIndex = d->currentLV->indexOfTopLevelItem(item); if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) { return; } d->currentLV->takeTopLevelItem(itemIndex); d->currentLV->addTopLevelItem(item); d->currentLV->clearSelection(); item->setSelected(true); enableDisableButtons(item); Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::slotLeftButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->currentLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *right = d->currentLV->selectedItems().first(); QTreeWidgetItem *next = d->currentLV->itemBelow(right); if (!next) { next = d->currentLV->itemAbove(right); } d->currentLV->takeTopLevelItem(d->currentLV->indexOfTopLevelItem(right)); d->availableLV->addTopLevelItem(right); d->availableLV->sortItems(0, Qt::AscendingOrder); if (next) { next->setSelected(true); } enableDisableButtons(next); Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::slotRightButtonClicked() { #ifndef QT_NO_TREEWIDGET if (d->availableLV->selectedItems().isEmpty()) { return; } QTreeWidgetItem *left = d->availableLV->selectedItems().first(); QTreeWidgetItem *next = d->availableLV->itemBelow(left); if (!next) { next = d->availableLV->itemAbove(left); } d->availableLV->takeTopLevelItem(d->availableLV->indexOfTopLevelItem(left)); int newRightIndex = d->currentLV->topLevelItemCount(); if (!d->currentLV->selectedItems().isEmpty()) { QTreeWidgetItem *right = d->currentLV->selectedItems().first(); newRightIndex = d->currentLV->indexOfTopLevelItem(right); right->setSelected(false); } d->currentLV->insertTopLevelItem(newRightIndex, left); left->setSelected(true); enableDisableButtons(left); d->navTB[Private::Right]->setEnabled(next); if (next) { next->setSelected(true); } Q_EMIT changed(); #endif } void Kleo::DNAttributeOrderConfigWidget::virtual_hook(int, void *) {} diff --git a/src/ui/messagebox.cpp b/src/ui/messagebox.cpp index b73577445..267a4914a 100644 --- a/src/ui/messagebox.cpp +++ b/src/ui/messagebox.cpp @@ -1,266 +1,265 @@ /* messagebox.cpp This file is part of libkleopatra, the KDE keymanagement library Copyright (c) 2004 Klarälvdalens 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 "messagebox.h" #include "auditlogviewer.h" #include "kleo_ui_debug.h" #include #include #include #include #include #include #include #include -#include #include #include using namespace Kleo; using namespace Kleo::Private; using namespace GpgME; using namespace QGpgME; // static void MessageBox::auditLog(QWidget *parent, const Job *job, const QString &caption) { if (!job) { return; } if (!GpgME::hasFeature(AuditLogFeature, 0) || !job->isAuditLogSupported()) { KMessageBox::information(parent, i18n("Your system does not have support for GnuPG Audit Logs"), i18n("System Error")); return; } const GpgME::Error err = job->auditLogError(); if (err && err.code() != GPG_ERR_NO_DATA) { KMessageBox::information(parent, i18n("An error occurred while trying to retrieve the GnuPG Audit Log:\n%1", QString::fromLocal8Bit(err.asString())), i18n("GnuPG Audit Log Error")); return; } const QString log = job->auditLogAsHtml(); if (log.isEmpty()) { KMessageBox::information(parent, i18n("No GnuPG Audit Log available for this operation."), i18n("No GnuPG Audit Log")); return; } auditLog(parent, log, caption); } // static void MessageBox::auditLog(QWidget *parent, const QString &log, const QString &caption) { AuditLogViewer *const alv = new AuditLogViewer(log, parent); alv->setAttribute(Qt::WA_DeleteOnClose); alv->setObjectName(QStringLiteral("alv")); alv->setWindowTitle(caption); alv->show(); } // static void MessageBox::auditLog(QWidget *parent, const Job *job) { auditLog(parent, job, i18n("GnuPG Audit Log Viewer")); } // static void MessageBox::auditLog(QWidget *parent, const QString &log) { auditLog(parent, log, i18n("GnuPG Audit Log Viewer")); } static QString to_information_string(const SigningResult &result) { return result.error() ? i18n("Signing failed: %1", QString::fromLocal8Bit(result.error().asString())) : i18n("Signing successful"); } static QString to_error_string(const SigningResult &result) { return to_information_string(result); } static QString to_information_string(const EncryptionResult &result) { return result.error() ? i18n("Encryption failed: %1", QString::fromLocal8Bit(result.error().asString())) : i18n("Encryption successful"); } static QString to_error_string(const EncryptionResult &result) { return to_information_string(result); } static QString to_information_string(const SigningResult &sresult, const EncryptionResult &eresult) { return to_information_string(sresult) + QLatin1Char('\n') + to_information_string(eresult); } static QString to_error_string(const SigningResult &sresult, const EncryptionResult &eresult) { return to_information_string(sresult, eresult); } // static void MessageBox::information(QWidget *parent, const SigningResult &result, const Job *job, KMessageBox::Options options) { information(parent, result, job, i18n("Signing Result"), options); } // static void MessageBox::information(QWidget *parent, const SigningResult &result, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Information, to_information_string(result), job, caption, options); } // static void MessageBox::error(QWidget *parent, const SigningResult &result, const Job *job, KMessageBox::Options options) { error(parent, result, job, i18n("Signing Error"), options); } // static void MessageBox::error(QWidget *parent, const SigningResult &result, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Critical, to_error_string(result), job, caption, options); } // static void MessageBox::information(QWidget *parent, const EncryptionResult &result, const Job *job, KMessageBox::Options options) { information(parent, result, job, i18n("Encryption Result"), options); } // static void MessageBox::information(QWidget *parent, const EncryptionResult &result, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Information, to_information_string(result), job, caption, options); } // static void MessageBox::error(QWidget *parent, const EncryptionResult &result, const Job *job, KMessageBox::Options options) { error(parent, result, job, i18n("Encryption Error"), options); } // static void MessageBox::error(QWidget *parent, const EncryptionResult &result, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Critical, to_error_string(result), job, caption, options); } // static void MessageBox::information(QWidget *parent, const SigningResult &sresult, const EncryptionResult &eresult, const Job *job, KMessageBox::Options options) { information(parent, sresult, eresult, job, i18n("Encryption Result"), options); } // static void MessageBox::information(QWidget *parent, const SigningResult &sresult, const EncryptionResult &eresult, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Information, to_information_string(sresult, eresult), job, caption, options); } // static void MessageBox::error(QWidget *parent, const SigningResult &sresult, const EncryptionResult &eresult, const Job *job, KMessageBox::Options options) { error(parent, sresult, eresult, job, i18n("Encryption Error"), options); } // static void MessageBox::error(QWidget *parent, const SigningResult &sresult, const EncryptionResult &eresult, const Job *job, const QString &caption, KMessageBox::Options options) { make(parent, QMessageBox::Critical, to_error_string(sresult, eresult), job, caption, options); } // static bool MessageBox::showAuditLogButton(const QGpgME::Job *job) { if (!job) { qCDebug(KLEO_UI_LOG) << "not showing audit log button (no job instance)"; return false; } if (!GpgME::hasFeature(GpgME::AuditLogFeature, 0)) { qCDebug(KLEO_UI_LOG) << "not showing audit log button (gpgme too old)"; return false; } if (!job->isAuditLogSupported()) { qCDebug(KLEO_UI_LOG) << "not showing audit log button (not supported)"; return false; } if (job->auditLogError().code() == GPG_ERR_NO_DATA) { qCDebug(KLEO_UI_LOG) << "not showing audit log button (GPG_ERR_NO_DATA)"; return false; } if (!job->auditLogError() && job->auditLogAsHtml().isEmpty()) { qCDebug(KLEO_UI_LOG) << "not showing audit log button (success, but result empty)"; return false; } return true; } // static void MessageBox::make(QWidget *parent, QMessageBox::Icon icon, const QString &text, const Job *job, const QString &caption, KMessageBox::Options options) { QDialog *dialog = new QDialog(parent); dialog->setWindowTitle(caption); QDialogButtonBox *box = new QDialogButtonBox(showAuditLogButton(job) ? (QDialogButtonBox::Yes | QDialogButtonBox::No) : QDialogButtonBox::Yes, parent); QPushButton *yesButton = box->button(QDialogButtonBox::Yes); yesButton->setDefault(true); //dialog->setEscapeButton(KDialog::Yes); dialog->setObjectName(QStringLiteral("error")); dialog->setModal(true); KGuiItem::assign(yesButton, KStandardGuiItem::ok()); if (GpgME::hasFeature(GpgME::AuditLogFeature, 0)) { KGuiItem::assign(box->button(QDialogButtonBox::No), KGuiItem(i18n("&Show Audit Log"))); } if (QDialogButtonBox::No == KMessageBox::createKMessageBox(dialog, box, icon, text, QStringList(), QString(), nullptr, options)) { auditLog(nullptr, job); } } diff --git a/src/utils/formatting.cpp b/src/utils/formatting.cpp index c927be178..265980e1a 100644 --- a/src/utils/formatting.cpp +++ b/src/utils/formatting.cpp @@ -1,1059 +1,1058 @@ /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*- utils/formatting.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 "formatting.h" #include "kleo/dn.h" #include #include #include #include #include #include #include #include #include #include // for Qt::escape #include #include -#include #include #include "models/keycache.h" using namespace GpgME; using namespace Kleo; // // Name // QString Formatting::prettyName(int proto, const char *id, const char *name_, const char *comment_) { if (proto == OpenPGP) { const QString name = QString::fromUtf8(name_); if (name.isEmpty()) { return QString(); } const QString comment = QString::fromUtf8(comment_); if (comment.isEmpty()) { return name; } return QStringLiteral("%1 (%2)").arg(name, comment); } if (proto == CMS) { const DN subject(id); const QString cn = subject[QStringLiteral("CN")].trimmed(); if (cn.isEmpty()) { return subject.prettyDN(); } return cn; } return QString(); } QString Formatting::prettyNameAndEMail(int proto, const char *id, const char *name_, const char *email_, const char *comment_) { return prettyNameAndEMail(proto, QString::fromUtf8(id), QString::fromUtf8(name_), prettyEMail(email_, id), QString::fromUtf8(comment_)); } QString Formatting::prettyNameAndEMail(int proto, const QString &id, const QString &name, const QString &email, const QString &comment) { if (proto == OpenPGP) { if (name.isEmpty()) { if (email.isEmpty()) { return QString(); } else if (comment.isEmpty()) { return QStringLiteral("<%1>").arg(email); } else { return QStringLiteral("(%2) <%1>").arg(email, comment); } } if (email.isEmpty()) { if (comment.isEmpty()) { return name; } else { return QStringLiteral("%1 (%2)").arg(name, comment); } } if (comment.isEmpty()) { return QStringLiteral("%1 <%2>").arg(name, email); } else { return QStringLiteral("%1 (%3) <%2>").arg(name, email, comment); } } if (proto == CMS) { const DN subject(id); const QString cn = subject[QStringLiteral("CN")].trimmed(); if (cn.isEmpty()) { return subject.prettyDN(); } return cn; } return QString(); } QString Formatting::prettyUserID(const UserID &uid) { if (uid.parent().protocol() == OpenPGP) { return prettyNameAndEMail(uid); } const QByteArray id = QByteArray(uid.id()).trimmed(); if (id.startsWith('<')) { return prettyEMail(uid.email(), uid.id()); } if (id.startsWith('(')) // ### parse uri/dns: { return QString::fromUtf8(uid.id()); } else { return DN(uid.id()).prettyDN(); } } QString Formatting::prettyKeyID(const char *id) { if (!id) { return QString(); } return QLatin1String("0x") + QString::fromLatin1(id).toUpper(); } QString Formatting::prettyNameAndEMail(const UserID &uid) { return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment()); } QString Formatting::prettyNameAndEMail(const Key &key) { return prettyNameAndEMail(key.userID(0)); } QString Formatting::prettyName(const Key &key) { return prettyName(key.userID(0)); } QString Formatting::prettyName(const UserID &uid) { return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment()); } QString Formatting::prettyName(const UserID::Signature &sig) { return prettyName(OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment()); } // // EMail // QString Formatting::prettyEMail(const Key &key) { for (unsigned int i = 0, end = key.numUserIDs(); i < end; ++i) { const QString email = prettyEMail(key.userID(i)); if (!email.isEmpty()) { return email; } } return QString(); } QString Formatting::prettyEMail(const UserID &uid) { return prettyEMail(uid.email(), uid.id()); } QString Formatting::prettyEMail(const UserID::Signature &sig) { return prettyEMail(sig.signerEmail(), sig.signerUserID()); } QString Formatting::prettyEMail(const char *email_, const char *id) { QString email, name, comment; if (email_ && KEmailAddress::splitAddress(QString::fromUtf8(email_), name, email, comment) == KEmailAddress::AddressOk) { return email; } else { return DN(id)[QStringLiteral("EMAIL")].trimmed(); } } // // Tooltip // namespace { static QString protect_whitespace(QString s) { static const QLatin1Char SP(' '), NBSP('\xA0'); return s.replace(SP, NBSP); } template QString format_row(const QString &field, const T_arg &arg) { return QStringLiteral("%1:%2").arg(protect_whitespace(field), arg); } QString format_row(const QString &field, const QString &arg) { return QStringLiteral("%1:%2").arg(protect_whitespace(field), arg.toHtmlEscaped()); } QString format_row(const QString &field, const char *arg) { return format_row(field, QString::fromUtf8(arg)); } QString format_keytype(const Key &key) { const Subkey subkey = key.subkey(0); if (key.hasSecret()) { return i18n("%1-bit %2 (secret key available)", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString())); } else { return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString())); } } QString format_subkeytype(const Subkey &subkey) { const auto algo = subkey.publicKeyAlgorithm(); if (algo == Subkey::AlgoECC || algo == Subkey::AlgoECDSA || algo == Subkey::AlgoECDH || algo == Subkey::AlgoEDDSA) { return QString::fromStdString(subkey.algoName()); } return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString())); } QString format_keyusage(const Key &key) { QStringList capabilities; if (key.canReallySign()) { if (key.isQualified()) { capabilities.push_back(i18n("Signing (Qualified)")); } else { capabilities.push_back(i18n("Signing")); } } if (key.canEncrypt()) { capabilities.push_back(i18n("Encryption")); } if (key.canCertify()) { capabilities.push_back(i18n("Certifying User-IDs")); } if (key.canAuthenticate()) { capabilities.push_back(i18n("SSH Authentication")); } return capabilities.join(QStringLiteral(", ")); } QString format_subkeyusage(const Subkey &subkey) { QStringList capabilities; if (subkey.canSign()) { if (subkey.isQualified()) { capabilities.push_back(i18n("Signing (Qualified)")); } else { capabilities.push_back(i18n("Signing")); } } if (subkey.canEncrypt()) { capabilities.push_back(i18n("Encryption")); } if (subkey.canCertify()) { capabilities.push_back(i18n("Certifying User-IDs")); } if (subkey.canAuthenticate()) { capabilities.push_back(i18n("SSH Authentication")); } return capabilities.join(QStringLiteral(", ")); } static QString time_t2string(time_t t) { QDateTime dt; dt.setTime_t(t); return QLocale().toString(dt, QLocale::ShortFormat); } static QString make_red(const QString &txt) { return QLatin1String("") + txt.toHtmlEscaped() + QLatin1String(""); } } QString Formatting::toolTip(const Key &key, int flags) { if (flags == 0 || (key.protocol() != CMS && key.protocol() != OpenPGP)) { return QString(); } const Subkey subkey = key.subkey(0); QString result; if (flags & Validity) { if (key.protocol() == OpenPGP || (key.keyListMode() & Validate)) if (key.isRevoked()) { result = make_red(i18n("Revoked")); } else if (key.isExpired()) { result = make_red(i18n("Expired")); } else if (key.isDisabled()) { result = i18n("Disabled"); } else { unsigned int fullyTrusted = 0; for (const auto &uid: key.userIDs()) { if (uid.validity() >= UserID::Validity::Full) { fullyTrusted++; } } if (fullyTrusted == key.numUserIDs()) { result = i18n("All User-IDs are certified."); const auto compliance = complianceStringForKey(key); if (!compliance.isEmpty()) { result += QStringLiteral("
") + compliance; } } else { result = i18np("One User-ID is not certified.", "%1 User-IDs are not certified.", key.numUserIDs() - fullyTrusted); } } else { result = i18n("The validity cannot be checked at the moment."); } } if (flags == Validity) { return result; } result += QLatin1String(""); if (key.protocol() == CMS) { if (flags & SerialNumber) { result += format_row(i18n("Serial number"), key.issuerSerial()); } if (flags & Issuer) { result += format_row(i18n("Issuer"), key.issuerName()); } } if (flags & UserIDs) { const std::vector uids = key.userIDs(); if (!uids.empty()) result += format_row(key.protocol() == CMS ? i18n("Subject") : i18n("User-ID"), prettyUserID(uids.front())); if (uids.size() > 1) for (std::vector::const_iterator it = uids.begin() + 1, end = uids.end(); it != end; ++it) if (!it->isRevoked() && !it->isInvalid()) { result += format_row(i18n("a.k.a."), prettyUserID(*it)); } } if (flags & ExpiryDates) { result += format_row(i18n("Created"), time_t2string(subkey.creationTime())); if (key.isExpired()) { result += format_row(i18n("Expired"), time_t2string(subkey.expirationTime())); } else if (!subkey.neverExpires()) { result += format_row(i18n("Expires"), time_t2string(subkey.expirationTime())); } } if (flags & CertificateType) { result += format_row(i18n("Type"), format_keytype(key)); } if (flags & CertificateUsage) { result += format_row(i18n("Usage"), format_keyusage(key)); } if (flags & KeyID) { result += format_row(i18n("Key-ID"), QString::fromLatin1(key.shortKeyID())); } if (flags & Fingerprint) { result += format_row(i18n("Fingerprint"), key.primaryFingerprint()); } if (flags & OwnerTrust) { if (key.protocol() == OpenPGP) { result += format_row(i18n("Certification trust"), ownerTrustShort(key)); } else if (key.isRoot()) { result += format_row(i18n("Trusted issuer?"), key.userID(0).validity() == UserID::Ultimate ? i18n("Yes") : /* else */ i18n("No")); } } if (flags & StorageLocation) { if (const char *card = subkey.cardSerialNumber()) { result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card))); } else { result += format_row(i18n("Stored"), i18nc("stored...", "on this computer")); } } if (flags & Subkeys) { for (const auto &sub: key.subkeys()) { result += QLatin1String("
"); result += format_row(i18n("Subkey"), sub.fingerprint()); if (sub.isRevoked()) { result += format_row(i18n("Status"), i18n("Revoked")); } else if (sub.isExpired()) { result += format_row(i18n("Status"), i18n("Expired")); } if (flags & ExpiryDates) { result += format_row(i18n("Created"), time_t2string(sub.creationTime())); if (key.isExpired()) { result += format_row(i18n("Expired"), time_t2string(sub.expirationTime())); } else if (!subkey.neverExpires()) { result += format_row(i18n("Expires"), time_t2string(sub.expirationTime())); } } if (flags & CertificateType) { result += format_row(i18n("Type"), format_subkeytype(sub)); } if (flags & CertificateUsage) { result += format_row(i18n("Usage"), format_subkeyusage(sub)); } if (flags & StorageLocation) { if (const char *card = sub.cardSerialNumber()) { result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card))); } else { result += format_row(i18n("Stored"), i18nc("stored...", "on this computer")); } } } } result += QLatin1String("
"); return result; } // // Creation and Expiration // namespace { static QDate time_t2date(time_t t) { if (!t) { return QDate(); } QDateTime dt; dt.setTime_t(t); return dt.date(); } static QString date2string(const QDate &date) { return QLocale().toString(date, QLocale::ShortFormat); } template QString expiration_date_string(const T &tee) { return tee.neverExpires() ? QString() : date2string(time_t2date(tee.expirationTime())); } template QDate creation_date(const T &tee) { return time_t2date(tee.creationTime()); } template QDate expiration_date(const T &tee) { return time_t2date(tee.expirationTime()); } } QString Formatting::dateString(time_t t) { return date2string(time_t2date(t)); } QString Formatting::expirationDateString(const Key &key) { return expiration_date_string(key.subkey(0)); } QString Formatting::expirationDateString(const Subkey &subkey) { return expiration_date_string(subkey); } QString Formatting::expirationDateString(const UserID::Signature &sig) { return expiration_date_string(sig); } QDate Formatting::expirationDate(const Key &key) { return expiration_date(key.subkey(0)); } QDate Formatting::expirationDate(const Subkey &subkey) { return expiration_date(subkey); } QDate Formatting::expirationDate(const UserID::Signature &sig) { return expiration_date(sig); } QString Formatting::creationDateString(const Key &key) { return date2string(creation_date(key.subkey(0))); } QString Formatting::creationDateString(const Subkey &subkey) { return date2string(creation_date(subkey)); } QString Formatting::creationDateString(const UserID::Signature &sig) { return date2string(creation_date(sig)); } QDate Formatting::creationDate(const Key &key) { return creation_date(key.subkey(0)); } QDate Formatting::creationDate(const Subkey &subkey) { return creation_date(subkey); } QDate Formatting::creationDate(const UserID::Signature &sig) { return creation_date(sig); } // // Types // QString Formatting::displayName(Protocol p) { if (p == CMS) { return i18nc("X.509/CMS encryption standard", "X.509"); } if (p == OpenPGP) { return i18n("OpenPGP"); } return i18nc("Unknown encryption protocol", "Unknown"); } QString Formatting::type(const Key &key) { return displayName(key.protocol()); } QString Formatting::type(const Subkey &subkey) { return QString::fromUtf8(subkey.publicKeyAlgorithmAsString()); } // // Status / Validity // QString Formatting::ownerTrustShort(const Key &key) { return ownerTrustShort(key.ownerTrust()); } QString Formatting::ownerTrustShort(Key::OwnerTrust trust) { switch (trust) { case Key::Unknown: return i18nc("unknown trust level", "unknown"); case Key::Never: return i18n("untrusted"); case Key::Marginal: return i18nc("marginal trust", "marginal"); case Key::Full: return i18nc("full trust", "full"); case Key::Ultimate: return i18nc("ultimate trust", "ultimate"); case Key::Undefined: return i18nc("undefined trust", "undefined"); default: Q_ASSERT(!"unexpected owner trust value"); break; } return QString(); } QString Formatting::validityShort(const Subkey &subkey) { if (subkey.isRevoked()) { return i18n("revoked"); } if (subkey.isExpired()) { return i18n("expired"); } if (subkey.isDisabled()) { return i18n("disabled"); } if (subkey.isInvalid()) { return i18n("invalid"); } return i18nc("as in good/valid signature", "good"); } QString Formatting::validityShort(const UserID &uid) { if (uid.isRevoked()) { return i18n("revoked"); } if (uid.isInvalid()) { return i18n("invalid"); } switch (uid.validity()) { case UserID::Unknown: return i18nc("unknown trust level", "unknown"); case UserID::Undefined: return i18nc("undefined trust", "undefined"); case UserID::Never: return i18n("untrusted"); case UserID::Marginal: return i18nc("marginal trust", "marginal"); case UserID::Full: return i18nc("full trust", "full"); case UserID::Ultimate: return i18nc("ultimate trust", "ultimate"); } return QString(); } QString Formatting::validityShort(const UserID::Signature &sig) { switch (sig.status()) { case UserID::Signature::NoError: if (!sig.isInvalid()) { /* See RFC 4880 Section 5.2.1 */ switch (sig.certClass()) { case 0x10: /* Generic */ case 0x11: /* Persona */ case 0x12: /* Casual */ case 0x13: /* Positive */ return i18n("valid"); case 0x30: return i18n("revoked"); default: return i18n("class %1", sig.certClass()); } } Q_FALLTHROUGH(); // fall through: case UserID::Signature::GeneralError: return i18n("invalid"); case UserID::Signature::SigExpired: return i18n("expired"); case UserID::Signature::KeyExpired: return i18n("certificate expired"); case UserID::Signature::BadSignature: return i18nc("fake/invalid signature", "bad"); case UserID::Signature::NoPublicKey: { /* GnuPG returns the same error for no public key as for expired * or revoked certificates. */ const auto key = KeyCache::instance()->findByKeyIDOrFingerprint (sig.signerKeyID()); if (key.isNull()) { return i18n("no public key"); } else if (key.isExpired()) { return i18n("key expired"); } else if (key.isRevoked()) { return i18n("key revoked"); } else if (key.isDisabled()) { return i18n("key disabled"); } /* can't happen */ return QStringLiteral("unknwon"); } } return QString(); } QIcon Formatting::validityIcon(const UserID::Signature &sig) { switch (sig.status()) { case UserID::Signature::NoError: if (!sig.isInvalid()) { /* See RFC 4880 Section 5.2.1 */ switch (sig.certClass()) { case 0x10: /* Generic */ case 0x11: /* Persona */ case 0x12: /* Casual */ case 0x13: /* Positive */ return QIcon::fromTheme(QStringLiteral("emblem-success")); case 0x30: return QIcon::fromTheme(QStringLiteral("emblem-error")); default: return QIcon(); } } Q_FALLTHROUGH(); // fall through: case UserID::Signature::BadSignature: case UserID::Signature::GeneralError: return QIcon::fromTheme(QStringLiteral("emblem-error")); case UserID::Signature::SigExpired: case UserID::Signature::KeyExpired: return QIcon::fromTheme(QStringLiteral("emblem-information")); case UserID::Signature::NoPublicKey: return QIcon::fromTheme(QStringLiteral("emblem-question")); } return QIcon(); } QString Formatting::formatKeyLink(const Key &key) { if (key.isNull()) { return QString(); } return QStringLiteral("%2").arg(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key)); } QString Formatting::formatForComboBox(const GpgME::Key &key) { const QString name = prettyName(key); QString mail = prettyEMail(key); if (!mail.isEmpty()) { mail = QLatin1Char('<') + mail + QLatin1Char('>'); } return i18nc("name, email, key id", "%1 %2 (%3)", name, mail, QLatin1String(key.shortKeyID())).simplified(); } namespace { static QString keyToString(const Key &key) { Q_ASSERT(!key.isNull()); const QString email = Formatting::prettyEMail(key); const QString name = Formatting::prettyName(key); if (name.isEmpty()) { return email; } else if (email.isEmpty()) { return name; } else { return QStringLiteral("%1 <%2>").arg(name, email); } } } const char *Formatting::summaryToString(const Signature::Summary summary) { if (summary & Signature::Red) { return "RED"; } if (summary & Signature::Green) { return "GREEN"; } return "YELLOW"; } QString Formatting::signatureToString(const Signature &sig, const Key &key) { if (sig.isNull()) { return QString(); } const bool red = (sig.summary() & Signature::Red); const bool valid = (sig.summary() & Signature::Valid); if (red) if (key.isNull()) if (const char *fpr = sig.fingerprint()) { return i18n("Bad signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString())); } else { return i18n("Bad signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString())); } else { return i18n("Bad signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString())); } else if (valid) if (key.isNull()) if (const char *fpr = sig.fingerprint()) { return i18n("Good signature by unknown certificate %1.", QString::fromLatin1(fpr)); } else { return i18n("Good signature by an unknown certificate."); } else { return i18n("Good signature by %1.", keyToString(key)); } else if (key.isNull()) if (const char *fpr = sig.fingerprint()) { return i18n("Invalid signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString())); } else { return i18n("Invalid signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString())); } else { return i18n("Invalid signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString())); } } // // ImportResult // QString Formatting::importMetaData(const Import &import, const QStringList &ids) { const QString result = importMetaData(import); if (result.isEmpty()) { return QString(); } else return result + QLatin1Char('\n') + i18n("This certificate was imported from the following sources:") + QLatin1Char('\n') + ids.join(QLatin1Char('\n')); } QString Formatting::importMetaData(const Import &import) { if (import.isNull()) { return QString(); } if (import.error().isCanceled()) { return i18n("The import of this certificate was canceled."); } if (import.error()) return i18n("An error occurred importing this certificate: %1", QString::fromLocal8Bit(import.error().asString())); const unsigned int status = import.status(); if (status & Import::NewKey) return (status & Import::ContainedSecretKey) ? i18n("This certificate was new to your keystore. The secret key is available.") : i18n("This certificate is new to your keystore."); QStringList results; if (status & Import::NewUserIDs) { results.push_back(i18n("New user-ids were added to this certificate by the import.")); } if (status & Import::NewSignatures) { results.push_back(i18n("New signatures were added to this certificate by the import.")); } if (status & Import::NewSubkeys) { results.push_back(i18n("New subkeys were added to this certificate by the import.")); } return results.empty() ? i18n("The import contained no new data for this certificate. It is unchanged.") : results.join(QLatin1Char('\n')); } // // Overview in CertificateDetailsDialog // QString Formatting::formatOverview(const Key &key) { return toolTip(key, AllOptions); } QString Formatting::usageString(const Subkey &sub) { QStringList usageStrings; if (sub.canCertify()) { usageStrings << i18n("Certify"); } if (sub.canSign()) { usageStrings << i18n("Sign"); } if (sub.canEncrypt()) { usageStrings << i18n("Encrypt"); } if (sub.canAuthenticate()) { usageStrings << i18n("Authenticate"); } return usageStrings.join(QStringLiteral(", ")); } QString Formatting::summaryLine(const Key &key) { return keyToString(key) + QLatin1Char(' ') + i18nc("(validity, protocol, creation date)", "(%1, %2, created: %3)", Formatting::complianceStringShort(key), displayName(key.protocol()), Formatting::creationDateString(key)); } // Icon for certificate selection indication QIcon Formatting::iconForUid(const UserID &uid) { switch (uid.validity()) { case UserID::Ultimate: case UserID::Full: case UserID::Marginal: return QIcon::fromTheme(QStringLiteral("emblem-success")); case UserID::Never: return QIcon::fromTheme(QStringLiteral("emblem-error")); case UserID::Undefined: case UserID::Unknown: default: return QIcon::fromTheme(QStringLiteral("emblem-information")); } } QString Formatting::validity(const UserID &uid) { switch (uid.validity()) { case UserID::Ultimate: return i18n("The certificate is marked as your own."); case UserID::Full: return i18n("The certificate belongs to this recipient."); case UserID::Marginal: return i18n("The trust model indicates marginally that the certificate belongs to this recipient."); case UserID::Never: return i18n("This certificate should not be used."); case UserID::Undefined: case UserID::Unknown: default: return i18n("There is no indication that this certificate belongs to this recipient."); } } bool Formatting::uidsHaveFullValidity(const GpgME::Key &key) { bool oneValid = false; for (const auto &uid: key.userIDs()) { if (uid.isRevoked()) { /* Skip revoked uids */ continue; } if (uid.validity() < UserID::Validity::Full) { return false; } /* Only return true if we have found at least one * valid uid. E.g. if all uids are revoked we do * not want to return true here. */ oneValid = true; } return oneValid; } QString Formatting::complianceMode() { const QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig(); if (!config) { return QString(); } const QGpgME::CryptoConfigEntry *const entry = config->entry(QStringLiteral("gpg"), QStringLiteral("Configuration"), QStringLiteral("compliance")); if (!entry || entry->stringValue() == QStringLiteral("gnupg")) { return QString(); } return entry->stringValue(); } bool Formatting::isKeyDeVs(const GpgME::Key &key) { for (const auto &sub: key.subkeys()) { if (sub.isExpired() || sub.isRevoked()) { // Ignore old subkeys continue; } if (!sub.isDeVs()) { return false; } } return true; } QString Formatting::complianceStringForKey(const GpgME::Key &key) { // There will likely be more in the future for other institutions // for now we only have DE-VS if (complianceMode() == QStringLiteral("de-vs")) { if (uidsHaveFullValidity(key) && isKeyDeVs(key)) { return i18nc("VS-NfD conforming is a German standard for restricted documents. For which special restrictions about algorithms apply. The string describes if a key is compliant with that..", "May be used for VS-NfD-compliant communication."); } else { return i18nc("VS-NfD-conforming is a German standard for restricted documents. For which special restrictions about algorithms apply. The string describes if a key is compliant to that..", "May not be used for VS-NfD-compliant communication."); } } return QString(); } QString Formatting::complianceStringShort(const GpgME::Key &key) { if (Formatting::uidsHaveFullValidity(key)) { if (complianceMode() == QStringLiteral("de-vs") && Formatting::isKeyDeVs(key)) { return QStringLiteral("★ ") + i18nc("VS-NfD-conforming is a German standard for restricted documents for which special restrictions about algorithms apply. The string states that a key is compliant with that.", "VS-NfD-compliant"); } return i18nc("As in all user IDs are valid.", "certified"); } if (key.isExpired()) { return i18n("expired"); } return i18nc("As in not all user IDs are valid.", "not certified"); } QString Formatting::prettyID(const char *id) { if (!id) { return QString(); } return QString::fromLatin1(id).toUpper().replace(QRegularExpression(QStringLiteral("(....)")), QStringLiteral("\\1 ")).trimmed(); } QString Formatting::origin(int o) { switch (o) { case Key::OriginKS: return i18n("Keyserver"); case Key::OriginDane: return QStringLiteral("DANE"); case Key::OriginWKD: return QStringLiteral("WKD"); case Key::OriginURL: return QStringLiteral("URL"); case Key::OriginFile: return i18n("File import"); case Key::OriginSelf: return i18n("Generated"); case Key::OriginOther: case Key::OriginUnknown: default: return i18n("Unknown"); } }