KIO
proxyscout.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (c) 2003 Malte Starostik <malte@kde.org> 00003 Copyright (c) 2011 Dawit Alemayehu <adawit@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "proxyscout.h" 00022 00023 #include "config-kpac.h" 00024 00025 #include "discovery.h" 00026 #include "script.h" 00027 00028 #include <kdebug.h> 00029 #include <klocale.h> 00030 #include <knotification.h> 00031 #include <kprotocolmanager.h> 00032 #include <kpluginfactory.h> 00033 #include <kpluginloader.h> 00034 00035 #ifndef KPAC_NO_SOLID 00036 #include <solid/networking.h> 00037 #endif 00038 00039 #include <QtCore/QFileSystemWatcher> 00040 00041 #include <cstdlib> 00042 #include <ctime> 00043 00044 K_PLUGIN_FACTORY(ProxyScoutFactory, 00045 registerPlugin<KPAC::ProxyScout>(); 00046 ) 00047 K_EXPORT_PLUGIN(ProxyScoutFactory("KProxyScoutd")) 00048 00049 namespace KPAC 00050 { 00051 enum ProxyType { 00052 Unknown = -1, 00053 Proxy, 00054 Socks, 00055 Direct 00056 }; 00057 00058 static ProxyType proxyTypeFor(const QString& mode) 00059 { 00060 if (mode.compare(QLatin1String("PROXY"), Qt::CaseInsensitive) == 0) 00061 return Proxy; 00062 00063 if (mode.compare(QLatin1String("DIRECT"), Qt::CaseInsensitive) == 0) 00064 return Direct; 00065 00066 if (mode.compare(QLatin1String("SOCKS"), Qt::CaseInsensitive) == 0 || 00067 mode.compare(QLatin1String("SOCKS5"), Qt::CaseInsensitive) == 0) 00068 return Socks; 00069 00070 return Unknown; 00071 } 00072 00073 ProxyScout::QueuedRequest::QueuedRequest( const QDBusMessage &reply, const KUrl& u, bool sendall ) 00074 : transaction( reply ), url( u ), sendAll(sendall) 00075 { 00076 } 00077 00078 ProxyScout::ProxyScout(QObject* parent, const QList<QVariant>&) 00079 : KDEDModule(parent), 00080 m_componentData("proxyscout"), 00081 m_downloader( 0 ), 00082 m_script( 0 ), 00083 m_suspendTime( 0 ), 00084 m_debugArea (KDebug::registerArea("proxyscout")), 00085 m_watcher( 0 ) 00086 { 00087 #ifndef KPAC_NO_SOLID 00088 connect (Solid::Networking::notifier(), SIGNAL(shouldDisconnect()), SLOT(disconnectNetwork())); 00089 #endif 00090 } 00091 00092 ProxyScout::~ProxyScout() 00093 { 00094 delete m_script; 00095 } 00096 00097 QStringList ProxyScout::proxiesForUrl( const QString& checkUrl, const QDBusMessage &msg ) 00098 { 00099 KUrl url(checkUrl); 00100 00101 if (m_suspendTime) { 00102 if ( std::time( 0 ) - m_suspendTime < 300 ) { 00103 return QStringList (QLatin1String("DIRECT")); 00104 } 00105 m_suspendTime = 0; 00106 } 00107 00108 // Never use a proxy for the script itself 00109 if (m_downloader && url.equals(m_downloader->scriptUrl(), KUrl::CompareWithoutTrailingSlash)) { 00110 return QStringList (QLatin1String("DIRECT")); 00111 } 00112 00113 if (m_script) { 00114 return handleRequest(url); 00115 } 00116 00117 if (m_downloader || startDownload()) { 00118 msg.setDelayedReply(true); 00119 m_requestQueue.append( QueuedRequest( msg, url, true ) ); 00120 return QStringList(); // return value will be ignored 00121 } 00122 00123 return QStringList(QLatin1String("DIRECT")); 00124 } 00125 00126 QString ProxyScout::proxyForUrl( const QString& checkUrl, const QDBusMessage &msg ) 00127 { 00128 KUrl url(checkUrl); 00129 00130 if (m_suspendTime) { 00131 if ( std::time( 0 ) - m_suspendTime < 300 ) { 00132 return QLatin1String("DIRECT"); 00133 } 00134 m_suspendTime = 0; 00135 } 00136 00137 // Never use a proxy for the script itself 00138 if (m_downloader && url.equals(m_downloader->scriptUrl(), KUrl::CompareWithoutTrailingSlash)) { 00139 return QLatin1String("DIRECT"); 00140 } 00141 00142 if (m_script) { 00143 return handleRequest(url).first(); 00144 } 00145 00146 if (m_downloader || startDownload()) { 00147 msg.setDelayedReply(true); 00148 m_requestQueue.append( QueuedRequest( msg, url ) ); 00149 return QString(); // return value will be ignored 00150 } 00151 00152 return QLatin1String("DIRECT"); 00153 } 00154 00155 void ProxyScout::blackListProxy( const QString& proxy ) 00156 { 00157 m_blackList[ proxy ] = std::time( 0 ); 00158 } 00159 00160 void ProxyScout::reset() 00161 { 00162 delete m_script; 00163 m_script = 0; 00164 delete m_downloader; 00165 m_downloader = 0; 00166 delete m_watcher; 00167 m_watcher = 0; 00168 m_blackList.clear(); 00169 m_suspendTime = 0; 00170 KProtocolManager::reparseConfiguration(); 00171 } 00172 00173 bool ProxyScout::startDownload() 00174 { 00175 switch ( KProtocolManager::proxyType() ) 00176 { 00177 case KProtocolManager::WPADProxy: 00178 if (m_downloader && !qobject_cast<Discovery*>(m_downloader)) { 00179 delete m_downloader; 00180 m_downloader = 0; 00181 } 00182 if (!m_downloader) { 00183 m_downloader = new Discovery(this); 00184 connect(m_downloader, SIGNAL(result(bool)), this, SLOT(downloadResult(bool))); 00185 } 00186 break; 00187 case KProtocolManager::PACProxy: { 00188 if (m_downloader && !qobject_cast<Downloader*>(m_downloader)) { 00189 delete m_downloader; 00190 m_downloader = 0; 00191 } 00192 if (!m_downloader) { 00193 m_downloader = new Downloader(this); 00194 connect(m_downloader, SIGNAL(result(bool)), this, SLOT(downloadResult(bool))); 00195 } 00196 00197 const KUrl url (KProtocolManager::proxyConfigScript()); 00198 if (url.isLocalFile()) { 00199 if (!m_watcher) { 00200 m_watcher = new QFileSystemWatcher(this); 00201 connect (m_watcher, SIGNAL(fileChanged(QString)), SLOT(proxyScriptFileChanged(QString))); 00202 } 00203 proxyScriptFileChanged(url.path()); 00204 } else { 00205 delete m_watcher; 00206 m_watcher = 0; 00207 m_downloader->download( url ); 00208 } 00209 break; 00210 } 00211 default: 00212 return false; 00213 } 00214 00215 return true; 00216 } 00217 00218 void ProxyScout::disconnectNetwork() 00219 { 00220 // NOTE: We only connect to Solid's network notifier's shouldDisconnect 00221 // signal because we only want to redo WPAD when a network interface is 00222 // brought out of hibernation or restarted for whatever reason. 00223 reset(); 00224 } 00225 00226 void ProxyScout::downloadResult( bool success ) 00227 { 00228 if ( success ) { 00229 try 00230 { 00231 if (!m_script) { 00232 m_script = new Script(m_downloader->script()); 00233 } 00234 } 00235 catch ( const Script::Error& e ) 00236 { 00237 kWarning() << "Error:" << e.message(); 00238 KNotification *notify= new KNotification ( "script-error" ); 00239 notify->setText( i18n("The proxy configuration script is invalid:\n%1" , e.message() ) ); 00240 notify->setComponentData(m_componentData); 00241 notify->sendEvent(); 00242 success = false; 00243 } 00244 } else { 00245 KNotification *notify = new KNotification ("download-error"); 00246 notify->setText( m_downloader->error() ); 00247 notify->setComponentData(m_componentData); 00248 notify->sendEvent(); 00249 } 00250 00251 if (success) { 00252 for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) { 00253 if ((*it).sendAll) { 00254 const QVariant result (handleRequest((*it).url)); 00255 QDBusConnection::sessionBus().send((*it).transaction.createReply(result)); 00256 } else { 00257 const QVariant result (handleRequest((*it).url).first()); 00258 QDBusConnection::sessionBus().send((*it).transaction.createReply(result)); 00259 } 00260 } 00261 } else { 00262 for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) { 00263 QDBusConnection::sessionBus().send((*it).transaction.createReply(QString::fromLatin1("DIRECT"))); 00264 } 00265 } 00266 00267 m_requestQueue.clear(); 00268 00269 // Suppress further attempts for 5 minutes 00270 if ( !success ) { 00271 m_suspendTime = std::time( 0 ); 00272 } 00273 } 00274 00275 void ProxyScout::proxyScriptFileChanged(const QString& path) 00276 { 00277 // Should never get called if we do not have a watcher... 00278 Q_ASSERT(m_watcher); 00279 00280 // Remove the current file being watched... 00281 if (!m_watcher->files().isEmpty()) { 00282 m_watcher->removePaths(m_watcher->files()); 00283 } 00284 00285 // NOTE: QFileSystemWatcher only adds a path if it either exists or 00286 // is not already being monitored. 00287 m_watcher->addPath(path); 00288 00289 // Reload... 00290 m_downloader->download( KUrl( path ) ); 00291 } 00292 00293 QStringList ProxyScout::handleRequest( const KUrl& url ) 00294 { 00295 try 00296 { 00297 QStringList proxyList; 00298 const QString result = m_script->evaluate(url).trimmed(); 00299 const QStringList proxies = result.split(QLatin1Char(';'), QString::SkipEmptyParts); 00300 const int size = proxies.count(); 00301 00302 for (int i = 0; i < size; ++i) { 00303 QString mode, address; 00304 const QString proxy = proxies.at(i).trimmed(); 00305 const int index = proxy.indexOf(QLatin1Char(' ')); 00306 if (index == -1) { // Only "DIRECT" should match this! 00307 mode = proxy; 00308 address = proxy; 00309 } else { 00310 mode = proxy.left(index); 00311 address = proxy.mid(index + 1).trimmed(); 00312 } 00313 00314 const ProxyType type = proxyTypeFor(mode); 00315 if (type == Unknown) { 00316 continue; 00317 } 00318 00319 if (type == Proxy || type == Socks) { 00320 const int index = address.indexOf(QLatin1Char(':')); 00321 if (index == -1 || !KProtocolInfo::isKnownProtocol(address.left(index))) { 00322 const QString protocol ((type == Proxy ? QLatin1String("http://") : QLatin1String("socks://"))); 00323 const KUrl url (protocol + address); 00324 if (url.isValid()) { 00325 address = url.url(); 00326 } else { 00327 continue; 00328 } 00329 } 00330 } 00331 00332 if (type == Direct || !m_blackList.contains(address)) { 00333 proxyList << address; 00334 } else if (std::time(0) - m_blackList[address] > 1800) { // 30 minutes 00335 // black listing expired 00336 m_blackList.remove( address ); 00337 proxyList << address; 00338 } 00339 } 00340 00341 if (!proxyList.isEmpty()) { 00342 kDebug(m_debugArea) << proxyList; 00343 return proxyList; 00344 } 00345 // FIXME: blacklist 00346 } 00347 catch ( const Script::Error& e ) 00348 { 00349 kError() << e.message(); 00350 KNotification *n=new KNotification( "evaluation-error" ); 00351 n->setText( i18n( "The proxy configuration script returned an error:\n%1" , e.message() ) ); 00352 n->setComponentData(m_componentData); 00353 n->sendEvent(); 00354 } 00355 00356 return QStringList (QLatin1String("DIRECT")); 00357 } 00358 } 00359 00360 #include "proxyscout.moc" 00361 00362 // vim: ts=4 sw=4 et
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:21:24 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:21:24 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.