File indexing completed on 2024-04-28 03:53:52

0001 /*
0002     This file is part of the KDE Libraries
0003 
0004     SPDX-FileCopyrightText: 2000 Espen Sand <espen@kde.org>
0005     SPDX-FileCopyrightText: 2006 Nicolas GOUTTE <goutte@kde.org>
0006     SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org>
0007     SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
0008     SPDX-FileCopyrightText: 2017 Harald Sitter <sitter@kde.org>
0009     SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "kaboutdata.h"
0015 #include "kjsonutils.h"
0016 
0017 #include <QCommandLineOption>
0018 #include <QCommandLineParser>
0019 #include <QCoreApplication>
0020 #include <QFile>
0021 #include <QHash>
0022 #include <QJsonObject>
0023 #include <QList>
0024 #include <QLoggingCategory>
0025 #include <QSharedData>
0026 #include <QStandardPaths>
0027 #include <QTextStream>
0028 #include <QUrl>
0029 
0030 #include <algorithm>
0031 
0032 Q_DECLARE_LOGGING_CATEGORY(KABOUTDATA)
0033 // logging category for this framework, default: log stuff >= warning
0034 Q_LOGGING_CATEGORY(KABOUTDATA, "kf.coreaddons.kaboutdata", QtWarningMsg)
0035 
0036 class KAboutPersonPrivate : public QSharedData
0037 {
0038 public:
0039     QString _name;
0040     QString _task;
0041     QString _emailAddress;
0042     QString _webAddress;
0043     QUrl _avatarUrl;
0044 };
0045 
0046 KAboutPerson::KAboutPerson(const QString &_name, const QString &_task, const QString &_emailAddress, const QString &_webAddress, const QUrl &avatarUrl)
0047     : d(new KAboutPersonPrivate)
0048 {
0049     d->_name = _name;
0050     d->_task = _task;
0051     d->_emailAddress = _emailAddress;
0052     d->_webAddress = _webAddress;
0053     d->_avatarUrl = avatarUrl;
0054 }
0055 
0056 KAboutPerson::KAboutPerson(const QString &_name, const QString &_email, bool)
0057     : d(new KAboutPersonPrivate)
0058 {
0059     d->_name = _name;
0060     d->_emailAddress = _email;
0061 }
0062 
0063 KAboutPerson::KAboutPerson(const KAboutPerson &other) = default;
0064 
0065 KAboutPerson::~KAboutPerson() = default;
0066 
0067 QString KAboutPerson::name() const
0068 {
0069     return d->_name;
0070 }
0071 
0072 QString KAboutPerson::task() const
0073 {
0074     return d->_task;
0075 }
0076 
0077 QString KAboutPerson::emailAddress() const
0078 {
0079     return d->_emailAddress;
0080 }
0081 
0082 QString KAboutPerson::webAddress() const
0083 {
0084     return d->_webAddress;
0085 }
0086 
0087 QUrl KAboutPerson::avatarUrl() const
0088 {
0089     return d->_avatarUrl;
0090 }
0091 
0092 KAboutPerson &KAboutPerson::operator=(const KAboutPerson &other) = default;
0093 
0094 KAboutPerson KAboutPerson::fromJSON(const QJsonObject &obj)
0095 {
0096     const QString name = KJsonUtils::readTranslatedString(obj, QStringLiteral("Name"));
0097     const QString task = KJsonUtils::readTranslatedString(obj, QStringLiteral("Task"));
0098     const QString email = obj.value(QLatin1String("Email")).toString();
0099     const QString website = obj.value(QLatin1String("Website")).toString();
0100     const QUrl avatarUrl = obj.value(QLatin1String("AvatarUrl")).toVariant().toUrl();
0101     return KAboutPerson(name, task, email, website, avatarUrl);
0102 }
0103 
0104 class KAboutLicensePrivate : public QSharedData
0105 {
0106 public:
0107     KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType, KAboutLicense::VersionRestriction versionRestriction, const KAboutData *aboutData);
0108     KAboutLicensePrivate(const KAboutLicensePrivate &other);
0109 
0110     QString spdxID() const;
0111 
0112     KAboutLicense::LicenseKey _licenseKey;
0113     QString _licenseText;
0114     QString _pathToLicenseTextFile;
0115     KAboutLicense::VersionRestriction _versionRestriction;
0116     // needed for access to the possibly changing copyrightStatement()
0117     const KAboutData *_aboutData;
0118 };
0119 
0120 KAboutLicensePrivate::KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType,
0121                                            KAboutLicense::VersionRestriction versionRestriction,
0122                                            const KAboutData *aboutData)
0123     : QSharedData()
0124     , _licenseKey(licenseType)
0125     , _versionRestriction(versionRestriction)
0126     , _aboutData(aboutData)
0127 {
0128 }
0129 
0130 KAboutLicensePrivate::KAboutLicensePrivate(const KAboutLicensePrivate &other)
0131     : QSharedData(other)
0132     , _licenseKey(other._licenseKey)
0133     , _licenseText(other._licenseText)
0134     , _pathToLicenseTextFile(other._pathToLicenseTextFile)
0135     , _versionRestriction(other._versionRestriction)
0136     , _aboutData(other._aboutData)
0137 {
0138 }
0139 
0140 QString KAboutLicensePrivate::spdxID() const
0141 {
0142     switch (_licenseKey) {
0143     case KAboutLicense::GPL_V2:
0144         return QStringLiteral("GPL-2.0");
0145     case KAboutLicense::LGPL_V2:
0146         return QStringLiteral("LGPL-2.0");
0147     case KAboutLicense::BSDL:
0148         return QStringLiteral("BSD-2-Clause");
0149     case KAboutLicense::Artistic:
0150         return QStringLiteral("Artistic-1.0");
0151     case KAboutLicense::GPL_V3:
0152         return QStringLiteral("GPL-3.0");
0153     case KAboutLicense::LGPL_V3:
0154         return QStringLiteral("LGPL-3.0");
0155     case KAboutLicense::LGPL_V2_1:
0156         return QStringLiteral("LGPL-2.1");
0157     case KAboutLicense::MIT:
0158         return QStringLiteral("MIT");
0159     case KAboutLicense::Custom:
0160     case KAboutLicense::File:
0161     case KAboutLicense::Unknown:
0162         return QString();
0163     }
0164     return QString();
0165 }
0166 
0167 KAboutLicense::KAboutLicense()
0168     : d(new KAboutLicensePrivate(Unknown, {}, nullptr))
0169 {
0170 }
0171 
0172 KAboutLicense::KAboutLicense(LicenseKey licenseType, VersionRestriction versionRestriction, const KAboutData *aboutData)
0173     : d(new KAboutLicensePrivate(licenseType, versionRestriction, aboutData))
0174 {
0175 }
0176 
0177 KAboutLicense::KAboutLicense(LicenseKey licenseType, const KAboutData *aboutData)
0178     : d(new KAboutLicensePrivate(licenseType, OnlyThisVersion, aboutData))
0179 {
0180 }
0181 
0182 KAboutLicense::KAboutLicense(const KAboutData *aboutData)
0183     : d(new KAboutLicensePrivate(Unknown, OnlyThisVersion, aboutData))
0184 {
0185 }
0186 
0187 KAboutLicense::KAboutLicense(const KAboutLicense &other)
0188     : d(other.d)
0189 {
0190 }
0191 
0192 KAboutLicense::~KAboutLicense()
0193 {
0194 }
0195 
0196 void KAboutLicense::setLicenseFromPath(const QString &pathToFile)
0197 {
0198     d->_licenseKey = KAboutLicense::File;
0199     d->_pathToLicenseTextFile = pathToFile;
0200 }
0201 
0202 void KAboutLicense::setLicenseFromText(const QString &licenseText)
0203 {
0204     d->_licenseKey = KAboutLicense::Custom;
0205     d->_licenseText = licenseText;
0206 }
0207 
0208 QString KAboutLicense::text() const
0209 {
0210     QString result;
0211 
0212     const QString lineFeed = QStringLiteral("\n\n");
0213 
0214     if (d->_aboutData && !d->_aboutData->copyrightStatement().isEmpty()) {
0215         result = d->_aboutData->copyrightStatement() + lineFeed;
0216     }
0217 
0218     bool knownLicense = false;
0219     QString pathToFile; // rel path if known license
0220     switch (d->_licenseKey) {
0221     case KAboutLicense::File:
0222         pathToFile = d->_pathToLicenseTextFile;
0223         break;
0224     case KAboutLicense::GPL_V2:
0225         knownLicense = true;
0226         pathToFile = QStringLiteral("GPL_V2");
0227         break;
0228     case KAboutLicense::LGPL_V2:
0229         knownLicense = true;
0230         pathToFile = QStringLiteral("LGPL_V2");
0231         break;
0232     case KAboutLicense::BSDL:
0233         knownLicense = true;
0234         pathToFile = QStringLiteral("BSD");
0235         break;
0236     case KAboutLicense::Artistic:
0237         knownLicense = true;
0238         pathToFile = QStringLiteral("ARTISTIC");
0239         break;
0240     case KAboutLicense::GPL_V3:
0241         knownLicense = true;
0242         pathToFile = QStringLiteral("GPL_V3");
0243         break;
0244     case KAboutLicense::LGPL_V3:
0245         knownLicense = true;
0246         pathToFile = QStringLiteral("LGPL_V3");
0247         break;
0248     case KAboutLicense::LGPL_V2_1:
0249         knownLicense = true;
0250         pathToFile = QStringLiteral("LGPL_V21");
0251         break;
0252     case KAboutLicense::MIT:
0253         knownLicense = true;
0254         pathToFile = QStringLiteral("MIT");
0255         break;
0256     case KAboutLicense::Custom:
0257         if (!d->_licenseText.isEmpty()) {
0258             result = d->_licenseText;
0259             break;
0260         }
0261         Q_FALLTHROUGH();
0262     // fall through
0263     default:
0264         result += QCoreApplication::translate("KAboutLicense",
0265                                               "No licensing terms for this program have been specified.\n"
0266                                               "Please check the documentation or the source for any\n"
0267                                               "licensing terms.\n");
0268     }
0269 
0270     if (knownLicense) {
0271 #ifndef Q_OS_ANDROID
0272         pathToFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/licenses/") + pathToFile);
0273 #else
0274         pathToFile = QStringLiteral("assets:/share/kf6/licenses/") + pathToFile;
0275 #endif
0276         result += QCoreApplication::translate("KAboutLicense", "This program is distributed under the terms of the %1.").arg(name(KAboutLicense::ShortName));
0277         if (!pathToFile.isEmpty()) {
0278             result += lineFeed;
0279         }
0280     }
0281 
0282     if (!pathToFile.isEmpty()) {
0283         QFile file(pathToFile);
0284         if (file.open(QIODevice::ReadOnly)) {
0285             QTextStream str(&file);
0286             result += str.readAll();
0287         }
0288     }
0289 
0290     return result;
0291 }
0292 
0293 QString KAboutLicense::spdx() const
0294 {
0295     // SPDX licenses are comprised of an identifier (e.g. GPL-2.0), an optional + to denote 'or
0296     // later versions' and optional ' WITH $exception' to denote standardized exceptions from the
0297     // core license. As we do not offer exceptions we effectively only return GPL-2.0 or GPL-2.0+,
0298     // this may change in the future. To that end the documentation makes no assertions about the
0299     // actual content of the SPDX license expression we return.
0300     // Expressions can in theory also contain AND, OR and () to build constructs involving more than
0301     // one license. As this is outside the scope of a single license object we'll ignore this here
0302     // for now.
0303     // The expectation is that the return value is only run through spec-compliant parsers, so this
0304     // can potentially be changed.
0305 
0306     auto id = d->spdxID();
0307     if (id.isNull()) { // Guard against potential future changes which would allow 'Foo+' as input.
0308         return id;
0309     }
0310     return d->_versionRestriction == OrLaterVersions ? id.append(QLatin1Char('+')) : id;
0311 }
0312 
0313 QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const
0314 {
0315     QString licenseShort;
0316     QString licenseFull;
0317 
0318     switch (d->_licenseKey) {
0319     case KAboutLicense::GPL_V2:
0320         licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v2", "@item license (short name)");
0321         licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 2", "@item license");
0322         break;
0323     case KAboutLicense::LGPL_V2:
0324         licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2", "@item license (short name)");
0325         licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2", "@item license");
0326         break;
0327     case KAboutLicense::BSDL:
0328         licenseShort = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license (short name)");
0329         licenseFull = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license");
0330         break;
0331     case KAboutLicense::Artistic:
0332         licenseShort = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license (short name)");
0333         licenseFull = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license");
0334         break;
0335     case KAboutLicense::GPL_V3:
0336         licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v3", "@item license (short name)");
0337         licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 3", "@item license");
0338         break;
0339     case KAboutLicense::LGPL_V3:
0340         licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v3", "@item license (short name)");
0341         licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 3", "@item license");
0342         break;
0343     case KAboutLicense::LGPL_V2_1:
0344         licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2.1", "@item license (short name)");
0345         licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2.1", "@item license");
0346         break;
0347     case KAboutLicense::MIT:
0348         licenseShort = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license (short name)");
0349         licenseFull = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license");
0350         break;
0351     case KAboutLicense::Custom:
0352     case KAboutLicense::File:
0353         licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Custom", "@item license");
0354         break;
0355     default:
0356         licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Not specified", "@item license");
0357     }
0358 
0359     const QString result = (formatName == KAboutLicense::ShortName) ? licenseShort : (formatName == KAboutLicense::FullName) ? licenseFull : QString();
0360 
0361     return result;
0362 }
0363 
0364 KAboutLicense &KAboutLicense::operator=(const KAboutLicense &other)
0365 {
0366     d = other.d;
0367     return *this;
0368 }
0369 
0370 KAboutLicense::LicenseKey KAboutLicense::key() const
0371 {
0372     return d->_licenseKey;
0373 }
0374 
0375 KAboutLicense KAboutLicense::byKeyword(const QString &rawKeyword)
0376 {
0377     // Setup keyword->enum dictionary on first call.
0378     // Use normalized keywords, by the algorithm below.
0379     static const QHash<QByteArray, KAboutLicense::LicenseKey> licenseDict{
0380         {"gpl", KAboutLicense::GPL},           {"gplv2", KAboutLicense::GPL_V2},
0381         {"gplv2+", KAboutLicense::GPL_V2},     {"gpl20", KAboutLicense::GPL_V2},
0382         {"gpl20+", KAboutLicense::GPL_V2},     {"lgpl", KAboutLicense::LGPL},
0383         {"lgplv2", KAboutLicense::LGPL_V2},    {"lgplv2+", KAboutLicense::LGPL_V2},
0384         {"lgpl20", KAboutLicense::LGPL_V2},    {"lgpl20+", KAboutLicense::LGPL_V2},
0385         {"bsd", KAboutLicense::BSDL},          {"bsd2clause", KAboutLicense::BSDL},
0386         {"artistic", KAboutLicense::Artistic}, {"artistic10", KAboutLicense::Artistic},
0387         {"gplv3", KAboutLicense::GPL_V3},      {"gplv3+", KAboutLicense::GPL_V3},
0388         {"gpl30", KAboutLicense::GPL_V3},      {"gpl30+", KAboutLicense::GPL_V3},
0389         {"lgplv3", KAboutLicense::LGPL_V3},    {"lgplv3+", KAboutLicense::LGPL_V3},
0390         {"lgpl30", KAboutLicense::LGPL_V3},    {"lgpl30+", KAboutLicense::LGPL_V3},
0391         {"lgplv21", KAboutLicense::LGPL_V2_1}, {"lgplv21+", KAboutLicense::LGPL_V2_1},
0392         {"lgpl21", KAboutLicense::LGPL_V2_1},  {"lgpl21+", KAboutLicense::LGPL_V2_1},
0393         {"mit", KAboutLicense::MIT},
0394     };
0395 
0396     // Normalize keyword.
0397     QString keyword = rawKeyword;
0398     keyword = keyword.toLower();
0399     keyword.replace(QLatin1StringView("-or-later"), QLatin1StringView("+"));
0400     keyword.remove(QLatin1Char(' '));
0401     keyword.remove(QLatin1Char('.'));
0402     keyword.remove(QLatin1Char('-'));
0403 
0404     LicenseKey license = licenseDict.value(keyword.toLatin1(), KAboutLicense::Custom);
0405     auto restriction = keyword.endsWith(QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion;
0406     return KAboutLicense(license, restriction, nullptr);
0407 }
0408 
0409 class KAboutComponentPrivate : public QSharedData
0410 {
0411 public:
0412     QString _name;
0413     QString _description;
0414     QString _version;
0415     QString _webAddress;
0416     KAboutLicense _license;
0417 };
0418 
0419 KAboutComponent::KAboutComponent(const QString &_name,
0420                                  const QString &_description,
0421                                  const QString &_version,
0422                                  const QString &_webAddress,
0423                                  enum KAboutLicense::LicenseKey licenseType)
0424     : d(new KAboutComponentPrivate)
0425 {
0426     d->_name = _name;
0427     d->_description = _description;
0428     d->_version = _version;
0429     d->_webAddress = _webAddress;
0430     d->_license = KAboutLicense(licenseType, nullptr);
0431 }
0432 
0433 KAboutComponent::KAboutComponent(const QString &_name,
0434                                  const QString &_description,
0435                                  const QString &_version,
0436                                  const QString &_webAddress,
0437                                  const QString &pathToLicenseFile)
0438     : d(new KAboutComponentPrivate)
0439 {
0440     d->_name = _name;
0441     d->_description = _description;
0442     d->_version = _version;
0443     d->_webAddress = _webAddress;
0444     d->_license = KAboutLicense();
0445     d->_license.setLicenseFromPath(pathToLicenseFile);
0446 }
0447 
0448 KAboutComponent::KAboutComponent(const KAboutComponent &other) = default;
0449 
0450 KAboutComponent::~KAboutComponent() = default;
0451 
0452 QString KAboutComponent::name() const
0453 {
0454     return d->_name;
0455 }
0456 
0457 QString KAboutComponent::description() const
0458 {
0459     return d->_description;
0460 }
0461 
0462 QString KAboutComponent::version() const
0463 {
0464     return d->_version;
0465 }
0466 
0467 QString KAboutComponent::webAddress() const
0468 {
0469     return d->_webAddress;
0470 }
0471 
0472 KAboutLicense KAboutComponent::license() const
0473 {
0474     return d->_license;
0475 }
0476 
0477 KAboutComponent &KAboutComponent::operator=(const KAboutComponent &other) = default;
0478 
0479 class KAboutDataPrivate
0480 {
0481 public:
0482     KAboutDataPrivate()
0483         : customAuthorTextEnabled(false)
0484     {
0485     }
0486     QString _componentName;
0487     QString _displayName;
0488     QString _shortDescription;
0489     QString _copyrightStatement;
0490     QString _otherText;
0491     QString _homepageAddress;
0492     QList<KAboutPerson> _authorList;
0493     QList<KAboutPerson> _creditList;
0494     QList<KAboutPerson> _translatorList;
0495     QList<KAboutComponent> _componentList;
0496     QList<KAboutLicense> _licenseList;
0497     QVariant programLogo;
0498     QString customAuthorPlainText, customAuthorRichText;
0499     bool customAuthorTextEnabled;
0500 
0501     QString organizationDomain;
0502     QString desktopFileName;
0503 
0504     // Everything dr.konqi needs, we store as utf-8, so we
0505     // can just give it a pointer, w/o any allocations.
0506     QByteArray _internalProgramName;
0507     QByteArray _version;
0508     QByteArray _bugAddress;
0509     QByteArray productName;
0510 
0511     static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail);
0512 };
0513 
0514 KAboutData::KAboutData(const QString &_componentName,
0515                        const QString &_displayName,
0516                        const QString &_version,
0517                        const QString &_shortDescription,
0518                        enum KAboutLicense::LicenseKey licenseType,
0519                        const QString &_copyrightStatement,
0520                        const QString &text,
0521                        const QString &homePageAddress,
0522                        const QString &bugAddress)
0523     : d(new KAboutDataPrivate)
0524 {
0525     d->_componentName = _componentName;
0526     int p = d->_componentName.indexOf(QLatin1Char('/'));
0527     if (p >= 0) {
0528         d->_componentName = d->_componentName.mid(p + 1);
0529     }
0530 
0531     d->_displayName = _displayName;
0532     if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
0533         d->_internalProgramName = _displayName.toUtf8();
0534     }
0535     d->_version = _version.toUtf8();
0536     d->_shortDescription = _shortDescription;
0537     d->_licenseList.append(KAboutLicense(licenseType, this));
0538     d->_copyrightStatement = _copyrightStatement;
0539     d->_otherText = text;
0540     d->_homepageAddress = homePageAddress;
0541     d->_bugAddress = bugAddress.toUtf8();
0542 
0543     QUrl homePageUrl(homePageAddress);
0544     if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) {
0545         // Default domain if nothing else is better
0546         homePageUrl.setUrl(QStringLiteral("https://kde.org/"));
0547     }
0548 
0549     const QChar dotChar(QLatin1Char('.'));
0550     QStringList hostComponents = homePageUrl.host().split(dotChar);
0551 
0552     // Remove leading component unless 2 (or less) components are present
0553     if (hostComponents.size() > 2) {
0554         hostComponents.removeFirst();
0555     }
0556 
0557     d->organizationDomain = hostComponents.join(dotChar);
0558 
0559     // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
0560     // see KAboutData::desktopFileName() for details
0561 
0562     // desktop file name is reverse domain name
0563     std::reverse(hostComponents.begin(), hostComponents.end());
0564     hostComponents.append(_componentName);
0565 
0566     d->desktopFileName = hostComponents.join(dotChar);
0567 }
0568 
0569 KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version)
0570     : d(new KAboutDataPrivate)
0571 {
0572     d->_componentName = _componentName;
0573     int p = d->_componentName.indexOf(QLatin1Char('/'));
0574     if (p >= 0) {
0575         d->_componentName = d->_componentName.mid(p + 1);
0576     }
0577 
0578     d->_displayName = _displayName;
0579     if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
0580         d->_internalProgramName = _displayName.toUtf8();
0581     }
0582     d->_version = _version.toUtf8();
0583 
0584     // match behaviour of other constructors
0585     d->_licenseList.append(KAboutLicense(KAboutLicense::Unknown, this));
0586     d->_bugAddress = "submit@bugs.kde.org";
0587     d->organizationDomain = QStringLiteral("kde.org");
0588     // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
0589     // see KAboutData::desktopFileName() for details
0590     d->desktopFileName = QLatin1String("org.kde.") + d->_componentName;
0591 }
0592 
0593 KAboutData::~KAboutData() = default;
0594 
0595 KAboutData::KAboutData(const KAboutData &other)
0596     : d(new KAboutDataPrivate)
0597 {
0598     *d = *other.d;
0599     for (KAboutLicense &al : d->_licenseList) {
0600         al.d.detach();
0601         al.d->_aboutData = this;
0602     }
0603 }
0604 
0605 KAboutData &KAboutData::operator=(const KAboutData &other)
0606 {
0607     if (this != &other) {
0608         *d = *other.d;
0609         for (KAboutLicense &al : d->_licenseList) {
0610             al.d.detach();
0611             al.d->_aboutData = this;
0612         }
0613     }
0614     return *this;
0615 }
0616 
0617 KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
0618 {
0619     d->_authorList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
0620     return *this;
0621 }
0622 
0623 KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
0624 {
0625     d->_creditList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
0626     return *this;
0627 }
0628 
0629 KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress)
0630 {
0631     d->_translatorList = KAboutDataPrivate::parseTranslators(name, emailAddress);
0632     return *this;
0633 }
0634 
0635 KAboutData &KAboutData::addComponent(const QString &name,
0636                                      const QString &description,
0637                                      const QString &version,
0638                                      const QString &webAddress,
0639                                      KAboutLicense::LicenseKey licenseKey)
0640 {
0641     d->_componentList.append(KAboutComponent(name, description, version, webAddress, licenseKey));
0642     return *this;
0643 }
0644 
0645 KAboutData &
0646 KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile)
0647 {
0648     d->_componentList.append(KAboutComponent(name, description, version, webAddress, pathToLicenseFile));
0649     return *this;
0650 }
0651 
0652 KAboutData &KAboutData::setLicenseText(const QString &licenseText)
0653 {
0654     d->_licenseList[0] = KAboutLicense(this);
0655     d->_licenseList[0].setLicenseFromText(licenseText);
0656     return *this;
0657 }
0658 
0659 KAboutData &KAboutData::addLicenseText(const QString &licenseText)
0660 {
0661     // if the default license is unknown, overwrite instead of append
0662     KAboutLicense &firstLicense = d->_licenseList[0];
0663     KAboutLicense newLicense(this);
0664     newLicense.setLicenseFromText(licenseText);
0665     if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
0666         firstLicense = newLicense;
0667     } else {
0668         d->_licenseList.append(newLicense);
0669     }
0670 
0671     return *this;
0672 }
0673 
0674 KAboutData &KAboutData::setLicenseTextFile(const QString &pathToFile)
0675 {
0676     d->_licenseList[0] = KAboutLicense(this);
0677     d->_licenseList[0].setLicenseFromPath(pathToFile);
0678     return *this;
0679 }
0680 
0681 KAboutData &KAboutData::addLicenseTextFile(const QString &pathToFile)
0682 {
0683     // if the default license is unknown, overwrite instead of append
0684     KAboutLicense &firstLicense = d->_licenseList[0];
0685     KAboutLicense newLicense(this);
0686     newLicense.setLicenseFromPath(pathToFile);
0687     if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
0688         firstLicense = newLicense;
0689     } else {
0690         d->_licenseList.append(newLicense);
0691     }
0692     return *this;
0693 }
0694 
0695 KAboutData &KAboutData::setComponentName(const QString &componentName)
0696 {
0697     d->_componentName = componentName;
0698     return *this;
0699 }
0700 
0701 KAboutData &KAboutData::setDisplayName(const QString &_displayName)
0702 {
0703     d->_displayName = _displayName;
0704     d->_internalProgramName = _displayName.toUtf8();
0705     return *this;
0706 }
0707 
0708 KAboutData &KAboutData::setVersion(const QByteArray &_version)
0709 {
0710     d->_version = _version;
0711     return *this;
0712 }
0713 
0714 KAboutData &KAboutData::setShortDescription(const QString &_shortDescription)
0715 {
0716     d->_shortDescription = _shortDescription;
0717     return *this;
0718 }
0719 
0720 KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey)
0721 {
0722     return setLicense(licenseKey, KAboutLicense::OnlyThisVersion);
0723 }
0724 
0725 KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction)
0726 {
0727     d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this);
0728     return *this;
0729 }
0730 
0731 KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey)
0732 {
0733     return addLicense(licenseKey, KAboutLicense::OnlyThisVersion);
0734 }
0735 
0736 KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction)
0737 {
0738     // if the default license is unknown, overwrite instead of append
0739     KAboutLicense &firstLicense = d->_licenseList[0];
0740     if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
0741         firstLicense = KAboutLicense(licenseKey, versionRestriction, this);
0742     } else {
0743         d->_licenseList.append(KAboutLicense(licenseKey, versionRestriction, this));
0744     }
0745     return *this;
0746 }
0747 
0748 KAboutData &KAboutData::setCopyrightStatement(const QString &_copyrightStatement)
0749 {
0750     d->_copyrightStatement = _copyrightStatement;
0751     return *this;
0752 }
0753 
0754 KAboutData &KAboutData::setOtherText(const QString &_otherText)
0755 {
0756     d->_otherText = _otherText;
0757     return *this;
0758 }
0759 
0760 KAboutData &KAboutData::setHomepage(const QString &homepage)
0761 {
0762     d->_homepageAddress = homepage;
0763     return *this;
0764 }
0765 
0766 KAboutData &KAboutData::setBugAddress(const QByteArray &_bugAddress)
0767 {
0768     d->_bugAddress = _bugAddress;
0769     return *this;
0770 }
0771 
0772 KAboutData &KAboutData::setOrganizationDomain(const QByteArray &domain)
0773 {
0774     d->organizationDomain = QString::fromLatin1(domain.data());
0775     return *this;
0776 }
0777 
0778 KAboutData &KAboutData::setProductName(const QByteArray &_productName)
0779 {
0780     d->productName = _productName;
0781     return *this;
0782 }
0783 
0784 QString KAboutData::componentName() const
0785 {
0786     return d->_componentName;
0787 }
0788 
0789 QString KAboutData::productName() const
0790 {
0791     if (!d->productName.isEmpty()) {
0792         return QString::fromUtf8(d->productName);
0793     }
0794     return componentName();
0795 }
0796 
0797 const char *KAboutData::internalProductName() const
0798 {
0799     return d->productName.isEmpty() ? nullptr : d->productName.constData();
0800 }
0801 
0802 QString KAboutData::displayName() const
0803 {
0804     return d->_displayName;
0805 }
0806 
0807 /// @internal
0808 /// Return the program name. It is always pre-allocated.
0809 /// Needed for KCrash in particular.
0810 const char *KAboutData::internalProgramName() const
0811 {
0812     return d->_internalProgramName.constData();
0813 }
0814 
0815 QVariant KAboutData::programLogo() const
0816 {
0817     return d->programLogo;
0818 }
0819 
0820 KAboutData &KAboutData::setProgramLogo(const QVariant &image)
0821 {
0822     d->programLogo = image;
0823     return *this;
0824 }
0825 
0826 QString KAboutData::version() const
0827 {
0828     return QString::fromUtf8(d->_version.data());
0829 }
0830 
0831 /// @internal
0832 /// Return the untranslated and uninterpreted (to UTF8) string
0833 /// for the version information. Used in particular for KCrash.
0834 const char *KAboutData::internalVersion() const
0835 {
0836     return d->_version.constData();
0837 }
0838 
0839 QString KAboutData::shortDescription() const
0840 {
0841     return d->_shortDescription;
0842 }
0843 
0844 QString KAboutData::homepage() const
0845 {
0846     return d->_homepageAddress;
0847 }
0848 
0849 QString KAboutData::bugAddress() const
0850 {
0851     return QString::fromUtf8(d->_bugAddress.constData());
0852 }
0853 
0854 QString KAboutData::organizationDomain() const
0855 {
0856     return d->organizationDomain;
0857 }
0858 
0859 /// @internal
0860 /// Return the untranslated and uninterpreted (to UTF8) string
0861 /// for the bug mail address. Used in particular for KCrash.
0862 const char *KAboutData::internalBugAddress() const
0863 {
0864     if (d->_bugAddress.isEmpty()) {
0865         return nullptr;
0866     }
0867     return d->_bugAddress.constData();
0868 }
0869 
0870 QList<KAboutPerson> KAboutData::authors() const
0871 {
0872     return d->_authorList;
0873 }
0874 
0875 QList<KAboutPerson> KAboutData::credits() const
0876 {
0877     return d->_creditList;
0878 }
0879 
0880 QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail)
0881 {
0882     if (translatorName.isEmpty() || translatorName == QLatin1String("Your names")) {
0883         return {};
0884     }
0885 
0886     // use list of string views to delay creating new QString instances after the white-space trimming
0887     const QList<QStringView> nameList = QStringView(translatorName).split(QLatin1Char(','));
0888 
0889     QList<QStringView> emailList;
0890     if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails")) {
0891         emailList = QStringView(translatorEmail).split(QLatin1Char(','), Qt::KeepEmptyParts);
0892     }
0893 
0894     QList<KAboutPerson> personList;
0895     personList.reserve(nameList.size());
0896 
0897     auto eit = emailList.constBegin();
0898 
0899     for (const QStringView &name : nameList) {
0900         QStringView email;
0901         if (eit != emailList.constEnd()) {
0902             email = *eit;
0903             ++eit;
0904         }
0905 
0906         personList.append(KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true));
0907     }
0908 
0909     return personList;
0910 }
0911 
0912 QList<KAboutPerson> KAboutData::translators() const
0913 {
0914     return d->_translatorList;
0915 }
0916 
0917 QString KAboutData::aboutTranslationTeam()
0918 {
0919     return QCoreApplication::translate("KAboutData",
0920                                        "<p>KDE is translated into many languages thanks to the work "
0921                                        "of the translation teams all over the world.</p>"
0922                                        "<p>For more information on KDE internationalization "
0923                                        "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>",
0924                                        "replace this with information about your translation team");
0925 }
0926 
0927 QString KAboutData::otherText() const
0928 {
0929     return d->_otherText;
0930 }
0931 
0932 QList<KAboutComponent> KAboutData::components() const
0933 {
0934     return d->_componentList;
0935 }
0936 
0937 QList<KAboutLicense> KAboutData::licenses() const
0938 {
0939     return d->_licenseList;
0940 }
0941 
0942 QString KAboutData::copyrightStatement() const
0943 {
0944     return d->_copyrightStatement;
0945 }
0946 
0947 QString KAboutData::customAuthorPlainText() const
0948 {
0949     return d->customAuthorPlainText;
0950 }
0951 
0952 QString KAboutData::customAuthorRichText() const
0953 {
0954     return d->customAuthorRichText;
0955 }
0956 
0957 bool KAboutData::customAuthorTextEnabled() const
0958 {
0959     return d->customAuthorTextEnabled;
0960 }
0961 
0962 KAboutData &KAboutData::setCustomAuthorText(const QString &plainText, const QString &richText)
0963 {
0964     d->customAuthorPlainText = plainText;
0965     d->customAuthorRichText = richText;
0966 
0967     d->customAuthorTextEnabled = true;
0968 
0969     return *this;
0970 }
0971 
0972 KAboutData &KAboutData::unsetCustomAuthorText()
0973 {
0974     d->customAuthorPlainText = QString();
0975     d->customAuthorRichText = QString();
0976 
0977     d->customAuthorTextEnabled = false;
0978 
0979     return *this;
0980 }
0981 
0982 KAboutData &KAboutData::setDesktopFileName(const QString &desktopFileName)
0983 {
0984     d->desktopFileName = desktopFileName;
0985 
0986     return *this;
0987 }
0988 
0989 QString KAboutData::desktopFileName() const
0990 {
0991     return d->desktopFileName;
0992     // KF6: switch to this code and adapt API dox
0993 #if 0
0994     // if desktopFileName has been explicitly set, use that value
0995     if (!d->desktopFileName.isEmpty()) {
0996         return d->desktopFileName;
0997     }
0998 
0999     // return a string calculated on-the-fly from the current org domain & component name
1000     const QChar dotChar(QLatin1Char('.'));
1001     QStringList hostComponents = d->organizationDomain.split(dotChar);
1002 
1003     // desktop file name is reverse domain name
1004     std::reverse(hostComponents.begin(), hostComponents.end());
1005     hostComponents.append(componentName());
1006 
1007     return hostComponents.join(dotChar);
1008 #endif
1009 }
1010 
1011 class KAboutDataRegistry
1012 {
1013 public:
1014     KAboutDataRegistry()
1015         : m_appData(nullptr)
1016     {
1017     }
1018     ~KAboutDataRegistry()
1019     {
1020         delete m_appData;
1021     }
1022     KAboutDataRegistry(const KAboutDataRegistry &) = delete;
1023     KAboutDataRegistry &operator=(const KAboutDataRegistry &) = delete;
1024 
1025     KAboutData *m_appData;
1026 };
1027 
1028 Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry)
1029 
1030 namespace
1031 {
1032 void warnIfOutOfSync(const char *aboutDataString, const QString &aboutDataValue, const char *appDataString, const QString &appDataValue)
1033 {
1034     if (aboutDataValue != appDataValue) {
1035         qCWarning(KABOUTDATA) << appDataString << appDataValue << "is out-of-sync with" << aboutDataString << aboutDataValue;
1036     }
1037 }
1038 
1039 }
1040 
1041 KAboutData KAboutData::applicationData()
1042 {
1043     QCoreApplication *app = QCoreApplication::instance();
1044 
1045     KAboutData *aboutData = s_registry->m_appData;
1046 
1047     // not yet existing
1048     if (!aboutData) {
1049         // init from current Q*Application data
1050         aboutData = new KAboutData(QCoreApplication::applicationName(), QString(), QString());
1051         // Unset the default (KDE) bug address, this is likely a third-party app. https://bugs.kde.org/show_bug.cgi?id=473517
1052         aboutData->setBugAddress(QByteArray());
1053         // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1054         // we have to try to get them via the property system, as the static getter methods are
1055         // part of QtGui only. Disadvantage: requires an app instance.
1056         // Either get all or none of the properties & warn about it
1057         if (app) {
1058             aboutData->setOrganizationDomain(QCoreApplication::organizationDomain().toUtf8());
1059             aboutData->setVersion(QCoreApplication::applicationVersion().toUtf8());
1060             aboutData->setDisplayName(app->property("applicationDisplayName").toString());
1061             aboutData->setDesktopFileName(app->property("desktopFileName").toString());
1062         } else {
1063             qCWarning(KABOUTDATA) << "Could not initialize the properties of KAboutData::applicationData by the equivalent properties from Q*Application: no "
1064                                      "app instance (yet) existing.";
1065         }
1066 
1067         s_registry->m_appData = aboutData;
1068     } else {
1069         // check if in-sync with Q*Application metadata, as their setters could have been called
1070         // after the last KAboutData::setApplicationData, with different values
1071         warnIfOutOfSync("KAboutData::applicationData().componentName",
1072                         aboutData->componentName(),
1073                         "QCoreApplication::applicationName",
1074                         QCoreApplication::applicationName());
1075         warnIfOutOfSync("KAboutData::applicationData().version",
1076                         aboutData->version(),
1077                         "QCoreApplication::applicationVersion",
1078                         QCoreApplication::applicationVersion());
1079         warnIfOutOfSync("KAboutData::applicationData().organizationDomain",
1080                         aboutData->organizationDomain(),
1081                         "QCoreApplication::organizationDomain",
1082                         QCoreApplication::organizationDomain());
1083         if (app) {
1084             warnIfOutOfSync("KAboutData::applicationData().displayName",
1085                             aboutData->displayName(),
1086                             "QGuiApplication::applicationDisplayName",
1087                             app->property("applicationDisplayName").toString());
1088             warnIfOutOfSync("KAboutData::applicationData().desktopFileName",
1089                             aboutData->desktopFileName(),
1090                             "QGuiApplication::desktopFileName",
1091                             app->property("desktopFileName").toString());
1092         }
1093     }
1094 
1095     return *aboutData;
1096 }
1097 
1098 void KAboutData::setApplicationData(const KAboutData &aboutData)
1099 {
1100     if (s_registry->m_appData) {
1101         *s_registry->m_appData = aboutData;
1102     } else {
1103         s_registry->m_appData = new KAboutData(aboutData);
1104     }
1105 
1106     // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1107     // we have to try to set them via the property system, as the static getter methods are
1108     // part of QtGui only. Disadvantage: requires an app instance.
1109     // So set either all or none of the properties & warn about it
1110     QCoreApplication *app = QCoreApplication::instance();
1111     if (app) {
1112         app->setApplicationVersion(aboutData.version());
1113         app->setApplicationName(aboutData.componentName());
1114         app->setOrganizationDomain(aboutData.organizationDomain());
1115         app->setProperty("applicationDisplayName", aboutData.displayName());
1116         app->setProperty("desktopFileName", aboutData.desktopFileName());
1117     } else {
1118         qCWarning(KABOUTDATA) << "Could not initialize the equivalent properties of Q*Application: no instance (yet) existing.";
1119     }
1120 
1121     // KF6: Rethink the current relation between KAboutData::applicationData and the Q*Application metadata
1122     // Always overwriting the Q*Application metadata here, but not updating back the KAboutData
1123     // in applicationData() is unbalanced and can result in out-of-sync data if the Q*Application
1124     // setters have been called meanwhile
1125     // Options are to remove the overlapping properties of KAboutData for cleancode, or making the
1126     // overlapping properties official shadow properties of their Q*Application countparts, though
1127     // that increases behavioural complexity a little.
1128 }
1129 
1130 // only for KCrash (no memory allocation allowed)
1131 const KAboutData *KAboutData::applicationDataPointer()
1132 {
1133     if (s_registry.exists()) {
1134         return s_registry->m_appData;
1135     }
1136     return nullptr;
1137 }
1138 
1139 bool KAboutData::setupCommandLine(QCommandLineParser *parser)
1140 {
1141     if (!d->_shortDescription.isEmpty()) {
1142         parser->setApplicationDescription(d->_shortDescription);
1143     }
1144 
1145     parser->addHelpOption();
1146 
1147     QCoreApplication *app = QCoreApplication::instance();
1148     if (app && !app->applicationVersion().isEmpty()) {
1149         parser->addVersionOption();
1150     }
1151 
1152     return parser->addOption(QCommandLineOption(QStringLiteral("author"), QCoreApplication::translate("KAboutData CLI", "Show author information.")))
1153         && parser->addOption(QCommandLineOption(QStringLiteral("license"), QCoreApplication::translate("KAboutData CLI", "Show license information.")))
1154         && parser->addOption(QCommandLineOption(QStringLiteral("desktopfile"),
1155                                                 QCoreApplication::translate("KAboutData CLI", "The base file name of the desktop entry for this application."),
1156                                                 QCoreApplication::translate("KAboutData CLI", "file name")));
1157 }
1158 
1159 void KAboutData::processCommandLine(QCommandLineParser *parser)
1160 {
1161     bool foundArgument = false;
1162     if (parser->isSet(QStringLiteral("author"))) {
1163         foundArgument = true;
1164         if (d->_authorList.isEmpty()) {
1165             printf("%s\n",
1166                    qPrintable(QCoreApplication::translate("KAboutData CLI", "This application was written by somebody who wants to remain anonymous.")));
1167         } else {
1168             printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "%1 was written by:").arg(qAppName())));
1169             for (const KAboutPerson &person : std::as_const(d->_authorList)) {
1170                 QString authorData = QLatin1String("    ") + person.name();
1171                 if (!person.emailAddress().isEmpty()) {
1172                     authorData.append(QLatin1String(" <") + person.emailAddress() + QLatin1Char('>'));
1173                 }
1174                 printf("%s\n", qPrintable(authorData));
1175             }
1176         }
1177         if (!customAuthorTextEnabled()) {
1178             if (bugAddress() == QLatin1String("submit@bugs.kde.org")) {
1179                 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please use https://bugs.kde.org to report bugs.")));
1180             } else if (!bugAddress().isEmpty()) {
1181                 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please report bugs to %1.").arg(bugAddress())));
1182             }
1183         } else {
1184             printf("%s\n", qPrintable(customAuthorPlainText()));
1185         }
1186     } else if (parser->isSet(QStringLiteral("license"))) {
1187         foundArgument = true;
1188         for (const KAboutLicense &license : std::as_const(d->_licenseList)) {
1189             printf("%s\n", qPrintable(license.text()));
1190         }
1191     }
1192 
1193     const QString desktopFileName = parser->value(QStringLiteral("desktopfile"));
1194     if (!desktopFileName.isEmpty()) {
1195         d->desktopFileName = desktopFileName;
1196     }
1197 
1198     if (foundArgument) {
1199         ::exit(EXIT_SUCCESS);
1200     }
1201 }
1202 
1203 #include "moc_kaboutdata.cpp"