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.
21 #include "favicons_adaptor.h"
27 #include <QtCore/QCache>
30 #include <QImageReader>
32 #include <kicontheme.h>
34 #include <kstandarddirs.h>
36 #include <kconfiggroup.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
)
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
)
67 QString ext
= result
.right(4);
68 if (ext
== ".ico" || ext
== ".png" || ext
== ".xpm")
69 result
.remove(result
.length() - 4, 4);
74 struct FavIconsModulePrivate
76 virtual ~FavIconsModulePrivate() { delete config
; }
84 QString
makeIconName(const DownloadInfo
& download
, const KUrl
& iconURL
)
88 iconName
= download
.hostOrURL
;
90 iconName
= iconNameFromURL(iconURL
);
92 return "favicons/" + iconName
;
95 QMap
<KJob
*, DownloadInfo
> downloads
;
96 QStringList failedDownloads
;
98 QList
<KIO::Job
*> killJobs
;
99 KIO::MetaData metaData
;
101 QCache
<QString
,QString
> faviconsCache
;
104 FavIconsModule::FavIconsModule(QObject
* parent
, const QList
<QVariant
>&)
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()
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);
139 QString
FavIconsModule::iconForUrl(const KUrl
&url
)
141 if (url
.host().isEmpty())
145 QString simplifiedURL
= simplifyURL(url
);
147 QString
*iconURL
= d
->faviconsCache
[ removeSlash(simplifiedURL
) ];
151 icon
= d
->config
->group(QString()).readEntry( removeSlash(simplifiedURL
), QString() );
154 icon
= iconNameFromURL(KUrl( icon
));
158 icon
= "favicons/" + icon
;
160 if (QFile::exists(d
->faviconsDir
+icon
+".png"))
166 bool FavIconsModule::isIconOld(const QString
&icon
)
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
);
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
))
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())) {
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();
247 QBuffer
buffer(&download
.iconData
);
248 buffer
.open(QIODevice::ReadOnly
);
249 QImageReader
ir( &buffer
);
250 QSize
desired( 16,16 );
253 while( ir
.imageCount() > 1
254 && ir
.currentImageRect() != QRect(0, 0, desired
.width(), desired
.height())) {
255 if (!ir
.jumpToNextImage()) {
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" ) )
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
)
288 #include "favicons.moc"