12#include <config-libkleo.h>
14#include "formatting.h"
18#include "compliance.h"
19#include "cryptoconfig.h"
21#include "keyhelpers.h"
23#include <libkleo/dnattributes.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>
34#include <QGpgME/Protocol>
39#include <QRegularExpression>
42#include <gpgme++/importresult.h>
43#include <gpgme++/key.h>
54QIcon iconForValidityAndCompliance(UserID::Validity validity,
bool isCompliant)
57 case UserID::Ultimate:
59 case UserID::Marginal:
60 return isCompliant ? Formatting::successIcon() : Formatting::infoIcon();
62 return Formatting::errorIcon();
63 case UserID::Undefined:
66 return Formatting::infoIcon();
69QIcon iconForValidity(
const UserID &userId)
71 const bool keyIsCompliant = !DeVSCompliance::isActive() ||
72 (DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(userId.parent()));
73 return iconForValidityAndCompliance(userId.validity(), keyIsCompliant);
77QIcon Formatting::IconProvider::icon(
const GpgME::Key &key)
const
79 return icon(key.userID(0));
82QIcon Formatting::IconProvider::icon(
const GpgME::UserID &userID)
const
84 if (usage.canEncrypt() && !Kleo::canBeUsedForEncryption(userID.parent())) {
85 return Formatting::errorIcon();
87 if (usage.canSign() && !Kleo::canBeUsedForSigning(userID.parent())) {
88 return Formatting::errorIcon();
90 if (userID.parent().isBad() || userID.isBad()) {
91 return Formatting::errorIcon();
93 if (Kleo::isRevokedOrExpired(userID)) {
94 return Formatting::errorIcon();
96 return iconForValidity(userID);
99QIcon Formatting::IconProvider::icon(
const KeyGroup &group)
const
101 if (usage.canEncrypt() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForEncryption)) {
102 return Formatting::errorIcon();
104 if (usage.canSign() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForSigning)) {
105 return Formatting::errorIcon();
107 return validityIcon(group);
110QIcon Formatting::successIcon()
115QIcon Formatting::infoIcon()
120QIcon Formatting::questionIcon()
125QIcon Formatting::unavailableIcon()
130QIcon Formatting::warningIcon()
135QIcon Formatting::errorIcon()
144QString Formatting::prettyName(
int proto,
const char *
id,
const char *name_,
const char *comment_)
146 if (proto == GpgME::OpenPGP) {
148 if (name.isEmpty()) {
155 return QStringLiteral(
"%1 (%2)").arg(name, comment);
158 if (proto == GpgME::CMS) {
159 const QGpgME::DN subject(
id);
160 const QString cn = subject[QStringLiteral(
"CN")].trimmed();
162 subject.setAttributeOrder(DNAttributes::order());
163 return subject.prettyDN();
171QString Formatting::prettyNameAndEMail(
int proto,
const char *
id,
const char *name_,
const char *email_,
const char *comment_)
176QString Formatting::prettyNameAndEMail(
int proto,
const QString &
id,
const QString &name,
const QString &email,
const QString &comment)
178 if (proto == GpgME::OpenPGP) {
179 if (name.isEmpty()) {
180 if (email.isEmpty()) {
182 }
else if (comment.
isEmpty()) {
183 return QStringLiteral(
"<%1>").arg(email);
185 return QStringLiteral(
"(%2) <%1>").arg(email, comment);
188 if (email.isEmpty()) {
192 return QStringLiteral(
"%1 (%2)").arg(name, comment);
196 return QStringLiteral(
"%1 <%2>").arg(name, email);
198 return QStringLiteral(
"%1 (%3) <%2>").arg(name, email, comment);
202 if (proto == GpgME::CMS) {
203 const QGpgME::DN subject(
id);
204 const QString cn = subject[QStringLiteral(
"CN")].
trimmed();
206 subject.setAttributeOrder(DNAttributes::order());
207 return subject.prettyDN();
214QString Formatting::prettyUserID(
const UserID &uid)
216 if (uid.parent().protocol() == GpgME::OpenPGP) {
217 return prettyNameAndEMail(uid);
219 const QByteArray
id = QByteArray(uid.id()).trimmed();
220 if (
id.startsWith(
'<')) {
221 return prettyEMail(uid.email(), uid.id());
223 if (
id.startsWith(
'(')) {
227 return prettyDN(uid.id());
231QString Formatting::prettyKeyID(
const char *
id)
239QString Formatting::prettyNameAndEMail(
const UserID &uid)
241 return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment());
244QString Formatting::prettyNameAndEMail(
const Key &key)
246 return prettyNameAndEMail(key.userID(0));
249QString Formatting::prettyName(
const Key &key)
251 return prettyName(key.userID(0));
254QString Formatting::prettyName(
const UserID &uid)
256 return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment());
259QString Formatting::prettyName(
const UserID::Signature &sig)
261 return prettyName(GpgME::OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment());
268QString Formatting::prettyEMail(
const Key &key)
270 for (
unsigned int i = 0, end = key.numUserIDs(); i < end; ++i) {
271 const QString email = prettyEMail(key.userID(i));
272 if (!email.isEmpty()) {
279QString Formatting::prettyEMail(
const UserID &uid)
281 return prettyEMail(uid.email(), uid.id());
284QString Formatting::prettyEMail(
const UserID::Signature &sig)
286 return prettyEMail(sig.signerEmail(), sig.signerUserID());
289QString Formatting::prettyEMail(
const char *email_,
const char *
id)
297 return QGpgME::DN(
id)[QStringLiteral(
"EMAIL")].trimmed();
301QString Formatting::prettyDN(
const char *utf8DN)
303 QGpgME::DN dn{utf8DN};
304 dn.setAttributeOrder(DNAttributes::order());
305 return dn.prettyDN();
315static QString protect_whitespace(QString s)
317 static const QLatin1Char SP(
' ');
318 static const QLatin1Char NBSP(
'\xA0');
322template<
typename T_arg>
323QString format_row(
const QString &field,
const T_arg &arg)
325 return QStringLiteral(
"<tr><th>%1:</th><td>%2</td></tr>").
arg(protect_whitespace(field), arg);
327QString format_row(
const QString &field,
const QString &arg)
329 return QStringLiteral(
"<tr><th>%1:</th><td>%2</td></tr>").
arg(protect_whitespace(field), arg.
toHtmlEscaped());
331QString format_row(
const QString &field,
const char *arg)
336QString format_keytype(
const Key &key)
338 const Subkey subkey = key.subkey(0);
339 if (key.hasSecret()) {
340 return i18n(
"%1-bit %2 (secret key available)", subkey.length(), QLatin1StringView(subkey.publicKeyAlgorithmAsString()));
342 return i18n(
"%1-bit %2", subkey.length(), QLatin1StringView(subkey.publicKeyAlgorithmAsString()));
346QString format_keyusage(
const Key &key)
349 if (Kleo::keyHasSign(key)) {
350 if (key.isQualified()) {
356 if (Kleo::keyHasEncrypt(key)) {
359 if (Kleo::keyHasCertify(key)) {
362 if (Kleo::keyHasAuthenticate(key)) {
368static QString time_t2string(time_t t)
374static QString make_red(
const QString &txt)
376 return QLatin1StringView(
"<font color=\"red\">") + txt.
toHtmlEscaped() + QLatin1StringView(
"</font>");
381static QString toolTipInternal(
const GpgME::Key &key,
const GpgME::UserID &userID,
int flags)
383 if (flags == 0 || (key.protocol() != GpgME::CMS && key.protocol() != GpgME::OpenPGP)) {
387 const Subkey subkey = key.subkey(0);
390 if (flags & Formatting::Validity) {
391 if (key.protocol() == GpgME::OpenPGP || (key.keyListMode() & Validate)) {
392 if (key.isDisabled()) {
393 result =
i18n(
"Disabled");
394 }
else if (userID.isRevoked() || key.isRevoked()) {
395 result = make_red(
i18n(
"Revoked"));
396 }
else if (key.isExpired()) {
397 result = make_red(
i18n(
"Expired"));
398 }
else if (key.keyListMode() & GpgME::Validate) {
399 if (!userID.isNull()) {
400 if (userID.validity() >= UserID::Validity::Full) {
401 result =
i18n(
"User ID is certified.");
402 const auto compliance = Formatting::complianceStringForUserID(userID);
403 if (!compliance.isEmpty()) {
404 result += QStringLiteral(
"<br>") + compliance;
407 result =
i18n(
"User ID is not certified.");
410 unsigned int fullyTrusted = 0;
411 for (
const auto &uid : key.userIDs()) {
412 if (uid.validity() >= UserID::Validity::Full) {
416 if (fullyTrusted == key.numUserIDs()) {
417 result =
i18n(
"All User IDs are certified.");
418 const auto compliance = Formatting::complianceStringForKey(key);
419 if (!compliance.isEmpty()) {
420 result += QStringLiteral(
"<br>") + compliance;
423 result =
i18np(
"One User ID is not certified.",
"%1 User IDs are not certified.", key.numUserIDs() - fullyTrusted);
427 result =
i18n(
"The validity cannot be checked at the moment.");
430 result =
i18n(
"The validity cannot be checked at the moment.");
433 if (flags == Formatting::Validity) {
437 result += QLatin1StringView(
"<table border=\"0\">");
438 if (key.protocol() == GpgME::CMS) {
439 if (flags & Formatting::SerialNumber) {
440 result += format_row(
i18n(
"Serial number"), key.issuerSerial());
442 if (flags & Formatting::Issuer) {
443 result += format_row(
i18n(
"Issuer"), key.issuerName());
446 if (flags & Formatting::UserIDs) {
447 if (userID.isNull()) {
448 const std::vector<UserID> uids = key.userIDs();
450 result += format_row(key.protocol() == GpgME::CMS ?
i18n(
"Subject") :
i18n(
"User ID"), Formatting::prettyUserID(uids.front()));
452 if (uids.size() > 1) {
453 for (
auto it = uids.begin() + 1, end = uids.end(); it != end; ++it) {
454 if (!it->isRevoked() && !it->isInvalid()) {
455 result += format_row(
i18n(
"a.k.a."), Formatting::prettyUserID(*it));
460 result += format_row(key.protocol() == GpgME::CMS ?
i18n(
"Subject") :
i18n(
"User ID"), Formatting::prettyUserID(userID));
463 if (flags & Formatting::ExpiryDates) {
464 result += format_row(
i18n(
"Valid from"), time_t2string(subkey.creationTime()));
466 if (!subkey.neverExpires()) {
467 result += format_row(
i18n(
"Valid until"), time_t2string(subkey.expirationTime()));
471 if (flags & Formatting::CertificateType) {
472 result += format_row(
i18n(
"Type"), format_keytype(key));
474 if (flags & Formatting::CertificateUsage) {
475 result += format_row(
i18n(
"Usage"), format_keyusage(key));
477 if (flags & Formatting::KeyID) {
480 if (flags & Formatting::Fingerprint) {
481 result += format_row(
i18n(
"Fingerprint"), key.primaryFingerprint());
483 if (flags & Formatting::OwnerTrust) {
484 if (key.protocol() == GpgME::OpenPGP) {
485 result += format_row(
i18n(
"Certification trust"), Formatting::ownerTrustShort(key));
486 }
else if (key.isRoot()) {
487 result += format_row(
i18n(
"Trusted issuer?"), (userID.isNull() ? key.userID(0) : userID).validity() == UserID::Ultimate ?
i18n(
"Yes") :
i18n(
"No"));
490 if (flags & Formatting::StorageLocation) {
491 if (
const char *card = subkey.cardSerialNumber()) {
494 result += format_row(
i18n(
"Stored"),
i18nc(
"stored...",
"on this computer"));
497 result += QLatin1StringView(
"</table>");
502QString Formatting::toolTip(
const Key &key,
int flags)
504 return toolTipInternal(key, UserID(), flags);
509template<
typename Container>
510QString getValidityStatement(
const Container &keys)
512 const bool allKeysAreOpenPGP = std::all_of(keys.cbegin(), keys.cend(), [](
const Key &key) {
513 return key.protocol() == GpgME::OpenPGP;
515 const bool allKeysAreValidated = std::all_of(keys.cbegin(), keys.cend(), [](
const Key &key) {
516 return key.keyListMode() & Validate;
518 if (allKeysAreOpenPGP || allKeysAreValidated) {
519 const bool someKeysAreBad = std::any_of(keys.cbegin(), keys.cend(), std::mem_fn(&Key::isBad));
520 if (someKeysAreBad) {
521 return i18n(
"Some keys are revoked, expired, disabled, or invalid.");
523 const bool allKeysAreFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
524 if (allKeysAreFullyValid) {
525 return i18n(
"All keys are certified.");
527 return i18n(
"Some keys are not certified.");
531 return i18n(
"The validity of the keys cannot be checked at the moment.");
535QString Formatting::toolTip(
const KeyGroup &group,
int flags)
537 static const unsigned int maxNumKeysForTooltip = 20;
539 if (group.isNull()) {
543 const KeyGroup::Keys &keys = group.keys();
544 if (keys.size() == 0) {
545 return i18nc(
"@info:tooltip",
"This group does not contain any keys.");
548 if (Kleo::any_of(keys, [](
const auto &key) {
549 return !key.hasEncrypt();
551 return i18nc(
"@info:tooltip",
"Some of the certificates in this group cannot be used for encryption. Using this group can lead to unexpected results.");
554 const QString validity = (flags & Validity) ? getValidityStatement(keys) : QString();
555 if (flags == Validity) {
560 const unsigned int numKeysForTooltip = keys.size() > maxNumKeysForTooltip ? maxNumKeysForTooltip - 1 : keys.size();
563 result.
reserve(3 + 2 + numKeysForTooltip + 2);
564 if (!validity.isEmpty()) {
566 result.
push_back(validity.toHtmlEscaped());
567 result.
push_back(QStringLiteral(
"</p>"));
573 auto it = keys.cbegin();
574 for (
unsigned int i = 0; i < numKeysForTooltip; ++i, ++it) {
575 result.
push_back(QLatin1StringView(
"<br>") + Formatting::summaryLine(*it).toHtmlEscaped());
578 if (keys.size() > numKeysForTooltip) {
579 result.
push_back(QLatin1StringView(
"<br>")
580 +
i18ncp(
"this follows a list of keys",
"and 1 more key",
"and %1 more keys", keys.size() - numKeysForTooltip));
582 result.
push_back(QStringLiteral(
"</p>"));
584 return result.
join(QLatin1Char(
'\n'));
587QString Formatting::toolTip(
const UserID &userID,
int flags)
589 return toolTipInternal(userID.parent(), userID, flags);
598static QDate time_t2date(time_t t)
606static QString accessible_date_format()
609 "date format suitable for screen readers; "
610 "d: day as a number without a leading zero, "
611 "MMMM: localized month name, "
612 "yyyy: year as a four digit number",
617QString expiration_date_string(
const T &tee,
const QString &noExpiration)
619 return tee.neverExpires() ? noExpiration : Formatting::dateString(time_t2date(tee.expirationTime()));
622QDate creation_date(
const T &tee)
624 return time_t2date(tee.creationTime());
627QDate expiration_date(
const T &tee)
629 return time_t2date(tee.expirationTime());
633QString Formatting::dateString(time_t t)
635 return dateString(time_t2date(t));
638QString Formatting::dateString(
const QDate &date)
643QString Formatting::accessibleDate(time_t t)
645 return accessibleDate(time_t2date(t));
648QString Formatting::accessibleDate(
const QDate &date)
650 return QLocale().toString(date, accessible_date_format());
653QString Formatting::expirationDateString(
const Key &key,
const QString &noExpiration)
658 return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0)
659 ?
i18nc(
"@info the expiration date of the key is unknown",
"unknown")
660 : expiration_date_string(key.subkey(0), noExpiration);
663QString Formatting::expirationDateString(
const Subkey &subkey,
const QString &noExpiration)
665 return expiration_date_string(subkey, noExpiration);
668QString Formatting::expirationDateString(
const UserID::Signature &sig,
const QString &noExpiration)
670 return expiration_date_string(sig, noExpiration);
673QDate Formatting::expirationDate(
const Key &key)
675 return expiration_date(key.subkey(0));
678QDate Formatting::expirationDate(
const Subkey &subkey)
680 return expiration_date(subkey);
683QDate Formatting::expirationDate(
const UserID::Signature &sig)
685 return expiration_date(sig);
688QString Formatting::accessibleExpirationDate(
const Key &key,
const QString &noExpiration)
693 return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0)
694 ?
i18nc(
"@info the expiration date of the key is unknown",
"unknown")
695 : accessibleExpirationDate(key.subkey(0), noExpiration);
698QString Formatting::accessibleExpirationDate(
const Subkey &subkey,
const QString &noExpiration)
700 if (subkey.neverExpires()) {
701 return noExpiration.
isEmpty() ?
i18n(
"unlimited") : noExpiration;
703 return accessibleDate(expirationDate(subkey));
707QString Formatting::accessibleExpirationDate(
const UserID::Signature &sig,
const QString &noExpiration)
709 if (sig.neverExpires()) {
710 return noExpiration.
isEmpty() ?
i18n(
"unlimited") : noExpiration;
712 return accessibleDate(expirationDate(sig));
716QString Formatting::creationDateString(
const Key &key)
718 return dateString(creation_date(key.subkey(0)));
721QString Formatting::creationDateString(
const Subkey &subkey)
723 return dateString(creation_date(subkey));
726QString Formatting::creationDateString(
const UserID::Signature &sig)
728 return dateString(creation_date(sig));
731QDate Formatting::creationDate(
const Key &key)
733 return creation_date(key.subkey(0));
736QDate Formatting::creationDate(
const Subkey &subkey)
738 return creation_date(subkey);
741QDate Formatting::creationDate(
const UserID::Signature &sig)
743 return creation_date(sig);
746QString Formatting::accessibleCreationDate(
const Key &key)
748 return accessibleDate(creationDate(key));
751QString Formatting::accessibleCreationDate(
const Subkey &subkey)
753 return accessibleDate(creationDate(subkey));
760QString Formatting::displayName(GpgME::Protocol p)
762 if (p == GpgME::CMS) {
763 return i18nc(
"X.509/CMS encryption standard",
"S/MIME");
765 if (p == GpgME::OpenPGP) {
766 return i18n(
"OpenPGP");
768 return i18nc(
"Unknown encryption protocol",
"Unknown");
771QString Formatting::type(
const Key &key)
773 return displayName(key.protocol());
776QString Formatting::type(
const Subkey &subkey)
781QString Formatting::type(
const KeyGroup &group)
784 return i18nc(
"a group of keys/certificates",
"Group");
791QString Formatting::ownerTrustShort(
const Key &key)
793 return ownerTrustShort(key.ownerTrust());
796QString Formatting::ownerTrustShort(Key::OwnerTrust trust)
800 return i18nc(
"unknown trust level",
"unknown");
802 return i18n(
"untrusted");
804 return i18nc(
"marginal trust",
"marginal");
806 return i18nc(
"full trust",
"full");
808 return i18nc(
"ultimate trust",
"ultimate");
810 return i18nc(
"undefined trust",
"undefined");
812 Q_ASSERT(!
"unexpected owner trust value");
818QString Formatting::validityShort(
const Subkey &subkey)
820 if (subkey.isDisabled()) {
821 return i18n(
"disabled");
823 if (subkey.isRevoked()) {
824 return i18n(
"revoked");
826 if (subkey.isExpired()) {
827 return i18n(
"expired");
829 if (subkey.isInvalid()) {
830 return i18n(
"invalid");
832 return i18nc(
"as in 'this subkey is ok'",
"OK");
835QString Formatting::validityShort(
const UserID &uid)
837 if (uid.isRevoked()) {
838 return i18n(
"revoked");
840 if (uid.isInvalid()) {
841 return i18n(
"invalid");
843 switch (uid.validity()) {
844 case UserID::Unknown:
845 return i18nc(
"unknown trust level",
"unknown");
846 case UserID::Undefined:
847 return i18nc(
"undefined trust",
"undefined");
849 return i18n(
"untrusted");
850 case UserID::Marginal:
851 return i18nc(
"marginal trust",
"marginal");
853 return i18nc(
"full trust",
"full");
854 case UserID::Ultimate:
855 return i18nc(
"ultimate trust",
"ultimate");
860QString Formatting::validityShort(
const UserID::Signature &sig)
862 switch (sig.status()) {
863 case UserID::Signature::NoError:
864 if (!sig.isInvalid()) {
866 switch (sig.certClass()) {
871 return i18n(
"valid");
873 return i18n(
"revoked");
875 return i18n(
"class %1", sig.certClass());
880 case UserID::Signature::GeneralError:
881 return i18n(
"invalid");
882 case UserID::Signature::SigExpired:
883 return i18n(
"expired");
884 case UserID::Signature::KeyExpired:
885 return i18n(
"certificate expired");
886 case UserID::Signature::BadSignature:
887 return i18nc(
"fake/invalid signature",
"bad");
888 case UserID::Signature::NoPublicKey: {
891 const auto key = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID());
893 return i18n(
"no public key");
894 }
else if (key.isDisabled()) {
895 return i18n(
"key disabled");
896 }
else if (key.isRevoked()) {
897 return i18n(
"key revoked");
898 }
else if (key.isExpired()) {
899 return i18n(
"key expired");
902 return QStringLiteral(
"unknown");
908QIcon Formatting::validityIcon(
const UserID::Signature &sig)
910 switch (sig.status()) {
911 case UserID::Signature::NoError:
912 if (!sig.isInvalid()) {
914 switch (sig.certClass()) {
919 return Formatting::successIcon();
921 return Formatting::errorIcon();
928 case UserID::Signature::BadSignature:
929 case UserID::Signature::GeneralError:
930 return Formatting::errorIcon();
931 case UserID::Signature::SigExpired:
932 case UserID::Signature::KeyExpired:
933 return Formatting::infoIcon();
934 case UserID::Signature::NoPublicKey:
935 return Formatting::questionIcon();
940QString Formatting::formatKeyLink(
const Key &key)
945 return QStringLiteral(
"<a href=\"key:%1\">%2</a>").arg(QLatin1StringView(key.primaryFingerprint()), Formatting::prettyName(key));
948QString Formatting::formatForComboBox(
const GpgME::Key &key)
950 const QString name = prettyName(key);
951 QString
mail = prettyEMail(key);
952 if (!
mail.isEmpty()) {
953 mail = QLatin1Char(
'<') +
mail + QLatin1Char(
'>');
955 return i18nc(
"name, email, key id",
"%1 %2 (%3)", name, mail, QLatin1StringView(key.keyID())).
simplified();
958QString Formatting::nameAndEmailForSummaryLine(
const UserID &
id)
960 Q_ASSERT(!
id.isNull());
962 const QString email = Formatting::prettyEMail(
id);
963 const QString name = Formatting::prettyName(
id);
965 if (name.isEmpty()) {
967 }
else if (email.isEmpty()) {
970 return QStringLiteral(
"%1 <%2>").arg(name, email);
974QString Formatting::nameAndEmailForSummaryLine(
const Key &key)
976 Q_ASSERT(!key.isNull());
978 const QString email = Formatting::prettyEMail(key);
979 const QString name = Formatting::prettyName(key);
981 if (name.isEmpty()) {
983 }
else if (email.isEmpty()) {
986 return QStringLiteral(
"%1 <%2>").arg(name, email);
990const char *Formatting::summaryToString(
const Signature::Summary summary)
992 if (summary & Signature::Red) {
995 if (summary & Signature::Green) {
1001QString Formatting::signatureToString(
const Signature &sig,
const Key &key)
1007 const bool red = (sig.summary() & Signature::Red);
1008 const bool valid = (sig.summary() & Signature::Valid);
1012 if (
const char *fpr = sig.fingerprint()) {
1013 return i18n(
"Bad signature by unknown certificate %1: %2",
QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
1015 return i18n(
"Bad signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
1018 return i18n(
"Bad signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
1023 if (
const char *fpr = sig.fingerprint()) {
1026 return i18n(
"Good signature by an unknown certificate.");
1029 return i18n(
"Good signature by %1.", nameAndEmailForSummaryLine(key));
1032 }
else if (key.isNull()) {
1033 if (
const char *fpr = sig.fingerprint()) {
1034 return i18n(
"Invalid signature by unknown certificate %1: %2",
QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
1036 return i18n(
"Invalid signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
1039 return i18n(
"Invalid signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
1047QString Formatting::importMetaData(
const Import &
import,
const QStringList &ids)
1049 const QString result = importMetaData(
import);
1053 return result + QLatin1Char(
'\n') +
i18n(
"This certificate was imported from the following sources:") + QLatin1Char(
'\n') + ids.
join(QLatin1Char(
'\n'));
1057QString Formatting::importMetaData(
const Import &
import)
1059 if (
import.isNull()) {
1063 if (
import.
error().isCanceled()) {
1064 return i18n(
"The import of this certificate was canceled.");
1066 if (
import.
error()) {
1067 return i18n(
"An error occurred importing this certificate: %1", Formatting::errorAsString(
import.
error()));
1070 const unsigned int status =
import.status();
1071 if (
status & Import::NewKey) {
1072 return (
status & Import::ContainedSecretKey) ?
i18n(
"This certificate was new to your keystore. The secret key is available.")
1073 :
i18n(
"This certificate is new to your keystore.");
1076 QStringList results;
1077 if (
status & Import::NewUserIDs) {
1078 results.
push_back(
i18n(
"New user-ids were added to this certificate by the import."));
1080 if (
status & Import::NewSignatures) {
1081 results.
push_back(
i18n(
"New signatures were added to this certificate by the import."));
1083 if (
status & Import::NewSubkeys) {
1084 results.
push_back(
i18n(
"New subkeys were added to this certificate by the import."));
1087 return results.
empty() ?
i18n(
"The import contained no new data for this certificate. It is unchanged.") : results.join(QLatin1Char(
'\n'));
1090QString Formatting::usageString(
const Subkey &sub)
1092 QStringList usageStrings;
1093 if (sub.canCertify()) {
1094 usageStrings <<
i18n(
"Certify");
1096 if (sub.canSign()) {
1097 usageStrings <<
i18n(
"Sign");
1099 if (sub.canEncrypt()) {
1100 usageStrings <<
i18n(
"Encrypt");
1102 if (sub.canAuthenticate()) {
1103 usageStrings <<
i18n(
"Authenticate");
1105 if (sub.canRenc()) {
1106 usageStrings <<
i18nc(
"Means 'Additional Decryption Subkey'; Don't try translating that, though.",
"ADSK");
1108 return usageStrings.
join(QLatin1StringView(
", "));
1111QString Formatting::summaryLine(
const UserID &
id)
1113 return i18nc(
"name <email> (validity, protocol, creation date)",
1114 "%1 (%2, %3, created: %4)",
1115 nameAndEmailForSummaryLine(
id),
1116 Formatting::complianceStringShort(
id),
1117 displayName(
id.parent().protocol()),
1118 Formatting::creationDateString(
id.parent()));
1121QString Formatting::summaryLine(
const Key &key)
1123 return nameAndEmailForSummaryLine(key) + QLatin1Char(
' ')
1124 +
i18nc(
"(validity, protocol, creation date)",
1125 "(%1, %2, created: %3)",
1126 Formatting::complianceStringShort(key),
1127 displayName(key.protocol()),
1128 Formatting::creationDateString(key));
1131QString Formatting::summaryLine(
const KeyGroup &group)
1133 switch (group.source()) {
1134 case KeyGroup::ApplicationConfig:
1135 case KeyGroup::GnuPGConfig:
1136 return i18ncp(
"name of group of keys (n key(s), validity)",
1139 group.keys().size(),
1141 Formatting::complianceStringShort(group));
1142 case KeyGroup::Tags:
1143 return i18ncp(
"name of group of keys (n key(s), validity, tag)",
1144 "%2 (1 key, %3, tag)",
1145 "%2 (%1 keys, %3, tag)",
1146 group.keys().size(),
1148 Formatting::complianceStringShort(group));
1150 return i18ncp(
"name of group of keys (n key(s), validity, group ...)",
1151 "%2 (1 key, %3, unknown origin)",
1152 "%2 (%1 keys, %3, unknown origin)",
1153 group.keys().size(),
1155 Formatting::complianceStringShort(group));
1160QIcon Formatting::iconForUid(
const UserID &uid)
1162 if (Kleo::isRevokedOrExpired(uid)) {
1163 return Formatting::errorIcon();
1165 return iconForValidity(uid);
1168QString Formatting::validity(
const UserID &uid)
1170 switch (uid.validity()) {
1171 case UserID::Ultimate:
1172 return i18n(
"The certificate is marked as your own.");
1174 return i18n(
"The certificate belongs to this recipient.");
1175 case UserID::Marginal:
1176 return i18n(
"The trust model indicates marginally that the certificate belongs to this recipient.");
1178 return i18n(
"This certificate should not be used.");
1179 case UserID::Undefined:
1180 case UserID::Unknown:
1182 return i18n(
"There is no indication that this certificate belongs to this recipient.");
1186QString Formatting::validity(
const KeyGroup &group)
1188 if (group.isNull()) {
1192 const KeyGroup::Keys &keys = group.keys();
1193 if (keys.size() == 0) {
1194 return i18n(
"This group does not contain any keys.");
1197 return getValidityStatement(keys);
1202template<
typename Container>
1203UserID::Validity minimalValidity(
const Container &keys)
1205 const int minValidity = std::accumulate(keys.cbegin(), keys.cend(), UserID::Ultimate + 1, [](
int validity,
const Key &key) {
1206 return std::min<int>(validity, minimalValidityOfNotRevokedUserIDs(key));
1208 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::
Unknown;
1211template<
typename Container>
1212bool allKeysAreCompliant(
const Container &keys)
1214 if (!DeVSCompliance::isActive()) {
1217 if (!DeVSCompliance::isCompliant()) {
1220 return Kleo::all_of(keys, DeVSCompliance::keyIsCompliant);
1224QIcon Formatting::validityIcon(
const KeyGroup &group)
1226 if (Kleo::any_of(group.keys(), std::mem_fn(&Key::isBad))) {
1227 return Formatting::errorIcon();
1229 return iconForValidityAndCompliance(minimalValidity(group.keys()), allKeysAreCompliant(group.keys()));
1232QString Formatting::complianceMode()
1234 const auto complianceValue = getCryptoConfigStringValue(
"gpg",
"compliance");
1235 return complianceValue == QLatin1StringView(
"gnupg") ? QString() : complianceValue;
1238QString Formatting::complianceStringForKey(
const GpgME::Key &key)
1242 if (DeVSCompliance::isCompliant()) {
1243 return isRemoteKey(key)
1244 ?
i18nc(
"@info the compliance of the key with certain requirements is unknown",
"unknown")
1245 : DeVSCompliance::name(DeVSCompliance::keyIsCompliant(key));
1250QString Formatting::complianceStringForUserID(
const GpgME::UserID &userID)
1254 if (DeVSCompliance::isCompliant()) {
1255 return isRemoteKey(userID.parent())
1256 ?
i18nc(
"@info the compliance of the key with certain requirements is unknown",
"unknown")
1257 : DeVSCompliance::name(DeVSCompliance::userIDIsCompliant(userID));
1262QString Formatting::complianceStringShort(
const GpgME::UserID &
id)
1264 if (DeVSCompliance::isCompliant() && DeVSCompliance::userIDIsCompliant(
id)) {
1265 return QStringLiteral(
"★ ") + DeVSCompliance::name(
true);
1267 const bool keyValidityChecked = (
id.parent().keyListMode() & GpgME::Validate);
1268 if (keyValidityChecked &&
id.validity() >= UserID::Full) {
1269 return i18nc(
"As in 'this user ID is valid.'",
"certified");
1271 if (
id.parent().isDisabled()) {
1272 return i18n(
"disabled");
1274 if (
id.parent().isRevoked() ||
id.isRevoked()) {
1275 return i18n(
"revoked");
1277 if (
id.parent().isExpired() || isExpired(
id)) {
1278 return i18n(
"expired");
1280 if (
id.parent().isInvalid() ||
id.isInvalid()) {
1281 return i18n(
"invalid");
1283 if (keyValidityChecked) {
1284 return i18nc(
"As in 'this user ID is not certified'",
"not certified");
1287 return i18nc(
"The validity of this user ID has not been/could not be checked",
"not checked");
1290QString Formatting::complianceStringShort(
const GpgME::Key &key)
1292 if (DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(key)) {
1293 return QStringLiteral(
"★ ") + DeVSCompliance::name(
true);
1295 const bool keyValidityChecked = (key.keyListMode() & GpgME::Validate);
1296 if (key.isDisabled()) {
1297 return i18n(
"disabled");
1299 if (key.isRevoked()) {
1300 return i18n(
"revoked");
1302 if (key.isExpired()) {
1303 return i18n(
"expired");
1305 if (key.isInvalid()) {
1306 return i18n(
"invalid");
1308 if (keyValidityChecked && Kleo::allUserIDsHaveFullValidity(key)) {
1309 return i18nc(
"As in all user IDs are valid.",
"certified");
1311 if (keyValidityChecked) {
1312 return i18nc(
"As in not all user IDs are valid.",
"not certified");
1315 return i18nc(
"The validity of the user IDs has not been/could not be checked",
"not checked");
1318QString Formatting::complianceStringShort(
const KeyGroup &group)
1320 const KeyGroup::Keys &keys = group.keys();
1322 const bool allKeysFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
1323 if (allKeysFullyValid) {
1324 return i18nc(
"As in all keys are valid.",
"all certified");
1327 return i18nc(
"As in not all keys are valid.",
"not all certified");
1330QString Formatting::prettyID(
const char *
id)
1336 if (ret.
size() == 64) {
1339 return ret.
replace(QRegularExpression(QStringLiteral(
"(.....)")), QStringLiteral(
"\\1 ")).
trimmed();
1341 ret = ret.
replace(QRegularExpression(QStringLiteral(
"(....)")), QStringLiteral(
"\\1 ")).
trimmed();
1344 if (ret.
size() == 49) {
1345 ret.
insert(24, QLatin1Char(
' '));
1350QString Formatting::accessibleHexID(
const char *
id)
1352 static const QRegularExpression groupOfFourRegExp{QStringLiteral(
"(?:(.)(.)(.)(.))")};
1353 static const QRegularExpression groupOfFiveRegExp{QStringLiteral(
"(?:(.)(.)(.)(.)(.))")};
1357 if (ret.
size() == 64) {
1359 return ret.
replace(groupOfFiveRegExp, QStringLiteral(
"\\1 \\2 \\3 \\4 \\5, ")).
chopped(2);
1362 ret = ret.
replace(groupOfFourRegExp, QStringLiteral(
"\\1 \\2 \\3 \\4, ")).
chopped(2);
1367QString Formatting::origin(
int o)
1371 return i18n(
"Keyserver");
1372 case Key::OriginDane:
1373 return QStringLiteral(
"DANE");
1374 case Key::OriginWKD:
1375 return QStringLiteral(
"WKD");
1376 case Key::OriginURL:
1377 return QStringLiteral(
"URL");
1378 case Key::OriginFile:
1379 return i18n(
"File import");
1380 case Key::OriginSelf:
1381 return i18n(
"Generated");
1382 case Key::OriginOther:
1383 case Key::OriginUnknown:
1391QString formatTrustScope(
const char *trustScope)
1393 static const QRegularExpression escapedNonAlphaNum{QStringLiteral(R
"(\\([^0-9A-Za-z]))")};
1396 if (scopeRegExp.startsWith(u
"<[^>]+[@.]") && scopeRegExp.endsWith(u
">$")) {
1398 auto domain = scopeRegExp.mid(10, scopeRegExp.size() - 10 - 2);
1399 domain.replace(escapedNonAlphaNum, QStringLiteral(R
"(\1)"));
1406QString Formatting::trustSignatureDomain(
const GpgME::UserID::Signature &sig)
1408 return formatTrustScope(sig.trustScope());
1411QString Formatting::trustSignature(
const GpgME::UserID::Signature &sig)
1413 switch (sig.trustValue()) {
1414 case TrustSignatureTrust::Partial:
1415 return i18nc(
"Certifies this key as partially trusted introducer for 'domain name'.",
1416 "Certifies this key as partially trusted introducer for '%1'.",
1417 trustSignatureDomain(sig));
1418 case TrustSignatureTrust::Complete:
1419 return i18nc(
"Certifies this key as fully trusted introducer for 'domain name'.",
1420 "Certifies this key as fully trusted introducer for '%1'.",
1421 trustSignatureDomain(sig));
1427QString Formatting::errorAsString(
const GpgME::Error &error)
1431#if GPGMEPP_ERROR_HAS_ASSTDSTRING
1432 const std::string s =
error.asStdString();
1433 qCDebug(LIBKLEO_LOG) << __func__ <<
"gettext_use_utf8(-1) returns" << gettext_use_utf8(-1);
1434 qCDebug(LIBKLEO_LOG) << __func__ <<
"error:" << s;
1438 const char *s =
error.asString();
1439 qCDebug(LIBKLEO_LOG) << __func__ <<
"gettext_use_utf8(-1) returns" << gettext_use_utf8(-1);
1440 qCDebug(LIBKLEO_LOG) << __func__ <<
"error:" << s;
1441 qCDebug(LIBKLEO_LOG) << __func__ <<
"error (percent-encoded):" << QByteArray{s}.toPercentEncoding();
1445#if GPGMEPP_ERROR_HAS_ASSTDSTRING
1446 const std::string s =
error.asStdString();
1454QString Formatting::prettyAlgorithmName(
const std::string &algorithm)
1456 static const std::map<std::string, QString> displayNames = {
1457 {
"brainpoolP256r1",
i18nc(
"@info",
"ECC (Brainpool P-256)")},
1458 {
"brainpoolP384r1",
i18nc(
"@info",
"ECC (Brainpool P-384)")},
1459 {
"brainpoolP512r1",
i18nc(
"@info",
"ECC (Brainpool P-512)")},
1460 {
"curve25519",
i18nc(
"@info",
"ECC (Curve25519)")},
1461 {
"curve448",
i18nc(
"@info",
"ECC (Curve448)")},
1462 {
"ed25519",
i18nc(
"@info",
"ECC (Ed25519)")},
1463 {
"ed448",
i18nc(
"@info",
"ECC (Ed448)")},
1464 {
"cv25519",
i18nc(
"@info",
"ECC (Cv25519)")},
1465 {
"cv448",
i18nc(
"@info",
"ECC (Cv448)")},
1466 {
"nistp256",
i18nc(
"@info",
"ECC (NIST P-256)")},
1467 {
"nistp384",
i18nc(
"@info",
"ECC (NIST P-384)")},
1468 {
"nistp521",
i18nc(
"@info",
"ECC (NIST P-521)")},
1469 {
"rsa1024",
i18nc(
"@info",
"RSA 1024")},
1470 {
"rsa2048",
i18nc(
"@info",
"RSA 2048")},
1471 {
"rsa3072",
i18nc(
"@info",
"RSA 3072")},
1472 {
"rsa4096",
i18nc(
"@info",
"RSA 4096")},
1473 {
"dsa1024",
i18nc(
"@info",
"DSA 1024")},
1474 {
"dsa2048",
i18nc(
"@info",
"DSA 2048")},
1475 {
"elg1024",
i18nc(
"@info",
"Elgamal 1024")},
1476 {
"elg2048",
i18nc(
"@info",
"Elgamal 2048")},
1477 {
"elg3072",
i18nc(
"@info",
"Elgamal 3072")},
1478 {
"elg4096",
i18nc(
"@info",
"Elgamal 4096")},
1480 const auto it = displayNames.find(algorithm);
1484static QString formatValidSignatureWithTrustLevel(
const GpgME::UserID &
id)
1489 switch (
id.validity()) {
1490 case GpgME::UserID::Marginal:
1491 return i18n(
"The signature is valid but the trust in the certificate's validity is only marginal.");
1492 case GpgME::UserID::Full:
1493 return i18n(
"The signature is valid and the certificate's validity is fully trusted.");
1494 case GpgME::UserID::Ultimate:
1495 return i18n(
"The signature is valid and the certificate's validity is ultimately trusted.");
1496 case GpgME::UserID::Never:
1497 return i18n(
"The signature is valid but the certificate's validity is <em>not trusted</em>.");
1498 case GpgME::UserID::Unknown:
1499 return i18n(
"The signature is valid but the certificate's validity is unknown.");
1500 case GpgME::UserID::Undefined:
1502 return i18n(
"The signature is valid but the certificate's validity is undefined.");
1506static QString renderKeyLink(
const QString &fpr,
const QString &text)
1508 return QStringLiteral(
"<a href=\"key:%1\">%2</a>").
arg(fpr, text.
toHtmlEscaped());
1511static QString renderKey(
const GpgME::Key &key)
1514 return i18n(
"Unknown certificate");
1517 return renderKeyLink(QLatin1StringView(key.primaryFingerprint()),
1518 i18nc(
"User ID (Key ID)",
"%1 (%2)", Formatting::prettyNameAndEMail(key), Formatting::prettyID(key.subkey(0).keyID())));
1521static QString formatSigningInformation(
const GpgME::Signature &sig,
const GpgME::Key &key)
1531 QStringLiteral(
"<br/><a href='certificate:%1'>%2</a>").arg(QString::fromLatin1(sig.fingerprint()), Formatting::prettyID(sig.fingerprint()));
1533 return i18nc(
"1 is a date",
1534 "Signature created on %1 using an unknown certificate with fingerprint %2",
1535 QLocale().toString(dt, QLocale::ShortFormat),
1538 return i18n(
"Signature created using an unknown certificate with fingerprint %1",
id);
1542 text += i18nc(
"1 is a date",
"Signature created on %1 with certificate: %2", QLocale().toString(dt, QLocale::ShortFormat), renderKey(key));
1544 text += i18n(
"Signature created with certificate: %1", renderKey(key));
1547 if (Kleo::DeVSCompliance::isCompliant() && ((sig.summary() & GpgME::Signature::Valid) || (sig.summary() & GpgME::Signature::Green))) {
1548 text += (QStringLiteral(
"<br/>")
1549 + (sig.isDeVs() ? i18nc(
"%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
1550 "The signature is %1",
1551 Kleo::DeVSCompliance::name(true))
1552 : i18nc(
"%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
1553 "The signature <b>is not</b> %1.",
1554 Kleo::DeVSCompliance::name(true))));
1560static QString signatureSummaryToString(GpgME::Signature::Summary summary)
1562 if (summary & GpgME::Signature::None) {
1563 return i18n(
"Error: Signature not verified");
1564 }
else if ((summary & GpgME::Signature::Valid) || (summary & GpgME::Signature::Green)) {
1565 return i18n(
"Good signature");
1566 }
else if (summary & GpgME::Signature::KeyRevoked) {
1567 return i18n(
"Signing certificate was revoked");
1568 }
else if (summary & GpgME::Signature::KeyExpired) {
1569 return i18n(
"Signing certificate is expired");
1570 }
else if (summary & GpgME::Signature::KeyMissing) {
1571 return i18n(
"Certificate is not available");
1572 }
else if (summary & GpgME::Signature::SigExpired) {
1573 return i18n(
"Signature expired");
1574 }
else if (summary & GpgME::Signature::CrlMissing) {
1575 return i18n(
"CRL missing");
1576 }
else if (summary & GpgME::Signature::CrlTooOld) {
1577 return i18n(
"CRL too old");
1578 }
else if (summary & GpgME::Signature::BadPolicy) {
1579 return i18n(
"Bad policy");
1580 }
else if (summary & GpgME::Signature::SysError) {
1581 return i18n(
"System error");
1582 }
else if (summary & GpgME::Signature::Red) {
1583 return i18n(
"Bad signature");
1588static QLatin1StringView stripAngleBrackets(
const QLatin1StringView &str)
1593 if (str[0] ==
'<' && str[str.
size() - 1] ==
'>') {
1594 return str.
mid(1, str.
size() - 2);
1599QString Formatting::email(
const GpgME::UserID &uid)
1601 if (uid.parent().protocol() == GpgME::OpenPGP) {
1602 const QLatin1StringView email(uid.email());
1603 if (!email.isEmpty()) {
1604 return stripAngleBrackets(email).
toString();
1609 Q_ASSERT(uid.parent().protocol() == GpgME::CMS);
1611 const QLatin1StringView id(uid.id());
1612 if (!
id.isEmpty()) {
1614 return stripAngleBrackets(
id).
toString();
1616 return QGpgME::DN(
id)[QStringLiteral(
"EMAIL")].trimmed();
1621static GpgME::UserID findUserIDByMailbox(
const GpgME::Key &key,
const QString &email)
1623 const auto userIDs{key.userIDs()};
1624 for (
const GpgME::UserID &
id : userIDs) {
1632QString Kleo::Formatting::prettySignature(
const GpgME::Signature &sig,
const QString &sender)
1638 const GpgME::Key key = Kleo::KeyCache::instance()->findSigner(sig);
1640 const QString text = formatSigningInformation(sig, key) + QLatin1StringView(
"<br/>");
1643 if (sig.summary() & GpgME::Signature::Valid) {
1644 GpgME::UserID
id = findUserIDByMailbox(key, sender);
1646 for (
int i = 0, count = key.userIDs().size(); i < count; i++) {
1654 return text + formatValidSignatureWithTrustLevel(!
id.isNull() ?
id : key.userID(0));
1658 if ((sig.summary() & GpgME::Signature::Red)) {
1659 const QString ret = text +
i18n(
"The signature is invalid: %1", signatureSummaryToString(sig.summary()));
1660 if (sig.summary() & GpgME::Signature::SysError) {
1661 return ret + QStringLiteral(
" (%1)").
arg(Kleo::Formatting::errorAsString(sig.status()));
1667 if ((sig.summary() & GpgME::Signature::KeyMissing)) {
1668 return text +
i18n(
"You can search the certificate on a keyserver or import it from a file.");
1672 if ((sig.validity() & GpgME::Signature::Validity::Undefined)
1673 || (sig.validity() & GpgME::Signature::Validity::Unknown)
1674 || (sig.summary() == GpgME::Signature::Summary::None)) {
1676 + (key.protocol() == GpgME::OpenPGP
1677 ?
i18n(
"The used key is not certified by you or any trusted person.")
1678 :
i18n(
"The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
1682 const QString ret = text +
i18n(
"The signature is invalid: %1", signatureSummaryToString(sig.summary()));
1683 if (sig.summary() & GpgME::Signature::SysError) {
1684 return ret + QStringLiteral(
" (%1)").
arg(Kleo::Formatting::errorAsString(sig.status()));
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)
QByteArray fromStdString(const std::string &str)
QByteArray toPercentEncoding(const QByteArray &exclude, const QByteArray &include, char percent) const const
QDateTime fromSecsSinceEpoch(qint64 secs)
bool isValid() const const
QIcon fromTheme(const QString &name)
bool isEmpty() const const
QLatin1StringView mid(qsizetype start, qsizetype length) const const
qsizetype size() const const
QString toString() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
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
void truncate(qsizetype position)
QString join(QChar separator) const const