File indexing completed on 2024-10-06 03:40:58
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 }