1 /*
2 SPDX-FileCopyrightText: 2003 Joseph Wenninger <jowenn@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include <KLocalizedString>
8 #include <kio/slavebase.h>
9 #include <kservice.h>
10 #include <kservicegroup.h>
11 #include <sys/stat.h>
12 #include <time.h>
13
14 #include <QStandardPaths>
15 #include <QUrl>
16
17 // Pseudo plugin class to embed meta data
18 class KIOPluginForMetaData : public QObject
19 {
20 Q_OBJECT
21 Q_PLUGIN_METADATA(IID "org.kde.kio.slave.applications" FILE "applications.json")
22 };
23
24 class ApplicationsProtocol : public KIO::SlaveBase
25 {
26 public:
27 enum RunMode {
28 ProgramsMode,
29 ApplicationsMode,
30 };
31 ApplicationsProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app);
32 ~ApplicationsProtocol() override;
33 void get(const QUrl &url) override;
34 void stat(const QUrl &url) override;
35 void listDir(const QUrl &url) override;
36
37 private:
38 RunMode m_runMode;
39 };
40
41 extern "C" {
kdemain(int argc,char ** argv)42 Q_DECL_EXPORT int kdemain(int argc, char **argv)
43 {
44 QCoreApplication app(argc, argv);
45 app.setApplicationName("kio_applications");
46
47 ApplicationsProtocol slave(argv[1], argv[2], argv[3]);
48 slave.dispatchLoop();
49 return 0;
50 }
51 }
52
createFileEntry(KIO::UDSEntry & entry,const KService::Ptr & service,const QUrl & parentUrl)53 static void createFileEntry(KIO::UDSEntry &entry, const KService::Ptr &service, const QUrl &parentUrl)
54 {
55 entry.clear();
56 entry.fastInsert(KIO::UDSEntry::UDS_NAME, KIO::encodeFileName(service->name()));
57 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
58 const QString fileUrl = parentUrl.url() + '/' + service->desktopEntryName();
59 entry.fastInsert(KIO::UDSEntry::UDS_URL, fileUrl);
60 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500);
61 entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/x-desktop"));
62 entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
63 const QString localPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("%1.desktop").arg(service->desktopEntryName()));
64 entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_PATH, localPath);
65 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time(nullptr));
66 entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, service->icon());
67 }
68
createDirEntry(KIO::UDSEntry & entry,const QString & name,const QString & url,const QString & mime,const QString & iconName)69 static void createDirEntry(KIO::UDSEntry &entry, const QString &name, const QString &url, const QString &mime, const QString &iconName)
70 {
71 entry.clear();
72 entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
73 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
74 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500);
75 entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, mime);
76 if (!url.isEmpty())
77 entry.fastInsert(KIO::UDSEntry::UDS_URL, url);
78 entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, iconName);
79 }
80
ApplicationsProtocol(const QByteArray & protocol,const QByteArray & pool,const QByteArray & app)81 ApplicationsProtocol::ApplicationsProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app)
82 : SlaveBase(protocol, pool, app)
83 {
84 // Adjusts which part of the K Menu to virtualize.
85 if (protocol == "programs")
86 m_runMode = ProgramsMode;
87 else // if (protocol == "applications")
88 m_runMode = ApplicationsMode;
89 }
90
~ApplicationsProtocol()91 ApplicationsProtocol::~ApplicationsProtocol()
92 {
93 }
94
get(const QUrl & url)95 void ApplicationsProtocol::get(const QUrl &url)
96 {
97 KService::Ptr service = KService::serviceByDesktopName(url.fileName());
98 if (service && service->isValid()) {
99 const QString localPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("%1.desktop").arg(service->desktopEntryName()));
100 QUrl redirUrl(QUrl::fromLocalFile(localPath));
101 redirection(redirUrl);
102 finished();
103 } else {
104 error(KIO::ERR_IS_DIRECTORY, url.toDisplayString());
105 }
106 }
107
stat(const QUrl & url)108 void ApplicationsProtocol::stat(const QUrl &url)
109 {
110 KIO::UDSEntry entry;
111
112 QString servicePath(url.path());
113 if (!servicePath.endsWith('/'))
114 servicePath.append('/');
115 servicePath.remove(0, 1); // remove starting '/'
116
117 KServiceGroup::Ptr grp = KServiceGroup::group(servicePath);
118
119 if (grp && grp->isValid()) {
120 createDirEntry(entry,
121 ((m_runMode == ApplicationsMode) ? i18n("Applications") : i18n("Programs")),
122 url.url(),
123 QStringLiteral("inode/directory"),
124 grp->icon());
125 } else {
126 KService::Ptr service = KService::serviceByDesktopName(url.fileName());
127 if (service && service->isValid()) {
128 createFileEntry(entry, service, url);
129 } else {
130 error(KIO::ERR_SLAVE_DEFINED, i18n("Unknown application folder"));
131 return;
132 }
133 }
134
135 statEntry(entry);
136 finished();
137 }
138
listDir(const QUrl & url)139 void ApplicationsProtocol::listDir(const QUrl &url)
140 {
141 QString groupPath = url.path();
142 if (!groupPath.endsWith('/'))
143 groupPath.append('/');
144 groupPath.remove(0, 1); // remove starting '/'
145
146 KServiceGroup::Ptr grp = KServiceGroup::group(groupPath);
147
148 if (!grp || !grp->isValid()) {
149 error(KIO::ERR_DOES_NOT_EXIST, groupPath);
150 return;
151 }
152
153 unsigned int count = 0;
154 KIO::UDSEntry entry;
155
156 foreach (const KSycocaEntry::Ptr &e, grp->entries(true, true)) {
157 if (e->isType(KST_KServiceGroup)) {
158 KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e.data()));
159
160 // qDebug() << "ADDING SERVICE GROUP WITH PATH " << g->relPath();
161
162 // Avoid adding empty groups.
163 KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(g->relPath());
164 if (subMenuRoot->childCount() == 0)
165 continue;
166
167 // Ignore dotfiles.
168 if (g->name().startsWith('.'))
169 continue;
170
171 QString relPath = g->relPath();
172 QUrl dirUrl = url; // preserve protocol, whether that's programs:/ or applications:/
173 dirUrl.setPath('/' + relPath);
174 dirUrl = dirUrl.adjusted(QUrl::StripTrailingSlash);
175 // qDebug() << "ApplicationsProtocol: adding entry" << dirUrl;
176 createDirEntry(entry, g->caption(), dirUrl.url(), QStringLiteral("inode/directory"), g->icon());
177 } else {
178 KService::Ptr service(static_cast<KService *>(e.data()));
179
180 // qDebug() << "the entry name is" << service->desktopEntryName()
181 // << "with path" << service->entryPath();
182
183 if (!service->isApplication()) // how could this happen?
184 continue;
185 createFileEntry(entry, service, url);
186 }
187
188 listEntry(entry);
189 count++;
190 }
191
192 totalSize(count);
193 finished();
194 }
195
196 #include "kio_applications.moc"
197