QGIS API Documentation  3.6.0-Noosa (5873452)
qgsserver.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsserver.cpp
3  A server application supporting WMS / WFS / WCS
4  -------------------
5  begin : July 04, 2006
6  copyright : (C) 2006 by Marco Hugentobler & Ionut Iosifescu Enescu
7  : (C) 2015 by Alessandro Pasotti
8  email : marco dot hugentobler at karto dot baug dot ethz dot ch
9  : elpaso at itopen dot it
10  ***************************************************************************/
11 
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 //for CMAKE_INSTALL_PREFIX
22 #include "qgsconfig.h"
23 #include "qgsserver.h"
24 #include "qgsauthmanager.h"
25 #include "qgscapabilitiescache.h"
26 #include "qgsfontutils.h"
27 #include "qgsrequesthandler.h"
28 #include "qgsproject.h"
29 #include "qgsproviderregistry.h"
30 #include "qgslogger.h"
31 #include "qgsmapserviceexception.h"
33 #include "qgsserverlogger.h"
34 #include "qgsserverrequest.h"
36 #include "qgsservice.h"
37 #include "qgsserverparameters.h"
38 #include "qgsapplication.h"
39 
40 #include <QDomDocument>
41 #include <QNetworkDiskCache>
42 #include <QSettings>
43 #include <QDateTime>
44 
45 // TODO: remove, it's only needed by a single debug message
46 #include <fcgi_stdio.h>
47 #include <cstdlib>
48 
49 
50 
51 // Server status static initializers.
52 // Default values are for C++, SIP bindings will override their
53 // options in in init()
54 
55 QString *QgsServer::sConfigFilePath = nullptr;
56 QgsCapabilitiesCache *QgsServer::sCapabilitiesCache = nullptr;
57 QgsServerInterfaceImpl *QgsServer::sServerInterface = nullptr;
58 // Initialization must run once for all servers
59 bool QgsServer::sInitialized = false;
60 QgsServerSettings QgsServer::sSettings;
61 
62 QgsServiceRegistry *QgsServer::sServiceRegistry = nullptr;
63 
65 {
66  // QgsApplication must exist
67  if ( qobject_cast<QgsApplication *>( qApp ) == nullptr )
68  {
69  qFatal( "A QgsApplication must exist before a QgsServer instance can be created." );
70  abort();
71  }
72  init();
73  mConfigCache = QgsConfigCache::instance();
74 }
75 
76 QString &QgsServer::serverName()
77 {
78  static QString *name = new QString( QStringLiteral( "qgis_server" ) );
79  return *name;
80 }
81 
82 
83 QFileInfo QgsServer::defaultAdminSLD()
84 {
85  return QFileInfo( QStringLiteral( "admin.sld" ) );
86 }
87 
88 void QgsServer::setupNetworkAccessManager()
89 {
90  QSettings settings;
92  QNetworkDiskCache *cache = new QNetworkDiskCache( nullptr );
93  qint64 cacheSize = sSettings.cacheSize();
94  QString cacheDirectory = sSettings.cacheDirectory();
95  cache->setCacheDirectory( cacheDirectory );
96  cache->setMaximumCacheSize( cacheSize );
97  QgsMessageLog::logMessage( QStringLiteral( "cacheDirectory: %1" ).arg( cache->cacheDirectory() ), QStringLiteral( "Server" ), Qgis::Info );
98  QgsMessageLog::logMessage( QStringLiteral( "maximumCacheSize: %1" ).arg( cache->maximumCacheSize() ), QStringLiteral( "Server" ), Qgis::Info );
99  nam->setCache( cache );
100 }
101 
102 QFileInfo QgsServer::defaultProjectFile()
103 {
104  QDir currentDir;
105  fprintf( FCGI_stderr, "current directory: %s\n", currentDir.absolutePath().toUtf8().constData() );
106  QStringList nameFilterList;
107  nameFilterList << QStringLiteral( "*.qgs" )
108  << QStringLiteral( "*.qgz" );
109  QFileInfoList projectFiles = currentDir.entryInfoList( nameFilterList, QDir::Files, QDir::Name );
110  for ( int x = 0; x < projectFiles.size(); x++ )
111  {
112  QgsMessageLog::logMessage( projectFiles.at( x ).absoluteFilePath(), QStringLiteral( "Server" ), Qgis::Info );
113  }
114  if ( projectFiles.isEmpty() )
115  {
116  return QFileInfo();
117  }
118  return projectFiles.at( 0 );
119 }
120 
121 void QgsServer::printRequestParameters( const QMap< QString, QString> &parameterMap, Qgis::MessageLevel logLevel )
122 {
123  if ( logLevel > Qgis::Info )
124  {
125  return;
126  }
127 
128  QMap< QString, QString>::const_iterator pIt = parameterMap.constBegin();
129  for ( ; pIt != parameterMap.constEnd(); ++pIt )
130  {
131  QgsMessageLog::logMessage( pIt.key() + ":" + pIt.value(), QStringLiteral( "Server" ), Qgis::Info );
132  }
133 }
134 
135 QString QgsServer::configPath( const QString &defaultConfigPath, const QString &configPath )
136 {
137  QString cfPath( defaultConfigPath );
138  QString projectFile = sSettings.projectFile();
139  if ( !projectFile.isEmpty() )
140  {
141  cfPath = projectFile;
142  QgsDebugMsg( QStringLiteral( "QGIS_PROJECT_FILE:%1" ).arg( cfPath ) );
143  }
144  else
145  {
146  if ( configPath.isEmpty() )
147  {
148  QgsMessageLog::logMessage( QStringLiteral( "Using default configuration file path: %1" ).arg( defaultConfigPath ), QStringLiteral( "Server" ), Qgis::Info );
149  }
150  else
151  {
152  cfPath = configPath;
153  QgsDebugMsg( QStringLiteral( "MAP:%1" ).arg( cfPath ) );
154  }
155  }
156  return cfPath;
157 }
158 
159 bool QgsServer::init()
160 {
161  if ( sInitialized )
162  {
163  return false;
164  }
165 
166  QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
167  QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
168  QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
169 
171 
172 #if defined(SERVER_SKIP_ECW)
173  QgsMessageLog::logMessage( "Skipping GDAL ECW drivers in server.", "Server", Qgis::Info );
175  QgsApplication::skipGdalDriver( "JP2ECW" );
176 #endif
177 
178  // reload settings to take into account QCoreApplication and QgsApplication
179  // configuration
180  sSettings.load();
181 
182  // init and configure logger
185  if ( ! sSettings.logFile().isEmpty() )
186  {
187  QgsServerLogger::instance()->setLogFile( sSettings.logFile() );
188  }
189  else if ( sSettings.logStderr() )
190  {
192  }
193 
194  // log settings currently used
195  sSettings.logSummary();
196 
197  setupNetworkAccessManager();
198  QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
199 
200  // Instantiate the plugin directory so that providers are loaded
202  QgsMessageLog::logMessage( "Prefix PATH: " + QgsApplication::prefixPath(), QStringLiteral( "Server" ), Qgis::Info );
203  QgsMessageLog::logMessage( "Plugin PATH: " + QgsApplication::pluginPath(), QStringLiteral( "Server" ), Qgis::Info );
204  QgsMessageLog::logMessage( "PkgData PATH: " + QgsApplication::pkgDataPath(), QStringLiteral( "Server" ), Qgis::Info );
205  QgsMessageLog::logMessage( "User DB PATH: " + QgsApplication::qgisUserDatabaseFilePath(), QStringLiteral( "Server" ), Qgis::Info );
206  QgsMessageLog::logMessage( "Auth DB PATH: " + QgsApplication::qgisAuthDatabaseFilePath(), QStringLiteral( "Server" ), Qgis::Info );
207  QgsMessageLog::logMessage( "SVG PATHS: " + QgsApplication::svgPaths().join( QDir::separator() ), QStringLiteral( "Server" ), Qgis::Info );
208 
209  QgsApplication::createDatabase(); //init qgis.db (e.g. necessary for user crs)
210 
211  // Initialize the authentication system
212  // creates or uses qgis-auth.db in ~/.qgis3/ or directory defined by QGIS_AUTH_DB_DIR_PATH env variable
213  // set the master password as first line of file defined by QGIS_AUTH_PASSWORD_FILE env variable
214  // (QGIS_AUTH_PASSWORD_FILE variable removed from environment after accessing)
216 
217  QString defaultConfigFilePath;
218  QFileInfo projectFileInfo = defaultProjectFile(); //try to find a .qgs/.qgz file in the server directory
219  if ( projectFileInfo.exists() )
220  {
221  defaultConfigFilePath = projectFileInfo.absoluteFilePath();
222  QgsMessageLog::logMessage( "Using default project file: " + defaultConfigFilePath, QStringLiteral( "Server" ), Qgis::Info );
223  }
224  else
225  {
226  QFileInfo adminSLDFileInfo = defaultAdminSLD();
227  if ( adminSLDFileInfo.exists() )
228  {
229  defaultConfigFilePath = adminSLDFileInfo.absoluteFilePath();
230  }
231  }
232  // Store the config file path
233  sConfigFilePath = new QString( defaultConfigFilePath );
234 
235  //create cache for capabilities XML
236  sCapabilitiesCache = new QgsCapabilitiesCache();
237 
238  QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Roman" ) << QStringLiteral( "Bold" ) );
239 
240  sServiceRegistry = new QgsServiceRegistry();
241 
242  sServerInterface = new QgsServerInterfaceImpl( sCapabilitiesCache, sServiceRegistry, &sSettings );
243 
244  // Load service module
245  QString modulePath = QgsApplication::libexecPath() + "server";
246  qDebug() << "Initializing server modules from " << modulePath << endl;
247  sServiceRegistry->init( modulePath, sServerInterface );
248 
249  sInitialized = true;
250  QgsMessageLog::logMessage( QStringLiteral( "Server initialized" ), QStringLiteral( "Server" ), Qgis::Info );
251  return true;
252 }
253 
254 
255 
256 void QgsServer::putenv( const QString &var, const QString &val )
257 {
258 #ifdef _MSC_VER
259  _putenv_s( var.toStdString().c_str(), val.toStdString().c_str() );
260 #else
261  setenv( var.toStdString().c_str(), val.toStdString().c_str(), 1 );
262 #endif
263  sSettings.load( var );
264 }
265 
266 void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project )
267 {
269  QTime time; //used for measuring request time if loglevel < 1
270 
271  qApp->processEvents();
272 
273  if ( logLevel == Qgis::Info )
274  {
275  time.start();
276  }
277 
278  // Pass the filters to the requestHandler, this is needed for the following reasons:
279  // Allow server request to call sendResponse plugin hook if enabled
280  QgsFilterResponseDecorator responseDecorator( sServerInterface->filters(), response );
281 
282  //Request handler
283  QgsRequestHandler requestHandler( request, response );
284 
285  try
286  {
287  // TODO: split parse input into plain parse and processing from specific services
288  requestHandler.parseInput();
289  }
290  catch ( QgsMapServiceException &e )
291  {
292  QgsMessageLog::logMessage( "Parse input exception: " + e.message(), QStringLiteral( "Server" ), Qgis::Critical );
293  requestHandler.setServiceException( e );
294  }
295 
296  // Set the request handler into the interface for plugins to manipulate it
297  sServerInterface->setRequestHandler( &requestHandler );
298 
299  // Call requestReady() method (if enabled)
300  responseDecorator.start();
301 
302  // Plugins may have set exceptions
303  if ( !requestHandler.exceptionRaised() )
304  {
305  try
306  {
307  const QgsServerParameters params = request.serverParameters();
308  printRequestParameters( params.toMap(), logLevel );
309 
310  //Config file path
311  if ( ! project )
312  {
313  QString configFilePath = configPath( *sConfigFilePath, params.map() );
314 
315  // load the project if needed and not empty
316  project = mConfigCache->project( configFilePath );
317  if ( ! project )
318  {
319  throw QgsServerException( QStringLiteral( "Project file error" ) );
320  }
321 
322  sServerInterface->setConfigFilePath( configFilePath );
323  }
324  else
325  {
326  sServerInterface->setConfigFilePath( project->fileName() );
327  }
328 
329  if ( ! params.fileName().isEmpty() )
330  {
331  const QString value = QString( "attachment; filename=\"%1\"" ).arg( params.fileName() );
332  requestHandler.setResponseHeader( QStringLiteral( "Content-Disposition" ), value );
333  }
334 
335  // Lookup for service
336  QgsService *service = sServiceRegistry->getService( params.service(), params.version() );
337  if ( service )
338  {
339  service->executeRequest( request, responseDecorator, project );
340  }
341  else
342  {
343  throw QgsOgcServiceException( QStringLiteral( "Service configuration error" ),
344  QStringLiteral( "Service unknown or unsupported" ) );
345  }
346  }
347  catch ( QgsServerException &ex )
348  {
349  responseDecorator.write( ex );
350  }
351  catch ( QgsException &ex )
352  {
353  // Internal server error
354  response.sendError( 500, ex.what() );
355  }
356  }
357  // Terminate the response
358  responseDecorator.finish();
359 
360  // We are done using requestHandler in plugins, make sure we don't access
361  // to a deleted request handler from Python bindings
362  sServerInterface->clearRequestHandler();
363 
364  if ( logLevel == Qgis::Info )
365  {
366  QgsMessageLog::logMessage( "Request finished in " + QString::number( time.elapsed() ) + " ms", QStringLiteral( "Server" ), Qgis::Info );
367  }
368 }
369 
370 
371 #ifdef HAVE_SERVER_PYTHON_PLUGINS
372 void QgsServer::initPython()
373 {
374  // Init plugins
375  if ( ! QgsServerPlugins::initPlugins( sServerInterface ) )
376  {
377  QgsMessageLog::logMessage( QStringLiteral( "No server python plugins are available" ), QStringLiteral( "Server" ), Qgis::Info );
378  }
379  else
380  {
381  QgsMessageLog::logMessage( QStringLiteral( "Server python plugins loaded" ), QStringLiteral( "Server" ), Qgis::Info );
382  }
383 }
384 #endif
385 
QMap< QString, QString > toMap() const
Returns all parameters in a map.
QString cacheDirectory() const
Returns the cache directory.
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
virtual void sendError(int code, const QString &message)=0
Send error This method delegates error handling at the server level.
void setConfigFilePath(const QString &configFilePath) override
Set the configuration file path.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Exception base class for service exceptions.
static bool initPlugins(QgsServerInterface *interface)
Initializes the Python plugins.
Qgis::MessageLevel logLevel() const
Gets the current log level.
Provides a way to retrieve settings by prioritizing according to environment variables, ini file and default values.
Class defining decorator for calling filter&#39;s hooks.
QString projectFile() const
Returns the QGS project file to use.
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:66
QString message() const
Returns the exception message.
void putenv(const QString &var, const QString &val)
Set environment variable.
Definition: qgsserver.cpp:256
QString what() const
Definition: qgsexception.h:48
qint64 cacheSize() const
Returns the cache size.
QString map() const
Returns MAP parameter as a string or an empty string if not defined.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
void setLogLevel(Qgis::MessageLevel level)
Set the current log level.
Interfaces exposed by QGIS Server and made available to plugins.
static QString pluginPath()
Returns the path to the application plugin directory.
static bool createDatabase(QString *errorMessage=nullptr)
initialize qgis.db
virtual void executeRequest(const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project)=0
Execute the requests and set result in QgsServerRequest.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QString version() const
Returns VERSION parameter as a string or an empty string if not defined.
void init(const QString &nativeModulepath, QgsServerInterface *serverIface=nullptr)
Initialize registry, load modules and auto register services.
Reads and writes project states.
Definition: qgsproject.h:89
QgsServerFiltersMap filters() override
Returns the list of current QgsServerFilter.
static bool loadStandardTestFonts(const QStringList &loadstyles)
Loads standard test fonts from filesystem or qrc resource.
void setLogStderr()
Activates logging to stderr.
This class is an interface hiding the details of reading input and writing output from/to a wms reque...
static const char * QGIS_ORGANIZATION_NAME
const QgsProject * project(const QString &path)
If the project is not cached yet, then the project is read thanks to the path.
QString logFile() const
Returns the log file.
Exception base class for server exceptions.
static QString pkgDataPath()
Returns the common root path of all application data directories.
static void skipGdalDriver(const QString &driver)
Sets the GDAL_SKIP environment variable to include the specified driver and then calls GDALDriverMana...
QgsService Class defining interfaces for QGIS server services.
Definition: qgsservice.h:39
A cache for capabilities xml documents (by configuration file path)
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
static QgsAuthManager * authManager()
Returns the application&#39;s authentication manager instance.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
QString service() const
Returns SERVICE parameter as a string or an empty string if not defined.
QgsServiceRegistry Class defining the registry manager for QGIS server services.
static const char * QGIS_ORGANIZATION_DOMAIN
static void init(QString profileFolder=QString())
This method initializes paths etc for QGIS.
QgsService * getService(const QString &name, const QString &version=QString())
Retrieve a service from its name.
QgsServerParameters provides an interface to retrieve and manipulate global parameters received from ...
QgsServerParameters serverParameters() const
Returns parameters.
void load()
Load settings according to current environment variables.
static QgsServerLogger * instance()
Gets the singleton instance.
void logSummary() const
Log a summary of settings currently loaded.
bool init(const QString &pluginPath=QString(), const QString &authDatabasePath=QString())
init initialize QCA, prioritize qca-ossl plugin and optionally set up the authentication database ...
static QStringList svgPaths()
Returns the paths to svg directories.
void clearRequestHandler() override
Clear the request handler.
void setLogFile(const QString &filename=QString())
Set the current log file.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
Qgis::MessageLevel logLevel() const
Returns the log level.
static QString qgisAuthDatabaseFilePath()
Returns the path to the user authentication database file: qgis-auth.db.
static QgsConfigCache * instance()
Returns the current instance.
static QString libexecPath()
Returns the path with utility executables (help viewer, crssync, ...)
static QString prefixPath()
Returns the path to the application prefix directory.
static const char * QGIS_APPLICATION_NAME
Exception class for WMS service exceptions (for compatibility only).
bool logStderr() const
Returns whether logging to stderr is activated.
Defines a QGIS exception class.
Definition: qgsexception.h:34
network access manager for QGISThis class implements the QGIS network access manager.
QString fileName
Definition: qgsproject.h:93
QgsServer()
Creates the server instance.
Definition: qgsserver.cpp:64
void setRequestHandler(QgsRequestHandler *requestHandler) override
Set the request handler.
QString fileName() const
Returns FILE_NAME parameter as a string or an empty string if not defined.
void handleRequest(QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project=nullptr)
Handles the request.
Definition: qgsserver.cpp:266