File indexing completed on 2025-01-12 03:39:37
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "faviconrequestjob.h" 0010 #include <faviconscache_p.h> 0011 0012 #include "favicons_debug.h" 0013 0014 #include <KConfig> 0015 #include <KIO/TransferJob> 0016 #include <KLocalizedString> 0017 0018 #include <QBuffer> 0019 #include <QCache> 0020 #include <QDate> 0021 #include <QFileInfo> 0022 #include <QImage> 0023 #include <QImageReader> 0024 #include <QSaveFile> 0025 #include <QStandardPaths> 0026 #include <QUrl> 0027 0028 using namespace KIO; 0029 0030 static bool isIconOld(const QString &icon) 0031 { 0032 const QFileInfo info(icon); 0033 if (!info.exists()) { 0034 qCDebug(FAVICONS_LOG) << "isIconOld" << icon << "yes, no such file"; 0035 return true; // Trigger a new download on error 0036 } 0037 const QDate date = info.lastModified().date(); 0038 0039 qCDebug(FAVICONS_LOG) << "isIconOld" << icon << "?"; 0040 return date.daysTo(QDate::currentDate()) > 7; // arbitrary value (one week) 0041 } 0042 0043 class KIO::FavIconRequestJobPrivate 0044 { 0045 public: 0046 FavIconRequestJobPrivate(const QUrl &hostUrl, KIO::LoadType reload) 0047 : m_hostUrl(hostUrl) 0048 , m_reload(reload) 0049 { 0050 } 0051 0052 // slots 0053 void slotData(KIO::Job *job, const QByteArray &data); 0054 0055 QUrl m_hostUrl; 0056 QUrl m_iconUrl; 0057 QString m_iconFile; 0058 QByteArray m_iconData; 0059 KIO::LoadType m_reload; 0060 }; 0061 0062 FavIconRequestJob::FavIconRequestJob(const QUrl &hostUrl, LoadType reload, QObject *parent) 0063 : KCompositeJob(parent) 0064 , d(new FavIconRequestJobPrivate(hostUrl, reload)) 0065 { 0066 QMetaObject::invokeMethod(this, &FavIconRequestJob::doStart, Qt::QueuedConnection); 0067 } 0068 0069 FavIconRequestJob::~FavIconRequestJob() = default; 0070 0071 void FavIconRequestJob::setIconUrl(const QUrl &iconUrl) 0072 { 0073 d->m_iconUrl = iconUrl; 0074 } 0075 0076 QString FavIconRequestJob::iconFile() const 0077 { 0078 return d->m_iconFile; 0079 } 0080 0081 QUrl FavIconRequestJob::hostUrl() const 0082 { 0083 return d->m_hostUrl; 0084 } 0085 0086 void FavIconRequestJob::doStart() 0087 { 0088 KIO::FavIconsCache *cache = KIO::FavIconsCache::instance(); 0089 QUrl iconUrl = d->m_iconUrl; 0090 const bool isNewIconUrl = !iconUrl.isEmpty(); 0091 if (isNewIconUrl) { 0092 cache->setIconForUrl(d->m_hostUrl, d->m_iconUrl); 0093 } else { 0094 iconUrl = cache->iconUrlForUrl(d->m_hostUrl); 0095 } 0096 if (d->m_reload == NoReload) { 0097 const QString iconFile = cache->cachePathForIconUrl(iconUrl); 0098 if (!isIconOld(iconFile)) { 0099 qCDebug(FAVICONS_LOG) << "existing icon not old, reload not requested -> doing nothing"; 0100 d->m_iconFile = iconFile; 0101 emitResult(); 0102 return; 0103 } 0104 0105 if (cache->isFailedDownload(iconUrl)) { 0106 qCDebug(FAVICONS_LOG) << iconUrl << "already in failedDownloads, emitting error"; 0107 setError(KIO::ERR_DOES_NOT_EXIST); 0108 setErrorText(i18n("No favicon found for %1", d->m_hostUrl.host())); 0109 emitResult(); 0110 return; 0111 } 0112 } 0113 0114 qCDebug(FAVICONS_LOG) << "downloading" << iconUrl; 0115 KIO::TransferJob *job = KIO::get(iconUrl, d->m_reload, KIO::HideProgressInfo); 0116 QMap<QString, QString> metaData; 0117 metaData.insert(QStringLiteral("ssl_no_client_cert"), QStringLiteral("true")); 0118 metaData.insert(QStringLiteral("ssl_no_ui"), QStringLiteral("true")); 0119 metaData.insert(QStringLiteral("UseCache"), QStringLiteral("false")); 0120 metaData.insert(QStringLiteral("cookies"), QStringLiteral("none")); 0121 metaData.insert(QStringLiteral("no-www-auth"), QStringLiteral("true")); 0122 metaData.insert(QStringLiteral("errorPage"), QStringLiteral("false")); 0123 job->addMetaData(metaData); 0124 QObject::connect(job, &KIO::TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) { 0125 d->slotData(job, data); 0126 }); 0127 addSubjob(job); 0128 } 0129 0130 void FavIconRequestJob::slotResult(KJob *job) 0131 { 0132 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job); 0133 const QUrl &iconUrl = tjob->url(); 0134 KIO::FavIconsCache *cache = KIO::FavIconsCache::instance(); 0135 if (!job->error()) { 0136 QBuffer buffer(&d->m_iconData); 0137 buffer.open(QIODevice::ReadOnly); 0138 QImageReader ir(&buffer); 0139 QSize desired(16, 16); 0140 if (ir.canRead()) { 0141 while (ir.imageCount() > 1 && ir.currentImageRect() != QRect(0, 0, desired.width(), desired.height())) { 0142 if (!ir.jumpToNextImage()) { 0143 break; 0144 } 0145 } 0146 ir.setScaledSize(desired); 0147 const QImage img = ir.read(); 0148 if (!img.isNull()) { 0149 cache->ensureCacheExists(); 0150 const QString localPath = cache->cachePathForIconUrl(iconUrl); 0151 qCDebug(FAVICONS_LOG) << "Saving image to" << localPath; 0152 QSaveFile saveFile(localPath); 0153 if (saveFile.open(QIODevice::WriteOnly) && img.save(&saveFile, "PNG") && saveFile.commit()) { 0154 d->m_iconFile = localPath; 0155 } else { 0156 setError(KIO::ERR_CANNOT_WRITE); 0157 setErrorText(i18n("Error saving image to %1", localPath)); 0158 } 0159 } else { 0160 qCDebug(FAVICONS_LOG) << "QImageReader read() returned a null image"; 0161 } 0162 } else { 0163 qCDebug(FAVICONS_LOG) << "QImageReader canRead returned false"; 0164 } 0165 } else if (job->error() == KJob::KilledJobError) { // we killed it in slotData 0166 setError(KIO::ERR_WORKER_DEFINED); 0167 setErrorText(i18n("Icon file too big, download aborted")); 0168 } else { 0169 setError(job->error()); 0170 setErrorText(job->errorString()); // not errorText(), because "this" is a KJob, with no errorString building logic 0171 } 0172 d->m_iconData.clear(); // release memory 0173 if (d->m_iconFile.isEmpty()) { 0174 qCDebug(FAVICONS_LOG) << "adding" << iconUrl << "to failed downloads due to error:" << errorString(); 0175 cache->addFailedDownload(iconUrl); 0176 } else { 0177 cache->removeFailedDownload(iconUrl); 0178 } 0179 KCompositeJob::removeSubjob(job); 0180 emitResult(); 0181 } 0182 0183 void FavIconRequestJobPrivate::slotData(Job *job, const QByteArray &data) 0184 { 0185 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job); 0186 unsigned int oldSize = m_iconData.size(); 0187 // Size limit. Stop downloading if the file is huge. 0188 // Testcase (as of june 2008, at least): http://planet-soc.com/favicon.ico, 136K and strange format. 0189 // Another case: sites which redirect from "/favicon.ico" to "/" and return the main page. 0190 if (oldSize > 0x10000) { // 65K 0191 qCDebug(FAVICONS_LOG) << "Favicon too big, aborting download of" << tjob->url(); 0192 const QUrl iconUrl = tjob->url(); 0193 KIO::FavIconsCache::instance()->addFailedDownload(iconUrl); 0194 tjob->kill(KJob::EmitResult); 0195 } else { 0196 m_iconData.resize(oldSize + data.size()); 0197 memcpy(m_iconData.data() + oldSize, data.data(), data.size()); 0198 } 0199 } 0200 0201 #include "moc_faviconrequestjob.cpp"