00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "favicons.h"
00021 #include "favicons_adaptor.h"
00022
00023 #include <time.h>
00024
00025 #include <QBuffer>
00026 #include <QFile>
00027 #include <QtCore/QCache>
00028 #include <QImage>
00029 #include <QTimer>
00030 #include <QImageReader>
00031
00032 #include <kicontheme.h>
00033 #include <kconfig.h>
00034 #include <kstandarddirs.h>
00035 #include <kio/job.h>
00036 #include <kconfiggroup.h>
00037 #include <kdebug.h>
00038 #include <kpluginfactory.h>
00039 #include <kpluginloader.h>
00040
00041 K_PLUGIN_FACTORY(FavIconsFactory,
00042 registerPlugin<FavIconsModule>();
00043 )
00044 K_EXPORT_PLUGIN(FavIconsFactory("favicons"))
00045
00046 static QString simplifyURL(const KUrl &url)
00047 {
00048
00049 QString result = url.host() + url.path();
00050 for (int i = 0; i < result.length(); ++i)
00051 if (result[i] == '=')
00052 result[i] = '_';
00053 return result;
00054 }
00055
00056 static QString iconNameFromURL(const KUrl &iconURL)
00057 {
00058 if (iconURL.path() == "/favicon.ico")
00059 return iconURL.host();
00060
00061 QString result = simplifyURL(iconURL);
00062
00063 for (int i = 0; i < result.length(); ++i)
00064 if (result[i] == '/')
00065 result[i] = '_';
00066
00067 QString ext = result.right(4);
00068 if (ext == ".ico" || ext == ".png" || ext == ".xpm")
00069 result.remove(result.length() - 4, 4);
00070
00071 return result;
00072 }
00073
00074 struct FavIconsModulePrivate
00075 {
00076 virtual ~FavIconsModulePrivate() { delete config; }
00077
00078 struct DownloadInfo
00079 {
00080 QString hostOrURL;
00081 bool isHost;
00082 QByteArray iconData;
00083 };
00084 QString makeIconName(const DownloadInfo& download, const KUrl& iconURL)
00085 {
00086 QString iconName;
00087 if (download.isHost)
00088 iconName = download.hostOrURL;
00089 else
00090 iconName = iconNameFromURL(iconURL);
00091
00092 return "favicons/" + iconName;
00093 }
00094
00095 QMap<KJob *, DownloadInfo> downloads;
00096 QStringList failedDownloads;
00097 KConfig *config;
00098 QList<KIO::Job*> killJobs;
00099 KIO::MetaData metaData;
00100 QString faviconsDir;
00101 QCache<QString,QString> faviconsCache;
00102 };
00103
00104 FavIconsModule::FavIconsModule(QObject* parent, const QList<QVariant>&)
00105 : KDEDModule(parent)
00106 {
00107
00108 d = new FavIconsModulePrivate;
00109 d->faviconsDir = KGlobal::dirs()->saveLocation( "cache", "favicons/" );
00110 d->faviconsDir.truncate(d->faviconsDir.length()-9);
00111 d->metaData.insert("ssl_no_client_cert", "TRUE");
00112 d->metaData.insert("ssl_no_ui", "TRUE");
00113 d->metaData.insert("UseCache", "false");
00114 d->metaData.insert("cookies", "none");
00115 d->metaData.insert("no-auth", "true");
00116 d->config = new KConfig(KStandardDirs::locateLocal("data", "konqueror/faviconrc"));
00117
00118 new FavIconsAdaptor( this );
00119 }
00120
00121 FavIconsModule::~FavIconsModule()
00122 {
00123 delete d;
00124 }
00125
00126 static QString removeSlash(QString result)
00127 {
00128 for (unsigned int i = result.length() - 1; i > 0; --i)
00129 if (result[i] != '/')
00130 {
00131 result.truncate(i + 1);
00132 break;
00133 }
00134
00135 return result;
00136 }
00137
00138
00139 QString FavIconsModule::iconForUrl(const KUrl &url)
00140 {
00141 if (url.host().isEmpty())
00142 return QString();
00143
00144 QString icon;
00145 QString simplifiedURL = simplifyURL(url);
00146
00147 QString *iconURL = d->faviconsCache[ removeSlash(simplifiedURL) ];
00148 if (iconURL)
00149 icon = *iconURL;
00150 else
00151 icon = d->config->group(QString()).readEntry( removeSlash(simplifiedURL), QString() );
00152
00153 if (!icon.isEmpty())
00154 icon = iconNameFromURL(KUrl( icon ));
00155 else
00156 icon = url.host();
00157
00158 icon = "favicons/" + icon;
00159
00160 if (QFile::exists(d->faviconsDir+icon+".png"))
00161 return icon;
00162
00163 return QString();
00164 }
00165
00166 bool FavIconsModule::isIconOld(const QString &icon)
00167 {
00168 struct stat st;
00169 if (stat(QFile::encodeName(icon), &st) != 0)
00170 return true;
00171
00172 return (time(0) - st.st_mtime) > 604800;
00173 }
00174
00175 void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL)
00176 {
00177 const QString simplifiedURL = simplifyURL(url);
00178
00179 d->faviconsCache.insert(removeSlash(simplifiedURL), new QString(iconURL.url()) );
00180
00181 const QString iconName = "favicons/" + iconNameFromURL(iconURL);
00182 const QString iconFile = d->faviconsDir + iconName + ".png";
00183
00184 if (!isIconOld(iconFile)) {
00185 emit iconChanged(false, url.url(), iconName);
00186 return;
00187 }
00188
00189 startDownload(url.url(), false, iconURL);
00190 }
00191
00192 void FavIconsModule::downloadHostIcon(const KUrl &url)
00193 {
00194 const QString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png";
00195 if (!isIconOld(iconFile))
00196 return;
00197
00198 startDownload(url.host(), true, KUrl(url, "/favicon.ico"));
00199 }
00200
00201 void FavIconsModule::startDownload(const QString &hostOrURL, bool isHost, const KUrl &iconURL)
00202 {
00203 if (d->failedDownloads.contains(iconURL.url())) {
00204 return;
00205 }
00206
00207 KIO::Job *job = KIO::get(iconURL, KIO::NoReload, KIO::HideProgressInfo);
00208 job->addMetaData(d->metaData);
00209 job->addMetaData("errorPage", "false");
00210 connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)), SLOT(slotData(KIO::Job *, const QByteArray &)));
00211 connect(job, SIGNAL(result(KJob *)), SLOT(slotResult(KJob *)));
00212 connect(job, SIGNAL(infoMessage(KJob *, const QString &, const QString &)), SLOT(slotInfoMessage(KJob *, const QString &)));
00213 FavIconsModulePrivate::DownloadInfo download;
00214 download.hostOrURL = hostOrURL;
00215 download.isHost = isHost;
00216 d->downloads.insert(job, download);
00217 }
00218
00219 void FavIconsModule::slotData(KIO::Job *job, const QByteArray &data)
00220 {
00221 KIO::TransferJob* tjob = static_cast<KIO::TransferJob*>(job);
00222 FavIconsModulePrivate::DownloadInfo &download = d->downloads[job];
00223 unsigned int oldSize = download.iconData.size();
00224
00225
00226 if (oldSize > 0x10000) {
00227 kDebug() << "Favicon too big, aborting download of" << tjob->url();
00228 d->killJobs.append(job);
00229 QTimer::singleShot(0, this, SLOT(slotKill()));
00230 const KUrl iconURL = tjob->url();
00231 d->failedDownloads.append(iconURL.url());
00232 }
00233 download.iconData.resize(oldSize + data.size());
00234 memcpy(download.iconData.data() + oldSize, data.data(), data.size());
00235 }
00236
00237 void FavIconsModule::slotResult(KJob *job)
00238 {
00239 KIO::TransferJob* tjob = static_cast<KIO::TransferJob*>(job);
00240 FavIconsModulePrivate::DownloadInfo download = d->downloads[job];
00241 d->killJobs.removeAll(tjob);
00242 d->downloads.remove(job);
00243 const KUrl iconURL = tjob->url();
00244 QString iconName;
00245 if (!job->error())
00246 {
00247 QBuffer buffer(&download.iconData);
00248 buffer.open(QIODevice::ReadOnly);
00249 QImageReader ir( &buffer );
00250 QSize desired( 16,16 );
00251 if( ir.canRead() ) {
00252
00253 while( ir.imageCount() > 1
00254 && ir.currentImageRect() != QRect(0, 0, desired.width(), desired.height())) {
00255 if (!ir.jumpToNextImage()) {
00256 break;
00257 }
00258 }
00259 ir.setScaledSize( desired );
00260 QImage img = ir.read();
00261 if( !img.isNull() ) {
00262 iconName = d->makeIconName(download, iconURL);
00263 if( !img.save( d->faviconsDir + iconName + ".png", "PNG" ) )
00264 iconName.clear();
00265 else if (!download.isHost)
00266 d->config->group(QString()).writeEntry( removeSlash(download.hostOrURL), iconURL.url());
00267 }
00268 }
00269 }
00270 if (iconName.isEmpty())
00271 d->failedDownloads.append(iconURL.url());
00272
00273 emit iconChanged(download.isHost, download.hostOrURL, iconName);
00274 }
00275
00276 void FavIconsModule::slotInfoMessage(KJob *job, const QString &msg)
00277 {
00278 emit infoMessage(static_cast<KIO::TransferJob *>( job )->url().url(), msg);
00279 }
00280
00281 void FavIconsModule::slotKill()
00282 {
00283 Q_FOREACH(KIO::Job* job, d->killJobs)
00284 job->kill();
00285 d->killJobs.clear();
00286 }
00287
00288 #include "favicons.moc"
00289
00290