QGIS API Documentation  3.27.0-Master (597e8eebd4)
qgsserverparameters.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsserverparameters.cpp
3  --------------------
4  begin : Jun 27, 2018
5  copyright : (C) 2018 by Paul Blottiere
6  email : paul dot blottiere at oslandia dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsserverparameters.h"
19 #include "qgsserverexception.h"
21 #include "qgsmessagelog.h"
22 #include <QObject>
23 #include <QUrl>
24 #include <QNetworkReply>
25 #include <QNetworkRequest>
26 #include <QEventLoop>
27 
28 //
29 // QgsServerParameterDefinition
30 //
32  const QVariant defaultValue )
33  : mType( type )
34  , mDefaultValue( defaultValue )
35 {
36 }
37 
39 {
40  return QVariant::typeToName( mType );
41 }
42 
43 QColor QgsServerParameterDefinition::toColor( bool &ok ) const
44 {
45  ok = true;
46  QColor color = mDefaultValue.value<QColor>();
47  QString cStr = mValue.toString();
48 
49  if ( !cStr.isEmpty() )
50  {
51  // support hexadecimal notation to define colors
52  if ( cStr.startsWith( QLatin1String( "0x" ), Qt::CaseInsensitive ) )
53  {
54  cStr.replace( 0, 2, QStringLiteral( "#" ) );
55  }
56 
57  color = QColor( cStr );
58 
59  ok = color.isValid();
60  }
61 
62  return color;
63 }
64 
65 QString QgsServerParameterDefinition::toString( const bool defaultValue ) const
66 {
67  QString value = mValue.toString();
68 
69  if ( value.isEmpty() && defaultValue )
70  value = mDefaultValue.toString();
71 
72  return value;
73 }
74 
75 QStringList QgsServerParameterDefinition::toStringList( const char delimiter, const bool skipEmptyParts ) const
76 {
77  if ( skipEmptyParts )
78  {
79 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
80  return toString().split( delimiter, QString::SkipEmptyParts );
81 #else
82  return toString().split( delimiter, Qt::SkipEmptyParts );
83 #endif
84  }
85  else
86  {
87  QStringList list;
88  if ( !toString().isEmpty() )
89  {
90 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
91  list = toString().split( delimiter, QString::KeepEmptyParts );
92 #else
93  list = toString().split( delimiter, Qt::KeepEmptyParts );
94 #endif
95  }
96  return list;
97  }
98 }
99 
100 QList<QgsGeometry> QgsServerParameterDefinition::toGeomList( bool &ok, const char delimiter ) const
101 {
102  ok = true;
103  QList<QgsGeometry> geoms;
104 
105  const auto constStringList( toStringList( delimiter ) );
106  for ( const auto &wkt : constStringList )
107  {
108  const QgsGeometry g( QgsGeometry::fromWkt( wkt ) );
109 
110  if ( g.isGeosValid() )
111  {
112  geoms.append( g );
113  }
114  else
115  {
116  ok = false;
117  return QList<QgsGeometry>();
118  }
119  }
120 
121  return geoms;
122 }
123 
125 {
126  int pos = 0;
127  QStringList filters;
128  const QString filter = toString();
129 
130  while ( pos < filter.size() )
131  {
132  if ( pos + 1 < filter.size() && filter[pos] == '(' && filter[pos + 1] == '<' )
133  {
134  // OGC filter on multiple layers
135  int posEnd = filter.indexOf( "Filter>)", pos );
136  if ( posEnd < 0 )
137  {
138  posEnd = filter.size();
139  }
140  filters.append( filter.mid( pos + 1, posEnd - pos + 6 ) );
141  pos = posEnd + 8;
142  }
143  else if ( pos + 1 < filter.size() && filter[pos] == '(' && filter[pos + 1] == ')' )
144  {
145  // empty OGC filter
146  filters.append( "" );
147  pos += 2;
148  }
149  else if ( filter[pos] == '<' && pos + 7 < filter.size() && filter.mid( pos + 1, 6 ).compare( QLatin1String( "Filter" ) ) == 0 )
150  {
151  // Single OGC filter
152  filters.append( filter.mid( pos ) );
153  break;
154  }
155  else
156  {
157  pos += 1;
158  }
159  }
160 
161  return filters;
162 }
163 
165 {
166  int pos = 0;
167  QStringList filters;
168  const QString filter = toString();
169 
170  auto isOgcFilter = [filter]()
171  {
172  return filter.contains( QStringLiteral( "<Filter>" ) ) or filter.contains( QStringLiteral( "()" ) );
173  };
174 
175  while ( pos < filter.size() )
176  {
177  int posEnd = filter.indexOf( ';', pos );
178 
179  if ( posEnd == pos + 1 )
180  {
181  if ( ! isOgcFilter() )
182  filters.append( QString() );
183  pos = posEnd;
184  continue;
185  }
186 
187  if ( ! isOgcFilter() )
188  filters.append( filter.mid( pos, posEnd - pos ) );
189 
190  if ( posEnd < 0 )
191  {
192  pos = filter.size();
193  }
194  else
195  {
196  pos = posEnd + 1;
197  }
198  }
199 
200  if ( ! filter.isEmpty() && filter.back() == ';' )
201  {
202  filters.append( QString() );
203  }
204 
205  return filters;
206 }
207 
208 QList<QColor> QgsServerParameterDefinition::toColorList( bool &ok, const char delimiter ) const
209 {
210  ok = true;
211  QList<QColor> colors;
212 
213  const auto constStringList( toStringList( delimiter ) );
214  for ( const auto &part : constStringList )
215  {
216  QString cStr( part );
217  if ( !cStr.isEmpty() )
218  {
219  // support hexadecimal notation to define colors
220  if ( cStr.startsWith( QLatin1String( "0x" ), Qt::CaseInsensitive ) )
221  {
222  cStr.replace( 0, 2, QStringLiteral( "#" ) );
223  }
224 
225  const QColor color = QColor( cStr );
226  ok = color.isValid();
227 
228  if ( !ok )
229  {
230  return QList<QColor>();
231  }
232 
233  colors.append( color );
234  }
235  }
236 
237  return colors;
238 }
239 
240 QList<int> QgsServerParameterDefinition::toIntList( bool &ok, const char delimiter ) const
241 {
242  ok = true;
243  QList<int> ints;
244 
245  const auto constStringList( toStringList( delimiter ) );
246  for ( const auto &part : constStringList )
247  {
248  const int val = part.toInt( &ok );
249 
250  if ( !ok )
251  {
252  return QList<int>();
253  }
254 
255  ints.append( val );
256  }
257 
258  return ints;
259 }
260 
261 QList<double> QgsServerParameterDefinition::toDoubleList( bool &ok, const char delimiter ) const
262 {
263  ok = true;
264  QList<double> vals;
265 
266  const auto constStringList( toStringList( delimiter ) );
267  for ( const auto &part : constStringList )
268  {
269  const double val = part.toDouble( &ok );
270 
271  if ( !ok )
272  {
273  return QList<double>();
274  }
275 
276  vals.append( val );
277  }
278 
279  return vals;
280 }
281 
283 {
284  ok = true;
285  QgsRectangle extent;
286 
287  if ( !mValue.toString().isEmpty() )
288  {
289  QStringList corners = mValue.toString().split( ',' );
290 
291  if ( corners.size() == 4 )
292  {
293  double d[4];
294 
295  for ( int i = 0; i < 4; i++ )
296  {
297  corners[i].replace( ' ', '+' );
298  d[i] = corners[i].toDouble( &ok );
299  if ( !ok )
300  {
301  return QgsRectangle();
302  }
303  }
304 
305  if ( d[0] > d[2] || d[1] > d[3] )
306  {
307  ok = false;
308  return QgsRectangle();
309  }
310 
311  extent = QgsRectangle( d[0], d[1], d[2], d[3] );
312  }
313  else
314  {
315  ok = false;
316  return QgsRectangle();
317  }
318  }
319 
320  return extent;
321 }
322 
323 QString QgsServerParameterDefinition::loadUrl( bool &ok ) const
324 {
325  ok = true;
326 
327  // Get URL
328  const QUrl url = toUrl( ok );
329  if ( !ok )
330  {
331  return QString();
332  }
333 
334  // fetching content
335  QgsNetworkContentFetcher fetcher;
336  QEventLoop loop;
337  QObject::connect( &fetcher, &QgsNetworkContentFetcher::finished, &loop, &QEventLoop::quit );
338 
340  QObject::tr( "Request started [url: %1]" ).arg( url.toString() ),
341  QStringLiteral( "Server" ) );
342  QNetworkRequest request( url );
343  request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
344  request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
345  fetcher.fetchContent( request );
346 
347  //wait until content fetched
348  loop.exec( QEventLoop::ExcludeUserInputEvents );
349 
350  QNetworkReply *reply = fetcher.reply();
351  if ( !reply )
352  {
353  ok = false;
355  QObject::tr( "Request failed [error: no reply - url: %1]" ).arg( url.toString() ),
356  QStringLiteral( "Server" ) );
357  return QString();
358  }
359 
360  const QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
361  if ( !status.isNull() && status.toInt() >= 400 )
362  {
363  ok = false;
364  if ( reply->error() != QNetworkReply::NoError )
365  {
367  QObject::tr( "Request failed [error: %1 - url: %2]" ).arg( reply->errorString(), reply->url().toString() ),
368  QStringLiteral( "Server" ) );
369  }
370  const QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
372  QObject::tr( "Request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), reply->url().toString() ),
373  QStringLiteral( "Server" ) );
374  return QString();
375  }
376 
377  if ( reply->error() != QNetworkReply::NoError )
378  {
379  ok = false;
381  QObject::tr( "Request failed [error: %1 - url: %2]" ).arg( reply->errorString(), reply->url().toString() ),
382  QStringLiteral( "Server" ) );
383  return QString();
384  }
385 
387  QObject::tr( "Request finished [url: %1]" ).arg( url.toString() ),
388  QStringLiteral( "Server" ) );
389 
390  QString content = fetcher.contentAsString();
391  ok = ( !content.isEmpty() );
392  return content;
393 }
394 
396 {
397  ok = true;
398  QUrl val;
399 
400  if ( !mValue.toString().isEmpty() )
401  {
402  val = mValue.toUrl();
403  }
404 
405  ok = ( !val.isEmpty() && val.isValid() );
406  return val;
407 }
408 
410 {
411  ok = true;
412  int val = mDefaultValue.toInt();
413 
414  if ( !mValue.toString().isEmpty() )
415  {
416  val = mValue.toInt( &ok );
417  }
418 
419  return val;
420 }
421 
423 {
424  int val = mDefaultValue.toBool();
425 
426  if ( !mValue.toString().isEmpty() )
427  {
428  val = mValue.toBool();
429  }
430 
431  return val;
432 }
433 
435 {
436  ok = true;
437  double val = mDefaultValue.toDouble();
438 
439  if ( !mValue.toString().isEmpty() )
440  {
441  val = mValue.toDouble( &ok );
442  }
443 
444  return val;
445 }
446 
448 {
449  return mValue.canConvert( mType );
450 }
451 
453 {
454  throw QgsBadRequestException( QStringLiteral( "Invalid Parameter" ), msg );
455 }
456 
457 //
458 // QgsServerParameter
459 //
461  const QVariant::Type type, const QVariant defaultValue )
462  : QgsServerParameterDefinition( type, defaultValue )
463  , mName( name )
464 {
465 }
466 
468 {
470  {
471  return QStringLiteral( "VERSION" );
472  }
473  else
474  {
475  const QMetaEnum metaEnum( QMetaEnum::fromType<QgsServerParameter::Name>() );
476  return metaEnum.valueToKey( name );
477  }
478 }
479 
481 {
482  if ( name.compare( QLatin1String( "VERSION" ) ) == 0 )
483  {
485  }
486  else
487  {
488  const QMetaEnum metaEnum( QMetaEnum::fromType<QgsServerParameter::Name>() );
489  return ( QgsServerParameter::Name ) metaEnum.keyToValue( name.toUpper().toStdString().c_str() );
490  }
491 }
492 
494 {
495  const QString msg = QString( "%1 ('%2') cannot be converted into %3" ).arg( name( mName ), mValue.toString(), typeName() );
497 }
498 
499 //
500 // QgsServerParameters
501 //
503 {
509 }
510 
513 {
514  mUrlQuery = query;
515  load( query );
516 }
517 
518 void QgsServerParameters::save( const QgsServerParameter &parameter )
519 {
520  mParameters[ parameter.mName ] = parameter;
521 }
522 
523 void QgsServerParameters::add( const QString &key, const QString &value )
524 {
525  QUrlQuery query;
526  query.addQueryItem( key, value );
527  load( query );
528 }
529 
531 {
532  QUrlQuery query = mUrlQuery;
533 
534  if ( query.isEmpty() )
535  {
536  query.clear();
537 
538  const auto constMap( toMap().toStdMap() );
539  for ( const auto &param : constMap )
540  {
541  const QString value = QUrl::toPercentEncoding( QString( param.second ) );
542  query.addQueryItem( param.first, value );
543  }
544  }
545 
546  return query;
547 }
548 
550 {
551  remove( QgsServerParameter::name( name ) );
552 }
553 
554 void QgsServerParameters::remove( const QString &key )
555 {
556  if ( mUnmanagedParameters.contains( key ) )
557  {
558  mUnmanagedParameters.take( key );
559  }
560  else
561  {
562  const QgsServerParameter::Name paramName = QgsServerParameter::name( key );
563  if ( mParameters.contains( paramName ) )
564  {
565  mParameters.take( paramName );
566  }
567  }
568 }
569 
571 {
572  return value( QgsServerParameter::MAP ).toString();
573 }
574 
576 {
577  return value( QgsServerParameter::VERSION_SERVICE ).toString();
578 }
579 
581 {
582  return value( QgsServerParameter::FILE_NAME ).toString();
583 }
584 
586 {
587  QString serviceValue = value( QgsServerParameter::SERVICE ).toString();
588 
589  if ( serviceValue.isEmpty() )
590  {
591  // SERVICE not mandatory for WMS 1.3.0 GetMap & GetFeatureInfo
592  if ( request() == QLatin1String( "GetMap" ) \
593  || request() == QLatin1String( "GetFeatureInfo" ) )
594  {
595  serviceValue = "WMS";
596  }
597  }
598 
599  return serviceValue;
600 }
601 
602 QMap<QString, QString> QgsServerParameters::toMap() const
603 {
604  QMap<QString, QString> params = mUnmanagedParameters;
605 
606  for ( const auto &parameter : mParameters.toStdMap() )
607  {
608  if ( parameter.second.mValue.isNull() )
609  continue;
610 
611  if ( parameter.second.mName == QgsServerParameter::VERSION_SERVICE )
612  {
613  params["VERSION"] = parameter.second.mValue.toString();
614  }
615  else
616  {
617  const QString paramName = QgsServerParameter::name( parameter.first );
618  params[paramName] = parameter.second.mValue.toString();
619  }
620  }
621 
622  return params;
623 }
624 
626 {
627  return value( QgsServerParameter::REQUEST ).toString();
628 }
629 
630 QString QgsServerParameters::value( const QString &key ) const
631 {
632  if ( ! mParameters.contains( QgsServerParameter::name( key ) ) )
633  {
634  return mUnmanagedParameters[key];
635  }
636  else
637  {
638  return value( QgsServerParameter::name( key ) ).toString();
639  }
640 }
641 
643 {
644  return mParameters[name].mValue;
645 }
646 
647 void QgsServerParameters::load( const QUrlQuery &query )
648 {
649  // clean query string first
650  QUrlQuery cleanQuery( query );
651  cleanQuery.setQuery( query.query().replace( '+', QLatin1String( "%20" ) ) );
652 
653  // load parameters
654  const auto constQueryItems( cleanQuery.queryItems( QUrl::FullyDecoded ) );
655  for ( const auto &item : constQueryItems )
656  {
657  const QgsServerParameter::Name name = QgsServerParameter::name( item.first );
658  if ( name >= 0 )
659  {
660  mParameters[name].mValue = item.second;
661  if ( ! mParameters[name].isValid() )
662  {
663  mParameters[name].raiseError();
664  }
665  }
666  else if ( item.first.compare( QLatin1String( "VERSION" ), Qt::CaseInsensitive ) == 0 )
667  {
669  mParameters[name].mValue = item.second;
670  if ( ! mParameters[name].isValid() )
671  {
672  mParameters[name].raiseError();
673  }
674  }
675  else if ( ! loadParameter( item.first, item.second ) )
676  {
677  mUnmanagedParameters[item.first.toUpper()] = item.second;
678  }
679  }
680 }
681 
682 bool QgsServerParameters::loadParameter( const QString &, const QString & )
683 {
684  return false;
685 }
686 
688 {
689  mParameters.clear();
690  mUnmanagedParameters.clear();
691 }
Exception thrown in case of malformed request.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
HTTP network content fetcher.
void finished()
Emitted when content has loaded.
QNetworkReply * reply()
Returns a reference to the network reply.
QString contentAsString() const
Returns the fetched content as a string.
void fetchContent(const QUrl &url, const QString &authcfg=QString())
Fetches content from a remote URL and handles redirects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Definition of a parameter with basic conversion methods.
QList< QgsGeometry > toGeomList(bool &ok, char delimiter=',') const
Converts the parameter into a list of geometries.
QString loadUrl(bool &ok) const
Loads the data associated to the parameter converted into an url.
QUrl toUrl(bool &ok) const
Converts the parameter into an url.
QString toString(bool defaultValue=false) const
Converts the parameter into a string.
bool toBool() const
Converts the parameter into a boolean.
QList< double > toDoubleList(bool &ok, char delimiter=',') const
Converts the parameter into a list of doubles.
QStringList toStringList(char delimiter=',', bool skipEmptyParts=true) const
Converts the parameter into a list of strings.
virtual bool isValid() const
Returns true if the parameter is valid, false otherwise.
QgsServerParameterDefinition(const QVariant::Type type=QVariant::String, const QVariant defaultValue=QVariant(""))
Constructor for QgsServerParameterDefinition.
QString typeName() const
Returns the type of the parameter as a string.
static void raiseError(const QString &msg)
Raises an exception in case of an invalid parameters.
QStringList toExpressionList() const
Converts the parameter into a list of QGIS expressions.
int toInt(bool &ok) const
Converts the parameter into an integer.
QList< int > toIntList(bool &ok, char delimiter=',') const
Converts the parameter into a list of integers.
QColor toColor(bool &ok) const
Converts the parameter into a color.
double toDouble(bool &ok) const
Converts the parameter into a double.
QgsRectangle toRectangle(bool &ok) const
Converts the parameter into a rectangle.
QList< QColor > toColorList(bool &ok, char delimiter=',') const
Converts the parameter into a list of colors.
QStringList toOgcFilterList() const
Converts the parameter into a list of OGC filters.
Parameter common to all services (WMS, WFS, ...)
QgsServerParameter::Name mName
QgsServerParameter(const QgsServerParameter::Name name=QgsServerParameter::UNKNOWN, const QVariant::Type type=QVariant::String, const QVariant defaultValue=QVariant(""))
Constructor for QgsServerParameter.
Name
Parameter's name common to all services.
void raiseError() const
Raises an error in case of an invalid conversion.
static QString name(const QgsServerParameter::Name name)
Converts a parameter's name into its string representation.
QgsServerParameters provides an interface to retrieve and manipulate global parameters received from ...
QMap< QString, QString > toMap() const
Returns all parameters in a map.
QString map() const
Returns MAP parameter as a string or an empty string if not defined.
QgsServerParameters()
Constructor.
void add(const QString &key, const QString &value)
Adds a parameter.
QString service() const
Returns SERVICE parameter as a string or an empty string if not defined.
virtual QString request() const
Returns REQUEST parameter as a string or an empty string if not defined.
void clear()
Removes all parameters.
QString fileName() const
Returns FILE_NAME parameter as a string or an empty string if not defined.
virtual bool loadParameter(const QString &name, const QString &value)
Loads a parameter with a specific value.
QUrlQuery urlQuery() const
Returns a url query with underlying parameters.
QMap< QString, QString > mUnmanagedParameters
void load(const QUrlQuery &query)
Loads new parameters.
void remove(const QString &key)
Removes a parameter.
virtual QString version() const
Returns VERSION parameter as a string or an empty string if not defined.
QString value(const QString &key) const
Returns the value of a parameter.