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
17 extern int servicesDebugArea();
18
KServiceFactory(KSycoca * db)19 KServiceFactory::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 Q_ASSERT(str);
32 if (!str) {
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(saveOffset);
54 }
55 }
56
~KServiceFactory()57 KServiceFactory::~KServiceFactory()
58 {
59 delete m_nameDict;
60 delete m_relNameDict;
61 delete m_menuIdDict;
62 }
63
findServiceByName(const QString & _name)64 KService::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(_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
findServiceByDesktopName(const QString & _name)89 KService::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(_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
findServiceByDesktopPath(const QString & _name)113 KService::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(_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
findServiceByMenuId(const QString & _menuId)142 KService::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(_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
findServiceByStorageId(const QString & _storageId)166 KService::Ptr KServiceFactory::findServiceByStorageId(const QString &_storageId)
167 {
168 KService::Ptr service = findServiceByMenuId(_storageId);
169 if (service) {
170 return service;
171 }
172
173 service = findServiceByDesktopPath(_storageId);
174 if (service) {
175 return service;
176 }
177
178 if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId)) {
179 return KService::Ptr(new KService(_storageId));
180 }
181
182 QString tmp = _storageId;
183 tmp = tmp.mid(tmp.lastIndexOf(QLatin1Char('/')) + 1); // Strip dir
184
185 if (tmp.endsWith(QLatin1String(".desktop"))) {
186 tmp.chop(8);
187 }
188
189 if (tmp.endsWith(QLatin1String(".kdelnk"))) {
190 tmp.chop(7);
191 }
192
193 service = findServiceByDesktopName(tmp);
194
195 return service;
196 }
197
createEntry(int offset) const198 KService *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
allServices()215 KService::List KServiceFactory::allServices()
216 {
217 KService::List result;
218 const KSycocaEntry::List list = allEntries();
219 KSycocaEntry::List::const_iterator it = list.begin();
220 const KSycocaEntry::List::const_iterator end = list.end();
221 for (; it != end; ++it) {
222 const KSycocaEntry::Ptr entry = *it;
223 if (entry->isType(KST_KService)) {
224 KService::Ptr service(static_cast<KService *>(entry.data()));
225 result.append(service);
226 }
227 }
228 return result;
229 }
230
resourceDirs()231 QStringList KServiceFactory::resourceDirs()
232 {
233 return KSycocaFactory::allDirectories(QStringLiteral("kservices5")) + KSycocaFactory::allDirectories(QStringLiteral("applications"));
234 }
235
offers(int serviceTypeOffset,int serviceOffersOffset)236 QList<KServiceOffer> KServiceFactory::offers(int serviceTypeOffset, int serviceOffersOffset)
237 {
238 QList<KServiceOffer> list;
239
240 // Jump to the offer list
241 QDataStream *str = stream();
242 str->device()->seek(m_offerListOffset + serviceOffersOffset);
243
244 qint32 aServiceTypeOffset;
245 qint32 aServiceOffset;
246 qint32 initialPreference;
247 qint32 mimeTypeInheritanceLevel;
248 while (true) {
249 (*str) >> aServiceTypeOffset;
250 if (aServiceTypeOffset) {
251 (*str) >> aServiceOffset;
252 (*str) >> initialPreference;
253 (*str) >> mimeTypeInheritanceLevel;
254 if (aServiceTypeOffset == serviceTypeOffset) {
255 // Save stream position !
256 const qint64 savedPos = str->device()->pos();
257 // Create Service
258 KService *serv = createEntry(aServiceOffset);
259 if (serv) {
260 KService::Ptr servPtr(serv);
261 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 69)
262 list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel, servPtr->allowAsDefault()));
263 #else
264 list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel));
265 #endif
266 }
267 // Restore position
268 str->device()->seek(savedPos);
269 } else {
270 break; // too far
271 }
272 } else {
273 break; // 0 => end of list
274 }
275 }
276 return list;
277 }
278
serviceOffers(const KServiceType::Ptr & serviceType)279 KService::List KServiceFactory::serviceOffers(const KServiceType::Ptr &serviceType)
280 {
281 return serviceOffers(serviceType->offset(), serviceType->serviceOffersOffset());
282 }
283
serviceOffers(int serviceTypeOffset,int serviceOffersOffset)284 KService::List KServiceFactory::serviceOffers(int serviceTypeOffset, int serviceOffersOffset)
285 {
286 KService::List list;
287
288 // Jump to the offer list
289 QDataStream *str = stream();
290 str->device()->seek(m_offerListOffset + serviceOffersOffset);
291
292 qint32 aServiceTypeOffset;
293 qint32 aServiceOffset;
294 qint32 initialPreference;
295 qint32 mimeTypeInheritanceLevel;
296 while (true) {
297 (*str) >> aServiceTypeOffset;
298 if (aServiceTypeOffset) {
299 (*str) >> aServiceOffset;
300 (*str) >> initialPreference; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
301 (*str) >> mimeTypeInheritanceLevel; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
302 if (aServiceTypeOffset == serviceTypeOffset) {
303 // Save stream position !
304 const qint64 savedPos = str->device()->pos();
305 // Create service
306 KService *serv = createEntry(aServiceOffset);
307 if (serv) {
308 list.append(KService::Ptr(serv));
309 }
310 // Restore position
311 str->device()->seek(savedPos);
312 } else {
313 break; // too far
314 }
315 } else {
316 break; // 0 => end of list
317 }
318 }
319 return list;
320 }
321
hasOffer(const KServiceType::Ptr & serviceType,const KService::Ptr & testedService)322 bool KServiceFactory::hasOffer(const KServiceType::Ptr &serviceType, const KService::Ptr &testedService)
323 {
324 return hasOffer(serviceType->offset(), serviceType->serviceOffersOffset(), testedService->offset());
325 }
326
hasOffer(int serviceTypeOffset,int serviceOffersOffset,int testedServiceOffset)327 bool KServiceFactory::hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset)
328 {
329 // Save stream position
330 QDataStream *str = stream();
331 const qint64 savedPos = str->device()->pos();
332
333 // Jump to the offer list
334 str->device()->seek(m_offerListOffset + serviceOffersOffset);
335 bool found = false;
336 qint32 aServiceTypeOffset;
337 qint32 aServiceOffset;
338 qint32 initialPreference;
339 qint32 mimeTypeInheritanceLevel;
340 while (!found) {
341 (*str) >> aServiceTypeOffset;
342 if (aServiceTypeOffset) {
343 (*str) >> aServiceOffset;
344 (*str) >> initialPreference;
345 (*str) >> mimeTypeInheritanceLevel;
346 if (aServiceTypeOffset == serviceTypeOffset) {
347 if (aServiceOffset == testedServiceOffset) {
348 found = true;
349 }
350 } else {
351 break; // too far
352 }
353 } else {
354 break; // 0 => end of list
355 }
356 }
357 // Restore position
358 str->device()->seek(savedPos);
359 return found;
360 }
361
virtual_hook(int id,void * data)362 void KServiceFactory::virtual_hook(int id, void *data)
363 {
364 KSycocaFactory::virtual_hook(id, data);
365 }
366