add more spacing
[personal-kdebase.git] / apps / lib / konq / favicons / favicons.cpp
blob79224ea2f21799f7119ca18e36306b18e6e7f32d
1 /* This file is part of the KDE project
2 Copyright (C) 2001 Malte Starostik <malte@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "favicons.h"
21 #include "favicons_adaptor.h"
23 #include <time.h>
25 #include <QBuffer>
26 #include <QFile>
27 #include <QtCore/QCache>
28 #include <QImage>
29 #include <QTimer>
30 #include <QImageReader>
32 #include <kicontheme.h>
33 #include <kconfig.h>
34 #include <kstandarddirs.h>
35 #include <kio/job.h>
36 #include <kconfiggroup.h>
37 #include <kdebug.h>
38 #include <kpluginfactory.h>
39 #include <kpluginloader.h>
41 K_PLUGIN_FACTORY(FavIconsFactory,
42 registerPlugin<FavIconsModule>();
44 K_EXPORT_PLUGIN(FavIconsFactory("favicons"))
46 static QString simplifyURL(const KUrl &url)
48 // splat any = in the URL so it can be safely used as a config key
49 QString result = url.host() + url.path();
50 for (int i = 0; i < result.length(); ++i)
51 if (result[i] == '=')
52 result[i] = '_';
53 return result;
56 static QString iconNameFromURL(const KUrl &iconURL)
58 if (iconURL.path() == "/favicon.ico")
59 return iconURL.host();
61 QString result = simplifyURL(iconURL);
62 // splat / so it can be safely used as a file name
63 for (int i = 0; i < result.length(); ++i)
64 if (result[i] == '/')
65 result[i] = '_';
67 QString ext = result.right(4);
68 if (ext == ".ico" || ext == ".png" || ext == ".xpm")
69 result.remove(result.length() - 4, 4);
71 return result;
74 struct FavIconsModulePrivate
76 virtual ~FavIconsModulePrivate() { delete config; }
78 struct DownloadInfo
80 QString hostOrURL;
81 bool isHost;
82 QByteArray iconData;
84 QString makeIconName(const DownloadInfo& download, const KUrl& iconURL)
86 QString iconName;
87 if (download.isHost)
88 iconName = download.hostOrURL;
89 else
90 iconName = iconNameFromURL(iconURL);
92 return "favicons/" + iconName;
95 QMap<KJob *, DownloadInfo> downloads;
96 QStringList failedDownloads;
97 KConfig *config;
98 QList<KIO::Job*> killJobs;
99 KIO::MetaData metaData;
100 QString faviconsDir;
101 QCache<QString,QString> faviconsCache;
104 FavIconsModule::FavIconsModule(QObject* parent, const QList<QVariant>&)
105 : KDEDModule(parent)
107 // create our favicons folder so that KIconLoader knows about it
108 d = new FavIconsModulePrivate;
109 d->faviconsDir = KGlobal::dirs()->saveLocation( "cache", "favicons/" );
110 d->faviconsDir.truncate(d->faviconsDir.length()-9); // Strip off "favicons/"
111 d->metaData.insert("ssl_no_client_cert", "TRUE");
112 d->metaData.insert("ssl_no_ui", "TRUE");
113 d->metaData.insert("UseCache", "false");
114 d->metaData.insert("cookies", "none");
115 d->metaData.insert("no-auth", "true");
116 d->config = new KConfig(KStandardDirs::locateLocal("data", "konqueror/faviconrc"));
118 new FavIconsAdaptor( this );
121 FavIconsModule::~FavIconsModule()
123 delete d;
126 static QString removeSlash(QString result)
128 for (unsigned int i = result.length() - 1; i > 0; --i)
129 if (result[i] != '/')
131 result.truncate(i + 1);
132 break;
135 return result;
139 QString FavIconsModule::iconForUrl(const KUrl &url)
141 if (url.host().isEmpty())
142 return QString();
144 QString icon;
145 QString simplifiedURL = simplifyURL(url);
147 QString *iconURL = d->faviconsCache[ removeSlash(simplifiedURL) ];
148 if (iconURL)
149 icon = *iconURL;
150 else
151 icon = d->config->group(QString()).readEntry( removeSlash(simplifiedURL), QString() );
153 if (!icon.isEmpty())
154 icon = iconNameFromURL(KUrl( icon ));
155 else
156 icon = url.host();
158 icon = "favicons/" + icon;
160 if (QFile::exists(d->faviconsDir+icon+".png"))
161 return icon;
163 return QString();
166 bool FavIconsModule::isIconOld(const QString &icon)
168 struct stat st;
169 if (stat(QFile::encodeName(icon), &st) != 0)
170 return true; // Trigger a new download on error
172 return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week)
175 void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL)
177 const QString simplifiedURL = simplifyURL(url);
179 d->faviconsCache.insert(removeSlash(simplifiedURL), new QString(iconURL.url()) );
181 const QString iconName = "favicons/" + iconNameFromURL(iconURL);
182 const QString iconFile = d->faviconsDir + iconName + ".png";
184 if (!isIconOld(iconFile)) {
185 emit iconChanged(false, url.url(), iconName);
186 return;
189 startDownload(url.url(), false, iconURL);
192 void FavIconsModule::downloadHostIcon(const KUrl &url)
194 const QString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png";
195 if (!isIconOld(iconFile))
196 return;
198 startDownload(url.host(), true, KUrl(url, "/favicon.ico"));
201 void FavIconsModule::startDownload(const QString &hostOrURL, bool isHost, const KUrl &iconURL)
203 if (d->failedDownloads.contains(iconURL.url())) {
204 return;
207 KIO::Job *job = KIO::get(iconURL, KIO::NoReload, KIO::HideProgressInfo);
208 job->addMetaData(d->metaData);
209 job->addMetaData("errorPage", "false");
210 connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)), SLOT(slotData(KIO::Job *, const QByteArray &)));
211 connect(job, SIGNAL(result(KJob *)), SLOT(slotResult(KJob *)));
212 connect(job, SIGNAL(infoMessage(KJob *, const QString &, const QString &)), SLOT(slotInfoMessage(KJob *, const QString &)));
213 FavIconsModulePrivate::DownloadInfo download;
214 download.hostOrURL = hostOrURL;
215 download.isHost = isHost;
216 d->downloads.insert(job, download);
219 void FavIconsModule::slotData(KIO::Job *job, const QByteArray &data)
221 KIO::TransferJob* tjob = static_cast<KIO::TransferJob*>(job);
222 FavIconsModulePrivate::DownloadInfo &download = d->downloads[job];
223 unsigned int oldSize = download.iconData.size();
224 // Size limit. Stop downloading if the file is huge.
225 // Testcase (as of june 2008, at least): http://planet-soc.com/favicon.ico, 136K and strange format.
226 if (oldSize > 0x10000) {
227 kDebug() << "Favicon too big, aborting download of" << tjob->url();
228 d->killJobs.append(job);
229 QTimer::singleShot(0, this, SLOT(slotKill()));
230 const KUrl iconURL = tjob->url();
231 d->failedDownloads.append(iconURL.url());
233 download.iconData.resize(oldSize + data.size());
234 memcpy(download.iconData.data() + oldSize, data.data(), data.size());
237 void FavIconsModule::slotResult(KJob *job)
239 KIO::TransferJob* tjob = static_cast<KIO::TransferJob*>(job);
240 FavIconsModulePrivate::DownloadInfo download = d->downloads[job];
241 d->killJobs.removeAll(tjob);
242 d->downloads.remove(job);
243 const KUrl iconURL = tjob->url();
244 QString iconName;
245 if (!job->error())
247 QBuffer buffer(&download.iconData);
248 buffer.open(QIODevice::ReadOnly);
249 QImageReader ir( &buffer );
250 QSize desired( 16,16 );
251 if( ir.canRead() ) {
253 while( ir.imageCount() > 1
254 && ir.currentImageRect() != QRect(0, 0, desired.width(), desired.height())) {
255 if (!ir.jumpToNextImage()) {
256 break;
259 ir.setScaledSize( desired );
260 QImage img = ir.read();
261 if( !img.isNull() ) {
262 iconName = d->makeIconName(download, iconURL);
263 if( !img.save( d->faviconsDir + iconName + ".png", "PNG" ) )
264 iconName.clear();
265 else if (!download.isHost)
266 d->config->group(QString()).writeEntry( removeSlash(download.hostOrURL), iconURL.url());
270 if (iconName.isEmpty())
271 d->failedDownloads.append(iconURL.url());
273 emit iconChanged(download.isHost, download.hostOrURL, iconName);
276 void FavIconsModule::slotInfoMessage(KJob *job, const QString &msg)
278 emit infoMessage(static_cast<KIO::TransferJob *>( job )->url().url(), msg);
281 void FavIconsModule::slotKill()
283 Q_FOREACH(KIO::Job* job, d->killJobs)
284 job->kill();
285 d->killJobs.clear();
288 #include "favicons.moc"
290 // vim: ts=4 sw=4 et