Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docker/update-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ function pgSqlConnect($profile)
// we do isset instead of equality test against an empty string, to allow to specify
// that we want to use configuration set in environment variables
if (isset($profile['user'])) {
$str .= ' user=\''.$profile['user'].'\'';
$str .= ' user=\''.str_replace("'", "\\'", $profile['user']).'\'';
}

if (isset($profile['password'])) {
$str .= ' password=\''.$profile['password'].'\'';
$str .= ' password=\''.str_replace("'", "\\'", $profile['password']).'\'';
}
}

Expand Down
27 changes: 20 additions & 7 deletions lizmap/modules/lizmap/classes/qgisVectorLayer.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Give access to qgis mapLayer configuration.
*
* @author 3liz
* @copyright 2013-2023 3liz
* @copyright 2013-2026 3liz
*
* @see http://3liz.com
*
Expand All @@ -14,6 +14,7 @@
use GuzzleHttp\Psr7\StreamWrapper as Psr7StreamWrapper;
use Jelix\IniFile\IniModifier;
use JsonMachine\Items as JsonMachineItems;
use Lizmap\App\QgisConnectionStringParserException;
use Lizmap\Project\Project;
use Lizmap\Request\Proxy;
use Lizmap\Request\WFSRequest;
Expand Down Expand Up @@ -238,19 +239,31 @@ public function getDatasourceParameters()
return $this->dtParams;
}

$datasourceParser = new qgisVectorLayerDatasource(
$this->provider,
$this->datasource
);
$ds = array();
$parameters = array(
'dbname', 'service', 'host', 'port', 'user', 'password',
'sslmode', 'authcfg', 'key', 'estimatedmetadata', 'selectatid',
'srid', 'type', 'checkPrimaryKeyUnicity',
'table', 'geocol', 'sql', 'schema', 'tablename',
);

