File indexing completed on 2024-05-12 05:09:32
0001 /*************************************************************************** 0002 Copyright (C) 2003-2021 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include <config.h> 0026 0027 #include "fetchmanager.h" 0028 #include "configwidget.h" 0029 #include "messagehandler.h" 0030 #include "../entry.h" 0031 #include "../collection.h" 0032 #include "../utils/tellico_utils.h" 0033 #include "../tellico_debug.h" 0034 0035 #ifdef HAVE_YAZ 0036 #include "z3950fetcher.h" 0037 #endif 0038 #include "srufetcher.h" 0039 #include "execexternalfetcher.h" 0040 0041 #include <KLocalizedString> 0042 #include <KIconLoader> 0043 #include <KIO/Job> 0044 #include <KSharedConfig> 0045 0046 #include <QFileInfo> 0047 #include <QDir> 0048 #include <QTemporaryFile> 0049 0050 #define LOAD_ICON(name, group, size) \ 0051 KIconLoader::global()->loadIcon(name, static_cast<KIconLoader::Group>(group), size_) 0052 0053 using namespace Tellico; 0054 using Tellico::Fetch::Manager; 0055 0056 Tellico::Fetch::Manager* Tellico::Fetch::Manager::self() { 0057 static Manager self; 0058 return &self; 0059 } 0060 0061 Manager::Manager() : QObject(), m_currentFetcherIndex(-1), m_messager(new ManagerMessage()), 0062 m_count(0), m_loadDefaults(false) { 0063 // no need to load fetchers since the initializer does it for us 0064 0065 // m_keyMap.insert(FetchFirst, QString()); 0066 m_keyMap.insert(Title, i18n("Title")); 0067 m_keyMap.insert(Person, i18n("Person")); 0068 m_keyMap.insert(ISBN, i18n("ISBN")); 0069 m_keyMap.insert(UPC, i18n("UPC/EAN")); 0070 m_keyMap.insert(Keyword, i18n("Keyword")); 0071 m_keyMap.insert(DOI, i18n("DOI")); 0072 m_keyMap.insert(ArxivID, i18n("arXiv ID")); 0073 m_keyMap.insert(PubmedID, i18n("PubMed ID")); 0074 m_keyMap.insert(LCCN, i18n("LCCN")); 0075 m_keyMap.insert(Raw, i18n("Raw Query")); 0076 // m_keyMap.insert(FetchLast, QString()); 0077 } 0078 0079 Manager::~Manager() { 0080 delete m_messager; 0081 } 0082 0083 void Manager::registerFunction(int type_, const FetcherFunction& func_) { 0084 functionRegistry.insert(type_, func_); 0085 } 0086 0087 void Manager::loadFetchers() { 0088 m_fetchers.clear(); 0089 m_uuidHash.clear(); 0090 0091 KSharedConfigPtr config = KSharedConfig::openConfig(); 0092 if(config->hasGroup(QStringLiteral("Data Sources"))) { 0093 KConfigGroup configGroup(config, QStringLiteral("Data Sources")); 0094 int nSources = configGroup.readEntry("Sources Count", 0); 0095 for(int i = 0; i < nSources; ++i) { 0096 QString group = QStringLiteral("Data Source %1").arg(i); 0097 Fetcher::Ptr f = createFetcher(config, group); 0098 if(f) { 0099 addFetcher(f); 0100 } 0101 } 0102 m_loadDefaults = false; 0103 } else { // add default sources 0104 m_fetchers = defaultFetchers(); 0105 m_loadDefaults = true; 0106 } 0107 } 0108 0109 void Manager::addFetcher(Fetch::Fetcher::Ptr fetcher_) { 0110 Q_ASSERT(fetcher_); 0111 if(fetcher_) { 0112 m_fetchers.append(fetcher_); 0113 if(!fetcher_->messageHandler()) { 0114 fetcher_->setMessageHandler(m_messager); 0115 } 0116 m_uuidHash.insert(fetcher_->uuid(), fetcher_); 0117 } 0118 } 0119 0120 const Tellico::Fetch::FetcherVec& Manager::fetchers() { 0121 if(m_fetchers.isEmpty()) { 0122 loadFetchers(); 0123 } 0124 return m_fetchers; 0125 } 0126 0127 Tellico::Fetch::FetcherVec Manager::fetchers(int type_) { 0128 FetcherVec vec; 0129 foreach(Fetcher::Ptr fetcher, fetchers()) { 0130 if(fetcher->canFetch(type_)) { 0131 vec.append(fetcher); 0132 } 0133 } 0134 return vec; 0135 } 0136 0137 Tellico::Fetch::Fetcher::Ptr Manager::fetcherByUuid(const QString& uuid_) { 0138 return m_uuidHash.contains(uuid_) ? m_uuidHash[uuid_] : Fetcher::Ptr(); 0139 } 0140 0141 Tellico::Fetch::KeyMap Manager::keyMap(const QString& source_) { 0142 // an empty string means return all 0143 if(source_.isEmpty()) { 0144 return m_keyMap; 0145 } 0146 0147 // assume there's only one fetcher match 0148 Fetcher::Ptr foundFetcher; 0149 foreach(Fetcher::Ptr fetcher, fetchers()) { 0150 if(source_ == fetcher->source()) { 0151 foundFetcher = fetcher; 0152 break; 0153 } 0154 } 0155 if(!foundFetcher) { 0156 myWarning() << "no fetcher found!"; 0157 return KeyMap(); 0158 } 0159 0160 KeyMap map; 0161 for(KeyMap::ConstIterator it = m_keyMap.constBegin(); it != m_keyMap.constEnd(); ++it) { 0162 if(foundFetcher->canSearch(it.key())) { 0163 map.insert(it.key(), it.value()); 0164 } 0165 } 0166 return map; 0167 } 0168 0169 void Manager::startSearch(const QString& source_, Tellico::Fetch::FetchKey key_, const QString& value_, Tellico::Data::Collection::Type collType_) { 0170 if(value_.isEmpty()) { 0171 emit signalDone(); 0172 return; 0173 } 0174 0175 FetchRequest request(collType_, key_, value_); 0176 0177 // assume there's only one fetcher match 0178 int i = 0; 0179 m_currentFetcherIndex = -1; 0180 foreach(Fetcher::Ptr fetcher, fetchers()) { 0181 if(source_ == fetcher->source()) { 0182 ++m_count; // Fetcher::search() might emit done(), so increment before calling search() 0183 connect(fetcher.data(), &Fetcher::signalResultFound, 0184 this, &Manager::slotResultFound); 0185 connect(fetcher.data(), &Fetcher::signalDone, 0186 this, &Manager::slotFetcherDone); 0187 myLog() << "Starting search - source:" << source_ << "value:" << value_ << "key:" << key_; 0188 fetcher->startSearch(request); 0189 m_currentFetcherIndex = i; 0190 break; 0191 } 0192 ++i; 0193 } 0194 } 0195 0196 void Manager::continueSearch() { 0197 if(m_currentFetcherIndex < 0 || m_currentFetcherIndex >= static_cast<int>(m_fetchers.count())) { 0198 myDebug() << "can't continue!"; 0199 emit signalDone(); 0200 return; 0201 } 0202 Fetcher::Ptr fetcher = m_fetchers[m_currentFetcherIndex]; 0203 if(fetcher && fetcher->hasMoreResults()) { 0204 ++m_count; 0205 connect(fetcher.data(), &Fetcher::signalResultFound, 0206 this, &Manager::slotResultFound); 0207 connect(fetcher.data(), &Fetcher::signalDone, 0208 this, &Manager::slotFetcherDone); 0209 myLog() << "Continuing search - source:" << fetcher->source(); 0210 fetcher->continueSearch(); 0211 } else { 0212 emit signalDone(); 0213 } 0214 } 0215 0216 bool Manager::hasMoreResults() const { 0217 if(m_currentFetcherIndex < 0 || m_currentFetcherIndex >= static_cast<int>(m_fetchers.count())) { 0218 return false; 0219 } 0220 Fetcher::Ptr fetcher = m_fetchers[m_currentFetcherIndex]; 0221 return fetcher && fetcher->hasMoreResults(); 0222 } 0223 0224 void Manager::stop() { 0225 foreach(Fetcher::Ptr fetcher, m_fetchers) { 0226 if(fetcher->isSearching()) { 0227 fetcher->stop(); 0228 fetcher->saveConfig(); 0229 } 0230 } 0231 if(m_count != 0) { 0232 myDebug() << "count should be 0!"; 0233 } 0234 m_count = 0; 0235 } 0236 0237 void Manager::slotResultFound(Tellico::Fetch::FetchResult* result_) { 0238 Q_ASSERT(result_); 0239 myLog() << "Search result - source:" << result_->fetcher()->source() << "result:" << result_->title; 0240 Q_EMIT signalResultFound(result_); 0241 } 0242 0243 void Manager::slotFetcherDone(Tellico::Fetch::Fetcher* fetcher_) { 0244 Q_ASSERT(fetcher_); 0245 myLog() << "Search done - source:" << fetcher_->source(); 0246 fetcher_->disconnect(); // disconnect all signals 0247 fetcher_->saveConfig(); 0248 --m_count; 0249 if(m_count <= 0) { 0250 emit signalDone(); 0251 } 0252 } 0253 0254 bool Manager::canFetch(Tellico::Data::Collection::Type collType_) const { 0255 foreach(Fetcher::Ptr fetcher, m_fetchers) { 0256 if(fetcher->canFetch(collType_)) { 0257 return true; 0258 } 0259 } 0260 return false; 0261 } 0262 0263 Tellico::Fetch::Fetcher::Ptr Manager::createFetcher(KSharedConfigPtr config_, const QString& group_) { 0264 if(!config_->hasGroup(group_)) { 0265 myDebug() << "no config group for " << group_; 0266 return Fetcher::Ptr(); 0267 } 0268 0269 KConfigGroup config(config_, group_); 0270 0271 int fetchType = config.readEntry("Type", int(Fetch::Unknown)); 0272 if(fetchType == Fetch::Unknown) { 0273 myDebug() << "unknown type " << fetchType << ", skipping"; 0274 return Fetcher::Ptr(); 0275 } 0276 0277 // special case: the BoardGameGeek fetcher was originally implemented as a Ruby script 0278 // now, it's available with an XML API, so prefer the new version 0279 // so check for fetcher version and switch to the XML if version is missing or lower 0280 if(fetchType == Fetch::ExecExternal && 0281 config.readPathEntry("ExecPath", QString()).endsWith(QLatin1String("boardgamegeek.rb"))) { 0282 KConfigGroup generalConfig(config_, QStringLiteral("General Options")); 0283 if(generalConfig.readEntry("FetchVersion", 0) < 1) { 0284 fetchType = Fetch::BoardGameGeek; 0285 generalConfig.writeEntry("FetchVersion", 1); 0286 } 0287 } 0288 // special case: the Bedetheque fetcher was originally implemented as a Python script 0289 // now, it's available as a builtin data source, so prefer the new version 0290 // so check for fetcher version and switch to the newer if version is missing or lower 0291 if(fetchType == Fetch::ExecExternal && 0292 config.readPathEntry("ExecPath", QString()).endsWith(QStringLiteral("bedetheque.py"))) { 0293 KConfigGroup generalConfig(config_, QStringLiteral("General Options")); 0294 if(generalConfig.readEntry("FetchVersion", 0) < 2) { 0295 fetchType = Fetch::Bedetheque; 0296 generalConfig.writeEntry("FetchVersion", 2); 0297 } 0298 } 0299 0300 Fetcher::Ptr f; 0301 if(functionRegistry.contains(fetchType)) { 0302 f = functionRegistry.value(fetchType).create(this); 0303 f->readConfig(config); 0304 } 0305 return f; 0306 } 0307 0308 #define FETCHER_ADD(type) \ 0309 do { \ 0310 if(functionRegistry.contains(type)) { \ 0311 vec.append(functionRegistry.value(type).create(this)); \ 0312 } \ 0313 } while(false) 0314 0315 // static 0316 Tellico::Fetch::FetcherVec Manager::defaultFetchers() { 0317 FetcherVec vec; 0318 // books 0319 FETCHER_ADD(ISBNdb); 0320 FETCHER_ADD(OpenLibrary); 0321 FETCHER_ADD(GoogleBook); 0322 if(functionRegistry.contains(SRU)) { 0323 vec.append(SRUFetcher::libraryOfCongress(this)); 0324 } 0325 // comic books 0326 FETCHER_ADD(Bedetheque); 0327 FETCHER_ADD(ComicVine); 0328 // bibliographic 0329 FETCHER_ADD(Arxiv); 0330 FETCHER_ADD(GoogleScholar); 0331 FETCHER_ADD(BiblioShare); 0332 FETCHER_ADD(DBLP); 0333 FETCHER_ADD(HathiTrust); 0334 // music 0335 FETCHER_ADD(MusicBrainz); 0336 FETCHER_ADD(Itunes); 0337 // video games 0338 FETCHER_ADD(TheGamesDB); 0339 FETCHER_ADD(IGDB); 0340 FETCHER_ADD(VNDB); 0341 FETCHER_ADD(VideoGameGeek); 0342 FETCHER_ADD(VGCollect); 0343 // board games 0344 FETCHER_ADD(BoardGameGeek); 0345 // movies 0346 FETCHER_ADD(TheMovieDB); 0347 FETCHER_ADD(TVmaze); 0348 FETCHER_ADD(IMDB); 0349 // coins and stamps 0350 FETCHER_ADD(Colnect); 0351 FETCHER_ADD(Numista); 0352 QStringList langs = QLocale().uiLanguages(); 0353 if(!langs.isEmpty() && langs.first().contains(QLatin1Char('-'))) { 0354 // I'm not sure QT always include two-letter locale codes 0355 langs << langs.first().section(QLatin1Char('-'), 0, 0); 0356 } 0357 // only add IBS if user includes italian 0358 if(langs.contains(QStringLiteral("it"))) { 0359 FETCHER_ADD(IBS); 0360 } 0361 if(langs.contains(QStringLiteral("es"))) { 0362 FETCHER_ADD(FilmAffinity); 0363 } 0364 if(langs.contains(QStringLiteral("fr"))) { 0365 FETCHER_ADD(DVDFr); 0366 FETCHER_ADD(Allocine); 0367 } 0368 if(langs.contains(QStringLiteral("ru"))) { 0369 FETCHER_ADD(KinoPoisk); 0370 } 0371 if(langs.contains(QStringLiteral("ua"))) { 0372 FETCHER_ADD(KinoTeatr); 0373 } 0374 if(langs.contains(QStringLiteral("de"))) { 0375 FETCHER_ADD(Kino); 0376 } 0377 if(langs.contains(QStringLiteral("cn"))) { 0378 FETCHER_ADD(Douban); 0379 } 0380 if(langs.contains(QStringLiteral("dk"))) { 0381 FETCHER_ADD(DBC); 0382 } 0383 return vec; 0384 } 0385 0386 #undef FETCHER_ADD 0387 0388 Tellico::Fetch::FetcherVec Manager::createUpdateFetchers(int collType_) { 0389 if(m_loadDefaults) { 0390 return fetchers(collType_); 0391 } 0392 0393 FetcherVec vec; 0394 KConfigGroup config(KSharedConfig::openConfig(), "Data Sources"); 0395 int nSources = config.readEntry("Sources Count", 0); 0396 for(int i = 0; i < nSources; ++i) { 0397 QString group = QStringLiteral("Data Source %1").arg(i); 0398 // needs the KConfig* 0399 Fetcher::Ptr fetcher = createFetcher(KSharedConfig::openConfig(), group); 0400 if(fetcher && fetcher->canFetch(collType_) && fetcher->canUpdate()) { 0401 vec.append(fetcher); 0402 } 0403 } 0404 return vec; 0405 } 0406 0407 Tellico::Fetch::FetcherVec Manager::createUpdateFetchers(int collType_, Tellico::Fetch::FetchKey key_) { 0408 FetcherVec fetchers; 0409 // creates new fetchers 0410 FetcherVec allFetchers = createUpdateFetchers(collType_); 0411 foreach(Fetcher::Ptr fetcher, allFetchers) { 0412 if(fetcher->canSearch(key_)) { 0413 fetchers.append(fetcher); 0414 } 0415 } 0416 return fetchers; 0417 } 0418 0419 Tellico::Fetch::Fetcher::Ptr Manager::createUpdateFetcher(int collType_, const QString& source_) { 0420 Fetcher::Ptr newFetcher; 0421 // creates new fetchers 0422 FetcherVec fetchers = createUpdateFetchers(collType_); 0423 foreach(Fetcher::Ptr fetcher, fetchers) { 0424 if(fetcher->source() == source_) { 0425 newFetcher = fetcher; 0426 break; 0427 } 0428 } 0429 return newFetcher; 0430 } 0431 0432 void Manager::updateStatus(const QString& message_) { 0433 emit signalStatus(message_); 0434 } 0435 0436 Tellico::Fetch::NameTypeHash Manager::nameTypeHash() { 0437 Fetch::NameTypeHash hash; 0438 FunctionRegistry::const_iterator it = functionRegistry.constBegin(); 0439 while(it != functionRegistry.constEnd()) { 0440 hash.insert(functionRegistry.value(it.key()).name(), static_cast<Type>(it.key())); 0441 ++it; 0442 } 0443 0444 // now find all the scripts distributed with tellico 0445 QStringList files = Tellico::locateAllFiles(QStringLiteral("tellico/data-sources/*.spec")); 0446 foreach(const QString& file, files) { 0447 KConfig spec(file, KConfig::SimpleConfig); 0448 KConfigGroup specConfig(&spec, QString()); 0449 QString name = specConfig.readEntry("Name"); 0450 if(name.isEmpty()) { 0451 myDebug() << "no name for" << file; 0452 continue; 0453 } 0454 0455 bool enabled = specConfig.readEntry("Enabled", true); 0456 if(!enabled || !bundledScriptHasExecPath(file, specConfig)) { // no available exec 0457 continue; 0458 } 0459 0460 hash.insert(name, ExecExternal); 0461 m_scriptMap.insert(name, file); 0462 } 0463 return hash; 0464 } 0465 0466 // called when creating a new fetcher 0467 Tellico::Fetch::ConfigWidget* Manager::configWidget(QWidget* parent_, Tellico::Fetch::Type type_, const QString& name_) { 0468 ConfigWidget* w = nullptr; 0469 if(functionRegistry.contains(type_)) { 0470 w = functionRegistry.value(type_).configWidget(parent_); 0471 } else { 0472 myWarning() << "no widget defined for type =" << type_; 0473 } 0474 if(w && type_ == ExecExternal) { 0475 if(!name_.isEmpty() && m_scriptMap.contains(name_)) { 0476 // bundledScriptHasExecPath() actually needs to write the exec path 0477 // back to the config so the configWidget can read it. But if the spec file 0478 // is not readable, that doesn't work. So work around it with a copy to a temp file 0479 QTemporaryFile tmpFile; 0480 tmpFile.open(); 0481 QUrl from = QUrl::fromLocalFile(m_scriptMap[name_]); 0482 QUrl to = QUrl::fromLocalFile(tmpFile.fileName()); 0483 // have to overwrite since QTemporaryFile already created it 0484 KIO::Job* job = KIO::file_copy(from, to, -1, KIO::Overwrite); 0485 if(!job->exec()) { 0486 myDebug() << job->errorString(); 0487 } 0488 KConfig spec(to.path(), KConfig::SimpleConfig); 0489 KConfigGroup specConfig(&spec, QString()); 0490 // pass actual location of spec file 0491 if(name_ == specConfig.readEntry("Name") && bundledScriptHasExecPath(m_scriptMap[name_], specConfig)) { 0492 w->readConfig(specConfig); 0493 } else { 0494 myWarning() << "Can't read config file for " << to.path(); 0495 } 0496 } 0497 } 0498 0499 return w; 0500 } 0501 0502 // static 0503 QString Manager::typeName(Tellico::Fetch::Type type_) { 0504 if(self()->functionRegistry.contains(type_)) { 0505 return self()->functionRegistry.value(type_).name(); 0506 } 0507 myWarning() << "none found for" << type_; 0508 return QString(); 0509 } 0510 0511 QPixmap Manager::fetcherIcon(Tellico::Fetch::Fetcher* fetcher_, int group_, int size_) { 0512 Q_ASSERT(fetcher_); 0513 if(!fetcher_) { 0514 return QPixmap(); 0515 } 0516 if(!fetcher_->icon().isEmpty()) { 0517 return LOAD_ICON(fetcher_->icon(), group_, size_); 0518 } 0519 if(fetcher_->type() == Fetch::Z3950) { 0520 #ifdef HAVE_YAZ 0521 const Fetch::Z3950Fetcher* f = static_cast<const Fetch::Z3950Fetcher*>(fetcher_); 0522 QUrl u; 0523 u.setScheme(QStringLiteral("http")); 0524 u.setHost(f->host()); 0525 QString icon = Fetcher::favIcon(u); 0526 if(!icon.isEmpty()) { 0527 return LOAD_ICON(icon, group_, size_); 0528 } 0529 #endif 0530 } else 0531 if(fetcher_->type() == Fetch::ExecExternal) { 0532 const Fetch::ExecExternalFetcher* f = static_cast<const Fetch::ExecExternalFetcher*>(fetcher_); 0533 const QString p = f->execPath(); 0534 QUrl u; 0535 if(p.contains(QStringLiteral("allocine"))) { 0536 u = QUrl(QStringLiteral("http://www.allocine.fr")); 0537 } else if(p.contains(QStringLiteral("ministerio_de_cultura"))) { 0538 u = QUrl(QStringLiteral("http://www.mcu.es")); 0539 } else if(p.contains(QStringLiteral("dark_horse_comics"))) { 0540 u = QUrl(QStringLiteral("http://www.darkhorse.com")); 0541 } else if(p.contains(QStringLiteral("boardgamegeek"))) { 0542 u = QUrl(QStringLiteral("http://www.boardgamegeek.com")); 0543 } else if(p.contains(QStringLiteral("supercat"))) { 0544 u = QUrl(QStringLiteral("https://evergreen-ils.org")); 0545 } else if(f->source().contains(QStringLiteral("amarok"), Qt::CaseInsensitive)) { 0546 return LOAD_ICON(QStringLiteral("amarok"), group_, size_); 0547 } 0548 if(!u.isEmpty() && u.isValid()) { 0549 QString icon = Fetcher::favIcon(u); 0550 if(!icon.isEmpty()) { 0551 return LOAD_ICON(icon, group_, size_); 0552 } 0553 } 0554 } 0555 return fetcherIcon(fetcher_->type(), group_, size_); 0556 } 0557 0558 QPixmap Manager::fetcherIcon(Tellico::Fetch::Type type_, int group_, int size_) { 0559 QString name; 0560 if(self()->functionRegistry.contains(type_)) { 0561 name = self()->functionRegistry.value(type_).icon(); 0562 } else { 0563 myWarning() << "no pixmap defined for type =" << type_; 0564 } 0565 0566 if(name.isEmpty()) { 0567 // use default tellico application icon 0568 name = QStringLiteral("tellico"); 0569 } 0570 0571 QPixmap pix = KIconLoader::global()->loadIcon(name, static_cast<KIconLoader::Group>(group_), 0572 size_, KIconLoader::DefaultState, 0573 QStringList(), nullptr, true); 0574 if(pix.isNull()) { 0575 QIcon icon = QIcon::fromTheme(name); 0576 const int groupSize = KIconLoader::global()->currentSize(static_cast<KIconLoader::Group>(group_)); 0577 size_ = size_ == 0 ? groupSize : size_; 0578 pix = icon.pixmap(size_, size_); 0579 } 0580 if(pix.isNull()) { 0581 pix = KIconLoader::global()->loadIcon(name, KIconLoader::Toolbar); 0582 } 0583 return pix; 0584 } 0585 0586 Tellico::StringHash Manager::optionalFields(Fetch::Type type_) { 0587 if(self()->functionRegistry.contains(type_)) { 0588 return self()->functionRegistry.value(type_).optionalFields(); 0589 } 0590 return StringHash(); 0591 } 0592 0593 bool Manager::bundledScriptHasExecPath(const QString& specFile_, KConfigGroup& config_) { 0594 // make sure ExecPath is set and executable 0595 // for the bundled scripts, either the exec name is not set, in which case it is the 0596 // name of the spec file, minus the .spec, or the exec is set, and is local to the dir 0597 // if not, look for it 0598 QFileInfo specInfo(specFile_); 0599 QString exec = config_.readPathEntry("ExecPath", QString()); 0600 QFileInfo execInfo(exec); 0601 if(exec.isEmpty() || !execInfo.exists()) { 0602 exec = specInfo.canonicalPath() + QDir::separator() + specInfo.completeBaseName(); // remove ".spec" 0603 } else if(execInfo.isRelative()) { 0604 exec = specInfo.canonicalPath() + QDir::separator() + exec; 0605 } else if(!execInfo.isExecutable()) { 0606 myWarning() << "not executable:" << specFile_; 0607 return false; 0608 } 0609 execInfo.setFile(exec); 0610 if(!execInfo.exists() || !execInfo.isExecutable()) { 0611 myWarning() << "no exec file for" << specFile_; 0612 myWarning() << "exec =" << exec; 0613 return false; // we're not ok 0614 } 0615 0616 config_.writePathEntry("ExecPath", exec); 0617 config_.sync(); // might be readonly, but that's ok 0618 return true; 0619 }