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 m_downloader = new Discovery( this ); 00179 break; 00180 case KProtocolManager::PACProxy: 00181 { 00182 m_downloader = new Downloader( this ); 00183 KUrl url( KProtocolManager::proxyConfigScript() ); 00184 if (url.isLocalFile()) { 00185 if (!m_watcher) { 00186 m_watcher = new QFileSystemWatcher( this ); 00187 connect (m_watcher, SIGNAL(fileChanged(QString)), SLOT(proxyScriptFileChanged(QString))); 00188 } 00189 proxyScriptFileChanged(url.path()); 00190 } else { 00191 delete m_watcher; 00192 m_watcher = 0; 00193 m_downloader->download( url ); 00194 } 00195 break; 00196 } 00197 default: 00198 return false; 00199 } 00200 00201 connect(m_downloader, SIGNAL(result(bool)), SLOT(downloadResult(bool))); 00202 return true; 00203 } 00204 00205 void ProxyScout::disconnectNetwork() 00206 { 00207 // NOTE: We only connect to Solid's network notifier's shouldDisconnect 00208 // signal because we only want to redo WPAD when a network interface is 00209 // brought out of hibernation or restarted for whatever reason. 00210 reset(); 00211 } 00212 00213 void ProxyScout::downloadResult( bool success ) 00214 { 00215 if ( success ) { 00216 try 00217 { 00218 m_script = new Script( m_downloader->script() ); 00219 } 00220 catch ( const Script::Error& e ) 00221 { 00222 kWarning() << "Error:" << e.message(); 00223 KNotification *notify= new KNotification ( "script-error" ); 00224 notify->setText( i18n("The proxy configuration script is invalid:\n%1" , e.message() ) ); 00225 notify->setComponentData(m_componentData); 00226 notify->sendEvent(); 00227 success = false; 00228 } 00229 } else { 00230 KNotification *notify = new KNotification ("download-error"); 00231 notify->setText( m_downloader->error() ); 00232 notify->setComponentData(m_componentData); 00233 notify->sendEvent(); 00234 } 00235 00236 if (success) { 00237 for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) { 00238 if ((*it).sendAll) { 00239 const QVariant result (handleRequest((*it).url)); 00240 QDBusConnection::sessionBus().send((*it).transaction.createReply(result)); 00241 } else { 00242 const QVariant result (handleRequest((*it).url).first()); 00243 QDBusConnection::sessionBus().send((*it).transaction.createReply(result)); 00244 } 00245 } 00246 } else { 00247 for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) { 00248 QDBusConnection::sessionBus().send((*it).transaction.createReply(QString::fromLatin1("DIRECT"))); 00249 } 00250 } 00251 00252 m_requestQueue.clear(); 00253 m_downloader->deleteLater(); 00254 m_downloader = 0; 00255 // Suppress further attempts for 5 minutes 00256 if ( !success ) { 00257 m_suspendTime = std::time( 0 ); 00258 } 00259 } 00260 00261 void ProxyScout::proxyScriptFileChanged(const QString& path) 00262 { 00263 // Should never get called if we do not have a watcher... 00264 Q_ASSERT(m_watcher); 00265 00266 // if it does not exist, bogus file was given or it was deleted... 00267 if (QFile::exists(path)) { 00268 // if not contained, first attempt or file was renamed... 00269 if (!m_watcher->files().contains(path)) { 00270 m_watcher->removePaths(m_watcher->files()); 00271 m_watcher->addPath(path); 00272 } 00273 } 00274 00275 // Reload... 00276 m_downloader->download( KUrl( path ) ); 00277 } 00278 00279 QStringList ProxyScout::handleRequest( const KUrl& url ) 00280 { 00281 try 00282 { 00283 QStringList proxyList; 00284 const QString result = m_script->evaluate(url).trimmed(); 00285 const QStringList proxies = result.split(QLatin1Char(';'), QString::SkipEmptyParts); 00286 const int size = proxies.count(); 00287 00288 for (int i = 0; i < size; ++i) { 00289 QString mode, address; 00290 const QString proxy = proxies.at(i).trimmed(); 00291 const int index = proxy.indexOf(QLatin1Char(' ')); 00292 if (index == -1) { // Only "DIRECT" should match this! 00293 mode = proxy; 00294 address = proxy; 00295 } else { 00296 mode = proxy.left(index); 00297 address = proxy.mid(index + 1).trimmed(); 00298 } 00299 00300 const ProxyType type = proxyTypeFor(mode); 00301 if (type == Unknown) { 00302 continue; 00303 } 00304 00305 if (type == Proxy || type == Socks) { 00306 const int index = address.indexOf(QLatin1Char(':')); 00307 if (index == -1 || !KProtocolInfo::isKnownProtocol(address.left(index))) { 00308 const QString protocol ((type == Proxy ? QLatin1String("https://") : QLatin1String("socks://"))); 00309 const KUrl url (protocol + address); 00310 if (url.isValid()) { 00311 address = url.url(); 00312 } else { 00313 continue; 00314 } 00315 } 00316 } 00317 00318 if (type == Direct || !m_blackList.contains(address)) { 00319 proxyList << address; 00320 } else if (std::time(0) - m_blackList[address] > 1800) { // 30 minutes 00321 // black listing expired 00322 m_blackList.remove( address ); 00323 proxyList << address; 00324 } 00325 } 00326 00327 if (!proxyList.isEmpty()) { 00328 kDebug(m_debugArea) << proxyList; 00329 return proxyList; 00330 } 00331 // FIXME: blacklist 00332 } 00333 catch ( const Script::Error& e ) 00334 { 00335 kError() << e.message(); 00336 KNotification *n=new KNotification( "evaluation-error" ); 00337 n->setText( i18n( "The proxy configuration script returned an error:\n%1" , e.message() ) ); 00338 n->setComponentData(m_componentData); 00339 n->sendEvent(); 00340 } 00341 00342 return QStringList (QLatin1String("DIRECT")); 00343 } 00344 } 00345 00346 #include "proxyscout.moc" 00347 00348 // vim: ts=4 sw=4 et
KDE 4.7 API Reference