SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / kleopatra / utils / formatting.cpp
bloba08a6b21fdccc4434bd64d3cae6e5db4243d7253
1 /* -*- mode: c++; c-basic-offset:4 -*-
2 utils/formatting.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2007 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include <config-kleopatra.h>
35 #include "formatting.h"
37 #include <utils/kleo_assert.h>
39 #include <Libkleo/Dn>
41 #include <kmime/kmime_header_parsing.h>
43 #include <gpgme++/key.h>
44 #include <gpgme++/importresult.h>
46 #include <KLocalizedString>
48 #include <QString>
49 #include <QStringList>
50 #include <QDateTime>
51 #include <QTextDocument> // for Qt::escape
52 #include <QLocale>
54 using namespace GpgME;
55 using namespace Kleo;
56 using namespace KMime::Types;
57 using namespace KMime::HeaderParsing;
60 // Name
63 QString Formatting::prettyName(int proto, const char *id, const char *name_, const char *comment_)
66 if (proto == OpenPGP) {
67 const QString name = QString::fromUtf8(name_);
68 if (name.isEmpty()) {
69 return QString();
71 const QString comment = QString::fromUtf8(comment_);
72 if (comment.isEmpty()) {
73 return name;
75 return QStringLiteral("%1 (%2)").arg(name, comment);
78 if (proto == CMS) {
79 const DN subject(id);
80 const QString cn = subject[QStringLiteral("CN")].trimmed();
81 if (cn.isEmpty()) {
82 return subject.prettyDN();
84 return cn;
87 return QString();
90 QString Formatting::prettyNameAndEMail(int proto, const char *id, const char *name_, const char *email_, const char *comment_)
92 return prettyNameAndEMail(proto, QString::fromUtf8(id), QString::fromUtf8(name_), prettyEMail(email_, id), QString::fromUtf8(comment_));
95 QString Formatting::prettyNameAndEMail(int proto, const QString &id, const QString &name, const QString &email, const QString &comment)
98 if (proto == OpenPGP) {
99 if (name.isEmpty()) {
100 if (email.isEmpty()) {
101 return QString();
102 } else if (comment.isEmpty()) {
103 return QStringLiteral("<%1>").arg(email);
104 } else {
105 return QStringLiteral("(%2) <%1>").arg(email, comment);
108 if (email.isEmpty()) {
109 if (comment.isEmpty()) {
110 return name;
111 } else {
112 return QStringLiteral("%1 (%2)").arg(name, comment);
115 if (comment.isEmpty()) {
116 return QStringLiteral("%1 <%2>").arg(name, email);
117 } else {
118 return QStringLiteral("%1 (%3) <%2>").arg(name, email, comment);
122 if (proto == CMS) {
123 const DN subject(id);
124 const QString cn = subject[QStringLiteral("CN")].trimmed();
125 if (cn.isEmpty()) {
126 return subject.prettyDN();
128 return cn;
130 return QString();
133 QString Formatting::prettyUserID(const UserID &uid)
135 if (uid.parent().protocol() == OpenPGP) {
136 return prettyNameAndEMail(uid);
138 const QByteArray id = QByteArray(uid.id()).trimmed();
139 if (id.startsWith('<')) {
140 return prettyEMail(uid.email(), uid.id());
142 if (id.startsWith('('))
143 // ### parse uri/dns:
145 return QString::fromUtf8(uid.id());
146 } else {
147 return DN(uid.id()).prettyDN();
151 QString Formatting::prettyKeyID(const char *id)
153 if (!id) {
154 return QString();
156 return QLatin1String("0x") + QString::fromLatin1(id).toUpper();
159 QString Formatting::prettyNameAndEMail(const UserID &uid)
161 return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment());
164 QString Formatting::prettyNameAndEMail(const Key &key)
166 return prettyNameAndEMail(key.userID(0));
169 QString Formatting::prettyName(const Key &key)
171 return prettyName(key.userID(0));
174 QString Formatting::prettyName(const UserID &uid)
176 return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment());
179 QString Formatting::prettyName(const UserID::Signature &sig)
181 return prettyName(OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment());
185 // EMail
188 QString Formatting::prettyEMail(const Key &key)
190 for (unsigned int i = 0, end = key.numUserIDs(); i < end; ++i) {
191 const QString email = prettyEMail(key.userID(i));
192 if (!email.isEmpty()) {
193 return email;
196 return QString();
199 QString Formatting::prettyEMail(const UserID &uid)
201 return prettyEMail(uid.email(), uid.id());
204 QString Formatting::prettyEMail(const UserID::Signature &sig)
206 return prettyEMail(sig.signerEmail(), sig.signerUserID());
209 QString Formatting::prettyEMail(const char *email_, const char *id)
211 Mailbox mailBox;
212 if (email_ && parseMailbox(email_, email_ + strlen(email_), mailBox)) {
213 return mailBox.addrSpec().asPrettyString();
214 } else {
215 return DN(id)[QStringLiteral("EMAIL")].trimmed();
220 // Tooltip
223 namespace
226 static QString protect_whitespace(QString s)
228 static const QLatin1Char SP(' '), NBSP('\xA0');
229 return s.replace(SP, NBSP);
232 template <typename T_arg>
233 QString format_row(const QString &field, const T_arg &arg)
235 return i18n("<tr><th>%1:</th><td>%2</td></tr>", protect_whitespace(field), arg);
237 QString format_row(const QString &field, const QString &arg)
239 return i18n("<tr><th>%1:</th><td>%2</td></tr>", protect_whitespace(field), arg.toHtmlEscaped());
241 QString format_row(const QString &field, const char *arg)
243 return format_row(field, QString::fromUtf8(arg));
246 QString format_keytype(const Key &key)
248 const Subkey subkey = key.subkey(0);
249 if (key.hasSecret()) {
250 return i18n("%1-bit %2 (secret key available)", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
251 } else {
252 return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
256 QString format_keyusage(const Key &key)
258 QStringList capabilities;
259 if (key.canReallySign()) {
260 if (key.isQualified()) {
261 capabilities.push_back(i18n("Signing EMails and Files (Qualified)"));
262 } else {
263 capabilities.push_back(i18n("Signing EMails and Files"));
266 if (key.canEncrypt()) {
267 capabilities.push_back(i18n("Encrypting EMails and Files"));
269 if (key.canCertify()) {
270 capabilities.push_back(i18n("Certifying other Certificates"));
272 if (key.canAuthenticate()) {
273 capabilities.push_back(i18n("Authenticate against Servers"));
275 return capabilities.join(QStringLiteral(", "));
278 static QString time_t2string(time_t t)
280 QDateTime dt;
281 dt.setTime_t(t);
282 return QLocale().toString(dt, QLocale::ShortFormat);
285 static QString make_red(const QString &txt)
287 return QLatin1String("<font color=\"red\">") + txt.toHtmlEscaped() + QLatin1String("</font>");
292 QString Formatting::toolTip(const Key &key, int flags)
294 if (flags == 0 || (key.protocol() != CMS && key.protocol() != OpenPGP)) {
295 return QString();
298 const Subkey subkey = key.subkey(0);
300 QString result;
301 if (flags & Validity) {
302 if (key.protocol() == OpenPGP || (key.keyListMode() & Validate))
303 if (key.isRevoked()) {
304 result += make_red(i18n("This certificate has been revoked."));
305 } else if (key.isExpired()) {
306 result += make_red(i18n("This certificate has expired."));
307 } else if (key.isDisabled()) {
308 result += i18n("This certificate has been disabled locally.");
309 } else {
310 result += i18n("This certificate is currently valid.");
312 else {
313 result += i18n("The validity of this certificate cannot be checked at the moment.");
316 if (flags == Validity) {
317 return result;
320 result += QLatin1String("<table border=\"0\">");
321 if (key.protocol() == CMS) {
322 if (flags & SerialNumber) {
323 result += format_row(i18n("Serial number"), key.issuerSerial());
325 if (flags & Issuer) {
326 result += format_row(i18n("Issuer"), key.issuerName());
329 if (flags & UserIDs) {
330 const std::vector<UserID> uids = key.userIDs();
331 if (!uids.empty())
332 result += format_row(key.protocol() == CMS
333 ? i18n("Subject")
334 : i18n("User-ID"), prettyUserID(uids.front()));
335 if (uids.size() > 1)
336 for (std::vector<UserID>::const_iterator it = uids.begin() + 1, end = uids.end(); it != end; ++it)
337 if (!it->isRevoked() && !it->isInvalid()) {
338 result += format_row(i18n("a.k.a."), prettyUserID(*it));
341 if (flags & ExpiryDates)
342 result += format_row(i18n("Validity"),
343 subkey.neverExpires()
344 ? i18n("from %1 until forever", time_t2string(subkey.creationTime()))
345 : i18n("from %1 through %2", time_t2string(subkey.creationTime()), time_t2string(subkey.expirationTime())));
346 if (flags & CertificateType) {
347 result += format_row(i18n("Certificate type"), format_keytype(key));
349 if (flags & CertificateUsage) {
350 result += format_row(i18n("Certificate usage"), format_keyusage(key));
352 if (flags & KeyID) {
353 result += format_row(i18n("Key-ID"), QString::fromLatin1(key.shortKeyID()));
355 if (flags & Fingerprint) {
356 result += format_row(i18n("Fingerprint"), key.primaryFingerprint());
358 if (flags & OwnerTrust) {
359 if (key.protocol() == OpenPGP) {
360 result += format_row(i18n("Ownertrust"), ownerTrustShort(key));
361 } else if (key.isRoot()) {
362 result += format_row(i18n("Trusted issuer?"),
363 key.userID(0).validity() == UserID::Ultimate ? i18n("Yes") :
364 /* else */ i18n("No"));
368 if (flags & StorageLocation) {
369 if (const char *card = subkey.cardSerialNumber()) {
370 result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card)));
371 } else {
372 result += format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
375 result += QLatin1String("</table>");
377 return result;
381 // Creation and Expiration
384 namespace
386 static QDate time_t2date(time_t t)
388 if (!t) {
389 return QDate();
391 QDateTime dt;
392 dt.setTime_t(t);
393 return dt.date();
395 static QString date2string(const QDate &date)
397 return QLocale().toString(date, QLocale::ShortFormat);
400 template <typename T>
401 QString expiration_date_string(const T &tee)
403 return tee.neverExpires() ? QString() : date2string(time_t2date(tee.expirationTime()));
405 template <typename T>
406 QDate creation_date(const T &tee)
408 return time_t2date(tee.creationTime());
410 template <typename T>
411 QDate expiration_date(const T &tee)
413 return time_t2date(tee.expirationTime());
417 QString Formatting::expirationDateString(const Key &key)
419 return expiration_date_string(key.subkey(0));
422 QString Formatting::expirationDateString(const Subkey &subkey)
424 return expiration_date_string(subkey);
427 QString Formatting::expirationDateString(const UserID::Signature &sig)
429 return expiration_date_string(sig);
432 QDate Formatting::expirationDate(const Key &key)
434 return expiration_date(key.subkey(0));
437 QDate Formatting::expirationDate(const Subkey &subkey)
439 return expiration_date(subkey);
442 QDate Formatting::expirationDate(const UserID::Signature &sig)
444 return expiration_date(sig);
447 QString Formatting::creationDateString(const Key &key)
449 return date2string(creation_date(key.subkey(0)));
452 QString Formatting::creationDateString(const Subkey &subkey)
454 return date2string(creation_date(subkey));
457 QString Formatting::creationDateString(const UserID::Signature &sig)
459 return date2string(creation_date(sig));
462 QDate Formatting::creationDate(const Key &key)
464 return creation_date(key.subkey(0));
467 QDate Formatting::creationDate(const Subkey &subkey)
469 return creation_date(subkey);
472 QDate Formatting::creationDate(const UserID::Signature &sig)
474 return creation_date(sig);
478 // Types
481 QString Formatting::displayName(Protocol p)
483 if (p == CMS) {
484 return i18nc("X.509/CMS encryption standard", "X.509");
486 if (p == OpenPGP) {
487 return i18n("OpenPGP");
489 return i18nc("Unknown encryption protocol", "Unknown");
492 QString Formatting::type(const Key &key)
494 return displayName(key.protocol());
497 QString Formatting::type(const Subkey &subkey)
499 return QString::fromUtf8(subkey.publicKeyAlgorithmAsString());
503 // Status / Validity
506 QString Formatting::ownerTrustShort(const Key &key)
508 return ownerTrustShort(key.ownerTrust());
511 QString Formatting::ownerTrustShort(Key::OwnerTrust trust)
513 switch (trust) {
514 case Key::Unknown: return i18nc("unknown trust level", "unknown");
515 case Key::Never: return i18n("untrusted");
516 case Key::Marginal: return i18nc("marginal trust", "marginal");
517 case Key::Full: return i18nc("full trust", "full");
518 case Key::Ultimate: return i18nc("ultimate trust", "ultimate");
519 case Key::Undefined: return i18nc("undefined trust", "undefined");
520 default:
521 assert(!"unexpected owner trust value");
522 break;
524 return QString();
527 QString Formatting::validityShort(const Subkey &subkey)
529 if (subkey.isRevoked()) {
530 return i18n("revoked");
532 if (subkey.isExpired()) {
533 return i18n("expired");
535 if (subkey.isDisabled()) {
536 return i18n("disabled");
538 if (subkey.isInvalid()) {
539 return i18n("invalid");
541 return i18nc("as in good/valid signature", "good");
544 QString Formatting::validityShort(const UserID &uid)
546 if (uid.isRevoked()) {
547 return i18n("revoked");
549 if (uid.isInvalid()) {
550 return i18n("invalid");
552 switch (uid.validity()) {
553 case UserID::Unknown: return i18nc("unknown trust level", "unknown");
554 case UserID::Undefined: return i18nc("undefined trust", "undefined");
555 case UserID::Never: return i18n("untrusted");
556 case UserID::Marginal: return i18nc("marginal trust", "marginal");
557 case UserID::Full: return i18nc("full trust", "full");
558 case UserID::Ultimate: return i18nc("ultimate trust", "ultimate");
560 return QString();
563 QString Formatting::validityShort(const UserID::Signature &sig)
565 switch (sig.status()) {
566 case UserID::Signature::NoError:
567 if (!sig.isInvalid()) {
568 if (sig.certClass() > 0) {
569 return i18n("class %1", sig.certClass());
570 } else {
571 return i18nc("good/valid signature", "good");
574 // fall through:
575 case UserID::Signature::GeneralError:
576 return i18n("invalid");
577 case UserID::Signature::SigExpired: return i18n("expired");
578 case UserID::Signature::KeyExpired: return i18n("certificate expired");
579 case UserID::Signature::BadSignature: return i18nc("fake/invalid signature", "bad");
580 case UserID::Signature::NoPublicKey: return QString();
582 return QString();
585 QString Formatting::formatKeyLink(const Key &key)
587 if (key.isNull()) {
588 return QString();
590 return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key));
593 QString Formatting::formatForComboBox(const GpgME::Key &key)
595 const QString name = prettyName(key);
596 QString mail = prettyEMail(key);
597 if (!mail.isEmpty()) {
598 mail = QLatin1Char('<') + mail + QLatin1Char('>');
600 return i18nc("name, email, key id", "%1 %2 (%3)", name, mail, QLatin1String(key.shortKeyID())).simplified();
603 namespace
606 static QString keyToString(const Key &key)
609 kleo_assert(!key.isNull());
611 const QString email = Formatting::prettyEMail(key);
612 const QString name = Formatting::prettyName(key);
614 if (name.isEmpty()) {
615 return email;
616 } else if (email.isEmpty()) {
617 return name;
618 } else {
619 return QStringLiteral("%1 <%2>").arg(name, email);
625 const char *Formatting::summaryToString(const Signature::Summary summary)
627 if (summary & Signature::Red) {
628 return "RED";
630 if (summary & Signature::Green) {
631 return "GREEN";
633 return "YELLOW";
636 QString Formatting::signatureToString(const Signature &sig, const Key &key)
638 if (sig.isNull()) {
639 return QString();
642 const bool red = (sig.summary() & Signature::Red);
643 const bool valid = (sig.summary() & Signature::Valid);
645 if (red)
646 if (key.isNull())
647 if (const char *fpr = sig.fingerprint()) {
648 return i18n("Bad signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString()));
649 } else {
650 return i18n("Bad signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString()));
652 else {
653 return i18n("Bad signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString()));
656 else if (valid)
657 if (key.isNull())
658 if (const char *fpr = sig.fingerprint()) {
659 return i18n("Good signature by unknown certificate %1.", QString::fromLatin1(fpr));
660 } else {
661 return i18n("Good signature by an unknown certificate.");
663 else {
664 return i18n("Good signature by %1.", keyToString(key));
667 else if (key.isNull())
668 if (const char *fpr = sig.fingerprint()) {
669 return i18n("Invalid signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString()));
670 } else {
671 return i18n("Invalid signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString()));
673 else {
674 return i18n("Invalid signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString()));
679 // ImportResult
682 QString Formatting::importMetaData(const Import &import, const QStringList &ids)
684 const QString result = importMetaData(import);
685 if (result.isEmpty()) {
686 return QString();
687 } else
688 return result + QLatin1Char('\n') +
689 i18n("This certificate was imported from the following sources:") + QLatin1Char('\n') +
690 ids.join(QStringLiteral("\n"));
693 QString Formatting::importMetaData(const Import &import)
696 if (import.isNull()) {
697 return QString();
700 if (import.error().isCanceled()) {
701 return i18n("The import of this certificate was canceled.");
703 if (import.error())
704 return i18n("An error occurred importing this certificate: %1",
705 QString::fromLocal8Bit(import.error().asString()));
707 const unsigned int status = import.status();
708 if (status & Import::NewKey)
709 return (status & Import::ContainedSecretKey)
710 ? i18n("This certificate was new to your keystore. The secret key is available.")
711 : i18n("This certificate is new to your keystore.");
713 QStringList results;
714 if (status & Import::NewUserIDs) {
715 results.push_back(i18n("New user-ids were added to this certificate by the import."));
717 if (status & Import::NewSignatures) {
718 results.push_back(i18n("New signatures were added to this certificate by the import."));
720 if (status & Import::NewSubkeys) {
721 results.push_back(i18n("New subkeys were added to this certificate by the import."));
724 return results.empty()
725 ? i18n("The import contained no new data for this certificate. It is unchanged.")
726 : results.join(QStringLiteral("\n"));
730 // Overview in CertificateDetailsDialog
733 QString Formatting::formatOverview(const Key &key)
735 return toolTip(key, AllOptions);