favicons.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 2001 Malte Starostik <malte@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <string.h> 00021 #include <time.h> 00022 00023 #include <tqbuffer.h> 00024 #include <tqfile.h> 00025 #include <tqcache.h> 00026 #include <tqimage.h> 00027 #include <tqtimer.h> 00028 00029 #include <kdatastream.h> // DO NOT REMOVE, otherwise bool marshalling breaks 00030 #include <kicontheme.h> 00031 #include <kimageio.h> 00032 #include <ksimpleconfig.h> 00033 #include <kstandarddirs.h> 00034 #include <tdeio/job.h> 00035 00036 #include "favicons.moc" 00037 00038 struct FaviconsModulePrivate 00039 { 00040 virtual ~FaviconsModulePrivate() { delete config; } 00041 00042 struct DownloadInfo 00043 { 00044 TQString hostOrURL; 00045 bool isHost; 00046 TQByteArray iconData; 00047 }; 00048 TQMap<TDEIO::Job *, DownloadInfo> downloads; 00049 TQStringList failedDownloads; 00050 KSimpleConfig *config; 00051 TQPtrList<TDEIO::Job> killJobs; 00052 TDEIO::MetaData metaData; 00053 TQString faviconsDir; 00054 TQCache<TQString> faviconsCache; 00055 }; 00056 00057 FaviconsModule::FaviconsModule(const TQCString &obj) 00058 : KDEDModule(obj) 00059 { 00060 // create our favicons folder so that TDEIconLoader knows about it 00061 d = new FaviconsModulePrivate; 00062 d->faviconsDir = TDEGlobal::dirs()->saveLocation( "cache", "favicons/" ); 00063 d->faviconsDir.truncate(d->faviconsDir.length()-9); // Strip off "favicons/" 00064 d->metaData.insert("ssl_no_client_cert", "TRUE"); 00065 d->metaData.insert("ssl_militant", "TRUE"); 00066 d->metaData.insert("UseCache", "false"); 00067 d->metaData.insert("cookies", "none"); 00068 d->metaData.insert("no-auth", "true"); 00069 d->config = new KSimpleConfig(locateLocal("data", "konqueror/faviconrc")); 00070 d->killJobs.setAutoDelete(true); 00071 d->faviconsCache.setAutoDelete(true); 00072 } 00073 00074 FaviconsModule::~FaviconsModule() 00075 { 00076 delete d; 00077 } 00078 00079 TQString removeSlash(TQString result) 00080 { 00081 for (unsigned int i = result.length() - 1; i > 0; --i) 00082 if (result[i] != '/') 00083 { 00084 result.truncate(i + 1); 00085 break; 00086 } 00087 00088 return result; 00089 } 00090 00091 00092 TQString FaviconsModule::iconForURL(const KURL &url) 00093 { 00094 if (url.host().isEmpty()) 00095 return TQString::null; 00096 00097 TQString icon; 00098 TQString simplifiedURL = simplifyURL(url); 00099 00100 TQString *iconURL = d->faviconsCache.find( removeSlash(simplifiedURL) ); 00101 if (iconURL) 00102 icon = *iconURL; 00103 else 00104 icon = d->config->readEntry( removeSlash(simplifiedURL) ); 00105 00106 if (!icon.isEmpty()) 00107 icon = iconNameFromURL(KURL( icon )); 00108 else 00109 icon = url.host(); 00110 00111 icon = "favicons/" + icon; 00112 00113 if (TQFile::exists(d->faviconsDir+icon+".png")) 00114 return icon; 00115 00116 return TQString::null; 00117 } 00118 00119 TQString FaviconsModule::simplifyURL(const KURL &url) 00120 { 00121 // splat any = in the URL so it can be safely used as a config key 00122 TQString result = url.host() + url.path(); 00123 for (unsigned int i = 0; i < result.length(); ++i) 00124 if (result[i] == '=') 00125 result[i] = '_'; 00126 return result; 00127 } 00128 00129 TQString FaviconsModule::iconNameFromURL(const KURL &iconURL) 00130 { 00131 if (iconURL.path() == "/favicon.ico") 00132 return iconURL.host(); 00133 00134 TQString result = simplifyURL(iconURL); 00135 // splat / so it can be safely used as a file name 00136 for (unsigned int i = 0; i < result.length(); ++i) 00137 if (result[i] == '/') 00138 result[i] = '_'; 00139 00140 TQString ext = result.right(4); 00141 if (ext == ".ico" || ext == ".png" || ext == ".xpm") 00142 result.remove(result.length() - 4, 4); 00143 00144 return result; 00145 } 00146 00147 bool FaviconsModule::isIconOld(const TQString &icon) 00148 { 00149 struct stat st; 00150 if (stat(TQFile::encodeName(icon), &st) != 0) 00151 return true; // Trigger a new download on error 00152 00153 return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week) 00154 } 00155 00156 void FaviconsModule::setIconForURL(const KURL &url, const KURL &iconURL) 00157 { 00158 TQString simplifiedURL = simplifyURL(url); 00159 00160 d->faviconsCache.insert(removeSlash(simplifiedURL), new TQString(iconURL.url()) ); 00161 00162 TQString iconName = "favicons/" + iconNameFromURL(iconURL); 00163 TQString iconFile = d->faviconsDir + iconName + ".png"; 00164 00165 if (!isIconOld(iconFile)) { 00166 emit iconChanged(false, simplifiedURL, iconName); 00167 return; 00168 } 00169 00170 startDownload(simplifiedURL, false, iconURL); 00171 } 00172 00173 void FaviconsModule::downloadHostIcon(const KURL &url) 00174 { 00175 TQString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png"; 00176 if (!isIconOld(iconFile)) 00177 return; 00178 00179 startDownload(url.host(), true, KURL(url, "/favicon.ico")); 00180 } 00181 00182 void FaviconsModule::startDownload(const TQString &hostOrURL, bool isHost, const KURL &iconURL) 00183 { 00184 if (d->failedDownloads.contains(iconURL.url())) 00185 return; 00186 00187 TDEIO::Job *job = TDEIO::get(iconURL, false, false); 00188 job->addMetaData(d->metaData); 00189 connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), TQT_SLOT(slotData(TDEIO::Job *, const TQByteArray &))); 00190 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), TQT_SLOT(slotResult(TDEIO::Job *))); 00191 connect(job, TQT_SIGNAL(infoMessage(TDEIO::Job *, const TQString &)), TQT_SLOT(slotInfoMessage(TDEIO::Job *, const TQString &))); 00192 FaviconsModulePrivate::DownloadInfo download; 00193 download.hostOrURL = hostOrURL; 00194 download.isHost = isHost; 00195 d->downloads.insert(job, download); 00196 } 00197 00198 void FaviconsModule::slotData(TDEIO::Job *job, const TQByteArray &data) 00199 { 00200 FaviconsModulePrivate::DownloadInfo &download = d->downloads[job]; 00201 unsigned int oldSize = download.iconData.size(); 00202 if (oldSize > 0x10000) 00203 { 00204 d->killJobs.append(job); 00205 TQTimer::singleShot(0, this, TQT_SLOT(slotKill())); 00206 } 00207 download.iconData.resize(oldSize + data.size()); 00208 memcpy(download.iconData.data() + oldSize, data.data(), data.size()); 00209 } 00210 00211 void FaviconsModule::slotResult(TDEIO::Job *job) 00212 { 00213 FaviconsModulePrivate::DownloadInfo download = d->downloads[job]; 00214 d->downloads.remove(job); 00215 KURL iconURL = static_cast<TDEIO::TransferJob *>(job)->url(); 00216 TQString iconName; 00217 if (!job->error()) 00218 { 00219 TQBuffer buffer(download.iconData); 00220 buffer.open(IO_ReadOnly); 00221 TQImageIO io; 00222 io.setIODevice(TQT_TQIODEVICE(&buffer)); 00223 io.setParameters("size=16"); 00224 // Check here too, the job might have had no error, but the downloaded 00225 // file contains just a 404 message sent with a 200 status. 00226 // microsoft.com does that... (malte) 00227 if (io.read()) 00228 { 00229 // Some sites have nasty 32x32 icons, according to the MS docs 00230 // IE ignores them, well, we scale them, otherwise the location 00231 // combo / menu will look quite ugly 00232 if (io.image().width() != TDEIcon::SizeSmall || io.image().height() != TDEIcon::SizeSmall) 00233 io.setImage(io.image().smoothScale(TDEIcon::SizeSmall, TDEIcon::SizeSmall)); 00234 00235 if (download.isHost) 00236 iconName = download.hostOrURL; 00237 else 00238 iconName = iconNameFromURL(iconURL); 00239 00240 iconName = "favicons/" + iconName; 00241 00242 io.setIODevice(0); 00243 io.setFileName(d->faviconsDir + iconName + ".png"); 00244 io.setFormat("PNG"); 00245 if (!io.write()) 00246 iconName = TQString::null; 00247 else if (!download.isHost) 00248 d->config->writeEntry( removeSlash(download.hostOrURL), iconURL.url()); 00249 } 00250 } 00251 if (iconName.isEmpty()) 00252 d->failedDownloads.append(iconURL.url()); 00253 00254 emit iconChanged(download.isHost, download.hostOrURL, iconName); 00255 } 00256 00257 void FaviconsModule::slotInfoMessage(TDEIO::Job *job, const TQString &msg) 00258 { 00259 emit infoMessage(static_cast<TDEIO::TransferJob *>( job )->url(), msg); 00260 } 00261 00262 void FaviconsModule::slotKill() 00263 { 00264 d->killJobs.clear(); 00265 } 00266 00267 extern "C" { 00268 KDE_EXPORT KDEDModule *create_favicons(const TQCString &obj) 00269 { 00270 KImageIO::registerFormats(); 00271 return new FaviconsModule(obj); 00272 } 00273 } 00274 00275 // vim: ts=4 sw=4 et