22 #include "qgsconfig.h"
23 #include "qgsversion.h"
45 #include <QDomDocument>
46 #include <QNetworkDiskCache>
48 #include <QElapsedTimer>
51 #include <fcgi_stdio.h>
59 QString *QgsServer::sConfigFilePath =
nullptr;
63 bool QgsServer::sInitialized =
false;
72 if ( qobject_cast<QgsApplication *>( qApp ) ==
nullptr )
74 qFatal(
"A QgsApplication must exist before a QgsServer instance can be created." );
81 QFileInfo QgsServer::defaultAdminSLD()
83 return QFileInfo( QStringLiteral(
"admin.sld" ) );
86 void QgsServer::setupNetworkAccessManager()
88 const QSettings settings;
90 QNetworkDiskCache *cache =
new QNetworkDiskCache(
nullptr );
91 const qint64 cacheSize = sSettings()->cacheSize();
92 const QString cacheDirectory = sSettings()->cacheDirectory();
93 cache->setCacheDirectory( cacheDirectory );
94 cache->setMaximumCacheSize( cacheSize );
95 QgsMessageLog::logMessage( QStringLiteral(
"cacheDirectory: %1" ).arg( cache->cacheDirectory() ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
96 QgsMessageLog::logMessage( QStringLiteral(
"maximumCacheSize: %1" ).arg( cache->maximumCacheSize() ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
97 nam->setCache( cache );
100 QFileInfo QgsServer::defaultProjectFile()
102 const QDir currentDir;
103 fprintf( FCGI_stderr,
"current directory: %s\n", currentDir.absolutePath().toUtf8().constData() );
104 QStringList nameFilterList;
105 nameFilterList << QStringLiteral(
"*.qgs" )
106 << QStringLiteral(
"*.qgz" );
107 const QFileInfoList projectFiles = currentDir.entryInfoList( nameFilterList, QDir::Files, QDir::Name );
108 for (
int x = 0; x < projectFiles.size(); x++ )
110 QgsMessageLog::logMessage( projectFiles.at( x ).absoluteFilePath(), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
112 if ( projectFiles.isEmpty() )
116 return projectFiles.at( 0 );
119 void QgsServer::printRequestParameters(
const QMap< QString, QString> ¶meterMap,
Qgis::MessageLevel logLevel )
121 if ( logLevel > Qgis::MessageLevel::Info )
126 QMap< QString, QString>::const_iterator pIt = parameterMap.constBegin();
127 for ( ; pIt != parameterMap.constEnd(); ++pIt )
133 QString QgsServer::configPath(
const QString &defaultConfigPath,
const QString &configPath )
135 QString cfPath( defaultConfigPath );
136 const QString projectFile = sSettings()->projectFile();
137 if ( !projectFile.isEmpty() )
139 cfPath = projectFile;
140 QgsDebugMsg( QStringLiteral(
"QGIS_PROJECT_FILE:%1" ).arg( cfPath ) );
144 if ( configPath.isEmpty() )
147 if ( getenv(
"QGIS_PROJECT_FILE" ) )
149 cfPath = getenv(
"QGIS_PROJECT_FILE" );
150 QgsMessageLog::logMessage( QStringLiteral(
"Using configuration file path from environment: %1" ).arg( cfPath ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
152 else if ( ! defaultConfigPath.isEmpty() )
154 QgsMessageLog::logMessage( QStringLiteral(
"Using default configuration file path: %1" ).arg( defaultConfigPath ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
160 QgsDebugMsg( QStringLiteral(
"MAP:%1" ).arg( cfPath ) );
166 void QgsServer::initLocale()
169 if ( ! sSettings()->overrideSystemLocale().isEmpty() )
171 QLocale::setDefault( QLocale( sSettings()->overrideSystemLocale() ) );
174 QLocale currentLocale;
175 if ( sSettings()->showGroupSeparator() )
177 currentLocale.setNumberOptions( currentLocale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
181 currentLocale.setNumberOptions( currentLocale.numberOptions() |= QLocale::NumberOption::OmitGroupSeparator );
183 QLocale::setDefault( currentLocale );
186 bool QgsServer::init()
202 #if defined(SERVER_SKIP_ECW)
215 if ( ! sSettings()->logFile().isEmpty() )
219 else if ( sSettings()->logStderr() )
233 QStringLiteral(
"QGIS Server" ), Qgis::MessageLevel::Warning );
244 if ( !grid.isAvailable )
246 gridMessage.append( QStringLiteral(
"This transformation requires the grid file '%1', which is not available for use on the system.\n" ).arg( grid.shortName ) );
254 QStringLiteral(
"QGIS Server" ), Qgis::MessageLevel::Warning );
267 if ( !grid.isAvailable )
269 gridMessage.append( QStringLiteral(
"This transformation requires the grid file '%1', which is not available for use on the system.\n" ).arg( grid.shortName ) );
270 if ( !grid.url.isEmpty() )
272 if ( !grid.packageName.isEmpty() )
274 gridMessage.append( QStringLiteral(
"This grid is part of the '%1' package, available for download from %2.\n" ).arg( grid.packageName, grid.url ) );
278 gridMessage.append( QStringLiteral(
"This grid is available for download from %1.\n" ).arg( grid.url ) );
284 QString accuracyMessage;
286 accuracyMessage = QStringLiteral(
"Current transform '%1' has an accuracy of %2 meters, while the preferred transformation '%3' has accuracy %4 meters.\n" ).arg( availableOperation.
name )
287 .arg( availableOperation.
accuracy ).arg( preferredOperation.
name ).arg( preferredOperation.
accuracy );
288 else if ( preferredOperation.
accuracy >= 0 )
289 accuracyMessage = QStringLiteral(
"Current transform '%1' has an unknown accuracy, while the preferred transformation '%2' has accuracy %3 meters.\n" )
290 .arg( availableOperation.
name, preferredOperation.
name )
291 .arg( preferredOperation.
accuracy );
293 const QString longMessage = QStringLiteral(
"The preferred transform between '%1' and '%2' is not available for use on the system.\n" ).arg( sourceCrs.
userFriendlyIdentifier(),
295 + gridMessage + accuracyMessage;
303 const QString longMessage = QStringLiteral(
"No transform is available between %1 and %2: %3" )
311 QgsMessageLog::logMessage( QStringLiteral(
"QGIS Server Starting : %1 (%2)" ).arg( _QGIS_VERSION, QGSVERSION ),
"Server", Qgis::MessageLevel::Info );
314 sSettings()->logSummary();
316 setupNetworkAccessManager();
317 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
336 QString defaultConfigFilePath;
337 const QFileInfo projectFileInfo = defaultProjectFile();
338 if ( projectFileInfo.exists() )
340 defaultConfigFilePath = projectFileInfo.absoluteFilePath();
341 QgsMessageLog::logMessage(
"Using default project file: " + defaultConfigFilePath, QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
345 const QFileInfo adminSLDFileInfo = defaultAdminSLD();
346 if ( adminSLDFileInfo.exists() )
348 defaultConfigFilePath = adminSLDFileInfo.absoluteFilePath();
352 sConfigFilePath =
new QString( defaultConfigFilePath );
366 sServiceRegistry->init( modulePath, sServerInterface );
372 QgsMessageLog::logMessage( QStringLiteral(
"Server initialized" ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
382 qunsetenv( var.toUtf8().data() );
386 qputenv( var.toUtf8().data(), val.toUtf8() );
388 sSettings()->load( var );
398 qApp->processEvents();
429 const QString configFilePath = configPath( *sConfigFilePath, request.
serverParameters().
map() );
441 responseDecorator.
start();
446 response.
sendError( 500, QStringLiteral(
"Internal Server Error" ) );
456 printRequestParameters( params.
toMap(), logLevel );
461 const QString configFilePath = configPath( *sConfigFilePath, params.
map() );
464 if ( ! configFilePath.isEmpty() )
501 throw QgsServerException( QStringLiteral(
"Project file error. For OWS services: please provide a SERVICE and a MAP parameter pointing to a valid QGIS project file" ) );
504 if ( ! params.
fileName().isEmpty() )
506 const QString value = QString(
"attachment; filename=\"%1\"" ).arg( params.
fileName() );
507 requestHandler.
setResponseHeader( QStringLiteral(
"Content-Disposition" ), value );
519 QStringLiteral(
"Service unknown or unsupported. Current supported services (case-sensitive): WMS WFS WCS WMTS SampleService, or use a WFS3 (OGC API Features) endpoint" ) );
525 responseDecorator.
write( ex );
532 response.
sendError( 500, QStringLiteral(
"Internal Server Error" ) );
541 responseDecorator.
finish();
546 response.
sendError( 500, QStringLiteral(
"Internal Server Error" ) );
555 if ( logLevel == Qgis::MessageLevel::Info )
557 QgsMessageLog::logMessage(
"Request finished in " + QString::number(
QgsApplication::profiler()->profileTime( QStringLiteral(
"handleRequest" ), QStringLiteral(
"server" ) ) * 1000.0 ) +
" ms", QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
558 if ( sSettings->logProfile() )
560 std::function <void(
const QModelIndex &,
int )> profileFormatter;
561 profileFormatter = [ &profileFormatter ](
const QModelIndex & idx,
int level )
564 .arg( level > 0 ? QString().fill(
'-', level ) +
' ' : QString() )
567 .arg( QString::number(
QgsApplication::profiler()->data( idx, QgsRuntimeProfilerNode::Roles::Elapsed ).toDouble() * 1000.0 ) ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
572 profileFormatter( subIdx, level + 1 );
580 profileFormatter( idx, 0 );
592 #ifdef HAVE_SERVER_PYTHON_PLUGINS
593 void QgsServer::initPython()
598 QgsMessageLog::logMessage( QStringLiteral(
"No server python plugins are available" ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );
602 QgsMessageLog::logMessage( QStringLiteral(
"Server python plugins loaded" ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Info );