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