Implements a Proxy detection (WPAD) interface for Linux, Mac OSX and Windows. Offers a GUI for manual proxy settings and automatic WPAD detection. The GUI is based on QT.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.2 KiB
241 lines
8.2 KiB
/*! @file |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#ifndef PROXYFACE_HXX |
|
#define PROXYFACE_HXX |
|
|
|
#ifdef QT_NETWORK_LIB |
|
#include <QtNetwork/QNetworkProxy> |
|
#include <QtNetwork/QNetworkAccessManager> |
|
#include <QtNetwork/QNetworkReply> |
|
#include <QtCore/QTimer> |
|
#include <QtCore/QDebug> |
|
#include <map> |
|
#endif |
|
|
|
#include <list> |
|
#include <string> |
|
#include <sstream> |
|
|
|
//! auto proxy configuration |
|
namespace proxy { |
|
|
|
//! exceptions |
|
namespace exc { |
|
|
|
//! unspecific error |
|
class error: std::exception { |
|
public: |
|
virtual const char* what() const throw() { |
|
return "Auto Proxy Detection Error"; |
|
} |
|
}; |
|
|
|
} |
|
|
|
//! Proxy types |
|
enum Type { |
|
DIRECT, //< direct connection, no proxy |
|
DEFAULT, //< default proxy |
|
HTTP, //< HTTP proxy host |
|
SOCKS //<SOCKS5 proxy |
|
}; |
|
|
|
//! One proxy |
|
struct Proxy { |
|
Proxy(): type(DIRECT), port(0) {} |
|
Proxy(Type t, std::string h, unsigned int p): type(t), host(h), port(p) {} |
|
Proxy(Type t, std::string h, std::string p): type(t), host(h), port(0) { |
|
std::stringstream(p)>>port; |
|
} |
|
Type type; |
|
std::string host; |
|
unsigned int port; |
|
}; |
|
|
|
//! List of proxies, type -> url and port |
|
typedef std::list<Proxy> List; |
|
|
|
//! Unified Interface for accessing proxy::Face |
|
/*! Abstract interface, impementation for Unix and Windoze differs. |
|
|
|
Instanciate proxy::Face, which is a typedef to your |
|
platform's implementation. |
|
|
|
@code |
|
proxy::Face pf; // keep for program life time (because of caching) |
|
proxy::List pf.proxies("http://swisssign.com"); |
|
[...] // set proxy, read from http://swisssign.com |
|
@endcode |
|
|
|
@example test.cxx */ |
|
class Interface |
|
#ifdef QT_NETWORK_LIB |
|
: public QObject |
|
#endif |
|
{ |
|
#ifdef QT_NETWORK_LIB |
|
Q_OBJECT; |
|
#endif |
|
public: |
|
//! Keep your instance as long as possible, because of caching. |
|
Interface() { |
|
#ifdef QT_NETWORK_LIB |
|
if (!connect(&_timeout, SIGNAL(timeout()), SLOT(timeout()))) |
|
qFatal("connect failed"); |
|
#endif |
|
} |
|
virtual ~Interface() {} |
|
//! Get list of proxies for a given URL. |
|
virtual List proxies(const std::string& url) = 0; |
|
#ifdef QT_NETWORK_LIB |
|
//! If Qt Network is available you may check the proxies found |
|
/*! First checks for direct access to the target. If this is not |
|
possible, starts scan for proxies. |
|
|
|
Returns immediately and signals proxyFound() if a valid |
|
configuration has been found, or proxyError() in case of |
|
timeout. You should wait for either of the signals, before you |
|
can call this method again. |
|
|
|
@param url the url to find a proxy for |
|
@param timeout [ms] time to give up search */ |
|
void proxy(const std::string& url, int timeout=2000) { |
|
qDebug()<<"Search proxy for URL, direct and default" |
|
<<"url="<<url.data()<<"timeout="<<timeout; |
|
for (Requests::iterator it(_requests.begin()); it!=_requests.end(); ++it) |
|
delete it->second.first; |
|
_requests.clear(); |
|
_direct = true; // first try direct access |
|
QNetworkProxy directProxy(QNetworkProxy::NoProxy); |
|
setupProxyCheck(directProxy, url); |
|
QNetworkProxy defaultProxy; |
|
setupProxyCheck(defaultProxy, url); |
|
_timeout.setSingleShot(true); |
|
_timeout.setInterval(timeout); |
|
_timeout.start(); |
|
} |
|
//! If Qt Network is available you may check the proxies found |
|
/*! @copydoc proxy(const std::string&, int) */ |
|
void proxy(const QUrl& url, int timeout=2000) { |
|
proxy(url.toString().toStdString(), timeout); |
|
} |
|
//! If Qt Network is available you may check the proxies found |
|
/*! @copydoc proxy(const std::string&, int) */ |
|
void proxy(const QString& url, int timeout=2000) { |
|
proxy(url.toStdString(), timeout); |
|
} |
|
static QString toString(const QNetworkProxy& p) { |
|
switch (p.type()) { |
|
case QNetworkProxy::NoProxy: |
|
return QString("direct://"); |
|
case QNetworkProxy::DefaultProxy: |
|
if (QNetworkProxy::applicationProxy().type() |
|
!=QNetworkProxy::DefaultProxy) |
|
return QString("Default Proxy: %1") |
|
.arg(toString(QNetworkProxy::applicationProxy())); |
|
else |
|
return QString("Default Proxy: **ERROR**" |
|
" application proxy is configured as" |
|
" QNetworkProxy::DefaultProxy"); |
|
case QNetworkProxy::Socks5Proxy: |
|
return QString("socks://%1:%2").arg(p.hostName()).arg(p.port()); |
|
case QNetworkProxy::HttpProxy: |
|
return QString("http://%1:%2").arg(p.hostName()).arg(p.port()); |
|
case QNetworkProxy::HttpCachingProxy: |
|
return QString("http-cache://%1:%2").arg(p.hostName()).arg(p.port()); |
|
case QNetworkProxy::FtpCachingProxy: |
|
return QString("ftp-cache://%1:%2").arg(p.hostName()).arg(p.port()); |
|
} |
|
return QString("**ERROR** illegal proxy type"); |
|
} |
|
Q_SIGNALS: |
|
//! Signals a valid proxy for the URL requested earlier. |
|
void proxyFound(const QUrl&, const QNetworkProxy&); |
|
//! Signals a timeout, and no valid proxy for the URL requested earlier. |
|
void proxyError(QNetworkReply::NetworkError); |
|
private: |
|
void setupProxyCheck(const QNetworkProxy& prxy, const std::string& url) { |
|
qDebug()<<"Testing proxy for url:"<<toString(prxy)<<"url="<<url.data(); |
|
QNetworkAccessManager* manager(new QNetworkAccessManager); |
|
manager->setProxy(prxy); |
|
if (!connect(manager, SIGNAL(finished(QNetworkReply*)), |
|
this, SLOT(replyFinished(QNetworkReply*)))) |
|
qFatal("connect failed"); |
|
_requests.insert |
|
(std::make_pair(manager->head |
|
(QNetworkRequest |
|
(QUrl(QString::fromStdString(url)))), |
|
std::make_pair(manager, prxy))); |
|
} |
|
private Q_SLOTS: |
|
void timeout() { |
|
_timeout.stop(); |
|
std::string url; |
|
for (Requests::iterator it(_requests.begin()); |
|
it!=_requests.end(); ++it) { |
|
url = it->first->url().toString().toStdString(); |
|
delete it->second.first; |
|
} |
|
_requests.clear(); |
|
qDebug()<<"Proxy detection timed out"<<"url="<<url.data(); |
|
if (_direct && url.size()) { |
|
qDebug()<<"Direct or preconfigured proxy not available," |
|
" try autoproxy negotiation"; |
|
_direct = false; |
|
List l(proxies(url)); |
|
for (List::const_iterator it(l.begin()); it!=l.end(); ++it) { |
|
QNetworkProxy prxy((it->type==DEFAULT?QNetworkProxy::DefaultProxy |
|
:(it->type==HTTP?QNetworkProxy::HttpProxy |
|
:(it->type==SOCKS?QNetworkProxy::Socks5Proxy |
|
:QNetworkProxy::NoProxy))), |
|
QString::fromStdString(it->host), it->port); |
|
setupProxyCheck(prxy, url); |
|
} |
|
_timeout.start(); |
|
} else { |
|
qDebug()<<"No proxy at all, giving up - offline?"; |
|
proxyError(QNetworkReply::TimeoutError); |
|
} |
|
} |
|
void replyFinished(QNetworkReply* reply) { |
|
_timeout.stop(); |
|
QNetworkProxy prxy(_requests[reply].second); |
|
QUrl url(reply->url()); |
|
for (Requests::iterator it(_requests.begin()); it!=_requests.end(); ++it) |
|
delete it->second.first; |
|
_requests.clear(); |
|
qDebug()<<"SUCCESS - Valid proxy found for url:" |
|
<<"url="<<url<<"proxy="<<toString(prxy); |
|
proxyFound(url, prxy); |
|
} |
|
private: |
|
typedef std::map |
|
<QNetworkReply*, std::pair<QNetworkAccessManager*, QNetworkProxy> > |
|
Requests; |
|
Requests _requests; |
|
bool _direct; |
|
QTimer _timeout; |
|
#endif |
|
}; |
|
} |
|
|
|
#ifdef WIN32 |
|
// use windoze proprietary winhttp |
|
#include <proxyface/windoze.hxx> |
|
namespace proxy { |
|
typedef Windoze Face; |
|
} |
|
#else |
|
// normal operating systems: use http://code.google.com/p/libproxy |
|
#include <proxyface/unix.hxx> |
|
namespace proxy { |
|
typedef Unix Face; |
|
} |
|
#endif |
|
|
|
#endif
|
|
|