KIO

faviconrequestjob.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2016 David Faure <[email protected]>
4  SPDX-FileCopyrightText: 2001 Malte Starostik <[email protected]>
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 
28 using namespace KIO;
29 
30 static 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 
43 class KIO::FavIconRequestJobPrivate
44 {
45 public:
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 
62 FavIconRequestJob::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 
71 void FavIconRequestJob::setIconUrl(const QUrl &iconUrl)
72 {
73  d->m_iconUrl = iconUrl;
74 }
75 
77 {
78  return d->m_iconFile;
79 }
80 
82 {
83  return d->m_hostUrl;
84 }
85 
86 void 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 
130 void 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 
183 void 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"
@ ERR_WORKER_DEFINED
For worker specified errors that can be rich text.
Definition: global.h:281
void setIconUrl(const QUrl &iconUrl)
setIconUrl allows to set, for a specific URL, a different icon URL than the default one for the host ...
void setErrorText(const QString &errorText)
const QList< QKeySequence > & reload()
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:228
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Get (means: read).
const QUrl & url() const
Returns the SimpleJob's URL.
Definition: simplejob.cpp:70
bool kill(KillVerbosity verbosity=Quietly)
QUrl hostUrl() const
Returns the URL passed to the constructor.
virtual bool removeSubjob(KJob *job)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QMap::iterator insert(const Key &key, const T &value)
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
bool isNull() const const
virtual bool addSubjob(KJob *job)
FavIconRequestJob(const QUrl &hostUrl, KIO::LoadType reload=KIO::NoReload, QObject *parent=nullptr)
FavIconRequestJob constructor.
QueuedConnection
QDate currentDate()
QString iconFile() const
Returns the full local path to the icon from the cache.
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)
A namespace for KIO globals.
int size() const const
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition: job_base.h:274
void emitResult()
virtual QString errorString() const
int error() const
void data(KIO::Job *job, const QByteArray &data)
Data from the worker has arrived.
bool save(const QString &fileName, const char *format, int quality) const const
void setError(int errorCode)
qint64 daysTo(const QDate &d) const const
~FavIconRequestJob() override
Destructor.
char * data()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 6 2023 04:00:00 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.