File indexing completed on 2024-04-28 15:29:51

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999-2006 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "kservice.h"
0009 #include "kservicefactory_p.h"
0010 #include "ksycoca.h"
0011 #include "ksycocadict_p.h"
0012 #include "ksycocatype.h"
0013 #include "servicesdebug.h"
0014 #include <QDir>
0015 #include <QFile>
0016 
0017 extern int servicesDebugArea();
0018 
0019 KServiceFactory::KServiceFactory(KSycoca *db)
0020     : KSycocaFactory(KST_KServiceFactory, db)
0021     , m_nameDict(nullptr)
0022     , m_relNameDict(nullptr)
0023     , m_menuIdDict(nullptr)
0024 {
0025     m_offerListOffset = 0;
0026     m_nameDictOffset = 0;
0027     m_relNameDictOffset = 0;
0028     m_menuIdDictOffset = 0;
0029     if (!sycoca()->isBuilding()) {
0030         QDataStream *str = stream();
0031         Q_ASSERT(str);
0032         if (!str) {
0033             return;
0034         }
0035         // Read Header
0036         qint32 i;
0037         (*str) >> i;
0038         m_nameDictOffset = i;
0039         (*str) >> i;
0040         m_relNameDictOffset = i;
0041         (*str) >> i;
0042         m_offerListOffset = i;
0043         (*str) >> i;
0044         m_menuIdDictOffset = i;
0045 
0046         const qint64 saveOffset = str->device()->pos();
0047         // Init index tables
0048         m_nameDict = new KSycocaDict(str, m_nameDictOffset);
0049         // Init index tables
0050         m_relNameDict = new KSycocaDict(str, m_relNameDictOffset);
0051         // Init index tables
0052         m_menuIdDict = new KSycocaDict(str, m_menuIdDictOffset);
0053         str->device()->seek(saveOffset);
0054     }
0055 }
0056 
0057 KServiceFactory::~KServiceFactory()
0058 {
0059     delete m_nameDict;
0060     delete m_relNameDict;
0061     delete m_menuIdDict;
0062 }
0063 
0064 KService::Ptr KServiceFactory::findServiceByName(const QString &_name)
0065 {
0066     if (!sycocaDict()) {
0067         return KService::Ptr(); // Error!
0068     }
0069 
0070     // Warning : this assumes we're NOT building a database
0071     // But since findServiceByName isn't called in that case...
0072     // [ see KServiceTypeFactory for how to do it if needed ]
0073 
0074     int offset = sycocaDict()->find_string(_name);
0075     if (!offset) {
0076         return KService::Ptr(); // Not found
0077     }
0078 
0079     KService::Ptr newService(createEntry(offset));
0080 
0081     // Check whether the dictionary was right.
0082     if (newService && (newService->name() != _name)) {
0083         // No it wasn't...
0084         return KService::Ptr();
0085     }
0086     return newService;
0087 }
0088 
0089 KService::Ptr KServiceFactory::findServiceByDesktopName(const QString &_name)
0090 {
0091     if (!m_nameDict) {
0092         return KService::Ptr(); // Error!
0093     }
0094 
0095     // Warning : this assumes we're NOT building a database
0096     // KBuildServiceFactory reimplements it for the case where we are building one
0097 
0098     int offset = m_nameDict->find_string(_name);
0099     if (!offset) {
0100         return KService::Ptr(); // Not found
0101     }
0102 
0103     KService::Ptr newService(createEntry(offset));
0104 
0105     // Check whether the dictionary was right.
0106     if (newService && (newService->desktopEntryName() != _name)) {
0107         // No it wasn't...
0108         return KService::Ptr();
0109     }
0110     return newService;
0111 }
0112 
0113 KService::Ptr KServiceFactory::findServiceByDesktopPath(const QString &_name)
0114 {
0115     if (!m_relNameDict) {
0116         return KService::Ptr(); // Error!
0117     }
0118 
0119     // Warning : this assumes we're NOT building a database
0120     // KBuildServiceFactory reimplements it for the case where we are building one
0121 
0122     int offset = m_relNameDict->find_string(_name);
0123     if (!offset) {
0124         // qCDebug(SERVICES) << "findServiceByDesktopPath:" << _name << "not found";
0125         return KService::Ptr(); // Not found
0126     }
0127 
0128     KService::Ptr newService(createEntry(offset));
0129     if (!newService) {
0130         qCDebug(SERVICES) << "createEntry failed!";
0131     }
0132     // Check whether the dictionary was right
0133     // It's ok that it's wrong, for the case where we're looking up an unknown service,
0134     // and the hash value gave us another one.
0135     if (newService && (newService->entryPath() != _name)) {
0136         // No it wasn't...
0137         return KService::Ptr();
0138     }
0139     return newService;
0140 }
0141 
0142 KService::Ptr KServiceFactory::findServiceByMenuId(const QString &_menuId)
0143 {
0144     if (!m_menuIdDict) {
0145         return KService::Ptr(); // Error!
0146     }
0147 
0148     // Warning : this assumes we're NOT building a database
0149     // KBuildServiceFactory reimplements it for the case where we are building one
0150 
0151     int offset = m_menuIdDict->find_string(_menuId);
0152     if (!offset) {
0153         return KService::Ptr(); // Not found
0154     }
0155 
0156     KService::Ptr newService(createEntry(offset));
0157 
0158     // Check whether the dictionary was right.
0159     if (newService && (newService->menuId() != _menuId)) {
0160         // No it wasn't...
0161         return KService::Ptr();
0162     }
0163     return newService;
0164 }
0165 
0166 KService::Ptr KServiceFactory::findServiceByStorageId(const QString &_storageId)
0167 {
0168     KService::Ptr service = findServiceByMenuId(_storageId);
0169     if (service) {
0170         return service;
0171     }
0172 
0173     service = findServiceByDesktopPath(_storageId);
0174     if (service) {
0175         return service;
0176     }
0177 
0178     if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId)) {
0179         return KService::Ptr(new KService(_storageId));
0180     }
0181 
0182     QString tmp = _storageId;
0183     tmp = tmp.mid(tmp.lastIndexOf(QLatin1Char('/')) + 1); // Strip dir
0184 
0185     if (tmp.endsWith(QLatin1String(".desktop"))) {
0186         tmp.chop(8);
0187     }
0188 
0189     if (tmp.endsWith(QLatin1String(".kdelnk"))) {
0190         tmp.chop(7);
0191     }
0192 
0193     service = findServiceByDesktopName(tmp);
0194 
0195     return service;
0196 }
0197 
0198 KService *KServiceFactory::createEntry(int offset) const
0199 {
0200     KSycocaType type;
0201     QDataStream *str = sycoca()->findEntry(offset, type);
0202     if (type != KST_KService) {
0203         qCWarning(SERVICES) << "KServiceFactory: unexpected object entry in KSycoca database (type=" << int(type) << ")";
0204         return nullptr;
0205     }
0206     KService *newEntry = new KService(*str, offset);
0207     if (!newEntry->isValid()) {
0208         qCWarning(SERVICES) << "KServiceFactory: corrupt object in KSycoca database!";
0209         delete newEntry;
0210         newEntry = nullptr;
0211     }
0212     return newEntry;
0213 }
0214 
0215 KService::List KServiceFactory::allServices()
0216 {
0217     KService::List result;
0218     const KSycocaEntry::List list = allEntries();
0219     for (const auto &entryPtr : list) {
0220         if (entryPtr->isType(KST_KService)) {
0221             result.append(KService::Ptr(static_cast<KService *>(entryPtr.data())));
0222         }
0223     }
0224     return result;
0225 }
0226 
0227 QStringList KServiceFactory::resourceDirs()
0228 {
0229     return KSycocaFactory::allDirectories(QStringLiteral("kservices5")) + KSycocaFactory::allDirectories(QStringLiteral("applications"));
0230 }
0231 
0232 QList<KServiceOffer> KServiceFactory::offers(int serviceTypeOffset, int serviceOffersOffset)
0233 {
0234     QList<KServiceOffer> list;
0235 
0236     // Jump to the offer list
0237     QDataStream *str = stream();
0238     str->device()->seek(m_offerListOffset + serviceOffersOffset);
0239 
0240     qint32 aServiceTypeOffset;
0241     qint32 aServiceOffset;
0242     qint32 initialPreference;
0243     qint32 mimeTypeInheritanceLevel;
0244     while (true) {
0245         (*str) >> aServiceTypeOffset;
0246         if (aServiceTypeOffset) {
0247             (*str) >> aServiceOffset;
0248             (*str) >> initialPreference;
0249             (*str) >> mimeTypeInheritanceLevel;
0250             if (aServiceTypeOffset == serviceTypeOffset) {
0251                 // Save stream position !
0252                 const qint64 savedPos = str->device()->pos();
0253                 // Create Service
0254                 KService *serv = createEntry(aServiceOffset);
0255                 if (serv) {
0256                     KService::Ptr servPtr(serv);
0257 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 69)
0258                     list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel, servPtr->allowAsDefault()));
0259 #else
0260                     list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel));
0261 #endif
0262                 }
0263                 // Restore position
0264                 str->device()->seek(savedPos);
0265             } else {
0266                 break; // too far
0267             }
0268         } else {
0269             break; // 0 => end of list
0270         }
0271     }
0272     return list;
0273 }
0274 
0275 KService::List KServiceFactory::serviceOffers(const KServiceType::Ptr &serviceType)
0276 {
0277     return serviceOffers(serviceType->offset(), serviceType->serviceOffersOffset());
0278 }
0279 
0280 KService::List KServiceFactory::serviceOffers(int serviceTypeOffset, int serviceOffersOffset)
0281 {
0282     KService::List list;
0283 
0284     // Jump to the offer list
0285     QDataStream *str = stream();
0286     str->device()->seek(m_offerListOffset + serviceOffersOffset);
0287 
0288     qint32 aServiceTypeOffset;
0289     qint32 aServiceOffset;
0290     qint32 initialPreference;
0291     qint32 mimeTypeInheritanceLevel;
0292     while (true) {
0293         (*str) >> aServiceTypeOffset;
0294         if (aServiceTypeOffset) {
0295             (*str) >> aServiceOffset;
0296             (*str) >> initialPreference; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
0297             (*str) >> mimeTypeInheritanceLevel; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
0298             if (aServiceTypeOffset == serviceTypeOffset) {
0299                 // Save stream position !
0300                 const qint64 savedPos = str->device()->pos();
0301                 // Create service
0302                 KService *serv = createEntry(aServiceOffset);
0303                 if (serv) {
0304                     list.append(KService::Ptr(serv));
0305                 }
0306                 // Restore position
0307                 str->device()->seek(savedPos);
0308             } else {
0309                 break; // too far
0310             }
0311         } else {
0312             break; // 0 => end of list
0313         }
0314     }
0315     return list;
0316 }
0317 
0318 bool KServiceFactory::hasOffer(const KServiceType::Ptr &serviceType, const KService::Ptr &testedService)
0319 {
0320     return hasOffer(serviceType->offset(), serviceType->serviceOffersOffset(), testedService->offset());
0321 }
0322 
0323 bool KServiceFactory::hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset)
0324 {
0325     // Save stream position
0326     QDataStream *str = stream();
0327     const qint64 savedPos = str->device()->pos();
0328 
0329     // Jump to the offer list
0330     str->device()->seek(m_offerListOffset + serviceOffersOffset);
0331     bool found = false;
0332     qint32 aServiceTypeOffset;
0333     qint32 aServiceOffset;
0334     qint32 initialPreference;
0335     qint32 mimeTypeInheritanceLevel;
0336     while (!found) {
0337         (*str) >> aServiceTypeOffset;
0338         if (aServiceTypeOffset) {
0339             (*str) >> aServiceOffset;
0340             (*str) >> initialPreference;
0341             (*str) >> mimeTypeInheritanceLevel;
0342             if (aServiceTypeOffset == serviceTypeOffset) {
0343                 if (aServiceOffset == testedServiceOffset) {
0344                     found = true;
0345                 }
0346             } else {
0347                 break; // too far
0348             }
0349         } else {
0350             break; // 0 => end of list
0351         }
0352     }
0353     // Restore position
0354     str->device()->seek(savedPos);
0355     return found;
0356 }
0357 
0358 void KServiceFactory::virtual_hook(int id, void *data)
0359 {
0360     KSycocaFactory::virtual_hook(id, data);
0361 }