/* This file is part of the KDE project Copyright (C) 2001 Malte Starostik This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "favicons.h" #include "favicons_adaptor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(FavIconsFactory, registerPlugin();) K_EXPORT_PLUGIN(FavIconsFactory("favicons")) static bool isIconOld(const QString &iconFile) { const QFileInfo iconInfo(iconFile); const QDateTime iconLastModified = iconInfo.lastModified(); if (!iconInfo.isFile() || !iconLastModified.isValid()) { // kDebug() << "isIconOld" << iconFile << "yes, no such file"; return true; // Trigger a new download on error } // kDebug() << "isIconOld" << iconFile << "?"; const QDateTime currentTime = QDateTime::currentDateTime(); return ((currentTime.toTime_t() - iconLastModified.toTime_t()) > 604800); // arbitrary value (one week) } static QString iconNameFromURL(const QString &url) { return QString::fromLatin1("favicons/%1").arg(KUrl(url).host()); } static QString iconFilePath(const QString &iconName) { return QString::fromLatin1("%1/%2.png").arg(KGlobal::dirs()->saveLocation("cache"), iconName); } static QString faviconFromUrl(const QString &url, const QLatin1String &extension) { KUrl faviconUrl(url); faviconUrl.setPath(QString::fromLatin1("/favicon.%1").arg(extension)); faviconUrl.setEncodedQuery(QByteArray()); return faviconUrl.url(); } class FavIconsModulePrivate { public: KUrl::List queuedDownloads; KUrl::List failedDownloads; KIO::MetaData metaData; }; FavIconsModule::FavIconsModule(QObject* parent, const QList &args) : KDEDModule(parent), d(new FavIconsModulePrivate()) { Q_UNUSED(args); d->metaData.insert(QLatin1String("cache"), "reload"); d->metaData.insert(QLatin1String("no-www-auth"), QLatin1String("true")); new FavIconsAdaptor(this); } FavIconsModule::~FavIconsModule() { delete d; } QString FavIconsModule::iconForUrl(const QString &url) { if (url.isEmpty()) { return QString(); } const QString iconName = iconNameFromURL(url); const QString iconFile = iconFilePath(iconName); if (QFile::exists(iconFile)) { kDebug() << "URL" << url << "icon" << iconName; return iconName; } return QString(); } void FavIconsModule::downloadUrlIcon(const QString &url) { const QString iconName = iconNameFromURL(url); const QString iconFile = iconFilePath(iconName); if (!isIconOld(iconFile)) { kDebug() << "Icon for URL already downloaded" << url; emit iconChanged(url, iconName); return; } startDownload(url, iconFile); } void FavIconsModule::forceDownloadUrlIcon(const QString &url) { d->failedDownloads.removeAll(url); // force a download to happen const QString iconName = iconNameFromURL(url); const QString iconFile = iconFilePath(iconName); QFile::remove(iconFile); downloadUrlIcon(url); } void FavIconsModule::startDownload(const QString &url, const QString &iconFile) { const QString iconName = iconNameFromURL(url); if (d->queuedDownloads.contains(iconName)) { kDebug() << "Icon download queued for" << url; return; } if (d->failedDownloads.contains(url)) { kDebug() << "Icon download already failed for" << url; emit iconChanged(url, QString()); return; } d->queuedDownloads.append(iconName); const QString faviconUrl = faviconFromUrl(url, QLatin1String("ico")); startJob(url, faviconUrl, iconFile); } void FavIconsModule::startJob(const QString &url, const QString &faviconUrl, const QString &iconFile) { kDebug() << "Downloading" << faviconUrl << "as" << iconFile; KIO::StoredTransferJob *tjob = KIO::storedGet(faviconUrl, KIO::NoReload, KIO::HideProgressInfo); tjob->setAutoDelete(false); tjob->addMetaData(d->metaData); tjob->setProperty("faviconsUrl", url); tjob->setProperty("faviconsFile", iconFile); connect(tjob, SIGNAL(finished(KJob*)), SLOT(slotFinished(KJob*))); tjob->start(); } void FavIconsModule::slotFinished(KJob *kjob) { KIO::StoredTransferJob* tjob = qobject_cast(kjob); const QString faviconsUrl = tjob->property("faviconsUrl").toString(); const QString faviconsFile = tjob->property("faviconsFile").toString(); if (tjob->error()) { const QString tjoburl = tjob->url().url(); if (tjoburl.endsWith(QLatin1String(".ico"))) { const QString faviconUrl = faviconFromUrl(faviconsUrl, QLatin1String("png")); kDebug() << "Attempting alternative icon" << faviconUrl; tjob->deleteLater(); startJob(faviconsUrl, faviconUrl, faviconsFile); return; } kWarning() << "Job error" << tjob->errorString(); downloadError(faviconsUrl); tjob->deleteLater(); return; } QBuffer buffer; buffer.setData(tjob->data()); buffer.open(QIODevice::ReadOnly); QImageReader ir(&buffer); if (!ir.canRead()) { kWarning() << "Image reader cannot read the data" << ir.errorString(); downloadError(faviconsUrl); tjob->deleteLater(); return; } const QImage img = ir.read(); tjob->deleteLater(); if (img.isNull()) { kWarning() << "Image reader returned null image" << ir.errorString(); downloadError(faviconsUrl); return; } if (!img.save(faviconsFile, "PNG")) { kWarning() << "Error saving image to" << faviconsFile; downloadError(faviconsUrl); return; } downloadSuccess(faviconsUrl); } void FavIconsModule::downloadSuccess(const QString &url) { kDebug() << "Downloaded icon for" << url; const QString iconName = iconNameFromURL(url); d->queuedDownloads.removeAll(iconName); emit iconChanged(url, iconName); } void FavIconsModule::downloadError(const QString &url) { if (!d->failedDownloads.contains(url)) { kDebug() << "Adding" << url << "to failed downloads"; d->failedDownloads.append(url); } const QString iconName = iconNameFromURL(url); d->queuedDownloads.removeAll(iconName); emit iconChanged(url, QString()); } #include "moc_favicons.cpp"