File indexing completed on 2024-05-19 15:09:12

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