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 );