/* 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 #include #include #include #include #include #include #include // DO NOT REMOVE, otherwise bool marshalling breaks #include #include #include #include #include #include "favicons.moc" struct FaviconsModulePrivate { virtual ~FaviconsModulePrivate() { delete config; } struct DownloadInfo { TQString hostOrURL; bool isHost; TQByteArray iconData; }; TQMap downloads; TQStringList failedDownloads; KSimpleConfig *config; TQPtrList killJobs; KIO::MetaData metaData; TQString faviconsDir; TQCache faviconsCache; }; FaviconsModule::FaviconsModule(const TQCString &obj) : KDEDModule(obj) { // create our favicons folder so that KIconLoader knows about it d = new FaviconsModulePrivate; d->faviconsDir = KGlobal::dirs()->saveLocation( "cache", "favicons/" ); d->faviconsDir.truncate(d->faviconsDir.length()-9); // Strip off "favicons/" d->metaData.insert("ssl_no_client_cert", "TRUE"); d->metaData.insert("ssl_militant", "TRUE"); d->metaData.insert("UseCache", "false"); d->metaData.insert("cookies", "none"); d->metaData.insert("no-auth", "true"); d->config = new KSimpleConfig(locateLocal("data", "konqueror/faviconrc")); d->killJobs.setAutoDelete(true); d->faviconsCache.setAutoDelete(true); } FaviconsModule::~FaviconsModule() { delete d; } TQString removeSlash(TQString result) { for (unsigned int i = result.length() - 1; i > 0; --i) if (result[i] != '/') { result.truncate(i + 1); break; } return result; } TQString FaviconsModule::iconForURL(const KURL &url) { if (url.host().isEmpty()) return TQString::null; TQString icon; TQString simplifiedURL = simplifyURL(url); TQString *iconURL = d->faviconsCache.find( removeSlash(simplifiedURL) ); if (iconURL) icon = *iconURL; else icon = d->config->readEntry( removeSlash(simplifiedURL) ); if (!icon.isEmpty()) icon = iconNameFromURL(KURL( icon )); else icon = url.host(); icon = "favicons/" + icon; if (TQFile::exists(d->faviconsDir+icon+".png")) return icon; return TQString::null; } TQString FaviconsModule::simplifyURL(const KURL &url) { // splat any = in the URL so it can be safely used as a config key TQString result = url.host() + url.path(); for (unsigned int i = 0; i < result.length(); ++i) if (result[i] == '=') result[i] = '_'; return result; } TQString FaviconsModule::iconNameFromURL(const KURL &iconURL) { if (iconURL.path() == "/favicon.ico") return iconURL.host(); TQString result = simplifyURL(iconURL); // splat / so it can be safely used as a file name for (unsigned int i = 0; i < result.length(); ++i) if (result[i] == '/') result[i] = '_'; TQString ext = result.right(4); if (ext == ".ico" || ext == ".png" || ext == ".xpm") result.remove(result.length() - 4, 4); return result; } bool FaviconsModule::isIconOld(const TQString &icon) { struct stat st; if (stat(TQFile::encodeName(icon), &st) != 0) return true; // Trigger a new download on error return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week) } void FaviconsModule::setIconForURL(const KURL &url, const KURL &iconURL) { TQString simplifiedURL = simplifyURL(url); d->faviconsCache.insert(removeSlash(simplifiedURL), new TQString(iconURL.url()) ); TQString iconName = "favicons/" + iconNameFromURL(iconURL); TQString iconFile = d->faviconsDir + iconName + ".png"; if (!isIconOld(iconFile)) { emit iconChanged(false, simplifiedURL, iconName); return; } startDownload(simplifiedURL, false, iconURL); } void FaviconsModule::downloadHostIcon(const KURL &url) { TQString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png"; if (!isIconOld(iconFile)) return; startDownload(url.host(), true, KURL(url, "/favicon.ico")); } void FaviconsModule::startDownload(const TQString &hostOrURL, bool isHost, const KURL &iconURL) { if (d->failedDownloads.contains(iconURL.url())) return; KIO::Job *job = KIO::get(iconURL, false, false); job->addMetaData(d->metaData); connect(job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)), TQT_SLOT(slotData(KIO::Job *, const TQByteArray &))); connect(job, TQT_SIGNAL(result(KIO::Job *)), TQT_SLOT(slotResult(KIO::Job *))); connect(job, TQT_SIGNAL(infoMessage(KIO::Job *, const TQString &)), TQT_SLOT(slotInfoMessage(KIO::Job *, const TQString &))); FaviconsModulePrivate::DownloadInfo download; download.hostOrURL = hostOrURL; download.isHost = isHost; d->downloads.insert(job, download); } void FaviconsModule::slotData(KIO::Job *job, const TQByteArray &data) { FaviconsModulePrivate::DownloadInfo &download = d->downloads[job]; unsigned int oldSize = download.iconData.size(); if (oldSize > 0x10000) { d->killJobs.append(job); TQTimer::singleShot(0, this, TQT_SLOT(slotKill())); } download.iconData.resize(oldSize + data.size()); memcpy(download.iconData.data() + oldSize, data.data(), data.size()); } void FaviconsModule::slotResult(KIO::Job *job) { FaviconsModulePrivate::DownloadInfo download = d->downloads[job]; d->downloads.remove(job); KURL iconURL = static_cast(job)->url(); TQString iconName; if (!job->error()) { TQBuffer buffer(download.iconData); buffer.open(IO_ReadOnly); TQImageIO io; io.setIODevice(TQT_TQIODEVICE(&buffer)); io.setParameters("size=16"); // Check here too, the job might have had no error, but the downloaded // file contains just a 404 message sent with a 200 status. // microsoft.com does that... (malte) if (io.read()) { // Some sites have nasty 32x32 icons, according to the MS docs // IE ignores them, well, we scale them, otherwise the location // combo / menu will look quite ugly if (io.image().width() != KIcon::SizeSmall || io.image().height() != KIcon::SizeSmall) io.setImage(io.image().smoothScale(KIcon::SizeSmall, KIcon::SizeSmall)); if (download.isHost) iconName = download.hostOrURL; else iconName = iconNameFromURL(iconURL); iconName = "favicons/" + iconName; io.setIODevice(0); io.setFileName(d->faviconsDir + iconName + ".png"); io.setFormat("PNG"); if (!io.write()) iconName = TQString::null; else if (!download.isHost) d->config->writeEntry( removeSlash(download.hostOrURL), iconURL.url()); } } if (iconName.isEmpty()) d->failedDownloads.append(iconURL.url()); emit iconChanged(download.isHost, download.hostOrURL, iconName); } void FaviconsModule::slotInfoMessage(KIO::Job *job, const TQString &msg) { emit infoMessage(static_cast( job )->url(), msg); } void FaviconsModule::slotKill() { d->killJobs.clear(); } extern "C" { KDE_EXPORT KDEDModule *create_favicons(const TQCString &obj) { KImageIO::registerFormats(); return new FaviconsModule(obj); } } // vim: ts=4 sw=4 et