1 /* -*- mode: c++; c-basic-offset:4 -*-
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
33 #include <config-kleopatra.h>
35 #include "formatting.h"
37 #include <utils/kleo_assert.h>
41 #include <kmime/kmime_header_parsing.h>
43 #include <gpgme++/key.h>
44 #include <gpgme++/importresult.h>
46 #include <KLocalizedString>
49 #include <QStringList>
51 #include <QTextDocument> // for Qt::escape
54 using namespace GpgME
;
56 using namespace KMime::Types
;
57 using namespace KMime::HeaderParsing
;
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_
);
71 const QString comment
= QString::fromUtf8(comment_
);
72 if (comment
.isEmpty()) {
75 return QStringLiteral("%1 (%2)").arg(name
, comment
);
80 const QString cn
= subject
[QStringLiteral("CN")].trimmed();
82 return subject
.prettyDN();
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
) {
100 if (email
.isEmpty()) {
102 } else if (comment
.isEmpty()) {
103 return QStringLiteral("<%1>").arg(email
);
105 return QStringLiteral("(%2) <%1>").arg(email
, comment
);
108 if (email
.isEmpty()) {
109 if (comment
.isEmpty()) {
112 return QStringLiteral("%1 (%2)").arg(name
, comment
);
115 if (comment
.isEmpty()) {
116 return QStringLiteral("%1 <%2>").arg(name
, email
);
118 return QStringLiteral("%1 (%3) <%2>").arg(name
, email
, comment
);
123 const DN
subject(id
);
124 const QString cn
= subject
[QStringLiteral("CN")].trimmed();
126 return subject
.prettyDN();
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());
147 return DN(uid
.id()).prettyDN();
151 QString
Formatting::prettyKeyID(const char *id
)
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());
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()) {
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
)
212 if (email_
&& parseMailbox(email_
, email_
+ strlen(email_
), mailBox
)) {
213 return mailBox
.addrSpec().asPrettyString();
215 return DN(id
)[QStringLiteral("EMAIL")].trimmed();
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()));
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)"));
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
)
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
)) {
298 const Subkey subkey
= key
.subkey(0);
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.");
310 result
+= i18n("This certificate is currently valid.");
313 result
+= i18n("The validity of this certificate cannot be checked at the moment.");
316 if (flags
== Validity
) {
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();
332 result
+= format_row(key
.protocol() == CMS
334 : i18n("User-ID"), prettyUserID(uids
.front()));
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
));
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
)));
372 result
+= format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
375 result
+= QLatin1String("</table>");
381 // Creation and Expiration
386 static QDate
time_t2date(time_t t
)
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
);
481 QString
Formatting::displayName(Protocol p
)
484 return i18nc("X.509/CMS encryption standard", "X.509");
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());
506 QString
Formatting::ownerTrustShort(const Key
&key
)
508 return ownerTrustShort(key
.ownerTrust());
511 QString
Formatting::ownerTrustShort(Key::OwnerTrust 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");
521 assert(!"unexpected owner trust value");
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");
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());
571 return i18nc("good/valid signature", "good");
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();
585 QString
Formatting::formatKeyLink(const Key
&key
)
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();
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()) {
616 } else if (email
.isEmpty()) {
619 return QStringLiteral("%1 <%2>").arg(name
, email
);
625 const char *Formatting::summaryToString(const Signature::Summary summary
)
627 if (summary
& Signature::Red
) {
630 if (summary
& Signature::Green
) {
636 QString
Formatting::signatureToString(const Signature
&sig
, const Key
&key
)
642 const bool red
= (sig
.summary() & Signature::Red
);
643 const bool valid
= (sig
.summary() & Signature::Valid
);
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()));
650 return i18n("Bad signature by an unknown certificate: %1", QString::fromLocal8Bit(sig
.status().asString()));
653 return i18n("Bad signature by %1: %2", keyToString(key
), QString::fromLocal8Bit(sig
.status().asString()));
658 if (const char *fpr
= sig
.fingerprint()) {
659 return i18n("Good signature by unknown certificate %1.", QString::fromLatin1(fpr
));
661 return i18n("Good signature by an unknown certificate.");
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()));
671 return i18n("Invalid signature by an unknown certificate: %1", QString::fromLocal8Bit(sig
.status().asString()));
674 return i18n("Invalid signature by %1: %2", keyToString(key
), QString::fromLocal8Bit(sig
.status().asString()));
682 QString
Formatting::importMetaData(const Import
&import
, const QStringList
&ids
)
684 const QString result
= importMetaData(import
);
685 if (result
.isEmpty()) {
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()) {
700 if (import
.error().isCanceled()) {
701 return i18n("The import of this certificate was canceled.");
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.");
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
);