File indexing completed on 2024-10-06 03:39:24
0001 /* 0002 This file is part of KIO. 0003 SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org> 0004 SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "faviconscache_p.h" 0010 0011 #include <KConfig> 0012 #include <KConfigGroup> 0013 #include <QMutex> 0014 0015 #include <QCache> 0016 #include <QDir> 0017 #include <QFile> 0018 #include <QSet> 0019 #include <QStandardPaths> 0020 #include <QUrl> 0021 0022 using namespace KIO; 0023 0024 static QString portForUrl(const QUrl &url) 0025 { 0026 if (url.port() > 0) { 0027 return QLatin1Char('_') + QString::number(url.port()); 0028 } 0029 return QString(); 0030 } 0031 0032 static QString simplifyUrl(const QUrl &url) 0033 { 0034 // splat any = in the URL so it can be safely used as a config key 0035 QString result = url.host() + portForUrl(url) + url.path(); 0036 result.replace(QLatin1Char('='), QLatin1Char('_')); 0037 while (result.endsWith(QLatin1Char('/'))) { 0038 result.chop(1); 0039 } 0040 return result; 0041 } 0042 0043 static QString iconNameFromUrl(const QUrl &iconUrl) 0044 { 0045 if (iconUrl.path() == QLatin1String("/favicon.ico")) { 0046 return iconUrl.host() + portForUrl(iconUrl); 0047 } 0048 0049 QString result = simplifyUrl(iconUrl); 0050 // splat / so it can be safely used as a file name 0051 result.replace(QLatin1Char('/'), QLatin1Char('_')); 0052 0053 const int dotExtLen = 4; 0054 const QStringView ext = QStringView(result).right(dotExtLen); 0055 if (ext == QLatin1String(".ico") || ext == QLatin1String(".png") || ext == QLatin1String(".xpm")) { 0056 result.chop(dotExtLen); 0057 } 0058 0059 return result; 0060 } 0061 0062 //// 0063 0064 class KIO::FavIconsCachePrivate 0065 { 0066 public: 0067 FavIconsCachePrivate() 0068 : cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/favicons/")) 0069 , config(cacheDir + QStringLiteral("index")) 0070 { 0071 } 0072 0073 QString cachedIconUrlForUrl(const QUrl &url); 0074 0075 const QString cacheDir; 0076 QMutex mutex; // protects all the member variables below 0077 KConfig config; 0078 QCache<QString, QString> faviconsCache; 0079 QSet<QUrl> failedDownloads; 0080 }; 0081 0082 QString FavIconsCachePrivate::cachedIconUrlForUrl(const QUrl &url) 0083 { 0084 Q_ASSERT(!mutex.tryLock()); 0085 const QString simplifiedUrl = simplifyUrl(url); 0086 QString *cachedIconUrl = faviconsCache[simplifiedUrl]; 0087 return (cachedIconUrl ? *cachedIconUrl : config.group(QString()).readEntry(simplifiedUrl, QString())); 0088 } 0089 0090 FavIconsCache *FavIconsCache::instance() 0091 { 0092 static FavIconsCache s_cache; // remind me why we need Q_GLOBAL_STATIC, again, now that C++11 guarantees thread safety? 0093 return &s_cache; 0094 } 0095 0096 FavIconsCache::FavIconsCache() 0097 : d(new FavIconsCachePrivate) 0098 { 0099 } 0100 0101 FavIconsCache::~FavIconsCache() = default; 0102 0103 QString FavIconsCache::iconForUrl(const QUrl &url) 0104 { 0105 if (url.host().isEmpty()) { 0106 return QString(); 0107 } 0108 QMutexLocker locker(&d->mutex); 0109 const QString cachedIconUrl = d->cachedIconUrlForUrl(url); 0110 QString icon = d->cacheDir; 0111 if (!cachedIconUrl.isEmpty()) { 0112 icon += iconNameFromUrl(QUrl(cachedIconUrl)); 0113 } else { 0114 icon += url.host(); 0115 } 0116 icon += QStringLiteral(".png"); 0117 if (QFile::exists(icon)) { 0118 return icon; 0119 } 0120 return QString(); 0121 } 0122 0123 QUrl FavIconsCache::iconUrlForUrl(const QUrl &url) 0124 { 0125 QMutexLocker locker(&d->mutex); 0126 const QString cachedIconUrl = d->cachedIconUrlForUrl(url); 0127 if (!cachedIconUrl.isEmpty()) { 0128 return QUrl(cachedIconUrl); 0129 } else { 0130 QUrl iconUrl; 0131 iconUrl.setScheme(url.scheme()); 0132 iconUrl.setHost(url.host()); 0133 iconUrl.setPort(url.port()); 0134 iconUrl.setPath(QStringLiteral("/favicon.ico")); 0135 iconUrl.setUserInfo(url.userInfo()); 0136 return iconUrl; 0137 } 0138 } 0139 0140 void FavIconsCache::setIconForUrl(const QUrl &url, const QUrl &iconUrl) 0141 { 0142 QMutexLocker locker(&d->mutex); 0143 const QString simplifiedUrl = simplifyUrl(url); 0144 const QString iconUrlStr = iconUrl.url(); 0145 d->faviconsCache.insert(simplifiedUrl, new QString(iconUrlStr)); 0146 d->config.group(QString()).writeEntry(simplifiedUrl, iconUrlStr); 0147 d->config.sync(); 0148 } 0149 0150 QString FavIconsCache::cachePathForIconUrl(const QUrl &iconUrl) const 0151 { 0152 QMutexLocker locker(&d->mutex); 0153 const QString iconName = iconNameFromUrl(iconUrl); 0154 return d->cacheDir + iconName + QLatin1String(".png"); 0155 } 0156 0157 void FavIconsCache::ensureCacheExists() 0158 { 0159 QMutexLocker locker(&d->mutex); 0160 QDir().mkpath(d->cacheDir); 0161 } 0162 0163 void FavIconsCache::addFailedDownload(const QUrl &url) 0164 { 0165 QMutexLocker locker(&d->mutex); 0166 d->failedDownloads.insert(url); 0167 } 0168 0169 void FavIconsCache::removeFailedDownload(const QUrl &url) 0170 { 0171 QMutexLocker locker(&d->mutex); 0172 d->failedDownloads.remove(url); 0173 } 0174 0175 bool FavIconsCache::isFailedDownload(const QUrl &url) const 0176 { 0177 QMutexLocker locker(&d->mutex); 0178 return d->failedDownloads.contains(url); 0179 } 0180 0181 #include "moc_faviconscache_p.cpp"