1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999-2006 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "kservice.h"
9#include "kservicefactory_p.h"
10#include "ksycoca.h"
11#include "ksycocadict_p.h"
12#include "ksycocatype.h"
13#include "servicesdebug.h"
14#include <QDir>
15#include <QFile>
16
17extern int servicesDebugArea();
18
19KServiceFactory::KServiceFactory(KSycoca *db)
20 : KSycocaFactory(KST_KServiceFactory, db)
21 , m_nameDict(nullptr)
22 , m_relNameDict(nullptr)
23 , m_menuIdDict(nullptr)
24{
25 m_offerListOffset = 0;
26 m_nameDictOffset = 0;
27 m_relNameDictOffset = 0;
28 m_menuIdDictOffset = 0;
29 if (!sycoca()->isBuilding()) {
30 QDataStream *str = stream();
31 if (!str) {
32 qWarning() << "Could not open sycoca database, you must run kbuildsycoca first!";
33 return;
34 }
35 // Read Header
36 qint32 i;
37 (*str) >> i;
38 m_nameDictOffset = i;
39 (*str) >> i;
40 m_relNameDictOffset = i;
41 (*str) >> i;
42 m_offerListOffset = i;
43 (*str) >> i;
44 m_menuIdDictOffset = i;
45
46 const qint64 saveOffset = str->device()->pos();
47 // Init index tables
48 m_nameDict = new KSycocaDict(str, m_nameDictOffset);
49 // Init index tables
50 m_relNameDict = new KSycocaDict(str, m_relNameDictOffset);
51 // Init index tables
52 m_menuIdDict = new KSycocaDict(str, m_menuIdDictOffset);
53 str->device()->seek(pos: saveOffset);
54 }
55}
56
57KServiceFactory::~KServiceFactory()
58{
59 delete m_nameDict;
60 delete m_relNameDict;
61 delete m_menuIdDict;
62}
63
64KService::Ptr KServiceFactory::findServiceByName(const QString &_name)
65{
66 if (!sycocaDict()) {
67 return KService::Ptr(); // Error!
68 }
69
70 // Warning : this assumes we're NOT building a database
71 // But since findServiceByName isn't called in that case...
72 // [ see KServiceTypeFactory for how to do it if needed ]
73
74 int offset = sycocaDict()->find_string(key: _name);
75 if (!offset) {
76 return KService::Ptr(); // Not found
77 }
78
79 KService::Ptr newService(createEntry(offset));
80
81 // Check whether the dictionary was right.
82 if (newService && (newService->name() != _name)) {
83 // No it wasn't...
84 return KService::Ptr();
85 }
86 return newService;
87}
88
89KService::Ptr KServiceFactory::findServiceByDesktopName(const QString &_name)
90{
91 if (!m_nameDict) {
92 return KService::Ptr(); // Error!
93 }
94
95 // Warning : this assumes we're NOT building a database
96 // KBuildServiceFactory reimplements it for the case where we are building one
97
98 int offset = m_nameDict->find_string(key: _name);
99 if (!offset) {
100 return KService::Ptr(); // Not found
101 }
102
103 KService::Ptr newService(createEntry(offset));
104
105 // Check whether the dictionary was right.
106 if (newService && (newService->desktopEntryName() != _name)) {
107 // No it wasn't...
108 return KService::Ptr();
109 }
110 return newService;
111}
112
113KService::Ptr KServiceFactory::findServiceByDesktopPath(const QString &_name)
114{
115 if (!m_relNameDict) {
116 return KService::Ptr(); // Error!
117 }
118
119 // Warning : this assumes we're NOT building a database
120 // KBuildServiceFactory reimplements it for the case where we are building one
121
122 int offset = m_relNameDict->find_string(key: _name);
123 if (!offset) {
124 // qCDebug(SERVICES) << "findServiceByDesktopPath:" << _name << "not found";
125 return KService::Ptr(); // Not found
126 }
127
128 KService::Ptr newService(createEntry(offset));
129 if (!newService) {
130 qCDebug(SERVICES) << "createEntry failed!";
131 }
132 // Check whether the dictionary was right
133 // It's ok that it's wrong, for the case where we're looking up an unknown service,
134 // and the hash value gave us another one.
135 if (newService && (newService->entryPath() != _name)) {
136 // No it wasn't...
137 return KService::Ptr();
138 }
139 return newService;
140}
141
142KService::Ptr KServiceFactory::findServiceByMenuId(const QString &_menuId)
143{
144 if (!m_menuIdDict) {
145 return KService::Ptr(); // Error!
146 }
147
148 // Warning : this assumes we're NOT building a database
149 // KBuildServiceFactory reimplements it for the case where we are building one
150
151 int offset = m_menuIdDict->find_string(key: _menuId);
152 if (!offset) {
153 return KService::Ptr(); // Not found
154 }
155
156 KService::Ptr newService(createEntry(offset));
157
158 // Check whether the dictionary was right.
159 if (newService && (newService->menuId() != _menuId)) {
160 // No it wasn't...
161 return KService::Ptr();
162 }
163 return newService;
164}
165
166KService::Ptr KServiceFactory::findServiceByStorageId(const QString &_storageId)
167{
168 KService::Ptr service = findServiceByMenuId(menuId: _storageId);
169 if (service) {
170 return service;
171 }
172
173 service = findServiceByDesktopPath(name: _storageId);
174 if (service) {
175 return service;
176 }
177
178 if (!QDir::isRelativePath(path: _storageId) && QFile::exists(fileName: _storageId)) {
179 return KService::Ptr(new KService(_storageId));
180 }
181
182 QString tmp = _storageId;
183 tmp = tmp.mid(position: tmp.lastIndexOf(c: QLatin1Char('/')) + 1); // Strip dir
184
185 if (tmp.endsWith(s: QLatin1String(".desktop"))) {
186 tmp.chop(n: 8);
187 }
188
189 if (tmp.endsWith(s: QLatin1String(".kdelnk"))) {
190 tmp.chop(n: 7);
191 }
192
193 service = findServiceByDesktopName(name: tmp);
194
195 return service;
196}
197
198KService *KServiceFactory::createEntry(int offset) const
199{
200 KSycocaType type;
201 QDataStream *str = sycoca()->findEntry(offset, type);
202 if (type != KST_KService) {
203 qCWarning(SERVICES) << "KServiceFactory: unexpected object entry in KSycoca database (type=" << int(type) << ")";
204 return nullptr;
205 }
206 KService *newEntry = new KService(*str, offset);
207 if (!newEntry->isValid()) {
208 qCWarning(SERVICES) << "KServiceFactory: corrupt object in KSycoca database!";
209 delete newEntry;
210 newEntry = nullptr;
211 }
212 return newEntry;
213}
214
215KService::List KServiceFactory::allServices()
216{
217 KService::List result;
218 const KSycocaEntry::List list = allEntries();
219 for (const auto &entryPtr : list) {
220 if (entryPtr->isType(t: KST_KService)) {
221 result.append(t: KService::Ptr(static_cast<KService *>(entryPtr.data())));
222 }
223 }
224 return result;
225}
226
227QStringList KServiceFactory::resourceDirs()
228{
229 return KSycocaFactory::allDirectories(QStringLiteral("applications"));
230}
231
232QList<KServiceOffer> KServiceFactory::offers(int serviceTypeOffset, int serviceOffersOffset)
233{
234 QList<KServiceOffer> list;
235
236 // Jump to the offer list
237 QDataStream *str = stream();
238 str->device()->seek(pos: m_offerListOffset + serviceOffersOffset);
239
240 qint32 aServiceTypeOffset;
241 qint32 aServiceOffset;
242 qint32 offerPreference;
243 qint32 mimeTypeInheritanceLevel;
244 while (true) {
245 (*str) >> aServiceTypeOffset;
246 if (aServiceTypeOffset) {
247 (*str) >> aServiceOffset;
248 (*str) >> offerPreference;
249 (*str) >> mimeTypeInheritanceLevel;
250 if (aServiceTypeOffset == serviceTypeOffset) {
251 // Save stream position !
252 const qint64 savedPos = str->device()->pos();
253 // Create Service
254 KService *serv = createEntry(offset: aServiceOffset);
255 if (serv) {
256 KService::Ptr servPtr(serv);
257 list.append(t: KServiceOffer(servPtr, 1, mimeTypeInheritanceLevel));
258 }
259 // Restore position
260 str->device()->seek(pos: savedPos);
261 } else {
262 break; // too far
263 }
264 } else {
265 break; // 0 => end of list
266 }
267 }
268 return list;
269}
270
271KService::List KServiceFactory::serviceOffers(int serviceTypeOffset, int serviceOffersOffset)
272{
273 KService::List list;
274
275 // Jump to the offer list
276 QDataStream *str = stream();
277 str->device()->seek(pos: m_offerListOffset + serviceOffersOffset);
278
279 qint32 aServiceTypeOffset;
280 qint32 aServiceOffset;
281 qint32 offerPreference;
282 qint32 mimeTypeInheritanceLevel;
283 while (true) {
284 (*str) >> aServiceTypeOffset;
285 if (aServiceTypeOffset) {
286 (*str) >> aServiceOffset;
287 (*str) >> offerPreference; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
288 (*str) >> mimeTypeInheritanceLevel; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
289 if (aServiceTypeOffset == serviceTypeOffset) {
290 // Save stream position !
291 const qint64 savedPos = str->device()->pos();
292 // Create service
293 KService *serv = createEntry(offset: aServiceOffset);
294 if (serv) {
295 list.append(t: KService::Ptr(serv));
296 }
297 // Restore position
298 str->device()->seek(pos: savedPos);
299 } else {
300 break; // too far
301 }
302 } else {
303 break; // 0 => end of list
304 }
305 }
306 return list;
307}
308
309bool KServiceFactory::hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset)
310{
311 // Save stream position
312 QDataStream *str = stream();
313 const qint64 savedPos = str->device()->pos();
314
315 // Jump to the offer list
316 str->device()->seek(pos: m_offerListOffset + serviceOffersOffset);
317 bool found = false;
318 qint32 aServiceTypeOffset;
319 qint32 aServiceOffset;
320 qint32 offerPreference;
321 qint32 mimeTypeInheritanceLevel;
322 while (!found) {
323 (*str) >> aServiceTypeOffset;
324 if (aServiceTypeOffset) {
325 (*str) >> aServiceOffset;
326 (*str) >> offerPreference;
327 (*str) >> mimeTypeInheritanceLevel;
328 if (aServiceTypeOffset == serviceTypeOffset) {
329 if (aServiceOffset == testedServiceOffset) {
330 found = true;
331 }
332 } else {
333 break; // too far
334 }
335 } else {
336 break; // 0 => end of list
337 }
338 }
339 // Restore position
340 str->device()->seek(pos: savedPos);
341 return found;
342}
343
344void KServiceFactory::virtual_hook(int id, void *data)
345{
346 KSycocaFactory::virtual_hook(id, data);
347}
348

source code of kservice/src/services/kservicefactory.cpp