File indexing completed on 2024-05-12 17:09:47

0001 /*
0002     SPDX-FileCopyrightText: 2003 Joseph Wenninger <jowenn@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <KIO/WorkerBase>
0008 #include <KLocalizedString>
0009 #include <KService>
0010 #include <KServiceGroup>
0011 #include <sys/stat.h>
0012 #include <time.h>
0013 
0014 #include <QStandardPaths>
0015 #include <QUrl>
0016 #include <kio_version.h>
0017 
0018 // Pseudo plugin class to embed meta data
0019 class KIOPluginForMetaData : public QObject
0020 {
0021     Q_OBJECT
0022     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.applications" FILE "applications.json")
0023 };
0024 
0025 class ApplicationsProtocol : public KIO::WorkerBase
0026 {
0027 public:
0028     enum RunMode {
0029         ProgramsMode,
0030         ApplicationsMode,
0031     };
0032     ApplicationsProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app);
0033     ~ApplicationsProtocol() override;
0034     KIO::WorkerResult get(const QUrl &url) override;
0035     KIO::WorkerResult stat(const QUrl &url) override;
0036     KIO::WorkerResult listDir(const QUrl &url) override;
0037 
0038 private:
0039     RunMode m_runMode;
0040 };
0041 
0042 extern "C" {
0043 Q_DECL_EXPORT int kdemain(int argc, char **argv)
0044 {
0045     QCoreApplication app(argc, argv);
0046     app.setApplicationName("kio_applications");
0047 
0048     ApplicationsProtocol worker(argv[1], argv[2], argv[3]);
0049     worker.dispatchLoop();
0050     return 0;
0051 }
0052 }
0053 
0054 static void createFileEntry(KIO::UDSEntry &entry, const KService::Ptr &service, const QUrl &parentUrl)
0055 {
0056     entry.clear();
0057     entry.fastInsert(KIO::UDSEntry::UDS_NAME, KIO::encodeFileName(service->name()));
0058     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
0059     const QString fileUrl = parentUrl.url() + '/' + service->desktopEntryName();
0060     entry.fastInsert(KIO::UDSEntry::UDS_URL, fileUrl);
0061     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500);
0062     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/x-desktop"));
0063     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
0064     const QString localPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("%1.desktop").arg(service->desktopEntryName()));
0065     entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_PATH, localPath);
0066     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time(nullptr));
0067     entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, service->icon());
0068 }
0069 
0070 static void createDirEntry(KIO::UDSEntry &entry, const QString &name, const QString &url, const QString &mime, const QString &iconName)
0071 {
0072     entry.clear();
0073     entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
0074     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0075     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500);
0076     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, mime);
0077     if (!url.isEmpty())
0078         entry.fastInsert(KIO::UDSEntry::UDS_URL, url);
0079     entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, iconName);
0080 }
0081 
0082 ApplicationsProtocol::ApplicationsProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app)
0083     : WorkerBase(protocol, pool, app)
0084 {
0085     // Adjusts which part of the K Menu to virtualize.
0086     if (protocol == "programs")
0087         m_runMode = ProgramsMode;
0088     else // if (protocol == "applications")
0089         m_runMode = ApplicationsMode;
0090 }
0091 
0092 ApplicationsProtocol::~ApplicationsProtocol()
0093 {
0094 }
0095 
0096 KIO::WorkerResult ApplicationsProtocol::get(const QUrl &url)
0097 {
0098     KService::Ptr service = KService::serviceByDesktopName(url.fileName());
0099     if (service && service->isValid()) {
0100         const QString localPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("%1.desktop").arg(service->desktopEntryName()));
0101         QUrl redirUrl(QUrl::fromLocalFile(localPath));
0102         redirection(redirUrl);
0103         return KIO::WorkerResult::pass();
0104     } else {
0105         return KIO::WorkerResult::fail(KIO::ERR_IS_DIRECTORY, url.toDisplayString());
0106     }
0107 }
0108 
0109 KIO::WorkerResult ApplicationsProtocol::stat(const QUrl &url)
0110 {
0111     KIO::UDSEntry entry;
0112 
0113     QString servicePath(url.path());
0114     if (!servicePath.endsWith('/'))
0115         servicePath.append('/');
0116     servicePath.remove(0, 1); // remove starting '/'
0117 
0118     KServiceGroup::Ptr grp = KServiceGroup::group(servicePath);
0119 
0120     if (grp && grp->isValid()) {
0121         createDirEntry(entry,
0122                        ((m_runMode == ApplicationsMode) ? i18n("Applications") : i18n("Programs")),
0123                        url.url(),
0124                        QStringLiteral("inode/directory"),
0125                        grp->icon());
0126     } else {
0127         KService::Ptr service = KService::serviceByDesktopName(url.fileName());
0128         if (service && service->isValid()) {
0129             createFileEntry(entry, service, url);
0130         } else {
0131             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Unknown application folder"));
0132         }
0133     }
0134 
0135     statEntry(entry);
0136     return KIO::WorkerResult::pass();
0137 }
0138 
0139 KIO::WorkerResult ApplicationsProtocol::listDir(const QUrl &url)
0140 {
0141     QString groupPath = url.path();
0142     if (!groupPath.endsWith('/'))
0143         groupPath.append('/');
0144     groupPath.remove(0, 1); // remove starting '/'
0145 
0146     KServiceGroup::Ptr grp = KServiceGroup::group(groupPath);
0147 
0148     if (!grp || !grp->isValid()) {
0149         return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, groupPath);
0150     }
0151 
0152     unsigned int count = 0;
0153     KIO::UDSEntry entry;
0154 
0155     foreach (const KSycocaEntry::Ptr &e, grp->entries(true, true)) {
0156         if (e->isType(KST_KServiceGroup)) {
0157             KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e.data()));
0158 
0159             // qDebug() << "ADDING SERVICE GROUP WITH PATH " << g->relPath();
0160 
0161             // Avoid adding empty groups.
0162             KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(g->relPath());
0163             if (subMenuRoot->childCount() == 0)
0164                 continue;
0165 
0166             // Ignore dotfiles.
0167             if (g->name().startsWith('.'))
0168                 continue;
0169 
0170             QString relPath = g->relPath();
0171             QUrl dirUrl = url; // preserve protocol, whether that's programs:/ or applications:/
0172             dirUrl.setPath('/' + relPath);
0173             dirUrl = dirUrl.adjusted(QUrl::StripTrailingSlash);
0174             // qDebug() << "ApplicationsProtocol: adding entry" << dirUrl;
0175             createDirEntry(entry, g->caption(), dirUrl.url(), QStringLiteral("inode/directory"), g->icon());
0176         } else {
0177             KService::Ptr service(static_cast<KService *>(e.data()));
0178 
0179             // qDebug() << "the entry name is" << service->desktopEntryName()
0180             //         << "with path" << service->entryPath();
0181 
0182             if (!service->isApplication()) // how could this happen?
0183                 continue;
0184             createFileEntry(entry, service, url);
0185         }
0186 
0187         listEntry(entry);
0188         count++;
0189     }
0190 
0191     totalSize(count);
0192     return KIO::WorkerResult::pass();
0193 }
0194 
0195 #include "kio_applications.moc"