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.
 
 
 
 

290 lines
10 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 <QtNetwork/QSslError>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#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 QThread
#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(&_timeout1, SIGNAL(timeout()), SLOT(timeout())))
qFatal("connect failed");
if (!connect(&_timeout2, 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 timeout1=1000, int timeout2=30000) {
_url = url;
_timeout2.setSingleShot(true);
_timeout2.setInterval(timeout2);
_timeout1.setSingleShot(true);
_timeout1.setInterval(timeout1);
start(); // in own thread to prevent hanging at proxy request
}
//! If Qt Network is available you may check the proxies found
/*! @copydoc proxy(const std::string&, int) */
void proxy(const QUrl& url, int timeout1=1000, int timeout2=30000) {
proxy(url.toString().toStdString(), timeout1, timeout2);
}
//! If Qt Network is available you may check the proxies found
/*! @copydoc proxy(const std::string&, int) */
void proxy(const QString& url, int timeout1=1000, int timeout2=30000) {
proxy(url.toStdString(), timeout1, timeout2);
}
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);
protected:
void run() {
qDebug()<<"Search proxy for URL, direct and default"
<<"url="<<_url.data();
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);
_timeout1.start();
exec();
}
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*)),
SLOT(replyFinished(QNetworkReply*))))
qFatal("connect failed");
connect(manager,
SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));
connect(manager,
SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&,
QAuthenticator*)),
SLOT(proxyAuthenticationRequired(const QNetworkProxy&,
QAuthenticator*)));
connect(manager,
SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&)));
_requests.insert
(std::make_pair(manager->head
(QNetworkRequest
(QUrl(QString::fromStdString(url)))),
std::make_pair(manager, prxy)));
}
private Q_SLOTS:
void timeout() {
_timeout1.stop();
_timeout2.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);
}
QNetworkProxy directProxy(QNetworkProxy::NoProxy);
setupProxyCheck(directProxy, url);
QNetworkProxy defaultProxy;
setupProxyCheck(defaultProxy, url);
_timeout2.start();
} else {
qDebug()<<"No proxy at all, giving up - offline?";
proxyError(QNetworkReply::TimeoutError);
}
}
void replyFinished(QNetworkReply* reply) {
qDebug()<<"Proxydetection got reply with status:"
<<reply->error()<<reply->errorString();
if (reply->error()!=QNetworkReply::NoError) return; // wait for timeout
_timeout1.stop();
_timeout2.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);
}
void authenticationRequired(QNetworkReply*, QAuthenticator*) {
qDebug()<<"## "<<__PRETTY_FUNCTION__;
}
void proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*) {
qDebug()<<"## "<<__PRETTY_FUNCTION__;
}
void sslErrors(QNetworkReply*, const QList<QSslError>& l) {
qDebug()<<"## "<<__PRETTY_FUNCTION__;
for (QList<QSslError>::const_iterator it(l.begin()); it!=l.end(); ++it)
qDebug()<<" SSL-Error -> "<<it->errorString();
}
private:
typedef std::map
<QNetworkReply*, std::pair<QNetworkAccessManager*, QNetworkProxy> >
Requests;
Requests _requests;
bool _direct;
QTimer _timeout1;
QTimer _timeout2;
std::string _url;
#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