/*************************************************************************** qgsserverparameters.cpp -------------------- begin : Jun 27, 2018 copyright : (C) 2018 by Paul Blottiere email : paul dot blottiere at oslandia dot com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "qgsblockingnetworkrequest.h" #include "qgsserverparameters.h" #include "moc_qgsserverparameters.cpp" #include "qgsserverexception.h" #include "qgsmessagelog.h" #include "qgsvariantutils.h" #include #include #include #include #include // // QgsServerParameterDefinition // QgsServerParameterDefinition::QgsServerParameterDefinition( const QMetaType::Type type, const QVariant defaultValue ) : mType( type ) , mDefaultValue( defaultValue ) { } QgsServerParameterDefinition::QgsServerParameterDefinition( const QVariant::Type type, const QVariant defaultValue ) : QgsServerParameterDefinition( QgsVariantUtils::variantTypeToMetaType( type ), defaultValue ) { } QString QgsServerParameterDefinition::typeName() const { return QVariant::typeToName( mType ); } QColor QgsServerParameterDefinition::toColor( bool &ok ) const { ok = true; QColor color = mDefaultValue.value(); QString cStr = mValue.toString(); if ( !cStr.isEmpty() ) { // support hexadecimal notation to define colors if ( cStr.startsWith( QLatin1String( "0x" ), Qt::CaseInsensitive ) ) { cStr.replace( 0, 2, QStringLiteral( "#" ) ); } color = QColor( cStr ); ok = color.isValid(); } return color; } QString QgsServerParameterDefinition::toString( const bool defaultValue ) const { QString value = mValue.toString(); if ( value.isEmpty() && defaultValue ) value = mDefaultValue.toString(); return value; } QStringList QgsServerParameterDefinition::toStringList( const char delimiter, const bool skipEmptyParts ) const { if ( skipEmptyParts ) { return toString().split( delimiter, Qt::SkipEmptyParts ); } else { QStringList list; if ( !toString().isEmpty() ) { list = toString().split( delimiter, Qt::KeepEmptyParts ); } return list; } } QList QgsServerParameterDefinition::toGeomList( bool &ok, const char delimiter, const bool skipEmptyParts ) const { ok = true; QList geoms; const auto constStringList( toStringList( delimiter, skipEmptyParts ) ); for ( const auto &wkt : constStringList ) { const QgsGeometry g( QgsGeometry::fromWkt( wkt ) ); if ( g.isGeosValid() ) { geoms.append( g ); } else { ok = false; return QList(); } } return geoms; } QStringList QgsServerParameterDefinition::toOgcFilterList() const { int pos = 0; QStringList filters; const QString filter = toString(); while ( pos < filter.size() ) { if ( pos + 1 < filter.size() && filter[pos] == '(' && filter[pos + 1] == '<' ) { // OGC filter on multiple layers int posEnd = filter.indexOf( "Filter>)", pos ); if ( posEnd < 0 ) { posEnd = filter.size(); } filters.append( filter.mid( pos + 1, posEnd - pos + 6 ) ); pos = posEnd + 8; } else if ( pos + 1 < filter.size() && filter[pos] == '(' && filter[pos + 1] == ')' ) { // empty OGC filter filters.append( "" ); pos += 2; } else if ( filter[pos] == '<' && pos + 7 < filter.size() && filter.mid( pos + 1, 6 ).compare( QLatin1String( "Filter" ) ) == 0 ) { // Single OGC filter filters.append( filter.mid( pos ) ); break; } else { pos += 1; } } return filters; } QStringList QgsServerParameterDefinition::toExpressionList() const { int pos = 0; QStringList filters; const QString filter = toString(); auto isOgcFilter = [filter]() { return filter.contains( QStringLiteral( "" ) ) || filter.contains( QStringLiteral( "()" ) ); }; while ( pos < filter.size() ) { int posEnd = filter.indexOf( ';', pos ); if ( posEnd == pos + 1 ) { if ( !isOgcFilter() ) filters.append( QString() ); pos = posEnd; continue; } if ( !isOgcFilter() ) filters.append( filter.mid( pos, posEnd - pos ) ); if ( posEnd < 0 ) { pos = filter.size(); } else { pos = posEnd + 1; } } if ( !filter.isEmpty() && filter.back() == ';' ) { filters.append( QString() ); } return filters; } QList QgsServerParameterDefinition::toColorList( bool &ok, const char delimiter, bool skipEmptyParts ) const { ok = true; QList colors; const auto constStringList( toStringList( delimiter, skipEmptyParts ) ); for ( const auto &part : constStringList ) { QString cStr( part ); if ( !cStr.isEmpty() ) { // support hexadecimal notation to define colors if ( cStr.startsWith( QLatin1String( "0x" ), Qt::CaseInsensitive ) ) { cStr.replace( 0, 2, QStringLiteral( "#" ) ); } const QColor color = QColor( cStr ); ok = color.isValid(); if ( !ok ) { return QList(); } colors.append( color ); } } return colors; } QList QgsServerParameterDefinition::toIntList( bool &ok, const char delimiter, bool skipEmptyParts ) const { ok = true; QList ints; const auto constStringList( toStringList( delimiter, skipEmptyParts ) ); for ( const auto &part : constStringList ) { const int val = part.toInt( &ok ); if ( !ok ) { return QList(); } ints.append( val ); } return ints; } QList QgsServerParameterDefinition::toDoubleList( bool &ok, const char delimiter, bool skipEmptyParts ) const { ok = true; QList vals; const auto constStringList( toStringList( delimiter, skipEmptyParts ) ); for ( const auto &part : constStringList ) { const double val = part.toDouble( &ok ); if ( !ok ) { return QList(); } vals.append( val ); } return vals; } QgsRectangle QgsServerParameterDefinition::toRectangle( bool &ok ) const { ok = true; QgsRectangle extent; if ( !mValue.toString().isEmpty() ) { QStringList corners = mValue.toString().split( ',' ); if ( corners.size() == 4 ) { double d[4]; for ( int i = 0; i < 4; i++ ) { corners[i].replace( ' ', '+' ); d[i] = corners[i].toDouble( &ok ); if ( !ok ) { return QgsRectangle(); } } if ( d[0] > d[2] || d[1] > d[3] ) { ok = false; return QgsRectangle(); } extent = QgsRectangle( d[0], d[1], d[2], d[3] ); } else { ok = false; return QgsRectangle(); } } return extent; } QString QgsServerParameterDefinition::loadUrl( bool &ok ) const { ok = true; // Get URL const QUrl url = toUrl( ok ); if ( !ok ) { return QString(); } QNetworkRequest request( url ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); // fetching content QgsBlockingNetworkRequest newReq; const QgsBlockingNetworkRequest::ErrorCode errorCode = newReq.get( request, false ); if ( errorCode != QgsBlockingNetworkRequest::NoError ) { ok = false; QgsMessageLog::logMessage( QObject::tr( "Request failed [error: %1 - url: %2]" ).arg( newReq.errorMessage(), url.toString() ), QStringLiteral( "Server" ) ); return QString(); } QgsNetworkReplyContent reply = newReq.reply(); ok = !reply.content().isEmpty(); return reply.content(); } QUrl QgsServerParameterDefinition::toUrl( bool &ok ) const { ok = true; QUrl val; if ( !mValue.toString().isEmpty() ) { val = mValue.toUrl(); } ok = ( !val.isEmpty() && val.isValid() ); return val; } int QgsServerParameterDefinition::toInt( bool &ok ) const { ok = true; int val = mDefaultValue.toInt(); if ( !mValue.toString().isEmpty() ) { val = mValue.toInt( &ok ); } return val; } bool QgsServerParameterDefinition::toBool() const { int val = mDefaultValue.toBool(); if ( !mValue.toString().isEmpty() ) { val = mValue.toBool(); } return val; } double QgsServerParameterDefinition::toDouble( bool &ok ) const { ok = true; double val = mDefaultValue.toDouble(); if ( !mValue.toString().isEmpty() ) { val = mValue.toDouble( &ok ); } return val; } bool QgsServerParameterDefinition::isValid() const { return mValue.canConvert( mType ); } void QgsServerParameterDefinition::raiseError( const QString &msg ) { throw QgsBadRequestException( QStringLiteral( "Invalid Parameter" ), msg ); } // // QgsServerParameter // QgsServerParameter::QgsServerParameter( const QgsServerParameter::Name name, const QMetaType::Type type, const QVariant defaultValue ) : QgsServerParameterDefinition( type, defaultValue ) , mName( name ) { } QgsServerParameter::QgsServerParameter( const QgsServerParameter::Name name, const QVariant::Type type, const QVariant defaultValue ) : QgsServerParameter( name, QgsVariantUtils::variantTypeToMetaType( type ), defaultValue ) { } QString QgsServerParameter::name( const QgsServerParameter::Name name ) { if ( name == QgsServerParameter::VERSION_SERVICE ) { return QStringLiteral( "VERSION" ); } else { const QMetaEnum metaEnum( QMetaEnum::fromType() ); return metaEnum.valueToKey( name ); } } QgsServerParameter::Name QgsServerParameter::name( const QString &name ) { if ( name.compare( QLatin1String( "VERSION" ) ) == 0 ) { return QgsServerParameter::VERSION_SERVICE; } else { const QMetaEnum metaEnum( QMetaEnum::fromType() ); return ( QgsServerParameter::Name ) metaEnum.keyToValue( name.toUpper().toStdString().c_str() ); } } void QgsServerParameter::raiseError() const { const QString msg = QString( "%1 ('%2') cannot be converted into %3" ).arg( name( mName ), mValue.toString(), typeName() ); QgsServerParameterDefinition::raiseError( msg ); } // // QgsServerParameters // QgsServerParameters::QgsServerParameters() { save( QgsServerParameter( QgsServerParameter::SERVICE ) ); save( QgsServerParameter( QgsServerParameter::REQUEST ) ); save( QgsServerParameter( QgsServerParameter::VERSION_SERVICE ) ); save( QgsServerParameter( QgsServerParameter::MAP ) ); save( QgsServerParameter( QgsServerParameter::FILE_NAME ) ); } QgsServerParameters::QgsServerParameters( const QUrlQuery &query ) : QgsServerParameters() { mUrlQuery = query; load( query ); } void QgsServerParameters::save( const QgsServerParameter ¶meter ) { mParameters[parameter.mName] = parameter; } void QgsServerParameters::add( const QString &key, const QString &value ) { QUrlQuery query; query.addQueryItem( key, value ); load( query ); } QUrlQuery QgsServerParameters::urlQuery() const { QUrlQuery query = mUrlQuery; if ( query.isEmpty() ) { query.clear(); const auto constMap( toMap().toStdMap() ); for ( const auto ¶m : constMap ) { const QString value = QUrl::toPercentEncoding( QString( param.second ) ); query.addQueryItem( param.first, value ); } } return query; } void QgsServerParameters::remove( QgsServerParameter::Name name ) { remove( QgsServerParameter::name( name ) ); } void QgsServerParameters::remove( const QString &key ) { if ( mUnmanagedParameters.contains( key ) ) { mUnmanagedParameters.take( key ); } else { const QgsServerParameter::Name paramName = QgsServerParameter::name( key ); if ( mParameters.contains( paramName ) ) { mParameters.take( paramName ); } } } QString QgsServerParameters::map() const { return value( QgsServerParameter::MAP ).toString(); } QString QgsServerParameters::version() const { return value( QgsServerParameter::VERSION_SERVICE ).toString(); } QString QgsServerParameters::fileName() const { return value( QgsServerParameter::FILE_NAME ).toString(); } QString QgsServerParameters::service() const { QString serviceValue = value( QgsServerParameter::SERVICE ).toString(); if ( serviceValue.isEmpty() ) { // SERVICE not mandatory for WMS 1.3.0 GetMap & GetFeatureInfo if ( request() == QLatin1String( "GetMap" ) || request() == QLatin1String( "GetFeatureInfo" ) ) { serviceValue = "WMS"; } } return serviceValue; } QMap QgsServerParameters::toMap() const { QMap params = mUnmanagedParameters; for ( const auto ¶meter : mParameters.toStdMap() ) { if ( QgsVariantUtils::isNull( parameter.second.mValue ) ) continue; if ( parameter.second.mName == QgsServerParameter::VERSION_SERVICE ) { params["VERSION"] = parameter.second.mValue.toString(); } else { const QString paramName = QgsServerParameter::name( parameter.first ); params[paramName] = parameter.second.mValue.toString(); } } return params; } QString QgsServerParameters::request() const { return value( QgsServerParameter::REQUEST ).toString(); } QString QgsServerParameters::value( const QString &key ) const { if ( !mParameters.contains( QgsServerParameter::name( key ) ) ) { return mUnmanagedParameters[key]; } else { return value( QgsServerParameter::name( key ) ).toString(); } } QVariant QgsServerParameters::value( QgsServerParameter::Name name ) const { return mParameters[name].mValue; } void QgsServerParameters::load( const QUrlQuery &query ) { // clean query string first QUrlQuery cleanQuery( query ); cleanQuery.setQuery( query.query().replace( '+', QLatin1String( "%20" ) ) ); // load parameters const auto constQueryItems( cleanQuery.queryItems( QUrl::FullyDecoded ) ); for ( const auto &item : constQueryItems ) { const QgsServerParameter::Name name = QgsServerParameter::name( item.first ); if ( name >= 0 ) { mParameters[name].mValue = item.second; if ( !mParameters[name].isValid() ) { mParameters[name].raiseError(); } } else if ( item.first.compare( QLatin1String( "VERSION" ), Qt::CaseInsensitive ) == 0 ) { const QgsServerParameter::Name name = QgsServerParameter::VERSION_SERVICE; mParameters[name].mValue = item.second; if ( !mParameters[name].isValid() ) { mParameters[name].raiseError(); } } else if ( !loadParameter( item.first, item.second ) ) { mUnmanagedParameters[item.first.toUpper()] = item.second; } } } bool QgsServerParameters::loadParameter( const QString &, const QString & ) { return false; } void QgsServerParameters::clear() { mParameters.clear(); mUnmanagedParameters.clear(); }