QGIS API Documentation  2.12.0-Lyon
qgsnetworkaccessmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnetworkaccessmanager.cpp
3  This class implements a QNetworkManager with the ability to chain in
4  own proxy factories.
5 
6  -------------------
7  begin : 2010-05-08
8  copyright : (C) 2010 by Juergen E. Fischer
9  email : jef at norbit dot de
10 
11 ***************************************************************************/
12 
13 /***************************************************************************
14  * *
15  * This program is free software; you can redistribute it and/or modify *
16  * it under the terms of the GNU General Public License as published by *
17  * the Free Software Foundation; either version 2 of the License, or *
18  * (at your option) any later version. *
19  * *
20  ***************************************************************************/
21 
23 
24 #include <qgsapplication.h>
25 #include <qgsmessagelog.h>
26 #include <qgslogger.h>
27 #include <qgis.h>
28 
29 #include <QUrl>
30 #include <QSettings>
31 #include <QTimer>
32 #include <QNetworkReply>
33 #include <QNetworkDiskCache>
34 
35 #ifndef QT_NO_OPENSSL
36 #include <QSslConfiguration>
37 #endif
38 
39 #include "qgsauthmanager.h"
40 
41 
43 {
44  public:
47 
49  {
51 
52  // iterate proxies factories and take first non empty list
53  Q_FOREACH ( QNetworkProxyFactory *f, nam->proxyFactories() )
54  {
55  QList<QNetworkProxy> systemproxies = f->systemProxyForQuery( query );
56  if ( systemproxies.size() > 0 )
57  return systemproxies;
58 
59  QList<QNetworkProxy> proxies = f->queryProxy( query );
60  if ( proxies.size() > 0 )
61  return proxies;
62  }
63 
64  // no proxies from the proxy factor list check for excludes
65  if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
66  return QList<QNetworkProxy>() << nam->fallbackProxy();
67 
68  QString url = query.url().toString();
69 
70  Q_FOREACH ( const QString& exclude, nam->excludeList() )
71  {
72  if ( url.startsWith( exclude ) )
73  {
74  QgsDebugMsg( QString( "using default proxy for %1 [exclude %2]" ).arg( url, exclude ) );
75  return QList<QNetworkProxy>() << QNetworkProxy();
76  }
77  }
78 
79  if ( nam->useSystemProxy() )
80  {
81  QgsDebugMsg( QString( "requesting system proxy for query %1" ).arg( url ) );
83  if ( !proxies.isEmpty() )
84  {
85  QgsDebugMsg( QString( "using system proxy %1:%2 for query" )
86  .arg( proxies.first().hostName() ).arg( proxies.first().port() ) );
87  return proxies;
88  }
89  }
90 
91  QgsDebugMsg( QString( "using fallback proxy for %1" ).arg( url ) );
92  return QList<QNetworkProxy>() << nam->fallbackProxy();
93  }
94 };
95 
96 //
97 // Static calls to enforce singleton behaviour
98 //
100 {
102  return sInstance;
103 }
104 
106  : QNetworkAccessManager( parent )
107  , mUseSystemProxy( false )
108 {
110 }
111 
113 {
114 }
115 
117 {
118  mProxyFactories.insert( 0, factory );
119 }
120 
122 {
123  mProxyFactories.removeAll( factory );
124 }
125 
127 {
128  return mProxyFactories;
129 }
130 
132 {
133  return mExcludedURLs;
134 }
135 
137 {
138  return mFallbackProxy;
139 }
140 
142 {
143  QgsDebugMsg( QString( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
144  .arg( proxy.type() == QNetworkProxy::DefaultProxy ? "DefaultProxy" :
145  proxy.type() == QNetworkProxy::Socks5Proxy ? "Socks5Proxy" :
146  proxy.type() == QNetworkProxy::NoProxy ? "NoProxy" :
147  proxy.type() == QNetworkProxy::HttpProxy ? "HttpProxy" :
148  proxy.type() == QNetworkProxy::HttpCachingProxy ? "HttpCachingProxy" :
149  proxy.type() == QNetworkProxy::FtpCachingProxy ? "FtpCachingProxy" :
150  "Undefined",
151  proxy.hostName() )
152  .arg( proxy.port() )
153  .arg( proxy.user(),
154  proxy.password().isEmpty() ? "not set" : "set" ) );
155 
156  mFallbackProxy = proxy;
157  mExcludedURLs = excludes;
158 }
159 
160 QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
161 {
162  QSettings s;
163 
164  QNetworkRequest *pReq(( QNetworkRequest * ) &req ); // hack user agent
165 
166  QString userAgent = s.value( "/qgis/networkAndProxy/userAgent", "Mozilla/5.0" ).toString();
167  if ( !userAgent.isEmpty() )
168  userAgent += " ";
169  userAgent += QString( "QGIS/%1" ).arg( QGis::QGIS_VERSION );
170  pReq->setRawHeader( "User-Agent", userAgent.toUtf8() );
171 
172 #ifndef QT_NO_OPENSSL
173  bool ishttps = pReq->url().scheme().toLower() == "https";
174  if ( ishttps && !QgsAuthManager::instance()->isDisabled() )
175  {
176  QgsDebugMsg( "Adding trusted CA certs to request" );
177  QSslConfiguration sslconfig( pReq->sslConfiguration() );
178  sslconfig.setCaCertificates( QgsAuthManager::instance()->getTrustedCaCertsCache() );
179 
180  // check for SSL cert custom config
181  QString hostport( QString( "%1:%2" )
182  .arg( pReq->url().host().trimmed() )
183  .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
185  if ( !servconfig.isNull() )
186  {
187  QgsDebugMsg( QString( "Adding SSL custom config to request for %1" ).arg( hostport ) );
188  sslconfig.setProtocol( servconfig.sslProtocol() );
189  sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
190  sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
191  }
192 
193  pReq->setSslConfiguration( sslconfig );
194  }
195 #endif
196 
197  emit requestAboutToBeCreated( op, req, outgoingData );
198  QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
199 
200  emit requestCreated( reply );
201 
202  // abort request, when network timeout happens
203  QTimer *timer = new QTimer( reply );
204  timer->setObjectName( "timeoutTimer" );
205  connect( timer, SIGNAL( timeout() ), this, SLOT( abortRequest() ) );
206  timer->setSingleShot( true );
207  timer->start( s.value( "/qgis/networkAndProxy/networkTimeout", "20000" ).toInt() );
208 
209  connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), timer, SLOT( start() ) );
210  connect( reply, SIGNAL( uploadProgress( qint64, qint64 ) ), timer, SLOT( start() ) );
211 
212  return reply;
213 }
214 
215 void QgsNetworkAccessManager::abortRequest()
216 {
217  QTimer *timer = qobject_cast<QTimer *>( sender() );
218  Q_ASSERT( timer );
219 
220  QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
221  Q_ASSERT( reply );
222 
223  QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
224 
225  if ( reply->isRunning() )
226  reply->close();
227 
228  emit requestTimedOut( reply );
229 }
230 
231 QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl theControl )
232 {
233  switch ( theControl )
234  {
235  case QNetworkRequest::AlwaysNetwork:
236  return "AlwaysNetwork";
237  break;
238  case QNetworkRequest::PreferNetwork:
239  return "PreferNetwork";
240  break;
241  case QNetworkRequest::PreferCache:
242  return "PreferCache";
243  break;
244  case QNetworkRequest::AlwaysCache:
245  return "AlwaysCache";
246  break;
247  default:
248  break;
249  }
250  return "PreferNetwork";
251 }
252 
253 QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &theName )
254 {
255  if ( theName == "AlwaysNetwork" )
256  {
257  return QNetworkRequest::AlwaysNetwork;
258  }
259  else if ( theName == "PreferNetwork" )
260  {
261  return QNetworkRequest::PreferNetwork;
262  }
263  else if ( theName == "PreferCache" )
264  {
265  return QNetworkRequest::PreferCache;
266  }
267  else if ( theName == "AlwaysCache" )
268  {
269  return QNetworkRequest::AlwaysCache;
270  }
271  return QNetworkRequest::PreferNetwork;
272 }
273 
275 {
277  QStringList excludes;
278 
279  QSettings settings;
280 
281  mUseSystemProxy = false;
282 
283  if ( this != instance() )
284  {
285  Qt::ConnectionType connectionType = thread() == instance()->thread() ? Qt::AutoConnection : Qt::BlockingQueuedConnection;
286 
289  connectionType );
290 
291  connect( this, SIGNAL( proxyAuthenticationRequired( const QNetworkProxy &, QAuthenticator * ) ),
293  connectionType );
294 
295  connect( this, SIGNAL( requestTimedOut( QNetworkReply* ) ),
296  instance(), SIGNAL( requestTimedOut( QNetworkReply* ) ) );
297 
298 #ifndef QT_NO_OPENSSL
299  connect( this, SIGNAL( sslErrors( QNetworkReply *, const QList<QSslError> & ) ),
300  instance(), SIGNAL( sslErrors( QNetworkReply *, const QList<QSslError> & ) ),
301  connectionType );
302 #endif
303  }
304 
305  // check if proxy is enabled
306  bool proxyEnabled = settings.value( "proxy/proxyEnabled", false ).toBool();
307  if ( proxyEnabled )
308  {
309  excludes = settings.value( "proxy/proxyExcludedUrls", "" ).toString().split( "|", QString::SkipEmptyParts );
310 
311  //read type, host, port, user, passw from settings
312  QString proxyHost = settings.value( "proxy/proxyHost", "" ).toString();
313  int proxyPort = settings.value( "proxy/proxyPort", "" ).toString().toInt();
314  QString proxyUser = settings.value( "proxy/proxyUser", "" ).toString();
315  QString proxyPassword = settings.value( "proxy/proxyPassword", "" ).toString();
316 
317  QString proxyTypeString = settings.value( "proxy/proxyType", "" ).toString();
318 
319  if ( proxyTypeString == "DefaultProxy" )
320  {
321  mUseSystemProxy = true;
324  if ( !proxies.isEmpty() )
325  {
326  proxy = proxies.first();
327  }
328  QgsDebugMsg( "setting default proxy" );
329  }
330  else
331  {
332  QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
333  if ( proxyTypeString == "Socks5Proxy" )
334  {
335  proxyType = QNetworkProxy::Socks5Proxy;
336  }
337  else if ( proxyTypeString == "HttpProxy" )
338  {
339  proxyType = QNetworkProxy::HttpProxy;
340  }
341  else if ( proxyTypeString == "HttpCachingProxy" )
342  {
343  proxyType = QNetworkProxy::HttpCachingProxy;
344  }
345  else if ( proxyTypeString == "FtpCachingProxy" )
346  {
347  proxyType = QNetworkProxy::FtpCachingProxy;
348  }
349  QgsDebugMsg( QString( "setting proxy %1 %2:%3 %4/%5" )
350  .arg( proxyType )
351  .arg( proxyHost ).arg( proxyPort )
352  .arg( proxyUser, proxyPassword )
353  );
354  proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
355  }
356  }
357 
358  setFallbackProxyAndExcludes( proxy, excludes );
359 
360  QNetworkDiskCache *newcache = qobject_cast<QNetworkDiskCache*>( cache() );
361  if ( !newcache )
362  newcache = new QNetworkDiskCache( this );
363 
364  QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString();
365  qint64 cacheSize = settings.value( "cache/size", 50 * 1024 * 1024 ).toULongLong();
366  QgsDebugMsg( QString( "setCacheDirectory: %1" ).arg( cacheDirectory ) );
367  QgsDebugMsg( QString( "setMaximumCacheSize: %1" ).arg( cacheSize ) );
368  newcache->setCacheDirectory( cacheDirectory );
369  newcache->setMaximumCacheSize( cacheSize );
370  QgsDebugMsg( QString( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ) );
371  QgsDebugMsg( QString( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ) );
372 
373  if ( cache() != newcache )
374  setCache( newcache );
375 }
376 
378 {
379  QgsDebugMsg( "Entered" );
380  QNetworkReply * reply = get( request );
381  emit requestSent( reply, QObject::sender() );
382 }
383 
385 {
386  QgsDebugMsg( "Entered" );
387  if ( !reply )
388  {
389  return;
390  }
391  reply->abort();
392  reply->deleteLater();
393 }
static const char * QGIS_VERSION
Definition: qgis.h:42
const QStringList & excludeList() const
retrieve exclude list (urls shouldn't use the fallback proxy)
void requestCreated(QNetworkReply *)
virtual QNetworkReply * createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
QString password() const
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl theControl)
Get name for QNetworkRequest::CacheLoadControl.
void sendGet(const QNetworkRequest &request)
Send GET request, calls get().
void sslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
static QgsAuthManager * instance()
Enforce singleton pattern.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
QString user() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
quint16 port() const
QgsNetworkAccessManager(QObject *parent=0)
QObject * sender() const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QSslConfiguration sslConfiguration() const
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &theName)
Get QNetworkRequest::CacheLoadControl from name.
bool isRunning() const
QString host() const
bool isNull() const
Whether configuration is null (missing components)
const QNetworkProxy & fallbackProxy() const
retrieve fall back proxy (for urls that no factory returned proxies for)
Configuration container for SSL server connection exceptions or overrides.
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
void setupDefaultProxyAndCache()
Setup the NAM according to the user's settings.
int port() const
QThread * thread() const
virtual QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=0) override
QString toString(QFlags< QUrl::FormattingOption > options) const
qulonglong toULongLong(bool *ok) const
QString tr(const char *sourceText, const char *disambiguation, int n)
const QList< QNetworkProxyFactory * > proxyFactories() const
retrieve proxy factory list
QAbstractNetworkCache * cache() const
int size() const
void setCacheDirectory(const QString &cacheDir)
void setProxyFactory(QNetworkProxyFactory *factory)
QNetworkProxy proxy() const
QString hostName() const
void setCaCertificates(const QList< QSslCertificate > &certificates)
virtual QList< QNetworkProxy > queryProxy(const QNetworkProxyQuery &query)=0
void deleteReply(QNetworkReply *reply)
Abort and delete reply.
int toInt(bool *ok) const
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client's peer verify mode to use in connections.
QNetworkProxy::ProxyType type() const
void requestTimedOut(QNetworkReply *)
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
int toInt(bool *ok, int base) const
bool isEmpty() const
void setObjectName(const QString &name)
bool isEmpty() const
int removeAll(const T &value)
QString trimmed() const
void requestSent(QNetworkReply *reply, QObject *sender)
Emitted when request was sent by request()
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void setMaximumCacheSize(qint64 size)
void removeProxyFactory(QNetworkProxyFactory *factory)
remove a factory from the proxy factories list
QCoreApplication * instance()
void deleteLater()
T & first()
QString scheme() const
QString toLower() const
virtual void abort()=0
void insertProxyFactory(QNetworkProxyFactory *factory)
insert a factory into the proxy factories list
QUrl url() const
QString cacheDirectory() const
QVariant value(const QString &key, const QVariant &defaultValue) const
int sslPeerVerifyDepth() const
Number or SSL client's peer to verify in connections.
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
void insert(int i, const T &value)
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
QList< QNetworkProxy > systemProxyForQuery(const QNetworkProxyQuery &query)
QUrl url() const
const QgsAuthConfigSslServer getSslCertCustomConfigByHost(const QString &hostport)
Get an SSL certificate custom config by host:port.
bool toBool() const
void start(int msec)
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
void setUseSystemConfiguration(bool enable)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
virtual void close()
void requestAboutToBeCreated(QNetworkAccessManager::Operation, const QNetworkRequest &, QIODevice *)
void setCache(QAbstractNetworkCache *cache)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes)
set fallback proxy and URL that shouldn't use it.
void setSslConfiguration(const QSslConfiguration &config)
virtual QList< QNetworkProxy > queryProxy(const QNetworkProxyQuery &query=QNetworkProxyQuery()) override
qint64 maximumCacheSize() const
void setSingleShot(bool singleShot)
QByteArray toUtf8() const