File indexing completed on 2024-11-17 04:55:41

0001 /*
0002  *   SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "AppPackageKitResource.h"
0008 #include "utils.h"
0009 #include <AppStreamQt/developer.h>
0010 #include <AppStreamQt/icon.h>
0011 #include <AppStreamQt/image.h>
0012 #include <AppStreamQt/release.h>
0013 #include <AppStreamQt/screenshot.h>
0014 #include <AppStreamQt/version.h>
0015 #include <KLocalizedString>
0016 #include <KService>
0017 #include <PackageKit/Daemon>
0018 #include <QDebug>
0019 #include <QFile>
0020 #include <QIcon>
0021 #include <QProcess>
0022 #include <QStandardPaths>
0023 #include <QUrlQuery>
0024 #include <appstream/AppStreamUtils.h>
0025 
0026 using namespace Qt::StringLiterals;
0027 
0028 AppPackageKitResource::AppPackageKitResource(const AppStream::Component &data, const QString &packageName, PackageKitBackend *parent)
0029     : PackageKitResource(packageName, QString(), parent)
0030     , m_appdata(data)
0031 {
0032     Q_ASSERT(data.isValid());
0033 }
0034 
0035 QString AppPackageKitResource::name() const
0036 {
0037     if (m_name.isEmpty()) {
0038         if (!m_appdata.extends().isEmpty()) {
0039             const auto components = backend()->componentsById(m_appdata.extends().constFirst());
0040 
0041             if (components.isEmpty()) {
0042                 qWarning() << "couldn't find" << m_appdata.extends() << "which is supposedly extended by" << m_appdata.id();
0043             } else {
0044                 m_name = components.indexSafe(0)->name() + QLatin1String(" - ") + m_appdata.name();
0045             }
0046         }
0047 
0048         if (m_name.isEmpty()) {
0049             m_name = m_appdata.name();
0050         }
0051     }
0052     return m_name;
0053 }
0054 
0055 QString AppPackageKitResource::longDescription()
0056 {
0057     const auto desc = m_appdata.description();
0058     if (!desc.isEmpty()) {
0059         return desc;
0060     }
0061 
0062     return PackageKitResource::longDescription();
0063 }
0064 
0065 static QIcon componentIcon(const AppStream::Component &comp)
0066 {
0067     QIcon ret;
0068     const auto icons = comp.icons();
0069     for (const AppStream::Icon &icon : icons) {
0070         QStringList stock;
0071         switch (icon.kind()) {
0072         case AppStream::Icon::KindLocal:
0073             ret.addFile(icon.url().toLocalFile(), icon.size());
0074             break;
0075         case AppStream::Icon::KindCached:
0076             ret.addFile(icon.url().toLocalFile(), icon.size());
0077             break;
0078         case AppStream::Icon::KindStock: {
0079             const auto ret = QIcon::fromTheme(icon.name());
0080             if (!ret.isNull()) {
0081                 return ret;
0082             }
0083             break;
0084         }
0085         default:
0086             break;
0087         }
0088     }
0089     if (ret.isNull()) {
0090         ret = QIcon::fromTheme(QStringLiteral("package-x-generic"));
0091     }
0092     return ret;
0093 }
0094 
0095 QVariant AppPackageKitResource::icon() const
0096 {
0097     return componentIcon(m_appdata);
0098 }
0099 
0100 QJsonArray AppPackageKitResource::licenses()
0101 {
0102     return m_appdata.projectLicense().isEmpty() ? PackageKitResource::licenses() : AppStreamUtils::licenses(m_appdata);
0103 }
0104 
0105 QStringList AppPackageKitResource::mimetypes() const
0106 {
0107     return m_appdata.provided(AppStream::Provided::KindMimetype).items();
0108 }
0109 
0110 static constexpr auto s_addonKinds = {AppStream::Component::KindAddon, AppStream::Component::KindCodec};
0111 
0112 QStringList AppPackageKitResource::categories()
0113 {
0114     auto cats = m_appdata.categories();
0115     if (!kContainsValue(s_addonKinds, m_appdata.kind())) {
0116         cats.append(QStringLiteral("Application"));
0117     }
0118     return cats;
0119 }
0120 
0121 QString AppPackageKitResource::comment()
0122 {
0123     const auto summary = m_appdata.summary();
0124     if (!summary.isEmpty()) {
0125         return summary;
0126     }
0127 
0128     return PackageKitResource::comment();
0129 }
0130 
0131 QString AppPackageKitResource::appstreamId() const
0132 {
0133     return m_appdata.id();
0134 }
0135 
0136 QSet<QString> AppPackageKitResource::alternativeAppstreamIds() const
0137 {
0138     const AppStream::Provided::Kind AppStream_Provided_KindId = (AppStream::Provided::Kind)12; // Should be AppStream::Provided::KindId when released
0139     const auto ret = m_appdata.provided(AppStream_Provided_KindId).items();
0140     return QSet<QString>(ret.begin(), ret.end());
0141 }
0142 
0143 QUrl AppPackageKitResource::url() const
0144 {
0145     QUrl ret(QStringLiteral("appstream://") + appstreamId());
0146     const AppStream::Provided::Kind AppStream_Provided_KindId = (AppStream::Provided::Kind)12; // Should be AppStream::Provided::KindId when released
0147     auto provided = m_appdata.provided(AppStream_Provided_KindId).items();
0148     provided.removeAll(appstreamId()); // Just in case, it has happened before
0149     if (!provided.isEmpty()) {
0150         QUrlQuery qq;
0151         qq.addQueryItem(u"alt"_s, provided.join(QLatin1Char(',')));
0152         ret.setQuery(qq);
0153     }
0154     return ret;
0155 }
0156 
0157 QUrl AppPackageKitResource::homepage()
0158 {
0159     return m_appdata.url(AppStream::Component::UrlKindHomepage);
0160 }
0161 
0162 QUrl AppPackageKitResource::helpURL()
0163 {
0164     return m_appdata.url(AppStream::Component::UrlKindHelp);
0165 }
0166 
0167 QUrl AppPackageKitResource::bugURL()
0168 {
0169     return m_appdata.url(AppStream::Component::UrlKindBugtracker);
0170 }
0171 
0172 QUrl AppPackageKitResource::donationURL()
0173 {
0174     return m_appdata.url(AppStream::Component::UrlKindDonation);
0175 }
0176 
0177 QUrl AppPackageKitResource::contributeURL()
0178 {
0179     return m_appdata.url(AppStream::Component::UrlKindContribute);
0180 }
0181 
0182 AbstractResource::Type AppPackageKitResource::type() const
0183 {
0184     static QString desktop = QString::fromUtf8(qgetenv("XDG_CURRENT_DESKTOP"));
0185     const auto desktops = m_appdata.compulsoryForDesktops();
0186     return kContainsValue(s_addonKinds, m_appdata.kind()) ? Addon : (desktops.isEmpty() || !desktops.contains(desktop)) ? Application : Technical;
0187 }
0188 
0189 void AppPackageKitResource::fetchScreenshots()
0190 {
0191     Q_EMIT screenshotsFetched(AppStreamUtils::fetchScreenshots(m_appdata));
0192 }
0193 
0194 QStringList AppPackageKitResource::allPackageNames() const
0195 {
0196     auto ret = m_appdata.packageNames();
0197     if (ret.isEmpty()) {
0198         ret = QStringList{PackageKit::Daemon::packageName(availablePackageId())};
0199     }
0200     return ret;
0201 }
0202 
0203 QList<PackageState> AppPackageKitResource::addonsInformation()
0204 {
0205     const auto extendedBy = backend()->extendedBy(m_appdata.id());
0206     const auto allPackageNamesCached = allPackageNames();
0207     const auto resources = kFilter<QVector<AbstractResource *>>(extendedBy, [&](AbstractResource *r) {
0208         auto apkr = qobject_cast<AppPackageKitResource *>(r);
0209         return apkr && apkr->allPackageNames() != allPackageNamesCached;
0210     });
0211     return kTransform<QList<PackageState>>(resources, [](AbstractResource *r) {
0212         return PackageState(r->packageName(), r->name(), r->comment(), r->isInstalled());
0213     });
0214 }
0215 
0216 QStringList AppPackageKitResource::extends() const
0217 {
0218     return m_appdata.extends();
0219 }
0220 
0221 QString AppPackageKitResource::changelog() const
0222 {
0223     return PackageKitResource::changelog() + QLatin1String("<br />") + AppStreamUtils::changelogToHtml(m_appdata);
0224 }
0225 
0226 bool AppPackageKitResource::canExecute() const
0227 {
0228     return !m_appdata.launchable(AppStream::Launchable::KindDesktopId).entries().isEmpty();
0229 }
0230 
0231 void AppPackageKitResource::invokeApplication() const
0232 {
0233     const QString launchable = m_appdata.launchable(AppStream::Launchable::KindDesktopId).entries().constFirst();
0234 
0235     KService::Ptr service = KService::serviceByStorageId(launchable);
0236 
0237     if (!service) {
0238         Q_EMIT backend()->passiveMessage(i18n("Cannot launch %1", name()));
0239         return;
0240     }
0241     runService(service);
0242 }
0243 
0244 QString AppPackageKitResource::versionString()
0245 {
0246     const QString version = isInstalled() ? installedVersion() : availableVersion();
0247     return AppStreamUtils::versionString(version, m_appdata);
0248 }
0249 
0250 QDate AppPackageKitResource::releaseDate() const
0251 {
0252 #if ASQ_CHECK_VERSION(1, 0, 0)
0253     if (!m_appdata.releasesPlain().isEmpty()) {
0254         const auto release = m_appdata.releasesPlain().indexSafe(0).value();
0255 #else
0256     if (!m_appdata.releases().isEmpty()) {
0257         const auto release = m_appdata.releases().constFirst();
0258 #endif
0259         return release.timestamp().date();
0260     }
0261 
0262     return {};
0263 }
0264 
0265 QString AppPackageKitResource::author() const
0266 {
0267     QString name = m_appdata.developer().name();
0268 
0269     if (name.isEmpty()) {
0270         name = m_appdata.projectGroup();
0271     }
0272 
0273     return name;
0274 }
0275 
0276 void AppPackageKitResource::fetchChangelog()
0277 {
0278     Q_EMIT changelogFetched(changelog());
0279 }
0280 
0281 bool AppPackageKitResource::isCritical() const
0282 {
0283     return m_appdata.isCompulsoryForDesktop(qEnvironmentVariable("XDG_CURRENT_DESKTOP"));
0284 }
0285 
0286 QString AppPackageKitResource::contentRatingDescription() const
0287 {
0288     return AppStreamUtils::contentRatingDescription(m_appdata);
0289 }
0290 
0291 QString AppPackageKitResource::contentRatingText() const
0292 {
0293     return AppStreamUtils::contentRatingText(m_appdata);
0294 }
0295 
0296 AbstractResource::ContentIntensity AppPackageKitResource::contentRatingIntensity() const
0297 {
0298     return AppStreamUtils::contentRatingIntensity(m_appdata);
0299 }
0300 
0301 uint AppPackageKitResource::contentRatingMinimumAge() const
0302 {
0303     return AppStreamUtils::contentRatingMinimumAge(m_appdata);
0304 }