/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef PROXYFACE_HXX
# define PROXYFACE_HXX
# if HAVE_QT == 1
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
# include <QtNetwork/QNetworkProxy>
# include <QtNetwork/QNetworkAccessManager>
# include <QtNetwork/QNetworkReply>
# include <QtNetwork/QAuthenticator>
# include <QtNetwork/QSslError>
# include <QtNetwork/QHostInfo>
# include <QtCore/QTimer>
# include <QtCore/QDebug>
# include <QtCore/QThread>
# pragma GCC diagnostic pop
# include <map>
# ifndef PROXYFACE_LOG
# define PROXYFACE_LOG qDebug()<<__PRETTY_FUNCTION__
# endif
# endif
# include <list>
# include <string>
# include <sstream>
//! auto proxy configuration
namespace proxy {
std : : string version ( ) ;
//! 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 ;
/*! @page dyn Interaction Diagrams
@ section dynState State Diagram
@ dot
digraph A {
// actors
" start " [ shape = " circle " ] ;
// loops
subgraph clusterMainLoop {
label = " Main Loop " ;
" «thread loop» " ;
" «timer loop» " ;
" «network loop» " ;
}
node [ shape = box ] ;
// method declarations
" Interface::proxy " [ URL = " \r ef Interface::proxy " ] ;
" Interface::ping " [ URL = " \r ef Interface::ping " ] ;
" Interface::run " [ URL = " \r ef Interface::run " ] ;
" Interface::setupProxyCheck " [ URL = " \r ef Interface::setupProxyCheck " ] ;
// slot declarations
{
node [ shape = octagon ] ;
" Interface::timeout " [ URL = " \r ef Interface::timeout " ] ;
" Interface::threadFinished " [ URL = " \r ef Interface::threadFinished " ] ;
" Interface::replyFinished " [ URL = " \r ef Interface::replyFinished " ] ;
" Interface::uploadProgress " [ URL = " \r ef Interface::uploadProgress " ] ;
" Interface::downloadProgress "
[ URL = " \r ef Interface::downloadProgress " ] ;
" Interface::sslErrors " [ URL = " \r ef Interface::sslErrors " ] ;
}
// signal declarations
{
node [ shape = invhouse ] ;
" Interface::ProxyError " [ URL = " \r ef Interface::ProxyError " ] ;
" Interface::authenticationRequired "
[ URL = " \r ef Interface::authenticationRequired " ] ;
" Interface::proxyAuthenticationRequired "
[ URL = " \r ef Interface::proxyAuthenticationRequired " ] ;
" Interface::temporaryError " [ URL = " \r ef Interface::temporaryError " ] ;
" Interface::proxyFound " [ URL = " \r ef Interface::proxyFound " ] ;
}
// method interactions
" start " - > { " Interface::proxy " " Interface::ping " } ;
" Interface::ping " - > " Interface::setupProxyCheck " ;
" Interface::proxy " - > " Interface::timeout " [ label = " for test only: \n direct call, no _timeout1 " ] ;
" Interface::run " - > " «thread loop» " [ label = " proxies(url) " , URL = " \r ef Interface::proxies " ] ;
" Interface::setupProxyCheck " - > " Interface::threadFinished " ;
// networkmanager
" Interface::setupProxyCheck " - > " QNetworkAccessManager " [ label = " new " ] ;
" QNetworkAccessManager " - > " «network loop» " [ label = " QNetworkReply* QNetworkAccessManager::get() " ] ;
// slot interactions
" Interface::timeout " - > " Interface::run " [ label = " first time \n (own thread) " ] ;
" Interface::timeout " - > " Interface::ProxyError " [ label = " second time " ] ;
" Interface::threadFinished " - > " Interface::setupProxyCheck " [ label = " 1.) for all proxies " ] ;
" Interface::threadFinished " - > " «timer loop» " [ label = " 2.) _timeout2.start() " ] ;
" Interface::replyFinished " - > " Interface::temporaryError " [ label = " error " ] ;
" Interface::temporaryError " - > " «network loop» " [ label = " wait for timeout " ] ;
" Interface::replyFinished " - > " Interface::proxyFound " [ label = " success " ] ;
// signaling objects
" «timer loop» " - > " Interface::timeout " [ label = " _timeout1 " , URL = " \r ef Interface::_timeout1 " ] ;
" «timer loop» " - > " Interface::timeout " [ label = " _timeout2 " , URL = " \r ef Interface::_timeout2 " ] ;
" «thread loop» " - > {
" Interface::threadFinished " ;
} [ label = " finished " ] ;
" «network loop» " - > {
" Interface::replyFinished " ;
" Interface::uploadProgress " ;
" Interface::downloadProgress " ;
" Interface::authenticationRequired " ;
" Interface::proxyAuthenticationRequired " ;
" Interface::sslErrors " ;
}
}
@ enddot
@ section dynMSC Message State Chart
@ msc
| | | ;
application , Interface , QNetworkAccessManager , QNetworkReply ;
application - > Interface [ label = " Interface() " ] ;
application - > Interface [ label = " proxy(url) " ] ;
| | | ;
- - - [ label = " for each proxy " ] ;
Interface - > QNetworkAccessManager [ label = " new " ] ;
QNetworkAccessManager - > QNetworkReply [ label = " get(url) " ] ;
Interface - > Interface [ label = " _timeout2.start() " ] ;
- - - [ label = " " ] ;
| | | ;
QNetworkReply - > Interface [ label = " replyFinished() " ] ;
Interface - > application [ label = " proxyFound() " ] ;
| | | ;
@ endmsc */
//! 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 getproxylist . cxx */
class Interface
# if HAVE_QT == 1
: public QThread
# endif
{
# if HAVE_QT == 1
Q_OBJECT
# endif
public :
//! Keep your instance as long as possible, because of caching.
Interface ( )
# if HAVE_QT == 1
: _timeout1Paused ( false ) , _timeout2Paused ( false )
# endif
{
# if HAVE_QT == 1
PROXYFACE_LOG ;
if ( ! connect ( & _timeout1 , SIGNAL ( timeout ( ) ) , SLOT ( timeout ( ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( & _timeout2 , SIGNAL ( timeout ( ) ) , SLOT ( timeout ( ) ) ) )
qFatal ( " connect failed " ) ;
# ifndef Q_OS_WIN32
if ( ! connect ( this , SIGNAL ( finished ( ) ) , SLOT ( threadFinished ( ) ) ) )
qFatal ( " connect failed " ) ;
# endif
# endif
}
virtual ~ Interface ( ) { }
//! Get list of proxies for a given URL.
virtual List proxies ( const std : : string & url ) = 0 ;
# if HAVE_QT == 1
//! Reset, stop all outstanding checks
void reset ( ) {
_timeout1 . stop ( ) ;
_timeout2 . stop ( ) ;
for ( Requests : : iterator it ( _requests . begin ( ) ) ;
it ! = _requests . end ( ) ; + + it )
clean ( it - > second . first ) ;
_requests . clear ( ) ;
}
//! Pause timeouts and restart them later.
/*! Use while waiting for user input at proxy authentication.
@ see restart ( ) */
void pause ( ) {
if ( _timeout1 . isActive ( ) ) {
_timeout1 . stop ( ) ;
_timeout1Paused = true ;
}
if ( _timeout2 . isActive ( ) ) {
_timeout2 . stop ( ) ;
_timeout2Paused = true ;
}
}
//! Restart paused timeouts.
/*! Use while waiting for user input at proxy authentication.
@ see pause ( ) */
void restart ( ) {
if ( _timeout1Paused ) {
_timeout1 . start ( ) ;
_timeout1Paused = false ;
}
if ( _timeout2Paused ) {
_timeout2 . start ( ) ;
_timeout2Paused = false ;
}
}
//! Network Ping: Check access to a given address using default proxy.
void ping ( const std : : string & url , int timeout2 = 30000 ) {
if ( _requests . size ( ) | | _timeout1 . isActive ( ) | | _timeout2 . isActive ( ) )
// detection is already running, wait for that, don't restart
return ;
reset ( ) ;
_timeout2 . setSingleShot ( true ) ;
_timeout2 . setInterval ( timeout2 ) ;
_url = url ;
_direct = false ; // simulate second try
QNetworkProxy defaultProxy ;
setupProxyCheck ( defaultProxy , url ) ;
_timeout2 . start ( ) ;
}
void ping ( const QUrl & url , int timeout2 = 30000 ) {
ping ( url . toString ( ) . toStdString ( ) , timeout2 ) ;
}
void ping ( const QString & url , int timeout2 = 30000 ) {
ping ( url . toStdString ( ) , timeout2 ) ;
}
//! 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 = 5000 , int timeout2 = 30000 ) {
PROXYFACE_LOG ;
qDebug ( ) < < " Search proxy for URL, direct and default "
< < " url= " < < url . data ( ) < < " timeout1= " < < timeout1 ;
reset ( ) ;
_url = url ;
_direct = true ; // first try direct access
if ( getenv ( " HTTP_PROXY " ) ) {
QUrl proxy ( QString ( getenv ( " HTTP_PROXY " ) ) ) ;
QNetworkProxy envProxy ( QNetworkProxy : : HttpProxy ,
proxy . host ( ) , ( quint16 ) proxy . port ( ) ,
proxy . userName ( ) , proxy . password ( ) ) ;
setupProxyCheck ( envProxy , url ) ;
}
QNetworkProxy directProxy ( QNetworkProxy : : NoProxy ) ;
setupProxyCheck ( directProxy , url ) ;
QNetworkProxy defaultProxy ;
setupProxyCheck ( defaultProxy , url ) ;
_timeout2 . setSingleShot ( true ) ;
_timeout2 . setInterval ( timeout2 ) ;
_timeout1 . setSingleShot ( true ) ;
_timeout1 . setInterval ( timeout1 ) ;
_timeout1 . start ( ) ;
}
//! If Qt Network is available you may check the proxies found
/*! @copydoc proxy(const std::string&, int) */
void proxy ( const QUrl & url , int timeout1 = 5000 , int timeout2 = 30000 ) {
PROXYFACE_LOG ;
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 = 5000 , int timeout2 = 30000 ) {
PROXYFACE_LOG ;
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 ) ;
//! Signals an error during proxy detection.
void temporaryError ( QNetworkReply : : NetworkError , QString , QString ) ;
void authenticationRequired ( QNetworkReply * , QAuthenticator * ) ;
void proxyAuthenticationRequired ( const QNetworkProxy & , QAuthenticator * ) ;
private :
void setupProxyCheck ( const QNetworkProxy & prxy , const std : : string & url ) {
PROXYFACE_LOG ;
qDebug ( ) < < " Testing proxy for url: " < < toString ( prxy ) < < " url= " < < url . data ( ) ;
QNetworkAccessManager * manager ( new QNetworkAccessManager ) ;
QNetworkProxy p ( prxy . type ( ) = = QNetworkProxy : : DefaultProxy
? QNetworkProxy : : applicationProxy ( ) : prxy ) ;
qDebug ( ) < < " Testing proxy for url: " < < toString ( p ) < < " url= " < < url . data ( ) ;
manager - > setProxy ( p ) ;
if ( ! connect ( manager , SIGNAL ( finished ( QNetworkReply * ) ) ,
SLOT ( replyFinished ( QNetworkReply * ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( manager ,
SIGNAL ( authenticationRequired ( QNetworkReply * ,
QAuthenticator * ) ) ,
SIGNAL ( authenticationRequired ( QNetworkReply * ,
QAuthenticator * ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( manager ,
SIGNAL ( proxyAuthenticationRequired
( const QNetworkProxy & , QAuthenticator * ) ) ,
SIGNAL ( proxyAuthenticationRequired
( const QNetworkProxy & , QAuthenticator * ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( manager ,
SIGNAL ( proxyAuthenticationRequired
( const QNetworkProxy & , QAuthenticator * ) ) ,
SLOT ( proxyAuthenticationRequiredLog
( const QNetworkProxy & , QAuthenticator * ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( manager ,
SIGNAL ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ,
SLOT ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ) )
qFatal ( " connect failed " ) ;
QNetworkReply * reply ( 0 ) ;
_requests . insert
( std : : make_pair ( reply = manager - > get
( QNetworkRequest
( QUrl ( QString : : fromStdString ( url ) ) ) ) ,
std : : make_pair ( manager , prxy ) ) ) ;
if ( ! connect ( reply , SIGNAL ( uploadProgress ( qint64 , qint64 ) ) ,
SLOT ( uploadProgress ( qint64 , qint64 ) ) ) )
qFatal ( " connect failed " ) ;
if ( ! connect ( reply , SIGNAL ( downloadProgress ( qint64 , qint64 ) ) ,
SLOT ( downloadProgress ( qint64 , qint64 ) ) ) )
qFatal ( " connect failed " ) ;
}
void clean ( QNetworkAccessManager * manager ) {
if ( ! disconnect ( manager , SIGNAL ( finished ( QNetworkReply * ) ) ,
this , SLOT ( replyFinished ( QNetworkReply * ) ) ) )
qFatal ( " disconnect failed " ) ;
disconnect ( manager ,
SIGNAL ( authenticationRequired ( QNetworkReply * ,
QAuthenticator * ) ) ,
this ,
SIGNAL ( authenticationRequired ( QNetworkReply * ,
QAuthenticator * ) ) ) ;
disconnect ( manager ,
SIGNAL ( proxyAuthenticationRequired ( const QNetworkProxy & ,
QAuthenticator * ) ) ,
this ,
SIGNAL ( proxyAuthenticationRequired ( const QNetworkProxy & ,
QAuthenticator * ) ) ) ;
disconnect ( manager ,
SIGNAL ( proxyAuthenticationRequired ( const QNetworkProxy & ,
QAuthenticator * ) ) ,
this ,
SLOT ( proxyAuthenticationRequiredLog ( const QNetworkProxy & ,
QAuthenticator * ) ) ) ;
disconnect ( manager ,
SIGNAL ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ,
this ,
SLOT ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ) ;
//delete manager;
}
private Q_SLOTS :
void uploadProgress ( qint64 a , qint64 b ) {
qDebug ( ) < < " upload: " < < a < < " / " < < b ;
}
void downloadProgress ( qint64 a , qint64 b ) {
qDebug ( ) < < " download: " < < a < < " / " < < b ;
}
void proxyAuthenticationRequiredLog ( const QNetworkProxy & ,
QAuthenticator * auth ) {
qDebug ( ) < < " proxyAuthenticationRequired for " < < auth - > realm ( ) ;
}
void timeout ( ) {
PROXYFACE_LOG ;
reset ( ) ;
qDebug ( ) < < " Proxy detection timed out " < < " url= " < < _url . data ( ) ;
if ( _direct ) {
qDebug ( ) < < " Direct or preconfigured proxy not available, "
" try autoproxy negotiation " ;
_direct = false ;
# ifndef Q_OS_WIN32
start ( ) ; // autoproxy detection in own thread
# else
run ( ) ;
# endif
} else {
qDebug ( ) < < " No proxy at all, giving up - offline? " ;
proxyError ( QNetworkReply : : TimeoutError ) ;
}
}
void replyFinished ( QNetworkReply * reply ) {
PROXYFACE_LOG ;
qDebug ( ) < < " Proxydetection got reply with status: "
< < reply - > error ( ) < < reply - > errorString ( ) ;
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) {
temporaryError ( reply - > error ( ) , reply - > errorString ( ) ,
toString ( _requests [ reply ] . second ) ) ;
return ; // wait for timeout
}
QNetworkProxy prxy ( _requests [ reply ] . second ) ;
QUrl url ( reply - > url ( ) ) ;
reset ( ) ;
qDebug ( ) < < " SUCCESS - Valid proxy found for url: "
< < " url= " < < url < < " proxy= " < < toString ( prxy ) ;
proxyFound ( url , prxy ) ;
}
void sslErrors ( QNetworkReply * , const QList < QSslError > & l ) {
PROXYFACE_LOG ;
qDebug ( ) < < " ## " < < __PRETTY_FUNCTION__ ;
for ( QList < QSslError > : : const_iterator it ( l . begin ( ) ) ; it ! = l . end ( ) ; + + it )
qDebug ( ) < < " SSL-Error -> " < < it - > errorString ( ) ;
}
void threadFinished ( ) {
PROXYFACE_LOG ;
for ( List : : const_iterator it ( _proxies . begin ( ) ) ;
it ! = _proxies . 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 ) , ( quint16 ) it - > port ) ;
setupProxyCheck ( prxy , _url ) ;
}
QNetworkProxy directProxy ( QNetworkProxy : : NoProxy ) ;
setupProxyCheck ( directProxy , _url ) ;
QNetworkProxy defaultProxy ;
setupProxyCheck ( defaultProxy , _url ) ;
_timeout2 . start ( ) ;
}
protected :
void run ( ) {
PROXYFACE_LOG ;
_proxies = proxies ( _url ) ;
# ifdef Q_OS_WIN32
threadFinished ( ) ;
# endif
}
private :
typedef std : : map
< QNetworkReply * , std : : pair < QNetworkAccessManager * , QNetworkProxy > >
Requests ;
Requests _requests ;
bool _direct ;
QTimer _timeout1 ;
QTimer _timeout2 ;
bool _timeout1Paused ;
bool _timeout2Paused ;
List _proxies ;
std : : string _url ;
# endif
} ;
}
# ifdef WIN32
# ifdef QT_NETWORK_LIB___HAS_A_BUG /// @bug Crashes on Windows Qt 5.2.1
// use Qt if available
# include <proxyface / qtproxy.hxx>
namespace proxy {
typedef QtProxy Face ;
}
# else
// use windoze proprietary winhttp
# include <proxyface / windoze.hxx>
namespace proxy {
typedef Windoze Face ;
}
# endif
# else
# if HAVE_QT == 1
// use Qt if available (not yet linux)
# include <proxyface / qtproxy.hxx>
namespace proxy {
typedef QtProxy Face ;
}
# else
// no Qt support, nor proprietary: use http://code.google.com/p/libproxy
# include <proxyface / unix.hxx>
namespace proxy {
typedef Unix Face ;
}
# endif
# endif
# endif