mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[auth system] Data source integration for OWS connections
This commit is contained in:
parent
0d2b945c05
commit
907b019bb5
@ -19,9 +19,10 @@ class QgsGml: QObject
|
||||
* @param extent retrieved extents
|
||||
* @param userName username for authentication
|
||||
* @param password password for authentication
|
||||
* @param authcfg authentication configuration id
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent = 0, const QString& userName = QString(), const QString& password = QString() ) /PyName=getFeaturesUri/;
|
||||
int getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent = 0, const QString& userName = QString(), const QString& password = QString(), const QString& authcfg = QString() ) /PyName=getFeaturesUri/;
|
||||
|
||||
/** Read from GML data. Constructor uri param is ignored
|
||||
* Supports only UTF-8, UTF-16, ISO-8859-1, ISO-8859-1 XML encodings.
|
||||
|
@ -13,6 +13,7 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsgml.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgsgeometry.h"
|
||||
@ -69,7 +70,7 @@ QgsGml::~QgsGml()
|
||||
{
|
||||
}
|
||||
|
||||
int QgsGml::getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent, const QString& userName, const QString& password )
|
||||
int QgsGml::getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent, const QString& userName, const QString& password , const QString& authcfg )
|
||||
{
|
||||
mUri = uri;
|
||||
mWkbType = wkbType;
|
||||
@ -83,7 +84,19 @@ int QgsGml::getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangl
|
||||
mExtent.setMinimal();
|
||||
|
||||
QNetworkRequest request( mUri );
|
||||
if ( !userName.isNull() || !password.isNull() )
|
||||
if ( !authcfg.isEmpty() )
|
||||
{
|
||||
if ( !QgsAuthManager::instance()->updateNetworkRequest( request, authcfg ) )
|
||||
{
|
||||
QgsMessageLog::logMessage(
|
||||
tr( "GML Getfeature network request update failed for authcfg %1" ).arg( authcfg ),
|
||||
tr( "Network" ),
|
||||
QgsMessageLog::CRITICAL
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if ( !userName.isNull() || !password.isNull() )
|
||||
{
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( userName ).arg( password ).toAscii().toBase64() );
|
||||
}
|
||||
|
@ -56,9 +56,15 @@ class CORE_EXPORT QgsGml : public QObject
|
||||
* @param extent retrieved extents
|
||||
* @param userName username for authentication
|
||||
* @param password password for authentication
|
||||
* @param authcfg authentication configuration id
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent = 0, const QString& userName = QString(), const QString& password = QString() );
|
||||
int getFeatures( const QString& uri,
|
||||
QGis::WkbType* wkbType,
|
||||
QgsRectangle* extent = 0,
|
||||
const QString& userName = QString(),
|
||||
const QString& password = QString(),
|
||||
const QString& authcfg = QString() );
|
||||
|
||||
/** Read from GML data. Constructor uri param is ignored
|
||||
* Supports only UTF-8, UTF-16, ISO-8859-1, ISO-8859-1 XML encodings.
|
||||
|
@ -61,6 +61,13 @@ QgsOWSConnection::QgsOWSConnection( const QString & theService, const QString &
|
||||
mUri.setParam( "password", password );
|
||||
}
|
||||
|
||||
QString authcfg = settings.value( credentialsKey + "/authcfg" ).toString();
|
||||
if ( !authcfg.isEmpty() )
|
||||
{
|
||||
mUri.setParam( "authcfg", authcfg );
|
||||
}
|
||||
mConnectionInfo.append( ",authcfg=" + authcfg );
|
||||
|
||||
bool ignoreGetMap = settings.value( key + "/ignoreGetMapURI", false ).toBool();
|
||||
bool ignoreGetFeatureInfo = settings.value( key + "/ignoreGetFeatureInfoURI", false ).toBool();
|
||||
bool ignoreAxisOrientation = settings.value( key + "/ignoreAxisOrientation", false ).toBool();
|
||||
|
@ -23,10 +23,11 @@
|
||||
#include <QRegExpValidator>
|
||||
|
||||
QgsNewHttpConnection::QgsNewHttpConnection(
|
||||
QWidget *parent, const QString& baseKey, const QString& connName, Qt::WindowFlags fl ):
|
||||
QDialog( parent, fl ),
|
||||
mBaseKey( baseKey ),
|
||||
mOriginalConnName( connName )
|
||||
QWidget *parent, const QString& baseKey, const QString& connName, Qt::WindowFlags fl )
|
||||
: QDialog( parent, fl )
|
||||
, mBaseKey( baseKey )
|
||||
, mOriginalConnName( connName )
|
||||
, mAuthConfigSelect( 0 )
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
@ -49,6 +50,9 @@ QgsNewHttpConnection::QgsNewHttpConnection(
|
||||
cmbDpiMode->addItem( tr( "UMN" ) );
|
||||
cmbDpiMode->addItem( tr( "GeoServer" ) );
|
||||
|
||||
mAuthConfigSelect = new QgsAuthConfigSelect( this );
|
||||
tabAuth->insertTab( 1, mAuthConfigSelect, tr( "Configurations" ) );
|
||||
|
||||
if ( !connName.isEmpty() )
|
||||
{
|
||||
// populate the dialog with the information stored for the connection
|
||||
@ -92,6 +96,13 @@ QgsNewHttpConnection::QgsNewHttpConnection(
|
||||
|
||||
txtUserName->setText( settings.value( credentialsKey + "/username" ).toString() );
|
||||
txtPassword->setText( settings.value( credentialsKey + "/password" ).toString() );
|
||||
|
||||
QString authcfg = settings.value( credentialsKey + "/authcfg" ).toString();
|
||||
mAuthConfigSelect->setConfigId( authcfg );
|
||||
if ( !authcfg.isEmpty() )
|
||||
{
|
||||
tabAuth->setCurrentIndex( tabAuth->indexOf( mAuthConfigSelect ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mBaseKey != "/Qgis/connections-wms/" )
|
||||
@ -247,6 +258,8 @@ void QgsNewHttpConnection::accept()
|
||||
settings.setValue( credentialsKey + "/username", txtUserName->text() );
|
||||
settings.setValue( credentialsKey + "/password", txtPassword->text() );
|
||||
|
||||
settings.setValue( credentialsKey + "/authcfg", mAuthConfigSelect->configId() );
|
||||
|
||||
settings.setValue( mBaseKey + "/selected", txtName->text() );
|
||||
|
||||
QDialog::accept();
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "ui_qgsnewhttpconnectionbase.h"
|
||||
#include "qgisgui.h"
|
||||
#include "qgscontexthelp.h"
|
||||
#include "qgsauthconfigselect.h"
|
||||
|
||||
/*!
|
||||
* \brief Dialog to allow the user to configure and save connection
|
||||
* information for an HTTP Server for WMS, etc.
|
||||
@ -46,6 +48,7 @@ class GUI_EXPORT QgsNewHttpConnection : public QDialog, private Ui::QgsNewHttpCo
|
||||
QString mBaseKey;
|
||||
QString mCredentialsBaseKey;
|
||||
QString mOriginalConnName; //store initial name to delete entry in case of rename
|
||||
QgsAuthConfigSelect * mAuthConfigSelect;
|
||||
};
|
||||
|
||||
#endif // QGSNEWHTTPCONNECTION_H
|
||||
|
@ -9,7 +9,9 @@ SET(OWS_MOC_HDRS
|
||||
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../core
|
||||
../../core/auth
|
||||
../../gui
|
||||
../../gui/auth
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../ui
|
||||
)
|
||||
|
||||
|
@ -16,10 +16,17 @@ SET (WCS_MOC_HDRS
|
||||
|
||||
QT4_WRAP_CPP (WCS_MOC_SRCS ${WCS_MOC_HDRS})
|
||||
|
||||
INCLUDE_DIRECTORIES( . ../../core ../../core/raster ../../gui
|
||||
INCLUDE_DIRECTORIES(
|
||||
.
|
||||
../../core
|
||||
../../core/auth
|
||||
../../core/raster
|
||||
../../gui
|
||||
../../gui/auth
|
||||
../gdal
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../ui
|
||||
${GDAL_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY(wcsprovider MODULE ${WCS_SRCS} ${WCS_MOC_SRCS})
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgscoordinatetransform.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
@ -159,7 +160,12 @@ bool QgsWcsCapabilities::sendRequest( QString const & url )
|
||||
QgsDebugMsg( "url = " + url );
|
||||
mError = "";
|
||||
QNetworkRequest request( url );
|
||||
setAuthorization( request );
|
||||
if ( !setAuthorization( request ) )
|
||||
{
|
||||
mError = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mError, tr( "WCS" ) );
|
||||
return false;
|
||||
}
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mCacheLoadControl );
|
||||
QgsDebugMsg( QString( "mCacheLoadControl = %1" ).arg( mCacheLoadControl ) );
|
||||
@ -359,7 +365,13 @@ void QgsWcsCapabilities::capabilitiesReplyFinished()
|
||||
emit statusChanged( tr( "Capabilities request redirected." ) );
|
||||
|
||||
QNetworkRequest request( redirect.toUrl() );
|
||||
setAuthorization( request );
|
||||
if ( !setAuthorization( request ) )
|
||||
{
|
||||
mCapabilitiesResponse.clear();
|
||||
mError = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mError, tr( "WCS" ) );
|
||||
return;
|
||||
}
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork );
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
|
||||
@ -1162,14 +1174,19 @@ QString QgsWcsCapabilities::lastErrorFormat()
|
||||
return mErrorFormat;
|
||||
}
|
||||
|
||||
void QgsWcsCapabilities::setAuthorization( QNetworkRequest &request ) const
|
||||
bool QgsWcsCapabilities::setAuthorization( QNetworkRequest &request ) const
|
||||
{
|
||||
QgsDebugMsg( "entered" );
|
||||
if ( mUri.hasParam( "username" ) && mUri.hasParam( "password" ) )
|
||||
if ( mUri.hasParam( "authcfg" ) && !mUri.param( "authcfg" ).isEmpty() )
|
||||
{
|
||||
return QgsAuthManager::instance()->updateNetworkRequest( request, mUri.param( "authcfg" ) );
|
||||
}
|
||||
else if ( mUri.hasParam( "username" ) && mUri.hasParam( "password" ) )
|
||||
{
|
||||
QgsDebugMsg( "setAuthorization " + mUri.param( "username" ) );
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( mUri.param( "username" ) ).arg( mUri.param( "password" ) ).toAscii().toBase64() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsWcsCapabilities::showMessageBox( const QString& title, const QString& text )
|
||||
|
@ -153,7 +153,7 @@ class QgsWcsCapabilities : public QObject
|
||||
bool parseDescribeCoverageDom11( QByteArray const &xml, QgsWcsCoverageSummary *coverage );
|
||||
|
||||
//! set authorization header
|
||||
void setAuthorization( QNetworkRequest &request ) const;
|
||||
bool setAuthorization( QNetworkRequest &request ) const;
|
||||
|
||||
QString version() const { return mCapabilities.version; }
|
||||
|
||||
|
@ -408,6 +408,12 @@ bool QgsWcsProvider::parseUri( QString uriString )
|
||||
mAuth.mPassword = uri.param( "password" );
|
||||
QgsDebugMsg( "set password to " + mAuth.mPassword );
|
||||
|
||||
if ( uri.hasParam( "authcfg" ) )
|
||||
{
|
||||
mAuth.mAuthCfg = uri.param( "authcfg" );
|
||||
}
|
||||
QgsDebugMsg( "set authcfg to " + mAuth.mAuthCfg );
|
||||
|
||||
mIdentifier = uri.param( "identifier" );
|
||||
|
||||
mTime = uri.param( "time" );
|
||||
@ -1667,6 +1673,7 @@ int QgsWcsDownloadHandler::sErrors = 0;
|
||||
|
||||
QgsWcsDownloadHandler::QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorization& auth, QNetworkRequest::CacheLoadControl cacheLoadControl, QByteArray& cachedData, const QString& wcsVersion, QgsError& cachedError )
|
||||
: mNAM( new QgsNetworkAccessManager )
|
||||
, mAuth( auth )
|
||||
, mEventLoop( new QEventLoop )
|
||||
, mCachedData( cachedData )
|
||||
, mWcsVersion( wcsVersion )
|
||||
@ -1675,7 +1682,12 @@ QgsWcsDownloadHandler::QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorizati
|
||||
mNAM->setupDefaultProxyAndCache();
|
||||
|
||||
QNetworkRequest request( url );
|
||||
auth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "Network request update failed for authentication config" ),
|
||||
tr( "WCS" ) );
|
||||
return;
|
||||
}
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl );
|
||||
|
||||
@ -1708,7 +1720,14 @@ void QgsWcsDownloadHandler::cacheReplyFinished()
|
||||
mCacheReply->deleteLater();
|
||||
|
||||
QgsDebugMsg( QString( "redirected getmap: %1" ).arg( redirect.toString() ) );
|
||||
mCacheReply = mNAM->get( QNetworkRequest( redirect.toUrl() ) );
|
||||
QNetworkRequest request( redirect.toUrl() );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "Network request update failed for authentication config" ),
|
||||
tr( "WCS" ) );
|
||||
return;
|
||||
}
|
||||
mCacheReply = mNAM->get( request );
|
||||
connect( mCacheReply, SIGNAL( finished() ), this, SLOT( cacheReplyFinished() ) );
|
||||
connect( mCacheReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( cacheReplyProgress( qint64, qint64 ) ) );
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "qgserror.h"
|
||||
#include "qgswcscapabilities.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsrasterdataprovider.h"
|
||||
#include "qgsgdalproviderbase.h"
|
||||
#include "qgsrectangle.h"
|
||||
@ -50,15 +51,24 @@ class QNetworkRequest;
|
||||
// TODO: merge with QgsWmsAuthorization?
|
||||
struct QgsWcsAuthorization
|
||||
{
|
||||
QgsWcsAuthorization( const QString& userName = QString(), const QString& password = QString() ) : mUserName( userName ), mPassword( password ) {}
|
||||
QgsWcsAuthorization( const QString& userName = QString(), const QString& password = QString(), const QString& authcfg = QString() )
|
||||
: mUserName( userName )
|
||||
, mPassword( password )
|
||||
, mAuthCfg( authcfg )
|
||||
{}
|
||||
|
||||
//! set authorization header
|
||||
void setAuthorization( QNetworkRequest &request ) const
|
||||
bool setAuthorization( QNetworkRequest &request ) const
|
||||
{
|
||||
if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
if ( !mAuthCfg.isEmpty() )
|
||||
{
|
||||
return QgsAuthManager::instance()->updateNetworkRequest( request, mAuthCfg );
|
||||
}
|
||||
else if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
{
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( mUserName ).arg( mPassword ).toAscii().toBase64() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Username for basic http authentication
|
||||
@ -66,6 +76,9 @@ struct QgsWcsAuthorization
|
||||
|
||||
//! Password for basic http authentication
|
||||
QString mPassword;
|
||||
|
||||
//! Authentication configuration ID
|
||||
QString mAuthCfg;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -424,6 +437,7 @@ class QgsWcsDownloadHandler : public QObject
|
||||
void finish() { QMetaObject::invokeMethod( mEventLoop, "quit", Qt::QueuedConnection ); }
|
||||
|
||||
QgsNetworkAccessManager* mNAM;
|
||||
QgsWcsAuthorization& mAuth;
|
||||
QEventLoop* mEventLoop;
|
||||
|
||||
QNetworkReply* mCacheReply;
|
||||
|
@ -25,13 +25,16 @@ QT4_WRAP_CPP(WFS_MOC_SRCS ${WFS_MOC_HDRS})
|
||||
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../core
|
||||
../../core/auth
|
||||
../../core/geometry
|
||||
../../gui
|
||||
../../gui/auth
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../ui
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}/geos
|
||||
${EXPAT_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY (wfsprovider MODULE ${WFS_SRCS} ${WFS_MOC_SRCS})
|
||||
|
@ -13,6 +13,7 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgswfscapabilities.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessagelog.h"
|
||||
@ -141,18 +142,27 @@ QString QgsWFSCapabilities::uriGetFeature( QString typeName, QString crsString,
|
||||
uri += "&username=" + mUri.param( "username" );
|
||||
uri += "&password=" + mUri.param( "password" );
|
||||
}
|
||||
if ( mUri.hasParam( "authcfg" ) )
|
||||
{
|
||||
uri += "&authcfg=" + mUri.param( "authcfg" );
|
||||
}
|
||||
QgsDebugMsg( uri );
|
||||
return uri;
|
||||
}
|
||||
|
||||
void QgsWFSCapabilities::setAuthorization( QNetworkRequest &request ) const
|
||||
bool QgsWFSCapabilities::setAuthorization( QNetworkRequest &request ) const
|
||||
{
|
||||
QgsDebugMsg( "entered" );
|
||||
if ( mUri.hasParam( "username" ) && mUri.hasParam( "password" ) )
|
||||
if ( mUri.hasParam( "authcfg" ) && !mUri.param( "authcfg" ).isEmpty() )
|
||||
{
|
||||
return QgsAuthManager::instance()->updateNetworkRequest( request, mUri.param( "authcfg" ) );
|
||||
}
|
||||
else if ( mUri.hasParam( "username" ) && mUri.hasParam( "password" ) )
|
||||
{
|
||||
QgsDebugMsg( "setAuthorization " + mUri.param( "username" ) );
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( mUri.param( "username" ) ).arg( mUri.param( "password" ) ).toAscii().toBase64() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsWFSCapabilities::requestCapabilities()
|
||||
@ -161,7 +171,15 @@ void QgsWFSCapabilities::requestCapabilities()
|
||||
mErrorMessage.clear();
|
||||
|
||||
QNetworkRequest request( uriGetCapabilities() );
|
||||
setAuthorization( request );
|
||||
if ( !setAuthorization( request ) )
|
||||
{
|
||||
mErrorCode = QgsWFSCapabilities::NetworkError;
|
||||
mErrorMessage = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
|
||||
emit gotCapabilities();
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
mCapabilitiesReply = QgsNetworkAccessManager::instance()->get( request );
|
||||
connect( mCapabilitiesReply, SIGNAL( finished() ), this, SLOT( capabilitiesReplyFinished() ) );
|
||||
@ -188,7 +206,16 @@ void QgsWFSCapabilities::capabilitiesReplyFinished()
|
||||
{
|
||||
QgsDebugMsg( "redirecting to " + redirect.toUrl().toString() );
|
||||
QNetworkRequest request( redirect.toUrl() );
|
||||
setAuthorization( request );
|
||||
if ( !setAuthorization( request ) )
|
||||
{
|
||||
mCaps.clear();
|
||||
mErrorCode = QgsWFSCapabilities::NetworkError;
|
||||
mErrorMessage = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
|
||||
emit gotCapabilities();
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork );
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
|
||||
|
@ -74,7 +74,7 @@ class QgsWFSCapabilities : public QObject
|
||||
GetCapabilities capabilities() { return mCaps; }
|
||||
|
||||
//! set authorization header
|
||||
void setAuthorization( QNetworkRequest &request ) const;
|
||||
bool setAuthorization( QNetworkRequest &request ) const;
|
||||
|
||||
signals:
|
||||
void gotCapabilities();
|
||||
|
@ -99,6 +99,7 @@ QgsWFSProvider::QgsWFSProvider( const QString& uri )
|
||||
|
||||
mAuth.mUserName = parameterFromUrl( "username" );
|
||||
mAuth.mPassword = parameterFromUrl( "password" );
|
||||
mAuth.mAuthCfg = parameterFromUrl( "authcfg" );
|
||||
|
||||
//fetch attributes of layer and type of its geometry attribute
|
||||
//WBC 111221: extracting geometry type here instead of getFeature allows successful
|
||||
@ -706,8 +707,14 @@ int QgsWFSProvider::getFeatureGET( const QString& uri, const QString& geometryAt
|
||||
QUrl getFeatureUrl( uri );
|
||||
getFeatureUrl.removeQueryItem( "username" );
|
||||
getFeatureUrl.removeQueryItem( "password" );
|
||||
getFeatureUrl.removeQueryItem( "authcfg" );
|
||||
QgsRectangle extent;
|
||||
if ( dataReader.getFeatures( getFeatureUrl.toString(), &mWKBType, mCached ? &mExtent : &extent, mAuth.mUserName, mAuth.mPassword ) != 0 )
|
||||
if ( dataReader.getFeatures( getFeatureUrl.toString(),
|
||||
&mWKBType,
|
||||
mCached ? &mExtent : &extent,
|
||||
mAuth.mUserName,
|
||||
mAuth.mPassword,
|
||||
mAuth.mAuthCfg ) != 0 )
|
||||
{
|
||||
QgsDebugMsg( "getWFSData returned with error" );
|
||||
return 1;
|
||||
@ -778,11 +785,17 @@ int QgsWFSProvider::describeFeatureTypeGET( const QString& uri, QString& geometr
|
||||
QUrl describeFeatureUrl( uri );
|
||||
describeFeatureUrl.removeQueryItem( "username" );
|
||||
describeFeatureUrl.removeQueryItem( "password" );
|
||||
describeFeatureUrl.removeQueryItem( "authcfg" );
|
||||
describeFeatureUrl.removeQueryItem( "SRSNAME" );
|
||||
describeFeatureUrl.removeQueryItem( "REQUEST" );
|
||||
describeFeatureUrl.addQueryItem( "REQUEST", "DescribeFeatureType" );
|
||||
QNetworkRequest request( describeFeatureUrl.toString() );
|
||||
mAuth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "Network request update failed for authentication config" ),
|
||||
tr( "WFS" ) );
|
||||
return 1;
|
||||
}
|
||||
QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
|
||||
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( networkRequestFinished() ) );
|
||||
@ -1366,6 +1379,7 @@ bool QgsWFSProvider::sendTransactionDocument( const QDomDocument& doc, QDomDocum
|
||||
QUrl typeDetectionUri( dataSourceUri() );
|
||||
typeDetectionUri.removeQueryItem( "username" );
|
||||
typeDetectionUri.removeQueryItem( "password" );
|
||||
typeDetectionUri.removeQueryItem( "authcfg" );
|
||||
typeDetectionUri.removeQueryItem( "REQUEST" );
|
||||
typeDetectionUri.removeQueryItem( "TYPENAME" );
|
||||
typeDetectionUri.removeQueryItem( "BBOX" );
|
||||
@ -1377,7 +1391,13 @@ bool QgsWFSProvider::sendTransactionDocument( const QDomDocument& doc, QDomDocum
|
||||
QString serverUrl = typeDetectionUri.toString();
|
||||
|
||||
QNetworkRequest request( serverUrl );
|
||||
mAuth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "Network request update failed for authentication config" ),
|
||||
tr( "WFS" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
request.setHeader( QNetworkRequest::ContentTypeHeader, "text/xml" );
|
||||
QNetworkReply* reply = QgsNetworkAccessManager::instance()->post( request, doc.toByteArray( -1 ) );
|
||||
|
||||
@ -1507,8 +1527,15 @@ void QgsWFSProvider::getLayerCapabilities()
|
||||
QUrl getCapabilitiesUrl( uri );
|
||||
getCapabilitiesUrl.removeQueryItem( "username" );
|
||||
getCapabilitiesUrl.removeQueryItem( "password" );
|
||||
getCapabilitiesUrl.removeQueryItem( "authcfg" );
|
||||
QNetworkRequest request( getCapabilitiesUrl.toString() );
|
||||
mAuth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
mCapabilities = 0;
|
||||
QgsMessageLog::logMessage( tr( "Network request update failed for authentication config" ),
|
||||
tr( "WFS" ) );
|
||||
return;
|
||||
}
|
||||
QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
|
||||
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( networkRequestFinished() ) );
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <QDomElement>
|
||||
#include "qgis.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
@ -35,15 +36,24 @@ class QgsSpatialIndex;
|
||||
// TODO: merge with QgsWmsAuthorization?
|
||||
struct QgsWFSAuthorization
|
||||
{
|
||||
QgsWFSAuthorization( const QString& userName = QString(), const QString& password = QString() ) : mUserName( userName ), mPassword( password ) {}
|
||||
QgsWFSAuthorization( const QString& userName = QString(), const QString& password = QString(), const QString& authcfg = QString() )
|
||||
: mUserName( userName )
|
||||
, mPassword( password )
|
||||
, mAuthCfg( authcfg )
|
||||
{}
|
||||
|
||||
//! set authorization header
|
||||
void setAuthorization( QNetworkRequest &request ) const
|
||||
bool setAuthorization( QNetworkRequest &request ) const
|
||||
{
|
||||
if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
if ( !mAuthCfg.isEmpty() )
|
||||
{
|
||||
return QgsAuthManager::instance()->updateNetworkRequest( request, mAuthCfg );
|
||||
}
|
||||
else if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
{
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( mUserName ).arg( mPassword ).toAscii().toBase64() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Username for basic http authentication
|
||||
@ -51,6 +61,9 @@ struct QgsWFSAuthorization
|
||||
|
||||
//! Password for basic http authentication
|
||||
QString mPassword;
|
||||
|
||||
//! Authentication configuration ID
|
||||
QString mAuthCfg;
|
||||
};
|
||||
|
||||
/** A provider reading features from a WFS server*/
|
||||
|
@ -24,11 +24,19 @@ SET (WMS_MOC_HDRS
|
||||
|
||||
QT4_WRAP_CPP (WMS_MOC_SRCS ${WMS_MOC_HDRS})
|
||||
|
||||
INCLUDE_DIRECTORIES( . ../../core ../../core/geometry ../../core/raster ../../gui
|
||||
INCLUDE_DIRECTORIES(
|
||||
.
|
||||
../../core
|
||||
../../core/auth
|
||||
../../core/geometry
|
||||
../../core/raster
|
||||
../../gui
|
||||
../../gui/auth
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../ui
|
||||
${GDAL_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QT_QTSCRIPT_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY(wmsprovider MODULE ${WMS_SRCS} ${WMS_MOC_SRCS})
|
||||
|
@ -44,6 +44,12 @@ bool QgsWmsSettings::parseUri( QString uriString )
|
||||
mAuth.mPassword = uri.param( "password" );
|
||||
QgsDebugMsg( "set password to " + mAuth.mPassword );
|
||||
|
||||
if ( uri.hasParam( "authcfg" ) )
|
||||
{
|
||||
mAuth.mAuthCfg = uri.param( "authcfg" );
|
||||
}
|
||||
QgsDebugMsg( "set authcfg to " + mAuth.mAuthCfg );
|
||||
|
||||
mAuth.mReferer = uri.param( "referer" );
|
||||
QgsDebugMsg( "set referer to " + mAuth.mReferer );
|
||||
|
||||
@ -1911,7 +1917,12 @@ bool QgsWmsCapabilitiesDownload::downloadCapabilities()
|
||||
mError.clear();
|
||||
|
||||
QNetworkRequest request( url );
|
||||
mAuth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
mError = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mError, tr( "WMS" ) );
|
||||
return false;
|
||||
}
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork );
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
|
||||
@ -1995,7 +2006,14 @@ void QgsWmsCapabilitiesDownload::capabilitiesReplyFinished()
|
||||
else
|
||||
{
|
||||
QNetworkRequest request( toUrl );
|
||||
mAuth.setAuthorization( request );
|
||||
if ( !mAuth.setAuthorization( request ) )
|
||||
{
|
||||
mHttpCapabilitiesResponse.clear();
|
||||
mError = tr( "Download of capabilities failed: network request update failed for authentication config" );
|
||||
QgsMessageLog::logMessage( mError, tr( "WMS" ) );
|
||||
emit downloadFinished();
|
||||
return;
|
||||
}
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork );
|
||||
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsraster.h"
|
||||
#include "qgsrectangle.h"
|
||||
|
||||
@ -434,12 +435,16 @@ struct QgsWmsParserSettings
|
||||
|
||||
struct QgsWmsAuthorization
|
||||
{
|
||||
QgsWmsAuthorization( const QString& userName = QString(), const QString& password = QString(), const QString& referer = QString() )
|
||||
: mUserName( userName ), mPassword( password ), mReferer( referer ) {}
|
||||
QgsWmsAuthorization( const QString& userName = QString(), const QString& password = QString(), const QString& referer = QString(), const QString& authcfg = QString() )
|
||||
: mUserName( userName ), mPassword( password ), mReferer( referer ), mAuthCfg( authcfg ) {}
|
||||
|
||||
void setAuthorization( QNetworkRequest &request ) const
|
||||
bool setAuthorization( QNetworkRequest &request ) const
|
||||
{
|
||||
if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
if ( !mAuthCfg.isEmpty() )
|
||||
{
|
||||
return QgsAuthManager::instance()->updateNetworkRequest( request, mAuthCfg );
|
||||
}
|
||||
else if ( !mUserName.isNull() || !mPassword.isNull() )
|
||||
{
|
||||
request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( mUserName ).arg( mPassword ).toAscii().toBase64() );
|
||||
}
|
||||
@ -448,6 +453,7 @@ struct QgsWmsAuthorization
|
||||
{
|
||||
request.setRawHeader( "Referer", QString( "%1" ).arg( mReferer ).toAscii() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Username for basic http authentication
|
||||
@ -459,7 +465,8 @@ struct QgsWmsAuthorization
|
||||
//! Referer for http requests
|
||||
QString mReferer;
|
||||
|
||||
|
||||
//! Authentication configuration ID
|
||||
QString mAuthCfg;
|
||||
};
|
||||
|
||||
|
||||
|
@ -59,6 +59,12 @@ QgsWMSConnection::QgsWMSConnection( QString theConnName ) :
|
||||
mUri.setParam( "password", password );
|
||||
}
|
||||
|
||||
QString authcfg = settings.value( credentialsKey + "/authcfg" ).toString();
|
||||
if ( !authcfg.isEmpty() )
|
||||
{
|
||||
mUri.setParam( "authcfg", authcfg );
|
||||
}
|
||||
|
||||
QString referer = settings.value( key + "/referer" ).toString();
|
||||
if ( !referer.isEmpty() )
|
||||
{
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>475</width>
|
||||
<height>463</height>
|
||||
<width>526</width>
|
||||
<height>721</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -27,13 +27,111 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="mGroupBox">
|
||||
<property name="title">
|
||||
<string>Connection details</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="8" column="0">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QTabWidget" name="tabAuth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Authentication</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&User name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtUserName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="txtUserName"/>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>If the service requires basic authentication, enter a user name and optional password</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtPassword</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="txtPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="txtName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Name of the new connection</string>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="lblDpiMode">
|
||||
<property name="text">
|
||||
<string>DPI-Mode</string>
|
||||
@ -56,45 +154,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>If the service requires basic authentication, enter a user name and optional password</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtPassword</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&User name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtUserName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="TextLabel1_2">
|
||||
<property name="text">
|
||||
@ -111,22 +170,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="txtName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Name of the new connection</string>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="txtUrl">
|
||||
<property name="toolTip">
|
||||
@ -134,55 +177,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="txtUserName"/>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="txtPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreGetFeatureInfoURI">
|
||||
<property name="text">
|
||||
<string>Ignore GetFeatureInfo URI reported in capabilities</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreGetMapURI">
|
||||
<property name="text">
|
||||
<string>Ignore GetMap/GetTile URI reported in capabilities</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreAxisOrientation">
|
||||
<property name="text">
|
||||
<string>Ignore axis orientation (WMS 1.3/WMTS)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxInvertAxisOrientation">
|
||||
<property name="text">
|
||||
<string>Invert axis orientation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSmoothPixmapTransform">
|
||||
<property name="text">
|
||||
<string>Smooth pixmap transform</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="txtReferer"/>
|
||||
<widget class="QComboBox" name="cmbDpiMode"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="lblReferer">
|
||||
<property name="text">
|
||||
<string>Referer</string>
|
||||
@ -192,8 +190,43 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QComboBox" name="cmbDpiMode"/>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="txtReferer"/>
|
||||
</item>
|
||||
<item row="15" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSmoothPixmapTransform">
|
||||
<property name="text">
|
||||
<string>Smooth pixmap transform</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxInvertAxisOrientation">
|
||||
<property name="text">
|
||||
<string>Invert axis orientation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreAxisOrientation">
|
||||
<property name="text">
|
||||
<string>Ignore axis orientation (WMS 1.3/WMTS)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreGetMapURI">
|
||||
<property name="text">
|
||||
<string>Ignore GetMap/GetTile URI reported in capabilities</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreGetFeatureInfoURI">
|
||||
<property name="text">
|
||||
<string>Ignore GetFeatureInfo URI reported in capabilities</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
Loading…
x
Reference in New Issue
Block a user