foreach ($parameters as $param) {
$ds[$param] = $datasourceParser->getDatasourceParameter($param);
try {
$datasourceParser = new qgisVectorLayerDatasource(
$this->provider,
$this->datasource
);

foreach ($parameters as $param) {
$ds[$param] = $datasourceParser->getDatasourceParameter($param);
}

} catch (QgisConnectionStringParserException $e) {
$error = 'Project '.$this->project->getKey().' layer '.$this->name.', error in datasource: '.$e->getMessage();
jLog::log($error, 'lizmapadmin');

// As we don't have parsed all parameters, probably it's better to trigger an exception instead of trying
// to connect to a database without any or bad connection parameters.
throw new Exception($error);
}

$this->dtParams = (object) $ds;
Expand Down
156 changes: 20 additions & 136 deletions lizmap/modules/lizmap/classes/qgisVectorLayerDatasource.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,16 @@
* Give access to qgis mapLayer configuration.
*
* @author 3liz
* @copyright 2013-2019 3liz
* @copyright 2013-2026 3liz
*
* @see http://3liz.com
*
* @license Mozilla Public License : http://www.mozilla.org/MPL/
*/
use Lizmap\App\SqlTools;

class qgisVectorLayerDatasource
{
/**
* @var array Regexes used to get datasource parameters
*/
protected $datasourceRegexes = array(
'dbname' => "dbname='?([^ ']+)'?(?: |$)",
'service' => "service='?([^ ']+)'?(?: |$)",
'host' => "host='?([^ ']+)'? port=",
'port' => 'port=([0-9]+)(?: |$)',
'user' => "user='?([^ ']+)'?(?: |$)",
'password' => array(
"password='((?:\\\\\\'|[^'])*)'(?: |$)",
'password="((?:\\\"|[^"])*)"(?: |$)',
"password=([^ ']+)(?: |$)",
),
'sslmode' => "sslmode='?([^ ']+)'?(?: |$)",
'authcfg' => "authcfg='?([^ ']+)'?(?: |$)",
'key' => "key='?([^ ']+)'?(?: |$)",
'estimatedmetadata' => 'estimatedmetadata=([^ ]+)(?: |$)',
'selectatid' => 'selectatid=([^ ]+)(?: |$)',
'srid' => 'srid=([0-9]+)(?: |$)',
'type' => 'type=([a-zA-Z]+)(?: |$)',
'checkPrimaryKeyUnicity' => "checkPrimaryKeyUnicity='([0-1]+)'(?: |$)",
'table' => 'table="(.+?)"($|\s)',
'geocol' => '\(([^ >]+)\)',
'sql' => ' sql=(.*)$',
);

protected $provider;

protected $datasource;
Expand All @@ -52,125 +27,33 @@ class qgisVectorLayerDatasource
public function __construct($provider, $datasource)
{
$this->provider = $provider;
$this->datasource = $datasource;
if ($this->provider == 'ogr' && preg_match('#layername=#', $datasource)) {
$this->datasource = $this->parseOgrConnection($datasource);
} else {
$this->datasource = SqlTools::parseQgisConnectionString($datasource);
}
}

public function getDatasourceParameter($param)
{
if ($this->provider == 'ogr' and preg_match('#layername=#', $this->datasource)) {
return $this->getDatasourceParameterOgr($param);
if (isset($this->datasource[$param])) {
return $this->datasource[$param];
}

return $this->getDatasourceParameterSql($param);
return '';
}

private function getDatasourceParameterSql($param)
private function parseOgrConnection($datasource)
{
$value = '';

// For tablename and schema, first get table
// and then get table name or schema
if ($param == 'tablename' or $param == 'schema') {
$table = $this->getDatasourceParameter('table');
if (substr($table, 0, 1) == '"') {
$exp = explode('.', str_replace('"', '', $table));
if ($param == 'tablename') {
$value = $exp[1];
} elseif ($param == 'schema') {
$value = $exp[0];
}
} else {
if ($param == 'tablename') {
$value = $table;
} elseif ($param == 'schema') {
$value = '';
}
}

return trim($value);
}

// For other parameters, use specific parameter regex
$regex = $this->datasourceRegexes[$param];

// Specific preg_match for the table, which can be very complex
$result = array();
$backSlashedQuoteReplacement = null;
if ($param == 'table') {
// We need to replace the \" in the datasource to avoid issues for complex sub-queries in table=()
$backSlashedQuoteReplacement = '@@@LIZMAP@@@';
preg_match(
'#'.$regex.'#s',
str_replace('\"', $backSlashedQuoteReplacement, $this->datasource),
$result
);
} elseif (is_array($regex)) {
foreach ($regex as $r) {
if (preg_match(
'#'.$r.'#s',
$this->datasource,
$result
)) {
break;
}
}
} else {
preg_match(
'#'.$regex.'#s',
$this->datasource,
$result
);
}

$nb_result = count($result);
if ((2 <= $nb_result) and ($nb_result <= 3) and strlen($result[1])) {
if ($param == 'table') {
// We replace back the backslahsed quote replacement in the value by double-quotes
$value = str_replace($backSlashedQuoteReplacement, '"', $result[1]);
} elseif ($param == 'password') {
$value = str_replace(array("\\'", '\"'), array("'", '"'), $result[1]);
} else {
$value = $result[1];
}

// Specific parsing for complex table parameter
if ($param == 'table') {
$table = $value;

// Complex sub-query
if (substr($table, 0, 1) == '(' and substr($table, -1) == ')') {
$table .= ' fooliz';
}
// Simple "schemaname"."table_name"
elseif (preg_match('#"."#', $table)) {
$table = '"'.$table.'"';
}
$value = $table;
}
}

return trim($value);
}

private function getDatasourceParameterOgr($param)
{
$split = explode('|', $this->datasource);
$dbname = $split[0];
$table = str_replace('layername=', '', $split[1]);
$split = explode('|', $datasource);
$dbname = trim($split[0]);
$table = trim(str_replace('layername=', '', $split[1]));
$sql = '';
if (count($split) == 3) {
$sql = str_replace('subset=', '', $split[2]);
}

// Handle schema and tablename like getDatasourceParameterSql does
if ($param == 'tablename') {
return trim($table);
}
if ($param == 'schema') {
return '';
$sql = trim(str_replace('subset=', '', $split[2]));
}

$ds = array(
return array(
'dbname' => $dbname,
'service' => '',
'host' => '',
Expand All @@ -186,10 +69,11 @@ private function getDatasourceParameterOgr($param)
'type' => '',
'checkPrimaryKeyUnicity' => '',
'table' => $table,
// Handle schema and tablename like getDatasourceParameterSql does
'tablename' => trim($table),
'schema' => '',
'geocol' => 'geom',
'sql' => $sql,
);

return trim($ds[$param]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Exception for the Qgis connection string parser.
*
* @author 3liz
* @copyright 2026 3liz
*
* @see https://3liz.com
*
* @license Mozilla Public License : http://www.mozilla.org/MPL/
*/

namespace Lizmap\App;

class QgisConnectionStringParserException extends \DomainException
{
protected $validParameters = array();

public function __construct(string $message = '', int $code = 0, $validParameters = array())
{
parent::__construct($message, $code);
$this->validParameters = $validParameters;
}

/**
* @return array list of parameters that the parser successfully parsed before reaching the syntax error
*/
public function getValidParameters(): array
{
return $this->validParameters;
}
}
Loading
Loading