QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
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 "qgis.h"
25#include "qgsapplication.h"
26#include "qgsauthmanager.h"
28#include "qgslogger.h"
29#include "qgsmessagelog.h"
30#include "qgsnetworkdiskcache.h"
31#include "qgsnetworkreply.h"
32#include "qgssettings.h"
35#include "qgssettingstree.h"
36
37#include <QAuthenticator>
38#include <QBuffer>
39#include <QNetworkReply>
40#include <QRecursiveMutex>
41#include <QStandardPaths>
42#include <QString>
43#include <QThreadStorage>
44#include <QTimer>
45#include <QUrl>
46#include <QUuid>
47
48#include "moc_qgsnetworkaccessmanager.cpp"
49
50using namespace Qt::StringLiterals;
51
52const QgsSettingsEntryInteger *QgsNetworkAccessManager::settingsNetworkTimeout = new QgsSettingsEntryInteger( u"network-timeout"_s, QgsSettingsTree::sTreeNetwork, 60000, QObject::tr( "Network timeout" ) );
53
54#ifndef QT_NO_SSL
55#include <QSslConfiguration>
56#endif
57
58#include "qgsnetworkdiskcache.h"
59#include "qgsauthmanager.h"
60
61QgsNetworkAccessManager *QgsNetworkAccessManager::sMainNAM = nullptr;
62
63static std::vector< std::pair< QString, std::function< void( QNetworkRequest * ) > > > sCustomPreprocessors;
64static std::vector< std::pair< QString, std::function< void( QNetworkRequest *, int &op, QByteArray *data ) > > > sCustomAdvancedPreprocessors;
65static std::vector< std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > > sCustomReplyPreprocessors;
66
68class QgsNetworkProxyFactory : public QNetworkProxyFactory
69{
70 public:
71 QgsNetworkProxyFactory() = default;
72
73 QList<QNetworkProxy> queryProxy( const QNetworkProxyQuery &query = QNetworkProxyQuery() ) override
74 {
75 QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
76
77 // iterate proxies factories and take first non empty list
78 const auto constProxyFactories = nam->proxyFactories();
79 for ( QNetworkProxyFactory *f : constProxyFactories )
80 {
81 QList<QNetworkProxy> systemproxies = QNetworkProxyFactory::systemProxyForQuery( query );
82 if ( !systemproxies.isEmpty() )
83 return systemproxies;
84
85 QList<QNetworkProxy> proxies = f->queryProxy( query );
86 if ( !proxies.isEmpty() )
87 return proxies;
88 }
89
90 // no proxies from the proxy factory list check for excludes
91 if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
92 return QList<QNetworkProxy>() << nam->fallbackProxy();
93
94 const QString url = query.url().toString();
95
96 const auto constNoProxyList = nam->noProxyList();
97 for ( const QString &noProxy : constNoProxyList )
98 {
99 if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
100 {
101 QgsDebugMsgLevel( u"don't using any proxy for %1 [exclude %2]"_s.arg( url, noProxy ), 4 );
102 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
103 }
104 }
105
106 const auto constExcludeList = nam->excludeList();
107 for ( const QString &exclude : constExcludeList )
108 {
109 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
110 {
111 QgsDebugMsgLevel( u"using default proxy for %1 [exclude %2]"_s.arg( url, exclude ), 4 );
112 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
113 }
114 }
115
116 if ( nam->useSystemProxy() )
117 {
118 QgsDebugMsgLevel( u"requesting system proxy for query %1"_s.arg( url ), 4 );
119 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
120 if ( !proxies.isEmpty() )
121 {
122 QgsDebugMsgLevel( u"using system proxy %1:%2 for query"_s.arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
123 return proxies;
124 }
125 }
126
127 QgsDebugMsgLevel( u"using fallback proxy for %1"_s.arg( url ), 4 );
128 return QList<QNetworkProxy>() << nam->fallbackProxy();
129 }
130};
132
134class QgsNetworkCookieJar : public QNetworkCookieJar
135{
136 Q_OBJECT
137
138 public:
139 QgsNetworkCookieJar( QgsNetworkAccessManager *parent )
140 : QNetworkCookieJar( parent )
141 , mNam( parent )
142 {}
143
144 bool deleteCookie( const QNetworkCookie &cookie ) override
145 {
146 const QMutexLocker locker( &mMutex );
147 if ( QNetworkCookieJar::deleteCookie( cookie ) )
148 {
149 emit mNam->cookiesChanged( allCookies() );
150 return true;
151 }
152 return false;
153 }
154 bool insertCookie( const QNetworkCookie &cookie ) override
155 {
156 const QMutexLocker locker( &mMutex );
157 if ( QNetworkCookieJar::insertCookie( cookie ) )
158 {
159 emit mNam->cookiesChanged( allCookies() );
160 return true;
161 }
162 return false;
163 }
164 bool setCookiesFromUrl( const QList<QNetworkCookie> &cookieList, const QUrl &url ) override
165 {
166 const QMutexLocker locker( &mMutex );
167 return QNetworkCookieJar::setCookiesFromUrl( cookieList, url );
168 }
169 bool updateCookie( const QNetworkCookie &cookie ) override
170 {
171 const QMutexLocker locker( &mMutex );
172 if ( QNetworkCookieJar::updateCookie( cookie ) )
173 {
174 emit mNam->cookiesChanged( allCookies() );
175 return true;
176 }
177 return false;
178 }
179
180 // Override these to make them public
181 QList<QNetworkCookie> allCookies() const
182 {
183 const QMutexLocker locker( &mMutex );
184 return QNetworkCookieJar::allCookies();
185 }
186 void setAllCookies( const QList<QNetworkCookie> &cookieList )
187 {
188 const QMutexLocker locker( &mMutex );
189 QNetworkCookieJar::setAllCookies( cookieList );
190 }
191
192 QgsNetworkAccessManager *mNam = nullptr;
193 mutable QRecursiveMutex mMutex;
194};
196
197
198//
199// Static calls to enforce singleton behavior
200//
202{
203 static QThreadStorage<QgsNetworkAccessManager> sInstances;
204 QgsNetworkAccessManager *nam = &sInstances.localData();
205
206 if ( nam->thread() == qApp->thread() )
207 sMainNAM = nam;
208
209 if ( !nam->mInitialized )
210 {
211 QgsDebugMsgLevel( u"Initializing new network access manager for %1thread: %2"_s.arg( QThread::currentThread() == qApp->thread() ? u"MAIN "_s : QString() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ), 2 );
212 nam->setupDefaultProxyAndCache( connectionType );
213 nam->setCacheDisabled( sMainNAM->cacheDisabled() );
214 }
215 else
216 {
217 QgsDebugMsgLevel( u"Network access manager retrieved for %1thread: %2"_s.arg( QThread::currentThread() == qApp->thread() ? u"MAIN "_s : QString() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ), 4 );
218 }
219
220 return nam;
221}
222
224 : QNetworkAccessManager( parent )
225 , mSslErrorHandlerSemaphore( 1 )
226 , mAuthRequestHandlerSemaphore( 1 )
227{
228 setRedirectPolicy( QNetworkRequest::NoLessSafeRedirectPolicy );
229 setProxyFactory( new QgsNetworkProxyFactory() );
230 setCookieJar( new QgsNetworkCookieJar( this ) );
231 enableStrictTransportSecurityStore( true );
232 setStrictTransportSecurityEnabled( true );
233}
234
235void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
236{
237 Q_ASSERT( sMainNAM == this );
238 mSslErrorHandler = std::move( handler );
239}
240
241void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
242{
243 Q_ASSERT( sMainNAM == this );
244 mAuthHandler = std::move( handler );
245}
246
247void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
248{
249 mProxyFactories.insert( 0, factory );
250}
251
252void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
253{
254 mProxyFactories.removeAll( factory );
255}
256
257const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
258{
259 return mProxyFactories;
260}
261
263{
264 return mExcludedURLs;
265}
266
268{
269 return mNoProxyURLs;
270}
271
272const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
273{
274 return mFallbackProxy;
275}
276
277void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
278{
280 u"proxy settings: (type:%1 host: %2:%3, user:%4, password:%5"_s
281 .arg(
282 proxy.type() == QNetworkProxy::DefaultProxy ? u"DefaultProxy"_s
283 : proxy.type() == QNetworkProxy::Socks5Proxy ? u"Socks5Proxy"_s
284 : proxy.type() == QNetworkProxy::NoProxy ? u"NoProxy"_s
285 : proxy.type() == QNetworkProxy::HttpProxy ? u"HttpProxy"_s
286 : proxy.type() == QNetworkProxy::HttpCachingProxy ? u"HttpCachingProxy"_s
287 : proxy.type() == QNetworkProxy::FtpCachingProxy ? u"FtpCachingProxy"_s
288 : u"Undefined"_s,
289 proxy.hostName()
290 )
291 .arg( proxy.port() )
292 .arg( proxy.user(), proxy.password().isEmpty() ? u"not set"_s : u"set"_s ),
293 4
294 );
295
296 mFallbackProxy = proxy;
297 mExcludedURLs = excludes;
298 // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
299 mExcludedURLs.erase(
300 std::remove_if(
301 mExcludedURLs.begin(),
302 mExcludedURLs.end(), // clazy:exclude=detaching-member
303 []( const QString &url ) { return url.trimmed().isEmpty(); }
304 ),
305 mExcludedURLs.end()
306 ); // clazy:exclude=detaching-member
307
308 mNoProxyURLs = noProxyURLs;
309 mNoProxyURLs.erase(
310 std::remove_if(
311 mNoProxyURLs.begin(),
312 mNoProxyURLs.end(), // clazy:exclude=detaching-member
313 []( const QString &url ) { return url.trimmed().isEmpty(); }
314 ),
315 mNoProxyURLs.end()
316 ); // clazy:exclude=detaching-member
317}
318
319QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
320{
321 QgsDebugMsgLevel( u"Creating new network request on thread: %1 for %2"_s.arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( req.url().toString() ), 3 );
322
323 const QgsSettings s;
324
325 // copy request so we can modify it
326 QNetworkRequest modifiedRequest( req );
327
328 QString userAgent = s.value( u"/qgis/networkAndProxy/userAgent"_s, "Mozilla/5.0" ).toString();
329 if ( !userAgent.isEmpty() )
330 userAgent += ' ';
331 userAgent += u"QGIS/%1/%2"_s.arg( Qgis::versionInt() ).arg( QSysInfo::prettyProductName() );
332 modifiedRequest.setRawHeader( "User-Agent", userAgent.toLatin1() );
333
334#ifndef QT_NO_SSL
335 const bool ishttps = modifiedRequest.url().scheme().compare( "https"_L1, Qt::CaseInsensitive ) == 0;
336 if ( ishttps && !QgsApplication::authManager()->isDisabled() )
337 {
338 QgsDebugMsgLevel( u"Adding trusted CA certs to request"_s, 3 );
339 QSslConfiguration sslconfig( modifiedRequest.sslConfiguration() );
340 // Merge trusted CAs with any additional CAs added by the authentication methods
341 sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates() ) );
342 // check for SSL cert custom config
343 const QString hostport( u"%1:%2"_s.arg( modifiedRequest.url().host().trimmed() ).arg( modifiedRequest.url().port() != -1 ? modifiedRequest.url().port() : 443 ) );
344 const QgsAuthConfigSslServer servconfig = QgsApplication::authManager()->sslCertCustomConfigByHost( hostport.trimmed() );
345 if ( !servconfig.isNull() )
346 {
347 QgsDebugMsgLevel( u"Adding SSL custom config to request for %1"_s.arg( hostport ), 2 );
348 sslconfig.setProtocol( servconfig.sslProtocol() );
349 sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
350 sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
351 }
352
353 modifiedRequest.setSslConfiguration( sslconfig );
354 }
355#endif
356
357 if ( modifiedRequest.url().port() != -1 )
358 {
359 QUrl requestUrl = modifiedRequest.url();
360 const QString scheme = requestUrl.scheme();
361 const bool isDefaultPort = ( scheme == "http"_L1 && requestUrl.port() == 80 ) || ( scheme == "https"_L1 && requestUrl.port() == 443 );
362 if ( isDefaultPort )
363 {
364 QgsDebugMsgLevel( u"Removing explicit default port %2 from url %1"_s.arg( requestUrl.port() ).arg( requestUrl.toString() ), 2 );
365 requestUrl.setPort( -1 );
366 modifiedRequest.setUrl( requestUrl );
367 }
368 }
369
370 if ( sMainNAM->mCacheDisabled )
371 {
372 // if caching is disabled then we override whatever the request actually has set!
373 modifiedRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
374 modifiedRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute, false );
375 }
376
377 for ( const auto &preprocessor : sCustomPreprocessors )
378 {
379 preprocessor.second( &modifiedRequest );
380 }
381
382 static QAtomicInt sRequestId = 0;
383 const int requestId = ++sRequestId;
384 QByteArray content;
385 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
386 {
387 content = buffer->buffer();
388 }
389
390 for ( const auto &preprocessor : sCustomAdvancedPreprocessors )
391 {
392 int intOp = static_cast< int >( op );
393 preprocessor.second( &modifiedRequest, intOp, &content );
394 op = static_cast< QNetworkAccessManager::Operation >( intOp );
395 }
396
397 emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, modifiedRequest, requestId, content ) );
399 emit requestAboutToBeCreated( op, modifiedRequest, outgoingData );
401 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, modifiedRequest, outgoingData );
402 reply->setProperty( "requestId", requestId );
403
404 emit requestCreated( QgsNetworkRequestParameters( op, reply->request(), requestId, content ) );
406 emit requestCreated( reply );
408
409 connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
410#ifndef QT_NO_SSL
411 connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
412#endif
413
414 for ( const auto &replyPreprocessor : sCustomReplyPreprocessors )
415 {
416 replyPreprocessor.second( modifiedRequest, reply );
417 }
418
419 // The timer will call abortRequest slot to abort the connection if needed.
420 // The timer is stopped by the finished signal and is restarted on downloadProgress and
421 // uploadProgress.
422 if ( timeout() )
423 {
424 QTimer *timer = new QTimer( reply );
425 timer->setObjectName( u"timeoutTimer"_s );
426 connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
427 timer->setSingleShot( true );
428 timer->start( timeout() );
429
430 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
431 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
432 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
433 }
434 QgsDebugMsgLevel( u"Created [reply:%1]"_s.arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
435
436 return reply;
437}
438
439void QgsNetworkAccessManager::abortRequest()
440{
441 QTimer *timer = qobject_cast<QTimer *>( sender() );
442 Q_ASSERT( timer );
443
444 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
445 Q_ASSERT( reply );
446
447 reply->abort();
448 QgsDebugMsgLevel( u"Abort [reply:%1] %2"_s.arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
449 QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
450 // Notify the application
451 emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
452 emit requestTimedOut( reply );
453}
454
455void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
456{
457 emit finished( QgsNetworkReplyContent( reply ) );
458}
459
460void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
461{
462 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
463 {
464 emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
465 }
466}
467
468int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
469{
470 return reply->property( "requestId" ).toInt();
471}
472
473void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
474{
475 Q_ASSERT( reply->manager() == this );
476
477 QTimer *timer = reply->findChild<QTimer *>( u"timeoutTimer"_s );
478 if ( timer && timer->isActive() )
479 {
480 timer->stop();
481 }
482}
483
484void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
485{
486 Q_ASSERT( reply->manager() == this );
487 // restart reply timeout
488 QTimer *timer = reply->findChild<QTimer *>( u"timeoutTimer"_s );
489 if ( timer )
490 {
491 Q_ASSERT( !timer->isActive() );
492 QgsDebugMsgLevel( u"Restarting network reply timeout"_s, 2 );
493 timer->setSingleShot( true );
494 timer->start( timeout() );
495 }
496}
497
498void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
499{
500 if ( reply->manager() == this )
501 {
502 restartTimeout( reply );
503 emit authRequestHandled( reply );
504 }
505}
506
507#ifndef QT_NO_SSL
508void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
509{
510 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
511 Q_ASSERT( reply );
512 Q_ASSERT( reply->manager() == this );
513
514 QgsDebugMsgLevel( u"Stopping network reply timeout whilst SSL error is handled"_s, 2 );
515 pauseTimeout( reply );
516
517 emit requestEncounteredSslErrors( getRequestId( reply ), errors );
518
519 // acquire semaphore a first time, so we block next acquire until release is called
520 mSslErrorHandlerSemaphore.acquire();
521
522 // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
523 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
524 emit sslErrorsOccurred( reply, errors );
525 if ( this != sMainNAM )
526 {
527 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
528 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
529 mSslErrorHandlerSemaphore.acquire();
530 mSslErrorHandlerSemaphore.release();
531 afterSslErrorHandled( reply );
532 }
533}
534
535void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
536{
537 if ( reply->manager() == this )
538 {
539 restartTimeout( reply );
540 emit sslErrorsHandled( reply );
541 }
542}
543
544void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
545{
546 mSslErrorHandler->handleSslErrors( reply, errors );
547 afterSslErrorHandled( reply );
548 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mSslErrorHandlerSemaphore.release();
549}
550
551#endif
552
553void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
554{
555 Q_ASSERT( reply );
556 Q_ASSERT( reply->manager() == this );
557
558 QgsDebugMsgLevel( u"Stopping network reply timeout whilst auth request is handled"_s, 2 );
559 pauseTimeout( reply );
560
561 emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
562
563 // acquire semaphore a first time, so we block next acquire until release is called
564 mAuthRequestHandlerSemaphore.acquire();
565
566 // in main thread this will trigger auth handler immediately and return once the request is satisfied,
567 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
568 emit authRequestOccurred( reply, auth );
569
570 if ( this != sMainNAM )
571 {
572 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
573 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
574 mAuthRequestHandlerSemaphore.acquire();
575 mAuthRequestHandlerSemaphore.release();
576 afterAuthRequestHandled( reply );
577 }
578}
579
581{
582 if ( this != sMainNAM )
583 {
584 sMainNAM->requestAuthOpenBrowser( url );
586 return;
587 }
588 mAuthHandler->handleAuthRequestOpenBrowser( url );
589}
590
592{
593 if ( this != sMainNAM )
594 {
595 sMainNAM->requestAuthCloseBrowser();
597 return;
598 }
599 mAuthHandler->handleAuthRequestCloseBrowser();
600}
601
603{
604 if ( this != sMainNAM )
605 {
607 }
608 emit authBrowserAborted();
609}
610
611void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
612{
613 mAuthHandler->handleAuthRequest( reply, auth );
614
615 emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
616
617 afterAuthRequestHandled( reply );
618 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mAuthRequestHandlerSemaphore.release();
619}
620
621QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
622{
623 switch ( control )
624 {
625 case QNetworkRequest::AlwaysNetwork:
626 return u"AlwaysNetwork"_s;
627 case QNetworkRequest::PreferNetwork:
628 return u"PreferNetwork"_s;
629 case QNetworkRequest::PreferCache:
630 return u"PreferCache"_s;
631 case QNetworkRequest::AlwaysCache:
632 return u"AlwaysCache"_s;
633 }
634 return u"PreferNetwork"_s;
635}
636
637QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
638{
639 if ( name == "AlwaysNetwork"_L1 )
640 {
641 return QNetworkRequest::AlwaysNetwork;
642 }
643 else if ( name == "PreferNetwork"_L1 )
644 {
645 return QNetworkRequest::PreferNetwork;
646 }
647 else if ( name == "PreferCache"_L1 )
648 {
649 return QNetworkRequest::PreferCache;
650 }
651 else if ( name == "AlwaysCache"_L1 )
652 {
653 return QNetworkRequest::AlwaysCache;
654 }
655 return QNetworkRequest::PreferNetwork;
656}
657
658void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
659{
660 mInitialized = true;
661 mUseSystemProxy = false;
662
663 Q_ASSERT( sMainNAM );
664
665 if ( sMainNAM != this )
666 {
667 connect( this, &QNetworkAccessManager::proxyAuthenticationRequired, sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired, connectionType );
668
669 connect( this, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ), sMainNAM, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ) );
670
671 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ), sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ) );
672
673 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ), sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
674
675 connect( this, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ), sMainNAM, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ) );
676
677 connect( this, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ), sMainNAM, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ) );
678
680
681#ifndef QT_NO_SSL
682 connect( this, &QNetworkAccessManager::sslErrors, sMainNAM, &QNetworkAccessManager::sslErrors, connectionType );
683
685#endif
686
688 connect( sMainNAM, &QgsNetworkAccessManager::cookiesChanged, this, &QgsNetworkAccessManager::syncCookies );
689 connect( this, &QgsNetworkAccessManager::cookiesChanged, sMainNAM, &QgsNetworkAccessManager::syncCookies );
690 }
691 else
692 {
693#ifndef QT_NO_SSL
694 if ( !mSslErrorHandler )
695 setSslErrorHandler( std::make_unique< QgsSslErrorHandler >() );
696#endif
697 if ( !mAuthHandler )
698 setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
699 }
700#ifndef QT_NO_SSL
701 connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
702#endif
703 connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
704 connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
705
706 connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
707
708 // check if proxy is enabled
709 const QgsSettings settings;
710 QNetworkProxy proxy;
711 QStringList excludes;
712 QStringList noProxyURLs;
713
714 const bool proxyEnabled = settings.value( u"proxy/proxyEnabled"_s, false ).toBool();
715 if ( proxyEnabled )
716 {
717 // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
718 // meaning the system one
719 excludes = settings.value( u"proxy/proxyExcludedUrls"_s, QStringList() ).toStringList();
720
721 noProxyURLs = settings.value( u"proxy/noProxyUrls"_s, QStringList() ).toStringList();
722
723 //read type, host, port, user, passw from settings
724 const QString proxyHost = settings.value( u"proxy/proxyHost"_s, "" ).toString();
725 const int proxyPort = settings.value( u"proxy/proxyPort"_s, "" ).toString().toInt();
726
727 const QString proxyUser = settings.value( u"proxy/proxyUser"_s, "" ).toString();
728 const QString proxyPassword = settings.value( u"proxy/proxyPassword"_s, "" ).toString();
729
730 const QString proxyTypeString = settings.value( u"proxy/proxyType"_s, "" ).toString();
731
732 if ( proxyTypeString == "DefaultProxy"_L1 )
733 {
734 mUseSystemProxy = true;
735 QNetworkProxyFactory::setUseSystemConfiguration( true );
736 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
737 if ( !proxies.isEmpty() )
738 {
739 proxy = proxies.first();
740 }
741 QgsDebugMsgLevel( u"setting default proxy"_s, 4 );
742 }
743 else
744 {
745 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
746 if ( proxyTypeString == "Socks5Proxy"_L1 )
747 {
748 proxyType = QNetworkProxy::Socks5Proxy;
749 }
750 else if ( proxyTypeString == "HttpProxy"_L1 )
751 {
752 proxyType = QNetworkProxy::HttpProxy;
753 }
754 else if ( proxyTypeString == "HttpCachingProxy"_L1 )
755 {
756 proxyType = QNetworkProxy::HttpCachingProxy;
757 }
758 else if ( proxyTypeString == "FtpCachingProxy"_L1 )
759 {
760 proxyType = QNetworkProxy::FtpCachingProxy;
761 }
762 QgsDebugMsgLevel( u"setting proxy %1 %2:%3 %4/%5"_s.arg( proxyType ).arg( proxyHost ).arg( proxyPort ).arg( proxyUser, proxyPassword ), 2 );
763 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
764 }
765 // Setup network proxy authentication configuration
766 const QString authcfg = settings.value( u"proxy/authcfg"_s, "" ).toString();
767 if ( !authcfg.isEmpty() )
768 {
769#ifdef HAVE_AUTH
770 QgsDebugMsgLevel( u"setting proxy from stored authentication configuration %1"_s.arg( authcfg ), 2 );
771 // Never crash! Never.
772 if ( QgsAuthManager *authManager = QgsApplication::authManager() )
773 authManager->updateNetworkProxy( proxy, authcfg );
774#else
775 QgsDebugError( u"Auth manager is not available - cannot update network proxy for authcfg: %1"_s.arg( authcfg ) );
776#endif
777 }
778 }
779
780 setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
781
782 QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
783 if ( !newcache )
784 newcache = new QgsNetworkDiskCache( this );
785
786 QString cacheDirectory = QgsSettingsRegistryCore::settingsNetworkCacheDirectory->value();
787 if ( cacheDirectory.isEmpty() )
788 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
789 newcache->setCacheDirectory( cacheDirectory );
790 qint64 cacheSize = QgsSettingsRegistryCore::settingsNetworkCacheSize->value();
791 newcache->setMaximumCacheSize( cacheSize );
792
793 QgsDebugMsgLevel( u"cacheDirectory: %1"_s.arg( newcache->cacheDirectory() ), 4 );
794 QgsDebugMsgLevel( u"maximumCacheSize: %1"_s.arg( newcache->maximumCacheSize() ), 4 );
795
796 if ( cache() != newcache )
797 setCache( newcache );
798
799 if ( this != sMainNAM )
800 {
801 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( static_cast<QgsNetworkCookieJar *>( sMainNAM->cookieJar() )->allCookies() );
802 }
803}
804
805void QgsNetworkAccessManager::syncCookies( const QList<QNetworkCookie> &cookies )
806{
807 if ( sender() != this )
808 {
809 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( cookies );
810 if ( this == sMainNAM )
811 {
812 emit cookiesChanged( cookies );
813 }
814 }
815}
816
818{
819 return settingsNetworkTimeout->value();
820}
821
823{
824 settingsNetworkTimeout->setValue( time );
825}
826
827QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback, Qgis::NetworkRequestFlags flags )
828{
829 QgsBlockingNetworkRequest br( flags );
830 br.setAuthCfg( authCfg );
831 br.get( request, forceRefresh, feedback );
832 return br.reply();
833}
834
836 QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback, Qgis::NetworkRequestFlags flags
837)
838{
839 QgsBlockingNetworkRequest br( flags );
840 br.setAuthCfg( authCfg );
841 ( void ) br.post( request, data, forceRefresh, feedback );
842 return br.reply();
843}
844
845QString QgsNetworkAccessManager::setRequestPreprocessor( const std::function<void( QNetworkRequest * )> &processor )
846{
847 QString id = QUuid::createUuid().toString();
848 sCustomPreprocessors.emplace_back( std::make_pair( id, processor ) );
849 return id;
850}
851
853{
854 const size_t prevCount = sCustomPreprocessors.size();
855 sCustomPreprocessors.erase(
856 std::remove_if( sCustomPreprocessors.begin(), sCustomPreprocessors.end(), [id]( std::pair< QString, std::function< void( QNetworkRequest * ) > > &a ) { return a.first == id; } ),
857 sCustomPreprocessors.end()
858 );
859 return prevCount != sCustomPreprocessors.size();
860}
861
863{
864 const size_t prevCount = sCustomAdvancedPreprocessors.size();
865 sCustomAdvancedPreprocessors.erase(
866 std::remove_if(
867 sCustomAdvancedPreprocessors.begin(),
868 sCustomAdvancedPreprocessors.end(),
869 [id]( std::pair< QString, std::function< void( QNetworkRequest *, int &, QByteArray * ) > > &a ) { return a.first == id; }
870 ),
871 sCustomAdvancedPreprocessors.end()
872 );
873 return prevCount != sCustomAdvancedPreprocessors.size();
874}
875
876QString QgsNetworkAccessManager::setAdvancedRequestPreprocessor( const std::function<void( QNetworkRequest *, int &, QByteArray * )> &processor )
877{
878 QString id = QUuid::createUuid().toString();
879 sCustomAdvancedPreprocessors.emplace_back( std::make_pair( id, processor ) );
880 return id;
881}
882
883QString QgsNetworkAccessManager::setReplyPreprocessor( const std::function<void( const QNetworkRequest &, QNetworkReply * )> &processor )
884{
885 QString id = QUuid::createUuid().toString();
886 sCustomReplyPreprocessors.emplace_back( std::make_pair( id, processor ) );
887 return id;
888}
889
891{
892 const size_t prevCount = sCustomReplyPreprocessors.size();
893 sCustomReplyPreprocessors.erase(
894 std::remove_if(
895 sCustomReplyPreprocessors.begin(), sCustomReplyPreprocessors.end(), [id]( std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > &a ) { return a.first == id; }
896 ),
897 sCustomReplyPreprocessors.end()
898 );
899 return prevCount != sCustomReplyPreprocessors.size();
900}
901
902void QgsNetworkAccessManager::preprocessRequest( QNetworkRequest *req ) const
903{
904 for ( const auto &preprocessor : sCustomPreprocessors )
905 {
906 preprocessor.second( req );
907 }
908}
909
910
911//
912// QgsNetworkRequestParameters
913//
914
915QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
916 : mOperation( operation )
917 , mRequest( request )
918 , mOriginatingThreadId( u"0x%2"_s.arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, '0'_L1 ) )
919 , mRequestId( requestId )
920 , mContent( content )
921 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
922 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
923{}
924
925
926//
927// QgsSslErrorHandler
928//
929
930void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
931{
932 Q_UNUSED( reply )
933 QgsDebugError( u"SSL errors occurred accessing URL:\n%1"_s.arg( reply->request().url().toString() ) );
934}
935
936//
937// QgsNetworkAuthenticationHandler
938//
939
940void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
941{
942 Q_UNUSED( reply )
943 QgsDebugError( u"Network reply required authentication, but no handler was in place to provide this authentication request while accessing the URL:\n%1"_s.arg( reply->request().url().toString() ) );
944}
945
947{
948 Q_UNUSED( url )
949 QgsDebugError( u"Network authentication required external browser to open URL %1, but no handler was in place"_s.arg( url.toString() ) );
950}
951
953{
954 QgsDebugError( u"Network authentication required external browser closed, but no handler was in place"_s );
955}
956
957// For QgsNetworkCookieJar
958#include "qgsnetworkaccessmanager.moc"
QFlags< NetworkRequestFlag > NetworkRequestFlags
Flags controlling behavior of network requests.
Definition qgis.h:197
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition qgis.cpp:687
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
Configuration container for SSL server connection exceptions or overrides.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
int sslPeerVerifyDepth() const
Number or SSL client's peer to verify in connections.
bool isNull() const
Whether configuration is null (missing components).
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client's peer verify mode to use in connections.
Singleton which offers an interface to manage the authentication configuration database and to utiliz...
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port)
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode post(QNetworkRequest &request, QIODevice *data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
QNetworkAccessManager with additional QGIS specific logic.
QStringList noProxyList() const
Returns the no proxy list.
void finished(QgsNetworkReplyContent reply)
Emitted whenever a pending network reply is finished.
static const QgsSettingsEntryInteger * settingsNetworkTimeout
Settings entry network timeout.
void cookiesChanged(const QList< QNetworkCookie > &cookies)
Emitted when the cookies changed.
void insertProxyFactory(QNetworkProxyFactory *factory)
Inserts a factory into the proxy factories list.
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 operation, const QNetworkRequest &request, QIODevice *device)
void abortAuthBrowser()
Abort any outstanding external browser login request.
void setCacheDisabled(bool disabled)
Sets whether all network caching should be disabled.
const QList< QNetworkProxyFactory * > proxyFactories() const
Returns a list of proxy factories used by the manager.
void downloadProgress(int requestId, qint64 bytesReceived, qint64 bytesTotal)
Emitted when a network reply receives a progress report.
void requestAuthOpenBrowser(const QUrl &url) const
Forwards an external browser login url opening request to the authentication handler.
void requestAuthCloseBrowser() const
Forwards an external browser login closure request to the authentication handler.
void requestEncounteredSslErrors(int requestId, const QList< QSslError > &errors)
Emitted when a network request encounters SSL errors.
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl control)
Returns the name for QNetworkRequest::CacheLoadControl.
void requestCreated(const QgsNetworkRequestParameters &request)
Emitted when a network request has been created.
static QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
static QString setReplyPreprocessor(const std::function< void(const QNetworkRequest &, QNetworkReply *)> &processor)
Sets a reply pre-processor function, which allows manipulation of QNetworkReply objects after they ar...
static bool removeRequestPreprocessor(const QString &id)
Removes the custom request pre-processor function with matching id.
void requestAuthDetailsAdded(int requestId, const QString &realm, const QString &user, const QString &password)
Emitted when network authentication details have been added to a request.
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &name)
Returns QNetworkRequest::CacheLoadControl from a name.
static bool removeAdvancedRequestPreprocessor(const QString &id)
Removes an advanced request pre-processor function with matching id.
QgsNetworkAccessManager(QObject *parent=nullptr)
void requestRequiresAuth(int requestId, const QString &realm)
Emitted when a network request prompts an authentication request.
void preprocessRequest(QNetworkRequest *req) const
Preprocesses request.
void setAuthHandler(std::unique_ptr< QgsNetworkAuthenticationHandler > handler)
Sets the application network authentication handler, which is used to respond to network authenticati...
static void setTimeout(int time)
Sets the maximum timeout time for network requests, in milliseconds.
static QString setAdvancedRequestPreprocessor(const std::function< void(QNetworkRequest *, int &op, QByteArray *data)> &processor)
Sets an advanced request pre-processor function, which allows manipulation of a network request befor...
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
static QgsNetworkReplyContent blockingPost(QNetworkRequest &request, const QByteArray &data, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a POST request to obtain the contents of the target request, using the given data,...
static int timeout()
Returns the network timeout length, in milliseconds.
void setupDefaultProxyAndCache(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Setup the QgsNetworkAccessManager (NAM) according to the user's settings.
static QString setRequestPreprocessor(const std::function< void(QNetworkRequest *request)> &processor)
Sets a request pre-processor function, which allows manipulation of a network request before it is pr...
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs)
Sets the fallback proxy and URLs which shouldn't use it.
static bool removeReplyPreprocessor(const QString &id)
Removes the custom reply pre-processor function with matching id.
QStringList excludeList() const
Returns the proxy exclude list.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void removeProxyFactory(QNetworkProxyFactory *factory)
Removes a factory from the proxy factories list.
void authBrowserAborted()
Emitted when external browser logins are to be aborted.
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
bool useSystemProxy() const
Returns whether the system proxy should be used.
QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
virtual void handleAuthRequest(QNetworkReply *reply, QAuthenticator *auth)
Called whenever network authentication requests are encountered during a network reply.
virtual void handleAuthRequestCloseBrowser()
Called to terminate a network authentication through external browser.
virtual void handleAuthRequestOpenBrowser(const QUrl &url)
Called to initiate a network authentication through external browser url.
Wrapper implementation of QNetworkDiskCache with all methods guarded by a mutex solely for internal u...
void setCacheDirectory(const QString &cacheDir)
qint64 maximumCacheSize() const
void setMaximumCacheSize(qint64 size)
QString cacheDirectory() const
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
Encapsulates parameters and properties of a network request.
int requestId() const
Returns a unique ID identifying the request.
@ AttributeInitiatorClass
Class name of original object which created the request.
@ AttributeInitiatorRequestId
Internal ID used by originator object to identify requests.
QNetworkAccessManager::Operation operation() const
Returns the request operation, e.g.
QNetworkRequest request() const
Returns the network request.
QByteArray content() const
Returns the request's content.
An integer settings entry.
static const QgsSettingsEntryInteger64 * settingsNetworkCacheSize
Settings entry network cache directory.
static const QgsSettingsEntryString * settingsNetworkCacheDirectory
Settings entry network cache directory.
static QgsSettingsTreeNode * sTreeNetwork
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
virtual void handleSslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
Called whenever SSL errors are encountered during a network reply.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59