QGIS API Documentation  3.11.0-Master (68611307d7)
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 #include "qgssettings.h"
29 #include "qgsnetworkdiskcache.h"
30 #include "qgsauthmanager.h"
31 #include "qgsnetworkreply.h"
33 
34 #include <QUrl>
35 #include <QTimer>
36 #include <QBuffer>
37 #include <QNetworkReply>
38 #include <QThreadStorage>
39 #include <QAuthenticator>
40 #include <QStandardPaths>
41 
42 #ifndef QT_NO_SSL
43 #include <QSslConfiguration>
44 #endif
45 
46 #include "qgsnetworkdiskcache.h"
47 #include "qgsauthmanager.h"
48 
49 QgsNetworkAccessManager *QgsNetworkAccessManager::sMainNAM = nullptr;
50 
52 class QgsNetworkProxyFactory : public QNetworkProxyFactory
53 {
54  public:
55  QgsNetworkProxyFactory() = default;
56 
57  QList<QNetworkProxy> queryProxy( const QNetworkProxyQuery &query = QNetworkProxyQuery() ) override
58  {
60 
61  // iterate proxies factories and take first non empty list
62  const auto constProxyFactories = nam->proxyFactories();
63  for ( QNetworkProxyFactory *f : constProxyFactories )
64  {
65  QList<QNetworkProxy> systemproxies = f->systemProxyForQuery( query );
66  if ( !systemproxies.isEmpty() )
67  return systemproxies;
68 
69  QList<QNetworkProxy> proxies = f->queryProxy( query );
70  if ( !proxies.isEmpty() )
71  return proxies;
72  }
73 
74  // no proxies from the proxy factory list check for excludes
75  if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
76  return QList<QNetworkProxy>() << nam->fallbackProxy();
77 
78  QString url = query.url().toString();
79 
80  const auto constNoProxyList = nam->noProxyList();
81  for ( const QString &noProxy : constNoProxyList )
82  {
83  if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
84  {
85  QgsDebugMsgLevel( QStringLiteral( "don't using any proxy for %1 [exclude %2]" ).arg( url, noProxy ), 4 );
86  return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
87  }
88  }
89 
90  const auto constExcludeList = nam->excludeList();
91  for ( const QString &exclude : constExcludeList )
92  {
93  if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
94  {
95  QgsDebugMsgLevel( QStringLiteral( "using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
96  return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
97  }
98  }
99 
100  if ( nam->useSystemProxy() )
101  {
102  QgsDebugMsgLevel( QStringLiteral( "requesting system proxy for query %1" ).arg( url ), 4 );
103  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
104  if ( !proxies.isEmpty() )
105  {
106  QgsDebugMsgLevel( QStringLiteral( "using system proxy %1:%2 for query" )
107  .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
108  return proxies;
109  }
110  }
111 
112  QgsDebugMsgLevel( QStringLiteral( "using fallback proxy for %1" ).arg( url ), 4 );
113  return QList<QNetworkProxy>() << nam->fallbackProxy();
114  }
115 };
117 
118 //
119 // Static calls to enforce singleton behavior
120 //
121 QgsNetworkAccessManager *QgsNetworkAccessManager::instance( Qt::ConnectionType connectionType )
122 {
123  static QThreadStorage<QgsNetworkAccessManager> sInstances;
124  QgsNetworkAccessManager *nam = &sInstances.localData();
125 
126  if ( nam->thread() == qApp->thread() )
127  sMainNAM = nam;
128 
129  if ( !nam->mInitialized )
130  nam->setupDefaultProxyAndCache( connectionType );
131 
132  return nam;
133 }
134 
136  : QNetworkAccessManager( parent )
137 {
138  setProxyFactory( new QgsNetworkProxyFactory() );
139 }
140 
141 void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
142 {
143  Q_ASSERT( sMainNAM == this );
144  mSslErrorHandler = std::move( handler );
145 }
146 
147 void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
148 {
149  Q_ASSERT( sMainNAM == this );
150  mAuthHandler = std::move( handler );
151 }
152 
153 void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
154 {
155  mProxyFactories.insert( 0, factory );
156 }
157 
158 void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
159 {
160  mProxyFactories.removeAll( factory );
161 }
162 
163 const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
164 {
165  return mProxyFactories;
166 }
167 
169 {
170  return mExcludedURLs;
171 }
172 
174 {
175  return mNoProxyURLs;
176 }
177 
178 const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
179 {
180  return mFallbackProxy;
181 }
182 
183 void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
184 {
185  QgsDebugMsgLevel( QStringLiteral( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
186  .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral( "DefaultProxy" ) :
187  proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral( "Socks5Proxy" ) :
188  proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral( "NoProxy" ) :
189  proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral( "HttpProxy" ) :
190  proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral( "HttpCachingProxy" ) :
191  proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral( "FtpCachingProxy" ) :
192  QStringLiteral( "Undefined" ),
193  proxy.hostName() )
194  .arg( proxy.port() )
195  .arg( proxy.user(),
196  proxy.password().isEmpty() ? QStringLiteral( "not set" ) : QStringLiteral( "set" ) ), 4 );
197 
198  mFallbackProxy = proxy;
199  mExcludedURLs = excludes;
200  // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
201  mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
202  []( const QString & url )
203  {
204  return url.trimmed().isEmpty();
205  } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
206 
207  mNoProxyURLs = noProxyURLs;
208  mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(), // clazy:exclude=detaching-member
209  []( const QString & url )
210  {
211  return url.trimmed().isEmpty();
212  } ), mNoProxyURLs.end() ); // clazy:exclude=detaching-member
213 }
214 
215 QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
216 {
217  QgsSettings s;
218 
219  QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
220 
221  QString userAgent = s.value( QStringLiteral( "/qgis/networkAndProxy/userAgent" ), "Mozilla/5.0" ).toString();
222  if ( !userAgent.isEmpty() )
223  userAgent += ' ';
224  userAgent += QStringLiteral( "QGIS/%1" ).arg( Qgis::versionInt() );
225  pReq->setRawHeader( "User-Agent", userAgent.toLatin1() );
226 
227 #ifndef QT_NO_SSL
228  bool ishttps = pReq->url().scheme().compare( QLatin1String( "https" ), Qt::CaseInsensitive ) == 0;
229  if ( ishttps && !QgsApplication::authManager()->isDisabled() )
230  {
231  QgsDebugMsgLevel( QStringLiteral( "Adding trusted CA certs to request" ), 3 );
232  QSslConfiguration sslconfig( pReq->sslConfiguration() );
233  // Merge trusted CAs with any additional CAs added by the authentication methods
234  sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
235  // check for SSL cert custom config
236  QString hostport( QStringLiteral( "%1:%2" )
237  .arg( pReq->url().host().trimmed() )
238  .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
240  if ( !servconfig.isNull() )
241  {
242  QgsDebugMsg( QStringLiteral( "Adding SSL custom config to request for %1" ).arg( hostport ) );
243  sslconfig.setProtocol( servconfig.sslProtocol() );
244  sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
245  sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
246  }
247 
248  pReq->setSslConfiguration( sslconfig );
249  }
250 #endif
251 
252  static QAtomicInt sRequestId = 0;
253  const int requestId = ++sRequestId;
254  QByteArray content;
255  if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
256  {
257  content = buffer->buffer();
258  }
259 
260  emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
262  emit requestAboutToBeCreated( op, req, outgoingData );
264  QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
265  reply->setProperty( "requestId", requestId );
266 
268  emit requestCreated( reply );
270 
271  connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
272 #ifndef QT_NO_SSL
273  connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
274 #endif
275 
276  // The timer will call abortRequest slot to abort the connection if needed.
277  // The timer is stopped by the finished signal and is restarted on downloadProgress and
278  // uploadProgress.
279  QTimer *timer = new QTimer( reply );
280  timer->setObjectName( QStringLiteral( "timeoutTimer" ) );
281  connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
282  timer->setSingleShot( true );
283  timer->start( timeout() );
284 
285  connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
286  connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
287  connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
288  QgsDebugMsgLevel( QStringLiteral( "Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
289 
290  return reply;
291 }
292 
293 #ifndef QT_NO_SSL
294 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
295 {
296  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
297  mSslErrorWaitCondition.wakeOne();
298 }
299 #endif
300 
301 void QgsNetworkAccessManager::abortRequest()
302 {
303  QTimer *timer = qobject_cast<QTimer *>( sender() );
304  Q_ASSERT( timer );
305 
306  QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
307  Q_ASSERT( reply );
308 
309  reply->abort();
310  QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
311  QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
312  // Notify the application
313  emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
314  emit requestTimedOut( reply );
315 }
316 
317 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
318 {
319  emit finished( QgsNetworkReplyContent( reply ) );
320 }
321 
322 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
323 {
324  if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
325  {
326  emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
327  }
328 }
329 
330 #ifndef QT_NO_SSL
331 void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
332 {
333  QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
334  Q_ASSERT( reply );
335  Q_ASSERT( reply->manager() == this );
336 
337  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ) );
338  pauseTimeout( reply );
339 
340  emit requestEncounteredSslErrors( getRequestId( reply ), errors );
341 
342  // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
343  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
344  emit sslErrorsOccurred( reply, errors );
345  if ( this != sMainNAM )
346  {
347  // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
348  // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
349  mSslErrorHandlerMutex.lock();
350  mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
351  mSslErrorHandlerMutex.unlock();
352  afterSslErrorHandled( reply );
353  }
354 }
355 
356 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
357 {
358  if ( reply->manager() == this )
359  {
360  restartTimeout( reply );
361  emit sslErrorsHandled( reply );
362  }
363  else if ( this == sMainNAM )
364  {
365  // notify other threads to allow them to handle the reply
366  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled(); // safe to call directly - the other thread will be stuck waiting for us
367  }
368 }
369 
370 void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
371 {
372  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
373  mAuthRequestWaitCondition.wakeOne();
374 }
375 
376 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
377 {
378  if ( reply->manager() == this )
379  {
380  restartTimeout( reply );
381  emit authRequestHandled( reply );
382  }
383  else if ( this == sMainNAM )
384  {
385  // notify other threads to allow them to handle the reply
386  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterAuthRequestHandled(); // safe to call directly - the other thread will be stuck waiting for us
387  }
388 }
389 
390 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
391 {
392  Q_ASSERT( reply->manager() == this );
393 
394  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
395  if ( timer && timer->isActive() )
396  {
397  timer->stop();
398  }
399 }
400 
401 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
402 {
403  Q_ASSERT( reply->manager() == this );
404  // restart reply timeout
405  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
406  if ( timer )
407  {
408  Q_ASSERT( !timer->isActive() );
409  QgsDebugMsg( QStringLiteral( "Restarting network reply timeout" ) );
410  timer->setSingleShot( true );
411  timer->start( timeout() );
412  }
413 }
414 
415 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
416 {
417  return reply->property( "requestId" ).toInt();
418 }
419 
420 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
421 {
422  mSslErrorHandler->handleSslErrors( reply, errors );
423  afterSslErrorHandled( reply );
424 }
425 
426 #endif
427 
428 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
429 {
430  Q_ASSERT( reply );
431  Q_ASSERT( reply->manager() == this );
432 
433  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ) );
434  pauseTimeout( reply );
435 
436  emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
437 
438  // in main thread this will trigger auth handler immediately and return once the request is satisfied,
439  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
440  emit authRequestOccurred( reply, auth );
441 
442  if ( this != sMainNAM )
443  {
444  // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
445  // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
446  mAuthRequestHandlerMutex.lock();
447  mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
448  mAuthRequestHandlerMutex.unlock();
449  afterAuthRequestHandled( reply );
450  }
451 }
452 
453 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
454 {
455  mAuthHandler->handleAuthRequest( reply, auth );
456 
457  emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
458 
459  afterAuthRequestHandled( reply );
460 }
461 
462 QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
463 {
464  switch ( control )
465  {
466  case QNetworkRequest::AlwaysNetwork:
467  return QStringLiteral( "AlwaysNetwork" );
468  case QNetworkRequest::PreferNetwork:
469  return QStringLiteral( "PreferNetwork" );
470  case QNetworkRequest::PreferCache:
471  return QStringLiteral( "PreferCache" );
472  case QNetworkRequest::AlwaysCache:
473  return QStringLiteral( "AlwaysCache" );
474  }
475  return QStringLiteral( "PreferNetwork" );
476 }
477 
478 QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
479 {
480  if ( name == QLatin1String( "AlwaysNetwork" ) )
481  {
482  return QNetworkRequest::AlwaysNetwork;
483  }
484  else if ( name == QLatin1String( "PreferNetwork" ) )
485  {
486  return QNetworkRequest::PreferNetwork;
487  }
488  else if ( name == QLatin1String( "PreferCache" ) )
489  {
490  return QNetworkRequest::PreferCache;
491  }
492  else if ( name == QLatin1String( "AlwaysCache" ) )
493  {
494  return QNetworkRequest::AlwaysCache;
495  }
496  return QNetworkRequest::PreferNetwork;
497 }
498 
499 void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
500 {
501  mInitialized = true;
502  mUseSystemProxy = false;
503 
504  Q_ASSERT( sMainNAM );
505 
506  if ( sMainNAM != this )
507  {
508  connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
509  sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
510  connectionType );
511 
512  connect( this, qgis::overload< QNetworkReply *>::of( &QgsNetworkAccessManager::requestTimedOut ),
513  sMainNAM, qgis::overload< QNetworkReply *>::of( &QgsNetworkAccessManager::requestTimedOut ) );
514 
515  connect( this, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ),
516  sMainNAM, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ) );
517 
518  connect( this, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestAboutToBeCreated ),
519  sMainNAM, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
520 
521  connect( this, qgis::overload< QgsNetworkReplyContent >::of( &QgsNetworkAccessManager::finished ),
522  sMainNAM, qgis::overload< QgsNetworkReplyContent >::of( &QgsNetworkAccessManager::finished ) );
523 
525 
526 #ifndef QT_NO_SSL
527  connect( this, &QNetworkAccessManager::sslErrors,
528  sMainNAM, &QNetworkAccessManager::sslErrors,
529  connectionType );
530 
532 #endif
533 
535  }
536  else
537  {
538 #ifndef QT_NO_SSL
539  setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
540 #endif
541  setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler>() );
542  }
543 #ifndef QT_NO_SSL
544  connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
545 #endif
546  connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
547  connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
548 
549  connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
550 
551  // check if proxy is enabled
552  QgsSettings settings;
553  QNetworkProxy proxy;
554  QStringList excludes;
555  QStringList noProxyURLs;
556 
557  bool proxyEnabled = settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool();
558  if ( proxyEnabled )
559  {
560  // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
561  // meaning the system one
562  excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
563 
564  noProxyURLs = settings.value( QStringLiteral( "proxy/noProxyUrls" ), QStringList() ).toStringList();
565 
566  //read type, host, port, user, passw from settings
567  QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
568  int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
569 
570  QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
571  QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
572 
573  QString proxyTypeString = settings.value( QStringLiteral( "proxy/proxyType" ), "" ).toString();
574 
575  if ( proxyTypeString == QLatin1String( "DefaultProxy" ) )
576  {
577  mUseSystemProxy = true;
578  QNetworkProxyFactory::setUseSystemConfiguration( true );
579  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
580  if ( !proxies.isEmpty() )
581  {
582  proxy = proxies.first();
583  }
584  QgsDebugMsgLevel( QStringLiteral( "setting default proxy" ), 4 );
585  }
586  else
587  {
588  QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
589  if ( proxyTypeString == QLatin1String( "Socks5Proxy" ) )
590  {
591  proxyType = QNetworkProxy::Socks5Proxy;
592  }
593  else if ( proxyTypeString == QLatin1String( "HttpProxy" ) )
594  {
595  proxyType = QNetworkProxy::HttpProxy;
596  }
597  else if ( proxyTypeString == QLatin1String( "HttpCachingProxy" ) )
598  {
599  proxyType = QNetworkProxy::HttpCachingProxy;
600  }
601  else if ( proxyTypeString == QLatin1String( "FtpCachingProxy" ) )
602  {
603  proxyType = QNetworkProxy::FtpCachingProxy;
604  }
605  QgsDebugMsg( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
606  .arg( proxyType )
607  .arg( proxyHost ).arg( proxyPort )
608  .arg( proxyUser, proxyPassword )
609  );
610  proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
611  }
612  // Setup network proxy authentication configuration
613  QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
614  if ( !authcfg.isEmpty( ) )
615  {
616  QgsDebugMsg( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
617  // Never crash! Never.
619  QgsApplication::authManager()->updateNetworkProxy( proxy, authcfg );
620  }
621  }
622 
623  setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
624 
625  QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
626  if ( !newcache )
627  newcache = new QgsNetworkDiskCache( this );
628 
629  QString cacheDirectory = settings.value( QStringLiteral( "cache/directory" ) ).toString();
630  if ( cacheDirectory.isEmpty() )
631  cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
632  qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 50 * 1024 * 1024 ).toLongLong();
633  newcache->setCacheDirectory( cacheDirectory );
634  newcache->setMaximumCacheSize( cacheSize );
635  QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 );
636  QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 );
637 
638  if ( cache() != newcache )
639  setCache( newcache );
640 }
641 
643 {
644  return QgsSettings().value( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt();
645 }
646 
648 {
649  QgsSettings().setValue( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), time );
650 }
651 
652 QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
653 {
655  br.setAuthCfg( authCfg );
656  br.get( request, forceRefresh, feedback );
657  return br.reply();
658 }
659 
660 QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
661 {
663  br.setAuthCfg( authCfg );
664  br.post( request, data, forceRefresh, feedback );
665  return br.reply();
666 }
667 
668 
669 //
670 // QgsNetworkRequestParameters
671 //
672 
673 QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
674  : mOperation( operation )
675  , mRequest( request )
676  , mOriginatingThreadId( QStringLiteral( "0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
677  , mRequestId( requestId )
678  , mContent( content )
679  , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
680  , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
681 {
682 }
683 
684 
685 //
686 // QgsSslErrorHandler
687 //
688 
689 void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
690 {
691  Q_UNUSED( reply )
692  QgsDebugMsg( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
693 }
694 
695 //
696 // QgsNetworkAuthenticationHandler
697 //
698 
699 void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
700 {
701  Q_UNUSED( reply )
702  QgsDebugMsg( QStringLiteral( "Network reply required authentication, but no handler was in place to provide this authentication request while accessing the URL:\n%1" ).arg( reply->request().url().toString() ) );
703 }
virtual void handleSslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
Called whenever SSL errors are encountered during a network reply.
bool isNull() const
Whether configuration is null (missing components)
static QList< QSslCertificate > casMerge(const QList< QSslCertificate > &bundle1, const QList< QSslCertificate > &bundle2)
casMerge merges two certificate bundles in a single one removing duplicates, the certificates from th...
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl control)
Returns the name for QNetworkRequest::CacheLoadControl.
int sslPeerVerifyDepth() const
Number or SSL client&#39;s peer to verify in connections.
Q_DECL_DEPRECATED void requestCreated(QNetworkReply *)
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkProxy with an authentication config.
void setCacheDirectory(const QString &cacheDir)
QgsNetworkRequestParameters()=default
Default constructor.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:731
Configuration container for SSL server connection exceptions or overrides.
QStringList excludeList() const
Returns the proxy exclude list.
Encapsulates parameters and properties of a network request.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &name)
Returns QNetworkRequest::CacheLoadControl from a name.
QgsNetworkAccessManager(QObject *parent=nullptr)
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client&#39;s peer verify mode to use in connections.
virtual void handleAuthRequest(QNetworkReply *reply, QAuthenticator *auth)
Called whenever network authentication requests are encountered during a network reply.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:45
ErrorCode post(QNetworkRequest &request, const QByteArray &data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
void finished(QgsNetworkReplyContent reply)
Emitted whenever a pending network reply is finished.
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition: qgis.cpp:281
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
QString cacheDirectory() const
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made...
void removeProxyFactory(QNetworkProxyFactory *factory)
Removes a factory from the proxy factories list.
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port) ...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
void setSslErrorHandler(std::unique_ptr< QgsSslErrorHandler > handler)
Sets the application SSL error handler, which is used to respond to SSL errors encountered during net...
Q_DECL_DEPRECATED void requestAboutToBeCreated(QNetworkAccessManager::Operation, const QNetworkRequest &, QIODevice *)
void insertProxyFactory(QNetworkProxyFactory *factory)
Inserts a factory into the proxy factories list.
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs)
Sets the fallback proxy and URLs which shouldn&#39;t use it.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:732
static QgsAuthManager * authManager()
Returns the application&#39;s authentication manager instance.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void requestAuthDetailsAdded(int requestId, const QString &realm, const QString &user, const QString &password)
Emitted when network authentication details have been added to a request.
void setupDefaultProxyAndCache(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Setup the QgsNetworkAccessManager (NAM) according to the user&#39;s settings.
void requestEncounteredSslErrors(int requestId, const QList< QSslError > &errors)
Emitted when a network request encounters SSL errors.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
qint64 maximumCacheSize() const
static int timeout()
Returns the network timeout length, in milliseconds.
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
void downloadProgress(int requestId, qint64 bytesReceived, qint64 bytesTotal)
Emitted when a network reply receives a progress report.
bool useSystemProxy() const
Returns whether the system proxy should be used.
void setAuthHandler(std::unique_ptr< QgsNetworkAuthenticationHandler > handler)
Sets the application network authentication handler, which is used to respond to network authenticati...
static QgsNetworkReplyContent blockingPost(QNetworkRequest &request, const QByteArray &data, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Posts a POST request to obtain the contents of the target request, using the given data...
static QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
QStringList noProxyList() const
Returns the no proxy list.
network access manager for QGISThis class implements the QGIS network access manager.
void setMaximumCacheSize(qint64 size)
Wrapper implementation of QNetworkDiskCache with all methods guarded by a mutex soly for internal use...
void requestRequiresAuth(int requestId, const QString &realm)
Emitted when a network request prompts an authentication request.
static void setTimeout(int time)
Sets the maximum timeout time for network requests, in milliseconds.
const QList< QNetworkProxyFactory * > proxyFactories() const
Returns a list of proxy factories used by the manager.