/* This file is part of KIO. SPDX-FileCopyrightText: 2001 Malte Starostik SPDX-FileCopyrightText: 2016 David Faure SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "faviconscache_p.h" #include #include #include #include #include #include #include #include #include using namespace KIO; static QString portForUrl(const QUrl &url) { if (url.port() > 0) { return QLatin1Char('_') + QString::number(url.port()); } return QString(); } static QString simplifyUrl(const QUrl &url) { // splat any = in the URL so it can be safely used as a config key QString result = url.host() + portForUrl(url) + url.path(); result.replace(QLatin1Char('='), QLatin1Char('_')); while (result.endsWith(QLatin1Char('/'))) { result.chop(1); } return result; } static QString iconNameFromUrl(const QUrl &iconUrl) { if (iconUrl.path() == QLatin1String("/favicon.ico")) { return iconUrl.host() + portForUrl(iconUrl); } QString result = simplifyUrl(iconUrl); // splat / so it can be safely used as a file name result.replace(QLatin1Char('/'), QLatin1Char('_')); const int dotExtLen = 4; const QStringView ext = QStringView(result).right(dotExtLen); if (ext == QLatin1String(".ico") || ext == QLatin1String(".png") || ext == QLatin1String(".xpm")) { result.chop(dotExtLen); } return result; } //// class KIO::FavIconsCachePrivate { public: FavIconsCachePrivate() : cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/favicons/")) , config(cacheDir + QStringLiteral("index")) { } QString cachedIconUrlForUrl(const QUrl &url); const QString cacheDir; QMutex mutex; // protects all the member variables below KConfig config; QCache faviconsCache; QSet failedDownloads; }; QString FavIconsCachePrivate::cachedIconUrlForUrl(const QUrl &url) { Q_ASSERT(!mutex.tryLock()); const QString simplifiedUrl = simplifyUrl(url); QString *cachedIconUrl = faviconsCache[simplifiedUrl]; return (cachedIconUrl ? *cachedIconUrl : config.group(QString()).readEntry(simplifiedUrl, QString())); } FavIconsCache *FavIconsCache::instance() { static FavIconsCache s_cache; // remind me why we need Q_GLOBAL_STATIC, again, now that C++11 guarantees thread safety? return &s_cache; } FavIconsCache::FavIconsCache() : d(new FavIconsCachePrivate) { } FavIconsCache::~FavIconsCache() = default; QString FavIconsCache::iconForUrl(const QUrl &url) { if (url.host().isEmpty()) { return QString(); } QMutexLocker locker(&d->mutex); const QString cachedIconUrl = d->cachedIconUrlForUrl(url); QString icon = d->cacheDir; if (!cachedIconUrl.isEmpty()) { icon += iconNameFromUrl(QUrl(cachedIconUrl)); } else { icon += url.host(); } icon += QStringLiteral(".png"); if (QFile::exists(icon)) { return icon; } return QString(); } QUrl FavIconsCache::iconUrlForUrl(const QUrl &url) { QMutexLocker locker(&d->mutex); const QString cachedIconUrl = d->cachedIconUrlForUrl(url); if (!cachedIconUrl.isEmpty()) { return QUrl(cachedIconUrl); } else { QUrl iconUrl; iconUrl.setScheme(url.scheme()); iconUrl.setHost(url.host()); iconUrl.setPort(url.port()); iconUrl.setPath(QStringLiteral("/favicon.ico")); iconUrl.setUserInfo(url.userInfo()); return iconUrl; } } void FavIconsCache::setIconForUrl(const QUrl &url, const QUrl &iconUrl) { QMutexLocker locker(&d->mutex); const QString simplifiedUrl = simplifyUrl(url); const QString iconUrlStr = iconUrl.url(); d->faviconsCache.insert(simplifiedUrl, new QString(iconUrlStr)); d->config.group(QString()).writeEntry(simplifiedUrl, iconUrlStr); d->config.sync(); } QString FavIconsCache::cachePathForIconUrl(const QUrl &iconUrl) const { QMutexLocker locker(&d->mutex); const QString iconName = iconNameFromUrl(iconUrl); return d->cacheDir + iconName + QLatin1String(".png"); } void FavIconsCache::ensureCacheExists() { QMutexLocker locker(&d->mutex); QDir().mkpath(d->cacheDir); } void FavIconsCache::addFailedDownload(const QUrl &url) { QMutexLocker locker(&d->mutex); d->failedDownloads.insert(url); } void FavIconsCache::removeFailedDownload(const QUrl &url) { QMutexLocker locker(&d->mutex); d->failedDownloads.remove(url); } bool FavIconsCache::isFailedDownload(const QUrl &url) const { QMutexLocker locker(&d->mutex); return d->failedDownloads.contains(url); } #include "moc_faviconscache_p.cpp"