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