forked from kamba4/sunders
Merge pull request 'Fix security issues' (#38) from security-fixes into main
Reviewed-on: #38
This commit is contained in:
commit
60694442c2
7 changed files with 160 additions and 107 deletions
|
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
include "config.php";
|
include "config.php";
|
||||||
|
|
||||||
|
$seqNum = $_SERVER['argv'][1] ?? '';
|
||||||
|
if (!ctype_digit($seqNum)) {
|
||||||
|
echo "Invalid sequence number\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quote a value for the generated SQL file. Values come from OSM data and are untrusted.
|
||||||
|
function sqlString($value) {
|
||||||
|
global $mysqli;
|
||||||
|
return "'" . $mysqli->real_escape_string((string) $value) . "'";
|
||||||
|
}
|
||||||
|
|
||||||
$elementTypes = array();
|
$elementTypes = array();
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$countDelete = 0;
|
$countDelete = 0;
|
||||||
|
|
@ -91,7 +103,7 @@ function endElement ($parser, $name) {
|
||||||
$mode = '';
|
$mode = '';
|
||||||
} else if ($name == 'node') {
|
} else if ($name == 'node') {
|
||||||
if ($mode == 'delete') {
|
if ($mode == 'delete') {
|
||||||
$id = $curNodeAttrs['id'];
|
$id = (int) $curNodeAttrs['id'];
|
||||||
|
|
||||||
if (! $deleteTagStmt->execute()) {
|
if (! $deleteTagStmt->execute()) {
|
||||||
echo "***** Error : Deleting tags $id : ". $deleteTagStmt->error . "\n";
|
echo "***** Error : Deleting tags $id : ". $deleteTagStmt->error . "\n";
|
||||||
|
|
@ -119,7 +131,7 @@ function endElement ($parser, $name) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$id = $curNodeAttrs['id'];
|
$id = (int) $curNodeAttrs['id'];
|
||||||
|
|
||||||
if ($mode == 'modify') {
|
if ($mode == 'modify') {
|
||||||
if (! $deleteTagStmt->execute()) {
|
if (! $deleteTagStmt->execute()) {
|
||||||
|
|
@ -140,36 +152,36 @@ function endElement ($parser, $name) {
|
||||||
|
|
||||||
$k = 'lat';
|
$k = 'lat';
|
||||||
$v = $curNodeAttrs['lat'];
|
$v = $curNodeAttrs['lat'];
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
|
|
||||||
$k = 'lon';
|
$k = 'lon';
|
||||||
$v = $curNodeAttrs['lon'];
|
$v = $curNodeAttrs['lon'];
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
|
|
||||||
$k = 'userid';
|
$k = 'userid';
|
||||||
$v = $curNodeAttrs['user'];
|
$v = $curNodeAttrs['user'];
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
|
|
||||||
$k = 'version';
|
$k = 'version';
|
||||||
$v = $curNodeAttrs['version'];
|
$v = $curNodeAttrs['version'];
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$k = 'timestamp';
|
$k = 'timestamp';
|
||||||
$v = $curNodeAttrs['timestamp'];
|
$v = $curNodeAttrs['timestamp'];
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
foreach($curNodeTags as $k => $v) {
|
foreach($curNodeTags as $k => $v) {
|
||||||
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, '$k', '$v');\n";
|
$sql_statement = $sql_statement . "INSERT INTO tag (id, k, v) VALUES ($id, " . sqlString($k) . ", " . sqlString($v) . ");\n";
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if ($mode == 'modify') {
|
} else if ($mode == 'modify') {
|
||||||
// delete former surveillance nodes that were modified to non-surveillance nodes
|
// delete former surveillance nodes that were modified to non-surveillance nodes
|
||||||
$id = $curNodeAttrs['id'];
|
$id = (int) $curNodeAttrs['id'];
|
||||||
|
|
||||||
if (! $deleteTagStmt->execute()) {
|
if (! $deleteTagStmt->execute()) {
|
||||||
echo "***** Error : Deleting tags $id for non-surveillance node : ". $deleteTagStmt->error . "\n";
|
echo "***** Error : Deleting tags $id for non-surveillance node : ". $deleteTagStmt->error . "\n";
|
||||||
|
|
@ -208,11 +220,13 @@ while ($data = fread($fp, 4096)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql="UPDATE sync_state SET v='" . $_SERVER['argv'][1] ."' WHERE k='sequenceNumber';";
|
$syncStmt = $mysqli->prepare("UPDATE sync_state SET v=? WHERE k='sequenceNumber'");
|
||||||
$mysqli->query($sql);
|
$syncStmt->bind_param('s', $seqNum);
|
||||||
$sql_statement = $sql_statement . "UPDATE sync_state SET v='" . $_SERVER['argv'][1] ."' WHERE k='sequenceNumber';\n";
|
$syncStmt->execute();
|
||||||
|
$syncStmt->close();
|
||||||
|
$sql_statement = $sql_statement . "UPDATE sync_state SET v='" . $seqNum ."' WHERE k='sequenceNumber';\n";
|
||||||
$sql_statement = $sql_statement . "COMMIT;";
|
$sql_statement = $sql_statement . "COMMIT;";
|
||||||
$sql_file = "/migrations/".$_SERVER['argv'][1].".sql";
|
$sql_file = "/migrations/".$seqNum.".sql";
|
||||||
|
|
||||||
file_put_contents($sql_file, $sql_statement);
|
file_put_contents($sql_file, $sql_statement);
|
||||||
$mysqli->close();
|
$mysqli->close();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
include "config.php";
|
include "config.php";
|
||||||
|
|
||||||
|
$seqNum = $_SERVER['argv'][1] ?? '';
|
||||||
|
if (!ctype_digit($seqNum)) {
|
||||||
|
echo "Invalid sequence number\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
$elementTypes = array();
|
$elementTypes = array();
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$countDelete = 0;
|
$countDelete = 0;
|
||||||
|
|
@ -281,8 +287,10 @@ while ($data = fread($fp, 4096)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql="UPDATE sync_state SET v='" . $_SERVER['argv'][1] ."' WHERE k='sequenceNumber';";
|
$syncStmt = $mysqli->prepare("UPDATE sync_state SET v=? WHERE k='sequenceNumber'");
|
||||||
$mysqli->query($sql);
|
$syncStmt->bind_param('s', $seqNum);
|
||||||
|
$syncStmt->execute();
|
||||||
|
$syncStmt->close();
|
||||||
$mysqli->commit();
|
$mysqli->commit();
|
||||||
|
|
||||||
printDebug();
|
printDebug();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,17 @@
|
||||||
FROM docker.io/library/php:apache
|
FROM docker.io/library/php:apache
|
||||||
|
|
||||||
RUN docker-php-ext-install mysqli bcmath
|
RUN docker-php-ext-install mysqli bcmath
|
||||||
COPY ./www/* /var/www/html
|
|
||||||
|
# Use PHP's production defaults (no error output to clients) and hide the PHP version.
|
||||||
|
RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
|
||||||
|
&& printf 'expose_php = Off\n' > "$PHP_INI_DIR/conf.d/zz-hardening.ini"
|
||||||
|
|
||||||
|
# Disable directory listings and reduce the server signature.
|
||||||
|
RUN sed -i 's/^\(\s*Options\) Indexes FollowSymLinks/\1 FollowSymLinks/' /etc/apache2/apache2.conf \
|
||||||
|
&& sed -i -e 's/^ServerTokens .*/ServerTokens Prod/' -e 's/^ServerSignature .*/ServerSignature Off/' /etc/apache2/conf-available/security.conf
|
||||||
|
|
||||||
|
COPY ./www/* /var/www/html
|
||||||
|
|
||||||
|
# The web server must not be able to create or modify files in the document root.
|
||||||
|
RUN chown -R root:root /var/www/html \
|
||||||
|
&& chmod -R u=rwX,go=rX /var/www/html
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,17 @@
|
||||||
|
|
||||||
define('MAX_POINTS_FOR_QUICKHULL', 3000);
|
define('MAX_POINTS_FOR_QUICKHULL', 3000);
|
||||||
|
|
||||||
|
// Fields set by this script; OSM tags with these keys are not passed on.
|
||||||
|
define('RESERVED_KEYS', array('lat', 'lon', 'id', 'count', 'multi', 'poly'));
|
||||||
|
|
||||||
|
define('JSON_FLAGS', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
|
||||||
|
|
||||||
|
function exitWithError($message) {
|
||||||
|
header('Content-type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(array('error' => $message), JSON_FLAGS);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
class OsmPoint {
|
class OsmPoint {
|
||||||
var $id;
|
var $id;
|
||||||
var $lat;
|
var $lat;
|
||||||
|
|
@ -179,63 +190,47 @@
|
||||||
|| !array_key_exists('zoom', $_GET)
|
|| !array_key_exists('zoom', $_GET)
|
||||||
|| !array_key_exists('width', $_GET)
|
|| !array_key_exists('width', $_GET)
|
||||||
|| !array_key_exists('height', $_GET)) {
|
|| !array_key_exists('height', $_GET)) {
|
||||||
header('Content-type: application/json');
|
exitWithError('bbox, zoom, width and height parameters are required. '
|
||||||
$result = '{"error":"bbox, zoom, width and height parameters are required. '
|
|
||||||
.(array_key_exists('bbox', $_GET) ? '' : 'bbox is empty. ')
|
.(array_key_exists('bbox', $_GET) ? '' : 'bbox is empty. ')
|
||||||
.(array_key_exists('zoom', $_GET) ? '' : 'zoom is empty. ')
|
.(array_key_exists('zoom', $_GET) ? '' : 'zoom is empty. ')
|
||||||
.(array_key_exists('width', $_GET) ? '' : 'width is empty. ')
|
.(array_key_exists('width', $_GET) ? '' : 'width is empty. ')
|
||||||
.(array_key_exists('height', $_GET) ? '' : 'height is empty. ')
|
.(array_key_exists('height', $_GET) ? '' : 'height is empty. '));
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check zoom */
|
/* Check zoom */
|
||||||
$zoom = $_GET['zoom'];
|
$zoom = $_GET['zoom'];
|
||||||
|
|
||||||
if ( !is_numeric($zoom)
|
if ( !is_string($zoom)
|
||||||
|
|| !is_numeric($zoom)
|
||||||
|| intval($zoom) < 1
|
|| intval($zoom) < 1
|
||||||
|| intval($zoom) > 19) {
|
|| intval($zoom) > 19) {
|
||||||
header('Content-type: application/json');
|
exitWithError('unexpected zoom value');
|
||||||
$result = '{"error":"unexpected zoom value : '
|
|
||||||
.htmlentities($zoom)
|
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check bounds box --> bbox=boundsSW.lng,boundsSW.lat,boundsNE.lng,boundsNE.lat */
|
/* Check bounds box --> bbox=boundsSW.lng,boundsSW.lat,boundsNE.lng,boundsNE.lat */
|
||||||
|
if (!is_string($_GET['bbox'])) {
|
||||||
|
exitWithError('unexpected bbox value');
|
||||||
|
}
|
||||||
$bbox = explode(',', $_GET['bbox']);
|
$bbox = explode(',', $_GET['bbox']);
|
||||||
|
|
||||||
if ( !is_numeric($bbox[0]) || $bbox[0] > 180 // 0 = SW.lng
|
if ( count($bbox) != 4
|
||||||
|
|| !is_numeric($bbox[0]) || $bbox[0] > 180 // 0 = SW.lng
|
||||||
|| !is_numeric($bbox[2]) || $bbox[2] < -180 // 2 = NE.lng
|
|| !is_numeric($bbox[2]) || $bbox[2] < -180 // 2 = NE.lng
|
||||||
|| !is_numeric($bbox[1]) || $bbox[1] < -90 || $bbox[1] > 90 // 1 = SW.lat
|
|| !is_numeric($bbox[1]) || $bbox[1] < -90 || $bbox[1] > 90 // 1 = SW.lat
|
||||||
|| !is_numeric($bbox[3]) || $bbox[3] < -90 || $bbox[3] > 90) { // 3 = NE.lat
|
|| !is_numeric($bbox[3]) || $bbox[3] < -90 || $bbox[3] > 90) { // 3 = NE.lat
|
||||||
header('Content-type: application/json');
|
exitWithError('unexpected bbox longitude and latitude values : '
|
||||||
$result = '{"error":"unexpected bbox longitude and latitude values : '
|
.'lon ['.$bbox[0].', '.$bbox[2].'], '
|
||||||
.'lon ['.htmlentities($bbox[0]).', '.htmlentities($bbox[2]).'], '
|
.'lat ['.$bbox[1].', '.$bbox[3].']');
|
||||||
.'lat ['.htmlentities($bbox[1]).', '.htmlentities($bbox[3]).']'
|
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bbox[0] >= $bbox[2]) { // 0 = SW.lng | 2 = NE.lng
|
if ($bbox[0] >= $bbox[2]) { // 0 = SW.lng | 2 = NE.lng
|
||||||
header('Content-type: application/json');
|
exitWithError('min longitude greater than max longitude : '
|
||||||
$result = '{"error":"min longitude greater than max longitude : '
|
.'lon ['.$bbox[0].', '.$bbox[2].']');
|
||||||
.'lon ['.htmlentities($bbox[0]).', '.htmlentities($bbox[2]).']'
|
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bbox[1] >= $bbox[3]) { // 1 = SW.lat | 3 = NE.lat
|
if ($bbox[1] >= $bbox[3]) { // 1 = SW.lat | 3 = NE.lat
|
||||||
header('Content-type: application/json');
|
exitWithError('min latitude greater than max latitude : '
|
||||||
$result = '{"error":"min latitude greater than max latitude : '
|
.'lat ['.$bbox[1].', '.$bbox[3].']');
|
||||||
.'lat ['.htmlentities($bbox[1]).', '.htmlentities($bbox[3]).']'
|
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$lonMin = $bbox[0]; // 0 = SW.lng
|
$lonMin = $bbox[0]; // 0 = SW.lng
|
||||||
|
|
@ -247,12 +242,7 @@
|
||||||
$pixelHeight = (int) $_GET['height'];
|
$pixelHeight = (int) $_GET['height'];
|
||||||
|
|
||||||
if ($pixelWidth == 0 || $pixelHeight == 0) {
|
if ($pixelWidth == 0 || $pixelHeight == 0) {
|
||||||
header('Content-type: application/json');
|
exitWithError('width or height is null : '.$pixelWidth.'x'.$pixelHeight);
|
||||||
$result = '{"error":"width or height is null : '
|
|
||||||
.htmlentities($pixelWidth).'x'.htmlentities($pixelHeight)
|
|
||||||
.'"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$lonWidth = $lonMax - $lonMin; // NE.lng - SW.lng
|
$lonWidth = $lonMax - $lonMin; // NE.lng - SW.lng
|
||||||
|
|
@ -268,10 +258,7 @@
|
||||||
/* Connect to database */
|
/* Connect to database */
|
||||||
$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB);
|
$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB);
|
||||||
if($mysqli->connect_errno) {
|
if($mysqli->connect_errno) {
|
||||||
header('Content-type: application/json');
|
exitWithError('error while connecting to db');
|
||||||
$result = '{"error":"error while connecting to db : ' . $mysqli->error . '"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$rqtLonMin = $lonMin; // SW.lng
|
$rqtLonMin = $lonMin; // SW.lng
|
||||||
|
|
@ -365,10 +352,7 @@
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
} else {
|
} else {
|
||||||
$mysqli->close();
|
$mysqli->close();
|
||||||
header('Content-type: application/json');
|
exitWithError('error while querying db');
|
||||||
$result = '{"error":"Error while request : ' . $mysqli->error . '"}';
|
|
||||||
echo $result;
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unify some clusters if nodes center are near */
|
/* Unify some clusters if nodes center are near */
|
||||||
|
|
@ -380,8 +364,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$result='[';
|
$result = array();
|
||||||
$separator='';
|
|
||||||
|
|
||||||
/* Writing selected points */
|
/* Writing selected points */
|
||||||
$sql='SELECT k, v
|
$sql='SELECT k, v
|
||||||
|
|
@ -393,53 +376,49 @@
|
||||||
$stmt->bind_param('d', $id);
|
$stmt->bind_param('d', $id);
|
||||||
$stmt->bind_result($k, $v);
|
$stmt->bind_result($k, $v);
|
||||||
|
|
||||||
/* Writing grouped items or not */
|
/* Writing grouped items or not. All values are passed on unescaped; the client
|
||||||
|
must escape them before inserting them into HTML. */
|
||||||
foreach($resArray as $val) {
|
foreach($resArray as $val) {
|
||||||
if ($val['count'] > 0) {
|
if ($val['count'] > 0) {
|
||||||
$result = $result.$separator
|
$entry = array(
|
||||||
.'{"lat":"'.($val['latitude'] / $val['count']).'"'
|
'lat' => (string) ($val['latitude'] / $val['count']),
|
||||||
.',"lon":"'.($val['longitude'] / $val['count']).'"';
|
'lon' => (string) ($val['longitude'] / $val['count']));
|
||||||
|
|
||||||
if ($val['count'] == 1) {
|
if ($val['count'] == 1) {
|
||||||
$id = $val['points'][0]->id;
|
$id = $val['points'][0]->id;
|
||||||
$result = $result.',"id":"'.$id.'"';
|
$entry['id'] = (string) $id;
|
||||||
|
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
while($stmt->fetch()) {
|
while($stmt->fetch()) {
|
||||||
$result = $result.',"'.htmlentities($k).'":"'.htmlentities($v, ENT_COMPAT, 'UTF-8').'"';
|
if (!in_array($k, RESERVED_KEYS, true)) {
|
||||||
|
$entry[$k] = (string) $v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$result = $result
|
$entry['count'] = (string) $val['count'];
|
||||||
.',"count":"'.$val['count'].'"'
|
$entry['multi'] = 'yes';
|
||||||
.',"multi":"yes"'
|
$entry['poly'] = array();
|
||||||
.',"poly":[';
|
|
||||||
|
|
||||||
if ($pointsCount < MAX_POINTS_FOR_QUICKHULL) {
|
if ($pointsCount < MAX_POINTS_FOR_QUICKHULL) {
|
||||||
$convexPoly = quickHull($val['points'], $val['count']);
|
$convexPoly = quickHull($val['points'], $val['count']);
|
||||||
$sepPoly = '';
|
|
||||||
foreach($convexPoly as $point) {
|
foreach($convexPoly as $point) {
|
||||||
$result = $result.$sepPoly.'{'
|
$entry['poly'][] = array(
|
||||||
.'"lat":"'.$point->lat.'"'
|
'lat' => (string) $point->lat,
|
||||||
.',"id":"'.$point->id.'"'
|
'id' => (string) $point->id,
|
||||||
.',"lon":"'.$point->lon.'"}';
|
'lon' => (string) $point->lon);
|
||||||
$sepPoly=',';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$result=$result.']';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $result.'}';
|
$result[] = $entry;
|
||||||
$separator=',';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
|
|
||||||
$result = $result . ']';
|
|
||||||
|
|
||||||
$mysqli->close();
|
$mysqli->close();
|
||||||
|
|
||||||
header('Content-type: application/json; Charset : utf-8');
|
header('Content-type: application/json; charset=utf-8');
|
||||||
echo $result;
|
echo json_encode($result, JSON_FLAGS);
|
||||||
?>
|
?>
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,11 @@ function onClick(e) {
|
||||||
function onStateChanged() {
|
function onStateChanged() {
|
||||||
if (ajaxRequest.readyState == 4) { // 0 = UNSENT, 1 = OPENED, 2 = HEADERS_RECEIVED, 3 = LOADING, 4 = DONE
|
if (ajaxRequest.readyState == 4) { // 0 = UNSENT, 1 = OPENED, 2 = HEADERS_RECEIVED, 3 = LOADING, 4 = DONE
|
||||||
if (ajaxRequest.status == 200) { // 200 = OK, 404 = Page not found
|
if (ajaxRequest.status == 200) { // 200 = OK, 404 = Page not found
|
||||||
plotList = eval('(' + ajaxRequest.responseText + ')');
|
try {
|
||||||
|
plotList = JSON.parse(ajaxRequest.responseText);
|
||||||
|
} catch(e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
removeMarkers();
|
removeMarkers();
|
||||||
for (i=0; i<plotList.length; i++) {
|
for (i=0; i<plotList.length; i++) {
|
||||||
var plotLatLng;
|
var plotLatLng;
|
||||||
|
|
@ -421,26 +425,39 @@ function drawCameraFocusDome(plotLatLng, focusRadius, focusColor) {
|
||||||
return(new L.Circle(plotLatLng, focusRadius, { weight:1, color:focusColor, fillOpacity:0.1 }));
|
return(new L.Circle(plotLatLng, focusRadius, { weight:1, color:focusColor, fillOpacity:0.1 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape a value for use in HTML text and in double-quoted attributes.
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
// Add camera popup to camera marker.
|
// Add camera popup to camera marker.
|
||||||
|
// All values in plot come unescaped from OSM data and must be escaped.
|
||||||
function addCameraDetailsData(plotMarker, plot) {
|
function addCameraDetailsData(plotMarker, plot) {
|
||||||
|
var id = escapeHtml(plot.id);
|
||||||
popupDataTable = '<table class="popup-content">'
|
popupDataTable = '<table class="popup-content">'
|
||||||
+ '<tr><td>id</td><td><a href="https://www.openstreetmap.org/node/' + (plot.id) + '">' + (plot.id) + '</a></td></tr>'
|
+ '<tr><td>id</td><td><a href="https://www.openstreetmap.org/node/' + encodeURIComponent(plot.id) + '">' + id + '</a></td></tr>'
|
||||||
// + '<tr><td>user osm</td><td>' + (plot.userid) + '</td></tr>'
|
// + '<tr><td>user osm</td><td>' + escapeHtml(plot.userid) + '</td></tr>'
|
||||||
+ '<tr><td>latitude</td><td>' + (plot.lat) + '</td></tr>'
|
+ '<tr><td>latitude</td><td>' + escapeHtml(plot.lat) + '</td></tr>'
|
||||||
+ '<tr><td>longitude</td><td>' + (plot.lon) + '</td></tr>';
|
+ '<tr><td>longitude</td><td>' + escapeHtml(plot.lon) + '</td></tr>';
|
||||||
for (x in plot) {
|
for (x in plot) {
|
||||||
if (plot[x] !== '' && x != 'multi' && x != 'id' && x != 'userid' && x != 'lat' && x != 'lon') {
|
if (plot[x] !== '' && x != 'multi' && x != 'id' && x != 'userid' && x != 'lat' && x != 'lon') {
|
||||||
popupDataTable = popupDataTable + '<tr><td>' + x + '</td><td>';
|
popupDataTable = popupDataTable + '<tr><td>' + escapeHtml(x) + '</td><td>';
|
||||||
var descr = plot[x];
|
var descr = String(plot[x]);
|
||||||
if (descr.substr(0, 4) == 'http') {
|
if (/^https?:\/\//i.test(descr)) {
|
||||||
|
var url = escapeHtml(descr);
|
||||||
var suffix = descr.slice(-3).toLowerCase();
|
var suffix = descr.slice(-3).toLowerCase();
|
||||||
if (suffix == 'jpg' || suffix == 'gif' || suffix == 'png') {
|
if (suffix == 'jpg' || suffix == 'gif' || suffix == 'png') {
|
||||||
popupDataTable = popupDataTable + '<a href="' + descr + '"><img alt="Link" src="' + descr + '" width="200"/></a>';
|
popupDataTable = popupDataTable + '<a href="' + url + '"><img alt="Link" src="' + url + '" width="200"/></a>';
|
||||||
} else {
|
} else {
|
||||||
popupDataTable = popupDataTable + '<a href="' + descr + '">Link</a>';
|
popupDataTable = popupDataTable + '<a href="' + url + '">Link</a>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
popupDataTable = popupDataTable + plot[x];
|
popupDataTable = popupDataTable + escapeHtml(descr);
|
||||||
}
|
}
|
||||||
popupDataTable = popupDataTable + '</td></tr>';
|
popupDataTable = popupDataTable + '</td></tr>';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,32 @@ function searchLocation(maxResults) {
|
||||||
searchLimit = 11;
|
searchLimit = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchScript = document.createElement('script');
|
var searchUrl = 'https://nominatim.openstreetmap.org/search?q=' + encodeURIComponent(searchString)
|
||||||
searchScript.src = 'https://nominatim.openstreetmap.org/search.php?q=' + searchString + '&format=json&json_callback=parseResult&limit=' + searchLimit;
|
+ '&format=json&limit=' + encodeURIComponent(searchLimit);
|
||||||
document.body.appendChild(searchScript);
|
fetch(searchUrl)
|
||||||
|
.then(function(response) { return response.json(); })
|
||||||
|
.then(parseResult)
|
||||||
|
.catch(function() { parseResult([]); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape a value for use in HTML text and in double-quoted attributes.
|
||||||
|
function escapeSearchHtml(value) {
|
||||||
|
return String(value)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
function parseResult(response) {
|
function parseResult(response) {
|
||||||
var searchResultDivs = '';
|
var searchResultDivs = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(response)) {
|
||||||
|
response = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (response.length == searchLimit) {
|
if (response.length == searchLimit) {
|
||||||
var lastResultIndex = searchLimit - 1;
|
var lastResultIndex = searchLimit - 1;
|
||||||
var nextSearchLimit = searchLimit + 10;
|
var nextSearchLimit = searchLimit + 10;
|
||||||
|
|
@ -67,10 +84,15 @@ function parseResult(response) {
|
||||||
// Show the current 10 search results.
|
// Show the current 10 search results.
|
||||||
for (var i=searchLimit-11; i<lastResultIndex; i++) {
|
for (var i=searchLimit-11; i<lastResultIndex; i++) {
|
||||||
searchResult = response[i];
|
searchResult = response[i];
|
||||||
|
var lat = Number(searchResult.lat);
|
||||||
|
var lon = Number(searchResult.lon);
|
||||||
|
if (!isFinite(lat) || !isFinite(lon)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
searchResultDivs +=
|
searchResultDivs +=
|
||||||
'<div class="search-result" onClick="gotoLocation(' + searchResult.lat + ', ' + searchResult.lon + ');return false;">' +
|
'<div class="search-result" onClick="gotoLocation(' + lat + ', ' + lon + ');return false;">' +
|
||||||
'<div class="text-medium">' + searchResult.class + ' / ' + searchResult.type + '</div>' +
|
'<div class="text-medium">' + escapeSearchHtml(searchResult.class) + ' / ' + escapeSearchHtml(searchResult.type) + '</div>' +
|
||||||
'<div>' + searchResult.display_name + '</div>' +
|
'<div>' + escapeSearchHtml(searchResult.display_name) + '</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
} else {
|
} else {
|
||||||
$i18n = $i18nCountries;
|
$i18n = $i18nCountries;
|
||||||
$i18nDefault = $i18nCountriesDefault;
|
$i18nDefault = $i18nCountriesDefault;
|
||||||
// Check if $key consists of 2 capital letters.
|
// Check if $key consists of 2 capital letters or is '--' (unknown country).
|
||||||
$key = preg_match('/\b[A-Z]{2}\b/', $valsArray[$i]) === 1 || '--' ? $valsArray[$i] : 'dontfindme';
|
$key = (preg_match('/^[A-Z]{2}$/', $valsArray[$i]) === 1 || $valsArray[$i] === '--') ? $valsArray[$i] : 'dontfindme';
|
||||||
}
|
}
|
||||||
if (!isValidValue($i18n, $i18nDefault, $key)) {
|
if (!isValidValue($i18n, $i18nDefault, $key)) {
|
||||||
// ignore 'vals' and 'cols'
|
// ignore 'vals' and 'cols'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue