KIO

faviconrequestjob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org>
4 SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "faviconrequestjob.h"
10#include <faviconscache_p.h>
11
12#include "favicons_debug.h"
13
14#include <KConfig>
15#include <KIO/TransferJob>
16#include <KLocalizedString>
17
18#include <QBuffer>
19#include <QCache>
20#include <QDate>
21#include <QFileInfo>
22#include <QImage>
23#include <QImageReader>
24#include <QSaveFile>
25#include <QStandardPaths>
26#include <QUrl>
27
28using namespace KIO;
29
30static bool isIconOld(const QString &icon)
31{
32 const QFileInfo info(icon);
33 if (!info.exists()) {
34 qCDebug(FAVICONS_LOG) << "isIconOld" << icon << "yes, no such file";
35 return true; // Trigger a new download on error
36 }
37 const QDate date = info.lastModified().date();
38
39 qCDebug(FAVICONS_LOG) << "isIconOld" << icon << "?";
40 return date.daysTo(QDate::currentDate()) > 7; // arbitrary value (one week)
41}
42
43class KIO::FavIconRequestJobPrivate
44{
45public:
46 FavIconRequestJobPrivate(const QUrl &hostUrl, KIO::LoadType reload)
47 : m_hostUrl(hostUrl)
48 , m_reload(reload)
49 {
50 }
51
52 // slots
53 void slotData(KIO::Job *job, const QByteArray &data);
54
55 QUrl m_hostUrl;
56 QUrl m_iconUrl;
57 QString m_iconFile;
58 QByteArray m_iconData;
59 KIO::LoadType m_reload;
60};
61
62FavIconRequestJob::FavIconRequestJob(const QUrl &hostUrl, LoadType reload, QObject *parent)
63 : KCompositeJob(parent)
64 , d(new FavIconRequestJobPrivate(hostUrl, reload))
65{
66 QMetaObject::invokeMethod(this, &FavIconRequestJob::doStart, Qt::QueuedConnection);
67}
68
70
72{
73 d->m_iconUrl = iconUrl;
74}
75
77{
78 return d->m_iconFile;
79}
80
82{
83 return d->m_hostUrl;
84}
85
86void FavIconRequestJob::doStart()
87{
88 KIO::FavIconsCache *cache = KIO::FavIconsCache::instance();
89 QUrl iconUrl = d->m_iconUrl;
90 const bool isNewIconUrl = !iconUrl.isEmpty();
91 if (isNewIconUrl) {
92 cache->setIconForUrl(d->m_hostUrl, d->m_iconUrl);
93 } else {
94 iconUrl = cache->iconUrlForUrl(d->m_hostUrl);
95 }
96 if (d->m_reload == NoReload) {
97 const QString iconFile = cache->cachePathForIconUrl(iconUrl);
98 if (!isIconOld(iconFile)) {
99 qCDebug(FAVICONS_LOG) << "existing icon not old, reload not requested -> doing nothing";
100 d->m_iconFile = iconFile;
101 emitResult();
102 return;
103 }
104
105 if (cache->isFailedDownload(iconUrl)) {
106 qCDebug(FAVICONS_LOG) << iconUrl << "already in failedDownloads, emitting error";
107 setError(KIO::ERR_DOES_NOT_EXIST);
108 setErrorText(i18n("No favicon found for %1", d->m_hostUrl.host()));
109 emitResult();
110 return;
111 }
112 }
113
114 qCDebug(FAVICONS_LOG) << "downloading" << iconUrl;
115 KIO::TransferJob *job = KIO::get(iconUrl, d->m_reload, KIO::HideProgressInfo);
116 QMap<QString, QString> metaData;
117 metaData.insert(QStringLiteral("ssl_no_client_cert"), QStringLiteral("true"));
118 metaData.insert(QStringLiteral("ssl_no_ui"), QStringLiteral("true"));
119 metaData.insert(QStringLiteral("UseCache"), QStringLiteral("false"));
120 metaData.insert(QStringLiteral("cookies"), QStringLiteral("none"));
121 metaData.insert(QStringLiteral("no-www-auth"), QStringLiteral("true"));
122 metaData.insert(QStringLiteral("errorPage"), QStringLiteral("false"));
123 job->addMetaData(metaData);
124 QObject::connect(job, &KIO::TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) {
125 d->slotData(job, data);
126 });
127 addSubjob(job);
128}
129
130void FavIconRequestJob::slotResult(KJob *job)
131{
132 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job);
133 const QUrl &iconUrl = tjob->url();
134 KIO::FavIconsCache *cache = KIO::FavIconsCache::instance();
135 if (!job->error()) {
136 QBuffer buffer(&d->m_iconData);
137 buffer.open(QIODevice::ReadOnly);
138 QImageReader ir(&buffer);
139 QSize desired(16, 16);
140 if (ir.canRead()) {
141 while (ir.imageCount() > 1 && ir.currentImageRect() != QRect(0, 0, desired.width(), desired.height())) {
142 if (!ir.jumpToNextImage()) {
143 break;
144 }
145 }
146 ir.setScaledSize(desired);
147 const QImage img = ir.read();
148 if (!img.isNull()) {
149 cache->ensureCacheExists();
150 const QString localPath = cache->cachePathForIconUrl(iconUrl);
151 qCDebug(FAVICONS_LOG) << "Saving image to" << localPath;
152 QSaveFile saveFile(localPath);
153 if (saveFile.open(QIODevice::WriteOnly) && img.save(&saveFile, "PNG") && saveFile.commit()) {
154 d->m_iconFile = localPath;
155 } else {
156 setError(KIO::ERR_CANNOT_WRITE);
157 setErrorText(i18n("Error saving image to %1", localPath));
158 }
159 } else {
160 qCDebug(FAVICONS_LOG) << "QImageReader read() returned a null image";
161 }
162 } else {
163 qCDebug(FAVICONS_LOG) << "QImageReader canRead returned false";
164 }
165 } else if (job->error() == KJob::KilledJobError) { // we killed it in slotData
167 setErrorText(i18n("Icon file too big, download aborted"));
168 } else {
169 setError(job->error());
170 setErrorText(job->errorString()); // not errorText(), because "this" is a KJob, with no errorString building logic
171 }
172 d->m_iconData.clear(); // release memory
173 if (d->m_iconFile.isEmpty()) {
174 qCDebug(FAVICONS_LOG) << "adding" << iconUrl << "to failed downloads due to error:" << errorString();
175 cache->addFailedDownload(iconUrl);
176 } else {
177 cache->removeFailedDownload(iconUrl);
178 }
180 emitResult();
181}
182
183void FavIconRequestJobPrivate::slotData(Job *job, const QByteArray &data)
184{
185 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job);
186 unsigned int oldSize = m_iconData.size();
187 // Size limit. Stop downloading if the file is huge.
188 // Testcase (as of june 2008, at least): http://planet-soc.com/favicon.ico, 136K and strange format.
189 // Another case: sites which redirect from "/favicon.ico" to "/" and return the main page.
190 if (oldSize > 0x10000) { // 65K
191 qCDebug(FAVICONS_LOG) << "Favicon too big, aborting download of" << tjob->url();
192 const QUrl iconUrl = tjob->url();
193 KIO::FavIconsCache::instance()->addFailedDownload(iconUrl);
194 tjob->kill(KJob::EmitResult);
195 } else {
196 m_iconData.resize(oldSize + data.size());
197 memcpy(m_iconData.data() + oldSize, data.data(), data.size());
198 }
199}
200
201#include "moc_faviconrequestjob.cpp"
virtual bool addSubjob(KJob *job)
virtual bool removeSubjob(KJob *job)
QUrl hostUrl() const
Returns the URL passed to the constructor.
void setIconUrl(const QUrl &iconUrl)
setIconUrl allows to set, for a specific URL, a different icon URL than the default one for the host ...
~FavIconRequestJob() override
Destructor.
FavIconRequestJob(const QUrl &hostUrl, KIO::LoadType reload=KIO::NoReload, QObject *parent=nullptr)
FavIconRequestJob constructor.
QString iconFile() const
Returns the full local path to the icon from the cache.
The base class for all jobs.
Definition job_base.h:45
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the worker.
Definition job.cpp:221
const QUrl & url() const
Returns the SimpleJob's URL.
Definition simplejob.cpp:70
The transfer job pumps data into and/or out of a KIO worker.
Definition transferjob.h:26
void data(KIO::Job *job, const QByteArray &data)
Data from the worker has arrived.
void setErrorText(const QString &errorText)
virtual QString errorString() const
void emitResult()
int error() const
void setError(int errorCode)
bool kill(KJob::KillVerbosity verbosity=KJob::Quietly)
QString i18n(const char *text, const TYPE &arg...)
A namespace for KIO globals.
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Get (means: read).
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ ERR_WORKER_DEFINED
For worker specified errors that can be rich text.
Definition global.h:184
const QList< QKeySequence > & reload()
char * data()
void resize(int size)
int size() const const
QDate currentDate()
qint64 daysTo(const QDate &d) const const
bool isNull() const const
bool save(const QString &fileName, const char *format, int quality) const const
QMap::iterator insert(const Key &key, const T &value)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QueuedConnection
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:45:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.