12#include <config-libkleo.h>
14#include "formatting.h"
18#include "compliance.h"
19#include "cryptoconfig.h"
21#include "keyhelpers.h"
23#include <libkleo/dn.h>
24#include <libkleo/keycache.h>
25#include <libkleo/keygroup.h>
27#include <libkleo_debug.h>
29#include <KEmailAddress>
30#include <KLocalizedString>
32#include <QGpgME/CryptoConfig>
33#include <QGpgME/Protocol>
38#include <QRegularExpression>
41#include <gpgme++/importresult.h>
42#include <gpgme++/key.h>
51QIcon iconForValidityAndCompliance(UserID::Validity validity,
bool isCompliant)
54 case UserID::Ultimate:
56 case UserID::Marginal:
57 return isCompliant ? Formatting::successIcon() : Formatting::infoIcon();
59 return Formatting::errorIcon();
60 case UserID::Undefined:
63 return Formatting::infoIcon();
66QIcon iconForValidity(
const UserID &userId)
68 const bool keyIsCompliant = !DeVSCompliance::isActive() ||
69 (DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(userId.parent()));
70 return iconForValidityAndCompliance(userId.validity(), keyIsCompliant);
74QIcon Formatting::IconProvider::icon(
const GpgME::Key &key)
const
76 return icon(key.userID(0));
79QIcon Formatting::IconProvider::icon(
const GpgME::UserID &userID)
const
81 if (usage.canEncrypt() && !Kleo::canBeUsedForEncryption(userID.parent())) {
82 return Formatting::errorIcon();
84 if (usage.canSign() && !Kleo::canBeUsedForSigning(userID.parent())) {
85 return Formatting::errorIcon();
87 if (userID.parent().isBad() || userID.isBad()) {
88 return Formatting::errorIcon();
90 if (Kleo::isRevokedOrExpired(userID)) {
91 return Formatting::errorIcon();
93 return iconForValidity(userID);
96QIcon Formatting::IconProvider::icon(
const KeyGroup &group)
const
98 if (usage.canEncrypt() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForEncryption)) {
99 return Formatting::errorIcon();
101 if (usage.canSign() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForSigning)) {
102 return Formatting::errorIcon();
104 return validityIcon(group);
107QIcon Formatting::successIcon()
112QIcon Formatting::infoIcon()
117QIcon Formatting::questionIcon()
122QIcon Formatting::unavailableIcon()
127QIcon Formatting::warningIcon()
132QIcon Formatting::errorIcon()
141QString Formatting::prettyName(
int proto,
const char *
id,
const char *name_,
const char *comment_)
143 if (proto == GpgME::OpenPGP) {
145 if (name.isEmpty()) {
152 return QStringLiteral(
"%1 (%2)").arg(name, comment);
155 if (proto == GpgME::CMS) {
156 const DN subject(
id);
159 return subject.prettyDN();
167QString Formatting::prettyNameAndEMail(
int proto,
const char *
id,
const char *name_,
const char *email_,
const char *comment_)
174 if (proto == GpgME::OpenPGP) {
175 if (name.isEmpty()) {
178 }
else if (comment.
isEmpty()) {
179 return QStringLiteral(
"<%1>").arg(email);
181 return QStringLiteral(
"(%2) <%1>").arg(email, comment);
188 return QStringLiteral(
"%1 (%2)").arg(name, comment);
192 return QStringLiteral(
"%1 <%2>").arg(name, email);
194 return QStringLiteral(
"%1 (%3) <%2>").arg(name, email, comment);
198 if (proto == GpgME::CMS) {
199 const DN subject(
id);
202 return subject.prettyDN();
209QString Formatting::prettyUserID(
const UserID &uid)
211 if (uid.parent().protocol() == GpgME::OpenPGP) {
212 return prettyNameAndEMail(uid);
215 if (
id.startsWith(
'<')) {
216 return prettyEMail(uid.email(), uid.id());
218 if (
id.startsWith(
'(')) {
226QString Formatting::prettyKeyID(
const char *
id)
234QString Formatting::prettyNameAndEMail(
const UserID &uid)
236 return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment());
239QString Formatting::prettyNameAndEMail(
const Key &key)
241 return prettyNameAndEMail(key.userID(0));
244QString Formatting::prettyName(
const Key &key)
246 return prettyName(key.userID(0));
249QString Formatting::prettyName(
const UserID &uid)
251 return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment());
254QString Formatting::prettyName(
const UserID::Signature &sig)
256 return prettyName(GpgME::OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment());
263QString Formatting::prettyEMail(
const Key &key)
265 for (
unsigned int i = 0, end = key.numUserIDs(); i <
end; ++i) {
266 const QString email = prettyEMail(key.userID(i));
274QString Formatting::prettyEMail(
const UserID &uid)
276 return prettyEMail(uid.email(), uid.id());
279QString Formatting::prettyEMail(
const UserID::Signature &sig)
281 return prettyEMail(sig.signerEmail(), sig.signerUserID());
284QString Formatting::prettyEMail(
const char *email_,
const char *
id)
292 return DN(
id)[QStringLiteral(
"EMAIL")].trimmed();
310template<
typename T_arg>
313 return QStringLiteral(
"<tr><th>%1:</th><td>%2</td></tr>").
arg(protect_whitespace(field), arg);
317 return QStringLiteral(
"<tr><th>%1:</th><td>%2</td></tr>").
arg(protect_whitespace(field), arg.
toHtmlEscaped());
324QString format_keytype(
const Key &key)
326 const Subkey subkey = key.subkey(0);
327 if (key.hasSecret()) {
328 return i18n(
"%1-bit %2 (secret key available)", subkey.length(),
QLatin1StringView(subkey.publicKeyAlgorithmAsString()));
334QString format_subkeytype(
const Subkey &subkey)
336 const auto algo = subkey.publicKeyAlgorithm();
338 if (algo == Subkey::AlgoECC || algo == Subkey::AlgoECDSA || algo == Subkey::AlgoECDH || algo == Subkey::AlgoEDDSA) {
344QString format_keyusage(
const Key &key)
347 if (Kleo::keyHasSign(key)) {
348 if (key.isQualified()) {
354 if (Kleo::keyHasEncrypt(key)) {
357 if (Kleo::keyHasCertify(key)) {
360 if (Kleo::keyHasAuthenticate(key)) {
366QString format_subkeyusage(
const Subkey &subkey)
369 if (subkey.canSign()) {
370 if (subkey.isQualified()) {
376 if (subkey.canEncrypt()) {
379 if (subkey.canCertify()) {
382 if (subkey.canAuthenticate()) {
388static QString time_t2string(time_t t)
401static QString toolTipInternal(
const GpgME::Key &key,
const GpgME::UserID &userID,
int flags)
403 if (flags == 0 || (key.protocol() != GpgME::CMS && key.protocol() != GpgME::OpenPGP)) {
407 const Subkey subkey = key.subkey(0);
410 if (flags & Formatting::Validity) {
411 if (key.protocol() == GpgME::OpenPGP || (key.keyListMode() & Validate)) {
412 if (userID.isRevoked() || key.isRevoked()) {
413 result = make_red(
i18n(
"Revoked"));
414 }
else if (key.isExpired()) {
415 result = make_red(
i18n(
"Expired"));
416 }
else if (key.isDisabled()) {
417 result =
i18n(
"Disabled");
418 }
else if (key.keyListMode() & GpgME::Validate) {
419 if (!userID.isNull()) {
420 if (userID.validity() >= UserID::Validity::Full) {
421 result =
i18n(
"User-ID is certified.");
422 const auto compliance = Formatting::complianceStringForUserID(userID);
423 if (!compliance.isEmpty()) {
424 result += QStringLiteral(
"<br>") + compliance;
427 result =
i18n(
"User-ID is not certified.");
430 unsigned int fullyTrusted = 0;
431 for (
const auto &uid : key.userIDs()) {
432 if (uid.validity() >= UserID::Validity::Full) {
436 if (fullyTrusted == key.numUserIDs()) {
437 result =
i18n(
"All User-IDs are certified.");
438 const auto compliance = Formatting::complianceStringForKey(key);
439 if (!compliance.isEmpty()) {
440 result += QStringLiteral(
"<br>") + compliance;
443 result =
i18np(
"One User-ID is not certified.",
"%1 User-IDs are not certified.", key.numUserIDs() - fullyTrusted);
447 result =
i18n(
"The validity cannot be checked at the moment.");
450 result =
i18n(
"The validity cannot be checked at the moment.");
453 if (flags == Formatting::Validity) {
458 if (key.protocol() == GpgME::CMS) {
459 if (flags & Formatting::SerialNumber) {
460 result += format_row(
i18n(
"Serial number"), key.issuerSerial());
462 if (flags & Formatting::Issuer) {
463 result += format_row(
i18n(
"Issuer"), key.issuerName());
466 if (flags & Formatting::UserIDs) {
467 if (userID.isNull()) {
468 const std::vector<UserID> uids = key.userIDs();
470 result += format_row(key.protocol() == GpgME::CMS ?
i18n(
"Subject") :
i18n(
"User-ID"), Formatting::prettyUserID(uids.front()));
472 if (uids.size() > 1) {
473 for (
auto it = uids.begin() + 1, end = uids.end(); it !=
end; ++it) {
474 if (!it->isRevoked() && !it->isInvalid()) {
475 result += format_row(
i18n(
"a.k.a."), Formatting::prettyUserID(*it));
480 result += format_row(key.protocol() == GpgME::CMS ?
i18n(
"Subject") :
i18n(
"User-ID"), Formatting::prettyUserID(userID));
483 if (flags & Formatting::ExpiryDates) {
484 result += format_row(
i18n(
"Valid from"), time_t2string(subkey.creationTime()));
486 if (!subkey.neverExpires()) {
487 result += format_row(
i18n(
"Valid until"), time_t2string(subkey.expirationTime()));
491 if (flags & Formatting::CertificateType) {
492 result += format_row(
i18n(
"Type"), format_keytype(key));
494 if (flags & Formatting::CertificateUsage) {
495 result += format_row(
i18n(
"Usage"), format_keyusage(key));
497 if (flags & Formatting::KeyID) {
500 if (flags & Formatting::Fingerprint) {
501 result += format_row(
i18n(
"Fingerprint"), key.primaryFingerprint());
503 if (flags & Formatting::OwnerTrust) {
504 if (key.protocol() == GpgME::OpenPGP) {
505 result += format_row(
i18n(
"Certification trust"), Formatting::ownerTrustShort(key));
506 }
else if (key.isRoot()) {
507 result += format_row(
i18n(
"Trusted issuer?"), (userID.isNull() ? key.userID(0) : userID).validity() == UserID::Ultimate ?
i18n(
"Yes") :
i18n(
"No"));
510 if (flags & Formatting::StorageLocation) {
511 if (
const char *card = subkey.cardSerialNumber()) {
514 result += format_row(
i18n(
"Stored"),
i18nc(
"stored...",
"on this computer"));
517 if (flags & Formatting::Subkeys) {
518 for (
const auto &sub : key.subkeys()) {
520 result += format_row(
i18n(
"Subkey"), sub.fingerprint());
521 if (sub.isRevoked()) {
522 result += format_row(
i18n(
"Status"),
i18n(
"Revoked"));
523 }
else if (sub.isExpired()) {
524 result += format_row(
i18n(
"Status"),
i18n(
"Expired"));
526 if (flags & Formatting::ExpiryDates) {
527 result += format_row(
i18n(
"Valid from"), time_t2string(sub.creationTime()));
529 if (!sub.neverExpires()) {
530 result += format_row(
i18n(
"Valid until"), time_t2string(sub.expirationTime()));
534 if (flags & Formatting::CertificateType) {
535 result += format_row(
i18n(
"Type"), format_subkeytype(sub));
537 if (flags & Formatting::CertificateUsage) {
538 result += format_row(
i18n(
"Usage"), format_subkeyusage(sub));
540 if (flags & Formatting::StorageLocation) {
541 if (
const char *card = sub.cardSerialNumber()) {
544 result += format_row(
i18n(
"Stored"),
i18nc(
"stored...",
"on this computer"));
554QString Formatting::toolTip(
const Key &key,
int flags)
556 return toolTipInternal(key, UserID(), flags);
561template<
typename Container>
562QString getValidityStatement(
const Container &keys)
564 const bool allKeysAreOpenPGP = std::all_of(keys.cbegin(), keys.cend(), [](
const Key &key) {
565 return key.protocol() == GpgME::OpenPGP;
567 const bool allKeysAreValidated = std::all_of(keys.cbegin(), keys.cend(), [](
const Key &key) {
568 return key.keyListMode() & Validate;
570 if (allKeysAreOpenPGP || allKeysAreValidated) {
571 const bool someKeysAreBad = std::any_of(keys.cbegin(), keys.cend(), std::mem_fn(&Key::isBad));
572 if (someKeysAreBad) {
573 return i18n(
"Some keys are revoked, expired, disabled, or invalid.");
575 const bool allKeysAreFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
576 if (allKeysAreFullyValid) {
577 return i18n(
"All keys are certified.");
579 return i18n(
"Some keys are not certified.");
583 return i18n(
"The validity of the keys cannot be checked at the moment.");
587QString Formatting::toolTip(
const KeyGroup &group,
int flags)
589 static const unsigned int maxNumKeysForTooltip = 20;
591 if (group.isNull()) {
595 const KeyGroup::Keys &keys = group.keys();
596 if (keys.size() == 0) {
597 return i18nc(
"@info:tooltip",
"This group does not contain any keys.");
600 if (Kleo::any_of(keys, [](
const auto &key) {
601 return !key.hasEncrypt();
603 return i18nc(
"@info:tooltip",
"Some of the certificates in this group cannot be used for encryption. Using this group can lead to unexpected results.");
606 const QString validity = (flags & Validity) ? getValidityStatement(keys) :
QString();
607 if (flags == Validity) {
612 const unsigned int numKeysForTooltip = keys.size() > maxNumKeysForTooltip ? maxNumKeysForTooltip - 1 : keys.size();
615 result.
reserve(3 + 2 + numKeysForTooltip + 2);
616 if (!validity.isEmpty()) {
618 result.
push_back(validity.toHtmlEscaped());
619 result.
push_back(QStringLiteral(
"</p>"));
625 auto it = keys.cbegin();
626 for (
unsigned int i = 0; i < numKeysForTooltip; ++i, ++it) {
630 if (keys.size() > numKeysForTooltip) {
632 +
i18ncp(
"this follows a list of keys",
"and 1 more key",
"and %1 more keys", keys.size() - numKeysForTooltip));
634 result.
push_back(QStringLiteral(
"</p>"));
639QString Formatting::toolTip(
const UserID &userID,
int flags)
641 return toolTipInternal(userID.parent(), userID, flags);
650static QDate time_t2date(time_t t)
658static QString accessible_date_format()
661 "date format suitable for screen readers; "
662 "d: day as a number without a leading zero, "
663 "MMMM: localized month name, "
664 "yyyy: year as a four digit number",
669QString expiration_date_string(
const T &tee,
const QString &noExpiration)
671 return tee.neverExpires() ? noExpiration : Formatting::dateString(time_t2date(tee.expirationTime()));
674QDate creation_date(
const T &tee)
676 return time_t2date(tee.creationTime());
679QDate expiration_date(
const T &tee)
681 return time_t2date(tee.expirationTime());
685QString Formatting::dateString(time_t t)
687 return dateString(time_t2date(t));
695QString Formatting::accessibleDate(time_t t)
697 return accessibleDate(time_t2date(t));
705QString Formatting::expirationDateString(
const Key &key,
const QString &noExpiration)
710 return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0)
711 ?
i18nc(
"@info the expiration date of the key is unknown",
"unknown")
712 : expiration_date_string(key.subkey(0), noExpiration);
715QString Formatting::expirationDateString(
const Subkey &subkey,
const QString &noExpiration)
717 return expiration_date_string(subkey, noExpiration);
720QString Formatting::expirationDateString(
const UserID::Signature &sig,
const QString &noExpiration)
722 return expiration_date_string(sig, noExpiration);
725QDate Formatting::expirationDate(
const Key &key)
727 return expiration_date(key.subkey(0));
730QDate Formatting::expirationDate(
const Subkey &subkey)
732 return expiration_date(subkey);
735QDate Formatting::expirationDate(
const UserID::Signature &sig)
737 return expiration_date(sig);
740QString Formatting::accessibleExpirationDate(
const Key &key,
const QString &noExpiration)
745 return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0)
746 ?
i18nc(
"@info the expiration date of the key is unknown",
"unknown")
747 : accessibleExpirationDate(key.subkey(0), noExpiration);
750QString Formatting::accessibleExpirationDate(
const Subkey &subkey,
const QString &noExpiration)
752 if (subkey.neverExpires()) {
753 return noExpiration.
isEmpty() ?
i18n(
"unlimited") : noExpiration;
755 return accessibleDate(expirationDate(subkey));
759QString Formatting::accessibleExpirationDate(
const UserID::Signature &sig,
const QString &noExpiration)
761 if (sig.neverExpires()) {
762 return noExpiration.
isEmpty() ?
i18n(
"unlimited") : noExpiration;
764 return accessibleDate(expirationDate(sig));
768QString Formatting::creationDateString(
const Key &key)
770 return dateString(creation_date(key.subkey(0)));
773QString Formatting::creationDateString(
const Subkey &subkey)
775 return dateString(creation_date(subkey));
778QString Formatting::creationDateString(
const UserID::Signature &sig)
780 return dateString(creation_date(sig));
783QDate Formatting::creationDate(
const Key &key)
785 return creation_date(key.subkey(0));
788QDate Formatting::creationDate(
const Subkey &subkey)
790 return creation_date(subkey);
793QDate Formatting::creationDate(
const UserID::Signature &sig)
795 return creation_date(sig);
798QString Formatting::accessibleCreationDate(
const Key &key)
800 return accessibleDate(creationDate(key));
803QString Formatting::accessibleCreationDate(
const Subkey &subkey)
805 return accessibleDate(creationDate(subkey));
812QString Formatting::displayName(GpgME::Protocol p)
814 if (p == GpgME::CMS) {
815 return i18nc(
"X.509/CMS encryption standard",
"S/MIME");
817 if (p == GpgME::OpenPGP) {
818 return i18n(
"OpenPGP");
820 return i18nc(
"Unknown encryption protocol",
"Unknown");
823QString Formatting::type(
const Key &key)
825 return displayName(key.protocol());
828QString Formatting::type(
const Subkey &subkey)
833QString Formatting::type(
const KeyGroup &group)
836 return i18nc(
"a group of keys/certificates",
"Group");
843QString Formatting::ownerTrustShort(
const Key &key)
845 return ownerTrustShort(key.ownerTrust());
848QString Formatting::ownerTrustShort(Key::OwnerTrust trust)
852 return i18nc(
"unknown trust level",
"unknown");
854 return i18n(
"untrusted");
856 return i18nc(
"marginal trust",
"marginal");
858 return i18nc(
"full trust",
"full");
860 return i18nc(
"ultimate trust",
"ultimate");
862 return i18nc(
"undefined trust",
"undefined");
864 Q_ASSERT(!
"unexpected owner trust value");
870QString Formatting::validityShort(
const Subkey &subkey)
872 if (subkey.isRevoked()) {
873 return i18n(
"revoked");
875 if (subkey.isExpired()) {
876 return i18n(
"expired");
878 if (subkey.isDisabled()) {
879 return i18n(
"disabled");
881 if (subkey.isInvalid()) {
882 return i18n(
"invalid");
884 return i18nc(
"as in good/valid signature",
"good");
887QString Formatting::validityShort(
const UserID &uid)
889 if (uid.isRevoked()) {
890 return i18n(
"revoked");
892 if (uid.isInvalid()) {
893 return i18n(
"invalid");
895 switch (uid.validity()) {
896 case UserID::Unknown:
897 return i18nc(
"unknown trust level",
"unknown");
898 case UserID::Undefined:
899 return i18nc(
"undefined trust",
"undefined");
901 return i18n(
"untrusted");
902 case UserID::Marginal:
903 return i18nc(
"marginal trust",
"marginal");
905 return i18nc(
"full trust",
"full");
906 case UserID::Ultimate:
907 return i18nc(
"ultimate trust",
"ultimate");
912QString Formatting::validityShort(
const UserID::Signature &sig)
914 switch (sig.status()) {
915 case UserID::Signature::NoError:
916 if (!sig.isInvalid()) {
918 switch (sig.certClass()) {
923 return i18n(
"valid");
925 return i18n(
"revoked");
927 return i18n(
"class %1", sig.certClass());
932 case UserID::Signature::GeneralError:
933 return i18n(
"invalid");
934 case UserID::Signature::SigExpired:
935 return i18n(
"expired");
936 case UserID::Signature::KeyExpired:
937 return i18n(
"certificate expired");
938 case UserID::Signature::BadSignature:
939 return i18nc(
"fake/invalid signature",
"bad");
940 case UserID::Signature::NoPublicKey: {
943 const auto key = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID());
945 return i18n(
"no public key");
946 }
else if (key.isExpired()) {
947 return i18n(
"key expired");
948 }
else if (key.isRevoked()) {
949 return i18n(
"key revoked");
950 }
else if (key.isDisabled()) {
951 return i18n(
"key disabled");
954 return QStringLiteral(
"unknown");
960QIcon Formatting::validityIcon(
const UserID::Signature &sig)
962 switch (sig.status()) {
963 case UserID::Signature::NoError:
964 if (!sig.isInvalid()) {
966 switch (sig.certClass()) {
971 return Formatting::successIcon();
973 return Formatting::errorIcon();
980 case UserID::Signature::BadSignature:
981 case UserID::Signature::GeneralError:
982 return Formatting::errorIcon();
983 case UserID::Signature::SigExpired:
984 case UserID::Signature::KeyExpired:
985 return Formatting::infoIcon();
986 case UserID::Signature::NoPublicKey:
987 return Formatting::questionIcon();
992QString Formatting::formatKeyLink(
const Key &key)
997 return QStringLiteral(
"<a href=\"key:%1\">%2</a>").arg(
QLatin1StringView(key.primaryFingerprint()), Formatting::prettyName(key));
1000QString Formatting::formatForComboBox(
const GpgME::Key &key)
1002 const QString name = prettyName(key);
1004 if (!
mail.isEmpty()) {
1010QString Formatting::nameAndEmailForSummaryLine(
const UserID &
id)
1012 Q_ASSERT(!
id.isNull());
1014 const QString email = Formatting::prettyEMail(
id);
1015 const QString name = Formatting::prettyName(
id);
1017 if (name.isEmpty()) {
1022 return QStringLiteral(
"%1 <%2>").arg(name, email);
1026QString Formatting::nameAndEmailForSummaryLine(
const Key &key)
1028 Q_ASSERT(!key.isNull());
1030 const QString email = Formatting::prettyEMail(key);
1031 const QString name = Formatting::prettyName(key);
1033 if (name.isEmpty()) {
1038 return QStringLiteral(
"%1 <%2>").arg(name, email);
1042const char *Formatting::summaryToString(
const Signature::Summary summary)
1044 if (summary & Signature::Red) {
1047 if (summary & Signature::Green) {
1053QString Formatting::signatureToString(
const Signature &sig,
const Key &key)
1059 const bool red = (sig.summary() & Signature::Red);
1060 const bool valid = (sig.summary() & Signature::Valid);
1064 if (
const char *fpr = sig.fingerprint()) {
1065 return i18n(
"Bad signature by unknown certificate %1: %2",
QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
1067 return i18n(
"Bad signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
1070 return i18n(
"Bad signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
1075 if (
const char *fpr = sig.fingerprint()) {
1078 return i18n(
"Good signature by an unknown certificate.");
1081 return i18n(
"Good signature by %1.", nameAndEmailForSummaryLine(key));
1084 }
else if (key.isNull()) {
1085 if (
const char *fpr = sig.fingerprint()) {
1086 return i18n(
"Invalid signature by unknown certificate %1: %2",
QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
1088 return i18n(
"Invalid signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
1091 return i18n(
"Invalid signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
1101 const QString result = importMetaData(
import);
1109QString Formatting::importMetaData(
const Import &
import)
1111 if (
import.isNull()) {
1115 if (
import.
error().isCanceled()) {
1116 return i18n(
"The import of this certificate was canceled.");
1118 if (
import.
error()) {
1119 return i18n(
"An error occurred importing this certificate: %1", Formatting::errorAsString(
import.
error()));
1122 const unsigned int status =
import.status();
1123 if (
status & Import::NewKey) {
1124 return (
status & Import::ContainedSecretKey) ?
i18n(
"This certificate was new to your keystore. The secret key is available.")
1125 :
i18n(
"This certificate is new to your keystore.");
1129 if (
status & Import::NewUserIDs) {
1130 results.
push_back(
i18n(
"New user-ids were added to this certificate by the import."));
1132 if (
status & Import::NewSignatures) {
1133 results.
push_back(
i18n(
"New signatures were added to this certificate by the import."));
1135 if (
status & Import::NewSubkeys) {
1136 results.
push_back(
i18n(
"New subkeys were added to this certificate by the import."));
1139 return results.
empty() ?
i18n(
"The import contained no new data for this certificate. It is unchanged.") : results.join(
QLatin1Char(
'\n'));
1146QString Formatting::formatOverview(
const Key &key)
1148 return toolTip(key, AllOptions);
1151QString Formatting::usageString(
const Subkey &sub)
1154 if (sub.canCertify()) {
1155 usageStrings <<
i18n(
"Certify");
1157 if (sub.canSign()) {
1158 usageStrings <<
i18n(
"Sign");
1160 if (sub.canEncrypt()) {
1161 usageStrings <<
i18n(
"Encrypt");
1163 if (sub.canAuthenticate()) {
1164 usageStrings <<
i18n(
"Authenticate");
1166 if (sub.canRenc()) {
1167 usageStrings <<
i18nc(
"Means 'Additional Decryption Subkey'; Don't try translating that, though.",
"ADSK");
1172QString Formatting::summaryLine(
const UserID &
id)
1174 return i18nc(
"name <email> (validity, protocol, creation date)",
1175 "%1 (%2, %3, created: %4)",
1176 nameAndEmailForSummaryLine(
id),
1177 Formatting::complianceStringShort(
id),
1178 displayName(
id.parent().protocol()),
1179 Formatting::creationDateString(
id.parent()));
1182QString Formatting::summaryLine(
const Key &key)
1184 return nameAndEmailForSummaryLine(key) +
QLatin1Char(
' ')
1185 +
i18nc(
"(validity, protocol, creation date)",
1186 "(%1, %2, created: %3)",
1187 Formatting::complianceStringShort(key),
1188 displayName(key.protocol()),
1189 Formatting::creationDateString(key));
1192QString Formatting::summaryLine(
const KeyGroup &group)
1194 switch (group.source()) {
1195 case KeyGroup::ApplicationConfig:
1196 case KeyGroup::GnuPGConfig:
1197 return i18ncp(
"name of group of keys (n key(s), validity)",
1200 group.keys().size(),
1202 Formatting::complianceStringShort(group));
1203 case KeyGroup::Tags:
1204 return i18ncp(
"name of group of keys (n key(s), validity, tag)",
1205 "%2 (1 key, %3, tag)",
1206 "%2 (%1 keys, %3, tag)",
1207 group.keys().size(),
1209 Formatting::complianceStringShort(group));
1211 return i18ncp(
"name of group of keys (n key(s), validity, group ...)",
1212 "%2 (1 key, %3, unknown origin)",
1213 "%2 (%1 keys, %3, unknown origin)",
1214 group.keys().size(),
1216 Formatting::complianceStringShort(group));
1221QIcon Formatting::iconForUid(
const UserID &uid)
1223 if (Kleo::isRevokedOrExpired(uid)) {
1224 return Formatting::errorIcon();
1226 return iconForValidity(uid);
1229QString Formatting::validity(
const UserID &uid)
1231 switch (uid.validity()) {
1232 case UserID::Ultimate:
1233 return i18n(
"The certificate is marked as your own.");
1235 return i18n(
"The certificate belongs to this recipient.");
1236 case UserID::Marginal:
1237 return i18n(
"The trust model indicates marginally that the certificate belongs to this recipient.");
1239 return i18n(
"This certificate should not be used.");
1240 case UserID::Undefined:
1241 case UserID::Unknown:
1243 return i18n(
"There is no indication that this certificate belongs to this recipient.");
1247QString Formatting::validity(
const KeyGroup &group)
1249 if (group.isNull()) {
1253 const KeyGroup::Keys &keys = group.keys();
1254 if (keys.size() == 0) {
1255 return i18n(
"This group does not contain any keys.");
1258 return getValidityStatement(keys);
1263template<
typename Container>
1264UserID::Validity minimalValidity(
const Container &keys)
1266 const int minValidity = std::accumulate(keys.cbegin(), keys.cend(), UserID::Ultimate + 1, [](
int validity,
const Key &key) {
1267 return std::min<int>(validity, minimalValidityOfNotRevokedUserIDs(key));
1269 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::
Unknown;
1272template<
typename Container>
1273bool allKeysAreCompliant(
const Container &keys)
1275 if (!DeVSCompliance::isActive()) {
1278 if (!DeVSCompliance::isCompliant()) {
1281 return Kleo::all_of(keys, DeVSCompliance::keyIsCompliant);
1285QIcon Formatting::validityIcon(
const KeyGroup &group)
1287 if (Kleo::any_of(group.keys(), std::mem_fn(&Key::isBad))) {
1288 return Formatting::errorIcon();
1290 return iconForValidityAndCompliance(minimalValidity(group.keys()), allKeysAreCompliant(group.keys()));
1293bool Formatting::uidsHaveFullValidity(
const Key &key)
1295 return allUserIDsHaveFullValidity(key);
1298QString Formatting::complianceMode()
1300 const auto complianceValue = getCryptoConfigStringValue(
"gpg",
"compliance");
1304bool Formatting::isKeyDeVs(
const GpgME::Key &key)
1306 return DeVSCompliance::allSubkeysAreCompliant(key);
1309QString Formatting::complianceStringForKey(
const GpgME::Key &key)
1313 if (DeVSCompliance::isCompliant()) {
1314 return isRemoteKey(key)
1315 ?
i18nc(
"@info the compliance of the key with certain requirements is unknown",
"unknown")
1316 : DeVSCompliance::name(DeVSCompliance::keyIsCompliant(key));
1321QString Formatting::complianceStringForUserID(
const GpgME::UserID &userID)
1325 if (DeVSCompliance::isCompliant()) {
1326 return isRemoteKey(userID.parent())
1327 ?
i18nc(
"@info the compliance of the key with certain requirements is unknown",
"unknown")
1328 : DeVSCompliance::name(DeVSCompliance::userIDIsCompliant(userID));
1333QString Formatting::complianceStringShort(
const GpgME::UserID &
id)
1335 if (DeVSCompliance::isCompliant() && DeVSCompliance::userIDIsCompliant(
id)) {
1336 return QStringLiteral(
"★ ") + DeVSCompliance::name(
true);
1338 const bool keyValidityChecked = (
id.parent().keyListMode() & GpgME::Validate);
1339 if (keyValidityChecked &&
id.validity() >= UserID::Full) {
1340 return i18nc(
"As in 'this user ID is valid.'",
"certified");
1342 if (
id.parent().isExpired() || isExpired(
id)) {
1343 return i18n(
"expired");
1345 if (
id.parent().isRevoked() ||
id.isRevoked()) {
1346 return i18n(
"revoked");
1348 if (
id.parent().isDisabled()) {
1349 return i18n(
"disabled");
1351 if (
id.parent().isInvalid() ||
id.isInvalid()) {
1352 return i18n(
"invalid");
1354 if (keyValidityChecked) {
1355 return i18nc(
"As in 'this user ID is not certified'",
"not certified");
1358 return i18nc(
"The validity of this user ID has not been/could not be checked",
"not checked");
1361QString Formatting::complianceStringShort(
const GpgME::Key &key)
1363 if (DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(key)) {
1364 return QStringLiteral(
"★ ") + DeVSCompliance::name(
true);
1366 const bool keyValidityChecked = (key.keyListMode() & GpgME::Validate);
1367 if (keyValidityChecked && Kleo::allUserIDsHaveFullValidity(key)) {
1368 return i18nc(
"As in all user IDs are valid.",
"certified");
1370 if (key.isExpired()) {
1371 return i18n(
"expired");
1373 if (key.isRevoked()) {
1374 return i18n(
"revoked");
1376 if (key.isDisabled()) {
1377 return i18n(
"disabled");
1379 if (key.isInvalid()) {
1380 return i18n(
"invalid");
1382 if (keyValidityChecked) {
1383 return i18nc(
"As in not all user IDs are valid.",
"not certified");
1386 return i18nc(
"The validity of the user IDs has not been/could not be checked",
"not checked");
1389QString Formatting::complianceStringShort(
const KeyGroup &group)
1391 const KeyGroup::Keys &keys = group.keys();
1393 const bool allKeysFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
1394 if (allKeysFullyValid) {
1395 return i18nc(
"As in all keys are valid.",
"all certified");
1398 return i18nc(
"As in not all keys are valid.",
"not all certified");
1401QString Formatting::prettyID(
const char *
id)
1409 if (ret.
size() == 49) {
1415QString Formatting::accessibleHexID(
const char *
id)
1417 static const QRegularExpression groupOfFourRegExp{QStringLiteral(
"(?:(.)(.)(.)(.))")};
1422 ret = ret.
replace(groupOfFourRegExp, QStringLiteral(
"\\1 \\2 \\3 \\4, ")).
chopped(2);
1427QString Formatting::origin(
int o)
1431 return i18n(
"Keyserver");
1432 case Key::OriginDane:
1433 return QStringLiteral(
"DANE");
1434 case Key::OriginWKD:
1435 return QStringLiteral(
"WKD");
1436 case Key::OriginURL:
1437 return QStringLiteral(
"URL");
1438 case Key::OriginFile:
1439 return i18n(
"File import");
1440 case Key::OriginSelf:
1441 return i18n(
"Generated");
1442 case Key::OriginOther:
1443 case Key::OriginUnknown:
1445 return i18n(
"Unknown");
1449QString Formatting::deVsString(
bool compliant)
1451 return DeVSCompliance::name(compliant);
1456QString formatTrustScope(
const char *trustScope)
1458 static const QRegularExpression escapedNonAlphaNum{QStringLiteral(R
"(\\([^0-9A-Za-z]))")};
1461 if (scopeRegExp.startsWith(u
"<[^>]+[@.]") && scopeRegExp.endsWith(u
">$")) {
1463 auto domain = scopeRegExp.mid(10, scopeRegExp.size() - 10 - 2);
1464 domain.replace(escapedNonAlphaNum, QStringLiteral(R
"(\1)"));
1471QString Formatting::trustSignatureDomain(
const GpgME::UserID::Signature &sig)
1473 return formatTrustScope(sig.trustScope());
1476QString Formatting::trustSignature(
const GpgME::UserID::Signature &sig)
1478 switch (sig.trustValue()) {
1479 case TrustSignatureTrust::Partial:
1480 return i18nc(
"Certifies this key as partially trusted introducer for 'domain name'.",
1481 "Certifies this key as partially trusted introducer for '%1'.",
1482 trustSignatureDomain(sig));
1483 case TrustSignatureTrust::Complete:
1484 return i18nc(
"Certifies this key as fully trusted introducer for 'domain name'.",
1485 "Certifies this key as fully trusted introducer for '%1'.",
1486 trustSignatureDomain(sig));
1492QString Formatting::errorAsString(
const GpgME::Error &error)
1496 const char *s =
error.asString();
1497 qCDebug(LIBKLEO_LOG) << __func__ <<
"gettext_use_utf8(-1) returns" << gettext_use_utf8(-1);
1498 qCDebug(LIBKLEO_LOG) << __func__ <<
"error:" << s;
1506QString Formatting::prettyAlgorithmName(
const std::string &algorithm)
1508 static const std::map<std::string, QString> displayNames = {
1509 {
"brainpoolP256r1",
i18nc(
"@info",
"ECC (Brainpool P-256)")},
1510 {
"brainpoolP384r1",
i18nc(
"@info",
"ECC (Brainpool P-384)")},
1511 {
"brainpoolP512r1",
i18nc(
"@info",
"ECC (Brainpool P-512)")},
1512 {
"curve25519",
i18nc(
"@info",
"ECC (Curve25519)")},
1513 {
"curve448",
i18nc(
"@info",
"ECC (Curve448)")},
1514 {
"ed25519",
i18nc(
"@info",
"ECC (Ed25519)")},
1515 {
"ed448",
i18nc(
"@info",
"ECC (Ed448)")},
1516 {
"cv25519",
i18nc(
"@info",
"ECC (Cv25519)")},
1517 {
"cv448",
i18nc(
"@info",
"ECC (Cv448)")},
1518 {
"nistp256",
i18nc(
"@info",
"ECC (NIST P-256)")},
1519 {
"nistp384",
i18nc(
"@info",
"ECC (NIST P-384)")},
1520 {
"nistp521",
i18nc(
"@info",
"ECC (NIST P-521)")},
1521 {
"rsa2048",
i18nc(
"@info",
"RSA 2048")},
1522 {
"rsa3072",
i18nc(
"@info",
"RSA 3072")},
1523 {
"rsa4096",
i18nc(
"@info",
"RSA 4096")},
1524 {
"dsa1024",
i18nc(
"@info",
"DSA 1024")},
1525 {
"dsa2048",
i18nc(
"@info",
"DSA 2048")},
1526 {
"elg1024",
i18nc(
"@info",
"Elgamal 1024")},
1527 {
"elg2048",
i18nc(
"@info",
"Elgamal 2048")},
1528 {
"elg3072",
i18nc(
"@info",
"Elgamal 3072")},
1529 {
"elg4096",
i18nc(
"@info",
"Elgamal 4096")},
1531 const auto it = displayNames.find(algorithm);
1532 return (it != displayNames.end()) ? it->second :
i18nc(
"@info",
"Unknown algorithm");
Q_SCRIPTABLE CaptureState status()
KCODECS_EXPORT EmailParseResult splitAddress(const QByteArray &address, QByteArray &displayName, QByteArray &addrSpec, QByteArray &comment)
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
Capabilities capabilities()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QAction * mail(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
QByteArray toPercentEncoding(const QByteArray &exclude, const QByteArray &include, char percent) const const
QByteArray trimmed() const const
QDateTime fromSecsSinceEpoch(qint64 secs)
QIcon fromTheme(const QString &name)
void push_back(parameter_type value)
void reserve(qsizetype size)
QString toString(QDate date, FormatType format) const const
QString arg(Args &&... args) const const
QString chopped(qsizetype len) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString simplified() const const
qsizetype size() const const
QString toHtmlEscaped() const const
QString toUpper() const const
QString trimmed() const const
QString join(QChar separator) const const