diff --git a/src/core/cache.cpp b/src/core/cache.cpp index b87b66b6..e79a94bd 100644 --- a/src/core/cache.cpp +++ b/src/core/cache.cpp @@ -1,258 +1,263 @@ /* Copyright (c) 2009 Frederik Gladhorn Copyright (c) 2010 Matthias Fuchs This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "cache.h" #include #include #include #include #include #include using namespace KNSCore; typedef QHash > CacheHash; Q_GLOBAL_STATIC(CacheHash, s_caches) Cache::Cache(const QString &appName): QObject(nullptr) { m_kns2ComponentName = appName; const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knewstuff3/"); QDir().mkpath(path); registryFile = path + appName + QStringLiteral(".knsregistry"); qCDebug(KNEWSTUFFCORE) << "Using registry file: " << registryFile; setProperty("dirty", false); //KF6 make normal variable } QSharedPointer Cache::getCache(const QString &appName) { CacheHash::const_iterator it = s_caches()->constFind(appName); if ((it != s_caches()->constEnd()) && !(*it).isNull()) { return QSharedPointer(*it); } QSharedPointer p(new Cache(appName)); s_caches()->insert(appName, QWeakPointer(p)); QObject::connect(p.data(), &QObject::destroyed, [appName] { if (auto cache = s_caches()) { cache->remove(appName); } }); return p; } Cache::~Cache() { } void Cache::readRegistry() { // read KNS2 registry first to migrate it readKns2MetaFiles(); QFile f(registryFile); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { if (QFileInfo::exists(registryFile)) { qWarning() << "The file " << registryFile << " could not be opened."; } return; } QXmlStreamReader reader(&f); if (reader.hasError() || !reader.readNextStartElement()) { qWarning() << "The file could not be parsed."; return; } if (reader.name() != QLatin1String("hotnewstuffregistry")) { qWarning() << "The file doesn't seem to be of interest."; return; } for (auto token = reader.readNext(); !reader.atEnd(); token = reader.readNext()) { if (token != QXmlStreamReader::StartElement) continue; EntryInternal e; e.setEntryXML(reader); e.setSource(EntryInternal::Cache); cache.insert(e); Q_ASSERT(reader.tokenType() == QXmlStreamReader::EndElement); } qCDebug(KNEWSTUFFCORE) << "Cache read... entries: " << cache.size(); } void Cache::readKns2MetaFiles() { qCDebug(KNEWSTUFFCORE) << "Loading KNS2 registry of files for the component: " << m_kns2ComponentName; const auto realAppName = m_kns2ComponentName.splitRef(':')[0]; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("knewstuff2-entries.registry"), QStandardPaths::LocateDirectory); for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { qCDebug(KNEWSTUFFCORE) << " + Load from directory '" + (*it) + "'."; QDir dir((*it)); const QStringList files = dir.entryList(QDir::Files | QDir::Readable); for (QStringList::const_iterator fit = files.begin(); fit != files.end(); ++fit) { QString filepath = (*it) + QLatin1Char('/') + (*fit); qCDebug(KNEWSTUFFCORE) << " Load from file '" + filepath + "'."; QFileInfo info(filepath); QFile f(filepath); // first see if this file is even for this app // because the registry contains entries for all apps // FIXMEE: should be able to do this with a filter on the entryList above probably QString thisAppName = QString::fromUtf8(QByteArray::fromBase64(info.baseName().toUtf8())); // NOTE: the ":" needs to always coincide with the separator character used in // the id(Entry*) method thisAppName = thisAppName.split(QLatin1Char(':'))[0]; if (thisAppName != realAppName) { continue; } if (!f.open(QIODevice::ReadOnly)) { qWarning() << "The file: " << filepath << " could not be opened."; continue; } QDomDocument doc; if (!doc.setContent(&f)) { qWarning() << "The file could not be parsed."; return; } qCDebug(KNEWSTUFFCORE) << "found entry: " << doc.toString(); QDomElement root = doc.documentElement(); if (root.tagName() != QLatin1String("ghnsinstall")) { qWarning() << "The file doesn't seem to be of interest."; return; } // The .meta files only contain one entry QDomElement stuff = root.firstChildElement(QStringLiteral("stuff")); EntryInternal e; e.setEntryXML(stuff); e.setSource(EntryInternal::Cache); if (e.payload().startsWith(QLatin1String("http://download.kde.org/khotnewstuff"))) { // This is 99% sure a opendesktop file, make it a real one. e.setProviderId(QStringLiteral("https://api.opendesktop.org/v1/")); e.setHomepage(QUrl(QString(QLatin1String("http://opendesktop.org/content/show.php?content=") + e.uniqueId()))); } else if (e.payload().startsWith(QLatin1String("http://edu.kde.org/contrib/kvtml/"))) { // kvmtl-1 e.setProviderId(QStringLiteral("http://edu.kde.org/contrib/kvtml/kvtml.xml")); } else if (e.payload().startsWith(QLatin1String("http://edu.kde.org/contrib/kvtml2/"))) { // kvmtl-2 e.setProviderId(QStringLiteral("http://edu.kde.org/contrib/kvtml2/provider41.xml")); } else { // we failed, skip qWarning() << "Could not load entry: " << filepath; continue; } e.setStatus(KNS3::Entry::Installed); cache.insert(e); QDomDocument tmp(QStringLiteral("yay")); tmp.appendChild(e.entryXML()); qCDebug(KNEWSTUFFCORE) << "new entry: " << tmp.toString(); f.close(); QDir dir; if (!dir.remove(filepath)) { qWarning() << "could not delete old kns2 .meta file: " << filepath; } else { qCDebug(KNEWSTUFFCORE) << "Migrated KNS2 entry to KNS3."; } } } setProperty("dirty", false); } EntryInternal::List Cache::registryForProvider(const QString &providerId) { EntryInternal::List entries; foreach (const EntryInternal &e, cache) { if (e.providerId() == providerId) { entries.append(e); } } return entries; } void Cache::writeRegistry() { if (!property("dirty").toBool()) return; qCDebug(KNEWSTUFFCORE) << "Write registry"; QFile f(registryFile); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Cannot write meta information to '" << registryFile << "'." << endl; return; } QDomDocument doc(QStringLiteral("khotnewstuff3")); doc.appendChild(doc.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""))); QDomElement root = doc.createElement(QStringLiteral("hotnewstuffregistry")); doc.appendChild(root); foreach (const EntryInternal &entry, cache) { // Write the entry, unless the policy is CacheNever and the entry is not installed. if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) { QDomElement exml = entry.entryXML(); root.appendChild(exml); } } QTextStream metastream(&f); metastream << doc.toByteArray(); setProperty("dirty", false); } void Cache::registerChangedEntry(const KNSCore::EntryInternal &entry) { setProperty("dirty", true); cache.insert(entry); } void Cache::insertRequest(const KNSCore::Provider::SearchRequest &request, const KNSCore::EntryInternal::List &entries) { // append new entries - requestCache[request.hashForRequest()].append(entries); + auto &cacheList = requestCache[request.hashForRequest()]; + for (const auto &entry : entries) { + if (!cacheList.contains(entry)) { + cacheList.append(entry); + } + } qCDebug(KNEWSTUFFCORE) << request.hashForRequest() << " add: " << entries.size() << " keys: " << requestCache.keys(); } EntryInternal::List Cache::requestFromCache(const KNSCore::Provider::SearchRequest &request) { qCDebug(KNEWSTUFFCORE) << request.hashForRequest(); return requestCache.value(request.hashForRequest()); }