File indexing completed on 2023-11-26 10:45:34

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         if (!str) {
0032             qWarning() << "Could not open sycoca database, you must run kbuildsycoca first!";
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("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                     list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel));
0258                 }
0259                 // Restore position
0260                 str->device()->seek(savedPos);
0261             } else {
0262                 break; // too far
0263             }
0264         } else {
0265             break; // 0 => end of list
0266         }
0267     }
0268     return list;
0269 }
0270 
0271 KService::List KServiceFactory::serviceOffers(int serviceTypeOffset, int serviceOffersOffset)
0272 {
0273     KService::List list;
0274 
0275     // Jump to the offer list
0276     QDataStream *str = stream();
0277     str->device()->seek(m_offerListOffset + serviceOffersOffset);
0278 
0279     qint32 aServiceTypeOffset;
0280     qint32 aServiceOffset;
0281     qint32 initialPreference;
0282     qint32 mimeTypeInheritanceLevel;
0283     while (true) {
0284         (*str) >> aServiceTypeOffset;
0285         if (aServiceTypeOffset) {
0286             (*str) >> aServiceOffset;
0287             (*str) >> initialPreference; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
0288             (*str) >> mimeTypeInheritanceLevel; // unused (remove once KMimeTypeTrader/KServiceTypeTrader are gone)
0289             if (aServiceTypeOffset == serviceTypeOffset) {
0290                 // Save stream position !
0291                 const qint64 savedPos = str->device()->pos();
0292                 // Create service
0293                 KService *serv = createEntry(aServiceOffset);
0294                 if (serv) {
0295                     list.append(KService::Ptr(serv));
0296                 }
0297                 // Restore position
0298                 str->device()->seek(savedPos);
0299             } else {
0300                 break; // too far
0301             }
0302         } else {
0303             break; // 0 => end of list
0304         }
0305     }
0306     return list;
0307 }
0308 
0309 bool KServiceFactory::hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset)
0310 {
0311     // Save stream position
0312     QDataStream *str = stream();
0313     const qint64 savedPos = str->device()->pos();
0314 
0315     // Jump to the offer list
0316     str->device()->seek(m_offerListOffset + serviceOffersOffset);
0317     bool found = false;
0318     qint32 aServiceTypeOffset;
0319     qint32 aServiceOffset;
0320     qint32 initialPreference;
0321     qint32 mimeTypeInheritanceLevel;
0322     while (!found) {
0323         (*str) >> aServiceTypeOffset;
0324         if (aServiceTypeOffset) {
0325             (*str) >> aServiceOffset;
0326             (*str) >> initialPreference;
0327             (*str) >> mimeTypeInheritanceLevel;
0328             if (aServiceTypeOffset == serviceTypeOffset) {
0329                 if (aServiceOffset == testedServiceOffset) {
0330                     found = true;
0331                 }
0332             } else {
0333                 break; // too far
0334             }
0335         } else {
0336             break; // 0 => end of list
0337         }
0338     }
0339     // Restore position
0340     str->device()->seek(savedPos);
0341     return found;
0342 }
0343 
0344 void KServiceFactory::virtual_hook(int id, void *data)
0345 {
0346     KSycocaFactory::virtual_hook(id, data);
0347 }