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 job->addMetaData(metaData);
123 QObject::connect(job, &KIO::TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) {
124 d->slotData(job, data);
125 });
126 addSubjob(job);
127}
128
129void FavIconRequestJob::slotResult(KJob *job)
130{
131 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job);
132 const QUrl &iconUrl = tjob->url();
133 KIO::FavIconsCache *cache = KIO::FavIconsCache::instance();
134 if (!job->error()) {
135 QBuffer buffer(&d->m_iconData);
136 buffer.open(QIODevice::ReadOnly);
137 QImageReader ir(&buffer);
138 QSize desired(16, 16);
139 if (ir.canRead()) {
140 while (ir.imageCount() > 1 && ir.currentImageRect() != QRect(0, 0, desired.width(), desired.height())) {
141 if (!ir.jumpToNextImage()) {
142 break;
143 }
144 }
145 ir.setScaledSize(desired);
146 const QImage img = ir.read();
147 if (!img.isNull()) {
148 cache->ensureCacheExists();
149 const QString localPath = cache->cachePathForIconUrl(iconUrl);
150 qCDebug(FAVICONS_LOG) << "Saving image to" << localPath;
151 QSaveFile saveFile(localPath);
152 if (saveFile.open(QIODevice::WriteOnly) && img.save(&saveFile, "PNG") && saveFile.commit()) {
153 d->m_iconFile = localPath;
154 } else {
155 setError(KIO::ERR_CANNOT_WRITE);
156 setErrorText(i18n("Error saving image to %1", localPath));
157 }
158 } else {
159 qCDebug(FAVICONS_LOG) << "QImageReader read() returned a null image";
160 }
161 } else {
162 qCDebug(FAVICONS_LOG) << "QImageReader canRead returned false";
163 }
164 } else if (job->error() == KJob::KilledJobError) { // we killed it in slotData
166 setErrorText(i18n("Icon file too big, download aborted"));
167 } else {
168 setError(job->error());
169 setErrorText(job->errorString()); // not errorText(), because "this" is a KJob, with no errorString building logic
170 }
171 d->m_iconData.clear(); // release memory
172 if (d->m_iconFile.isEmpty()) {
173 qCDebug(FAVICONS_LOG) << "adding" << iconUrl << "to failed downloads due to error:" << errorString();
174 cache->addFailedDownload(iconUrl);
175 } else {
176 cache->removeFailedDownload(iconUrl);
177 }
179 emitResult();
180}
181
182void FavIconRequestJobPrivate::slotData(Job *job, const QByteArray &data)
183{
184 KIO::TransferJob *tjob = static_cast<KIO::TransferJob *>(job);
185 unsigned int oldSize = m_iconData.size();
186 // Size limit. Stop downloading if the file is huge.
187 // Testcase (as of june 2008, at least): http://planet-soc.com/favicon.ico, 136K and strange format.
188 // Another case: sites which redirect from "/favicon.ico" to "/" and return the main page.
189 if (oldSize > 0x10000) { // 65K
190 qCDebug(FAVICONS_LOG) << "Favicon too big, aborting download of" << tjob->url();
191 const QUrl iconUrl = tjob->url();
192 KIO::FavIconsCache::instance()->addFailedDownload(iconUrl);
193 tjob->kill(KJob::EmitResult);
194 } else {
195 m_iconData.resize(oldSize + data.size());
196 memcpy(m_iconData.data() + oldSize, data.data(), data.size());
197 }
198}
199
200#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.
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.
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(qsizetype newSize, char c)
qsizetype size() const const
QDate currentDate()
qint64 daysTo(QDate d) const const
bool isNull() const const
bool save(QIODevice *device, const char *format, int quality) const const
iterator insert(const Key &key, const T &value)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QueuedConnection
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 11:56:19 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.