diff --git a/CHANGELOG.md b/CHANGELOG.md
index 477f592..9ebeb48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,14 +14,16 @@ IMPORTANT NOTE: This version only works on CACTI 1.x++!
## Changes
---- 1.5 ---
-* issue#124: Weathermap slow when some graphs are broken
-* issue#173: Data Source select queries slow or not working
-* issue#186: Popups are not working in standalone mode
+--- 1.6 ---
* issue#211: Warn Count is being reset too early
* issue: Attempt to keep the plugin basepath the weathermap directory
* issue: Miscellaneous security hardening and preparation for Cacti 1.3
* feature#219: Add Via Style as a Link form Option
+
+--- 1.5 ---
+* issue#124: Weathermap slow when some graphs are broken
+* issue#173: Data Source select queries slow or not working
+* issue#186: Popups are not working in standalone mode
* feature#193: Allow the Info URL to be either "Time View Graph" or "Classic View Graph"
--- 1.4 ---
diff --git a/INFO b/INFO
index 8d1a2cf..53a29dd 100644
--- a/INFO
+++ b/INFO
@@ -1,6 +1,6 @@
[info]
name = weathermap
-version = 1.5
+version = 1.6
longname = Weathermap Plugin
author = The Cacti Group, Howard Jones
email = developers@cacti.net
diff --git a/cli/cacti-mapper.php b/cli/cacti-mapper.php
index c73bd25..1346751 100644
--- a/cli/cacti-mapper.php
+++ b/cli/cacti-mapper.php
@@ -147,7 +147,7 @@
unset($interfaces[$key]);
$cleaned++;
} else {
- $interfaces[$key]['nicename'] = (isset($int['name']) ? $int['name'] : (isset($int['descr']) ? $int['descr'] : (isset($int['alias']) ? $int['alias'] : 'Interface #' . $int['index'])));
+ $interfaces[$key]['nicename'] = ($int['name'] ?? ($int['descr'] ?? ($int['alias'] ?? 'Interface #' . $int['index'])));
}
}
}
diff --git a/js/editor.js b/js/editor.js
index 7679d1a..ce5042c 100644
--- a/js/editor.js
+++ b/js/editor.js
@@ -1051,11 +1051,11 @@ function prime_link_form(name) {
// if that didn't 'stick', then we need to add the special value
if ($('#link_commentposout').val() != mylink.commentposout) {
- $('#link_commentposout').prepend("" + mylink.commentposout + "% ");
+ $('#link_commentposout').prepend($('', { selected: true, value: mylink.commentposout, text: mylink.commentposout + '%' }));
}
if ($('#link_commentposin').val() != mylink.commentposin) {
- $('#link_commentposin').prepend(" " + mylink.commentposin + "% ");
+ $('#link_commentposin').prepend($('', { selected: true, value: mylink.commentposin, text: mylink.commentposin + '%' }));
}
document.getElementById('link_nodename1').firstChild.nodeValue = mylink.a;
diff --git a/js/jquery.ddslick.js b/js/jquery.ddslick.js
index 345759a..f38d18e 100644
--- a/js/jquery.ddslick.js
+++ b/js/jquery.ddslick.js
@@ -192,7 +192,7 @@
});
// Watch for and handle keypress when popup options list is open.
- ddOptions.keydown(function(event) {
+ ddOptions.on('keydown', function(event) {
var ddOptions = $(this);
if (ddOptions.attr("aria-hidden") != "false") {
return;
@@ -361,7 +361,7 @@
//Check if already destroyed
if (pluginData) {
var originalElement = pluginData.original;
- $this.removeData("ddslick").unbind(".ddslick").replaceWith(originalElement);
+ $this.removeData("ddslick").off(".ddslick").replaceWith(originalElement);
}
});
};
diff --git a/js/map-cycle.js b/js/map-cycle.js
index d79f981..9ea988b 100644
--- a/js/map-cycle.js
+++ b/js/map-cycle.js
@@ -153,7 +153,7 @@ var WMcycler = {
},
initKeys: function (that) {
- $(document).keyup(function (event) {
+ $(document).on('keyup', function(event) {
if (event.keyCode === that.KEYCODE_ESCAPE) {
window.location.href = $('#cycle_stop').attr('href');
event.preventDefault();
@@ -178,13 +178,13 @@ var WMcycler = {
initEvents: function (that) {
- $("#cycle_pause").click(function () {
+ $("#cycle_pause").on('click', function() {
that.pauseAction();
});
- $("#cycle_next").click(function () {
+ $("#cycle_next").on('click', function() {
that.nextAction();
});
- $("#cycle_prev").click(function () {
+ $("#cycle_prev").on('click', function() {
that.previousAction();
});
},
diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php
index d5fb539..7f32d61 100644
--- a/lib/WeatherMap.class.php
+++ b/lib/WeatherMap.class.php
@@ -118,7 +118,7 @@ function Recognise($targetstring) {
// returns an array of two values (in,out). -1,-1 if it couldn't get valid data
// configline is passed in, to allow for better error messages
// itemtype and itemname may be used as part of the target (e.g. for TSV source line)
- // function ReadData($targetstring, $configline, $itemtype, $itemname, $map) { return (array(-1,-1)); }
+ // function ReadData($targetstring, $configline, $itemtype, $itemname, $map) { return ([-1,-1]); }
function ReadData($targetstring, &$map, &$item) {
return ([-1, -1, 0]);
}
@@ -830,7 +830,7 @@ function LoadPlugins($type = 'data', $dir = 'datasources') {
wm_debug("Relative path didn't exist. Trying $dir");
}
- // $this->datasourceclasses = array();
+ // $this->datasourceclasses = [];
$dh = opendir($dir);
if (!$dh) {
@@ -838,9 +838,11 @@ function LoadPlugins($type = 'data', $dir = 'datasources') {
// try to find it with the script, if the relative path fails
$srcdir = substr($_SERVER['argv'][0], 0, strrpos($_SERVER['argv'][0], '/'));
+ // nosemgrep: php.lang.security.injection.tainted-filename.tainted-filename
if (file_exists($srcdir)) {
$dir = $srcdir . '/' . $dir;
+ // nosemgrep: php.lang.security.injection.tainted-filename.tainted-filename
if (file_exists($dir)) {
$dh = opendir($dir);
} else {
@@ -856,7 +858,7 @@ function LoadPlugins($type = 'data', $dir = 'datasources') {
while ($file = readdir($dh)) {
$realfile = $dir . '/' . $file;
- if (is_file($realfile) && preg_match('/\.php$/', $realfile)) {
+ if (is_file($realfile) && preg_match('/\.php$/', $realfile)) { // nosemgrep: php.lang.security.injection.tainted-filename.tainted-filename
if (strpos($realfile, 'index.php') !== false) {
continue;
}
@@ -882,7 +884,7 @@ function LoadPlugins($type = 'data', $dir = 'datasources') {
wm_debug("Loaded $type Plugin class $class from $file");
- $this->plugins[$type][$class] = new $class;
+ $this->plugins[$type][$class] = new $class; // nosemgrep: php.lang.security.injection.tainted-object-instantiation.tainted-object-instantiation
if (! isset($this->plugins[$type][$class])) {
wm_debug("** Failed to create an object for plugin $type/$class");
@@ -907,7 +909,7 @@ function DatasourceInit() {
wm_debug("Running $ds_class" . '->Init()');
- // $ret = call_user_func(array($ds_class, 'Init'), $this);
+ // $ret = call_user_func([$ds_class, 'Init'], $this);
// assert('isset($this->plugins["data"][$ds_class])');
$ret = $this->plugins['data'][$ds_class]->Init($this);
@@ -965,7 +967,7 @@ function ProcessTargets() {
foreach ($this->datasourceclasses as $ds_class) {
if (!$matched) {
- // $recognised = call_user_func(array($ds_class, 'Recognise'), $targetstring);
+ // $recognised = call_user_func([$ds_class, 'Recognise'], $targetstring);
$recognised = $this->plugins['data'][$ds_class]->Recognise($targetstring);
if ($recognised) {
@@ -1131,15 +1133,15 @@ function ReadData() {
// in a half duplex link, in and out share a common bandwidth pool, so percentages need to include both
wm_debug('Calculating percentage using half-duplex');
- $myobj->outpercent = (($total_in + $total_out) / ($myobj->max_bandwidth_out)) * 100;
- $myobj->inpercent = (($total_out + $total_in) / ($myobj->max_bandwidth_in)) * 100;
+ $myobj->outpercent = ($myobj->max_bandwidth_out != 0) ? ((($total_in + $total_out) / $myobj->max_bandwidth_out) * 100) : 0;
+ $myobj->inpercent = ($myobj->max_bandwidth_in != 0) ? ((($total_out + $total_in) / $myobj->max_bandwidth_in) * 100) : 0;
if ($myobj->max_bandwidth_out != $myobj->max_bandwidth_in) {
wm_warn("ReadData: $type $name: You're using asymmetric bandwidth AND half-duplex in the same link. That makes no sense. [WMWARN44]");
}
} else {
- $myobj->outpercent = (($total_out) / ($myobj->max_bandwidth_out)) * 100;
- $myobj->inpercent = (($total_in) / ($myobj->max_bandwidth_in)) * 100;
+ $myobj->outpercent = ($myobj->max_bandwidth_out != 0) ? ((($total_out) / $myobj->max_bandwidth_out) * 100) : 0;
+ $myobj->inpercent = ($myobj->max_bandwidth_in != 0) ? ((($total_in) / $myobj->max_bandwidth_in) * 100) : 0;
}
// print $myobj->name."=>".$myobj->inpercent."%/".$myobj->outpercent."\n";
@@ -1257,11 +1259,7 @@ function ColourFromPercent($image, $percent, $scalename = 'DEFAULT', $name = '')
$nowarn_scalemisses = intval($this->get_hint('nowarn_scalemisses'));
$bt = debug_backtrace();
- $function = (isset($bt[1]['function']) ? $bt[1]['function'] : '');
-
- print "$function calls ColourFromPercent\n";
-
- exit();
+ $function = ($bt[1]['function'] ?? '');
if (isset($this->colours[$scalename])) {
$colours = $this->colours[$scalename];
@@ -2391,11 +2389,11 @@ function ReadConfig($input, $is_include = false) {
['NODE', '/^\s*LABELFONT\s+(\d+)\s*$/i', ['labelfont'=>1]],
['NODE', '/^\s*LABELANGLE\s+(0|90|180|270)\s*$/i', ['labelangle'=>1]],
- // array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', array('template'=>1)),
+ // array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', ['template'=>1]),
['LINK', '/^\s*OUTBWFORMAT\s+(.*)\s*$/i', ['bwlabelformats[OUT]'=>1, 'labelstyle'=>'--']],
['LINK', '/^\s*INBWFORMAT\s+(.*)\s*$/i', ['bwlabelformats[IN]'=>1, 'labelstyle'=>'--']],
- // array('NODE','/^\s*ICON\s+none\s*$/i',array('iconfile'=>'')),
+ // array('NODE','/^\s*ICON\s+none\s*$/i',['iconfile'=>'']),
['NODE', '/^\s*ICON\s+(\S+)\s*$/i', ['iconfile'=>1, 'iconscalew'=>'#0', 'iconscaleh'=>'#0']],
['NODE', '/^\s*ICON\s+(\S+)\s*$/i', ['iconfile'=>1]],
['NODE', '/^\s*ICON\s+(\d+)\s+(\d+)\s+(inpie|outpie|box|rbox|round|gauge|nink)\s*$/i', ['iconfile'=>3, 'iconscalew'=>1, 'iconscaleh'=>2]],
@@ -2656,7 +2654,7 @@ function ReadConfig($input, $is_include = false) {
}
}
- // array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', array('template'=>1)),
+ // array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', ['template'=>1]),
if (($last_seen == 'NODE' || $last_seen == 'LINK') && preg_match('/^\s*TEMPLATE\s+(\S+)\s*$/i', $buffer, $matches)) {
$tname = $matches[1];
@@ -3397,7 +3395,7 @@ function WriteConfig($filename) {
$top = nice_bandwidth($colour['top'], $this->kilo);
}
- $tag = (isset($colour['tag']) ? $colour['tag'] : '');
+ $tag = ($colour['tag'] ?? '');
if (($colour['red1'] == -1) && ($colour['green1'] == -1) && ($colour['blue1'] == -1)) {
$output .= sprintf("SCALE %s %-4s %-4s none %s\n", $scalename, $bottom, $top, $tag);
@@ -3551,7 +3549,7 @@ function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $with
foreach ($this->postprocessclasses as $post_class) {
wm_debug("Running $post_class" . '->run()');
- // call_user_func_array(array($post_class, 'run'), array(&$this));
+ // call_user_func_array([$post_class, 'run'], [&$this]);
$this->plugins['post'][$post_class]->run($this);
}
@@ -4266,7 +4264,7 @@ function CacheUpdate($agelimit = 600) {
if ((filemtime($realfile) < $configchanged) || ((time() - filemtime($realfile)) > $agelimit)) {
wm_debug("Cache: deleting $realfile");
- unlink($realfile);
+ unlink($realfile); // nosemgrep: php.lang.security.unlink-use.unlink-use
}
}
}
diff --git a/lib/WeatherMap.functions.php b/lib/WeatherMap.functions.php
index 73110bc..90ef0c6 100644
--- a/lib/WeatherMap.functions.php
+++ b/lib/WeatherMap.functions.php
@@ -995,7 +995,7 @@ function draw_straight($image, &$curvepoints, $widths, $outlinecolour, $fillcolo
$halfway = $totaldistance * ($q2_percent / 100);
$dirs = [OUT, IN];
- // $dirs = array(IN);
+ // $dirs = [IN];
[$halfway_x, $halfway_y, $halfwayindex] = find_distance_coords($curvepoints, $halfway);
@@ -1647,9 +1647,7 @@ function format_number($number, $precision = 2, $trailing_zeroes = 0) {
$decimal = substr($number, strlen($integer) + 1);
}
- if (!isset($decimal)) {
- $decimal = '';
- }
+ $decimal ??= '';
$integer = $sign * $integer;
diff --git a/lib/WeatherMapLink.class.php b/lib/WeatherMapLink.class.php
index 725db80..c03c8b0 100644
--- a/lib/WeatherMapLink.class.php
+++ b/lib/WeatherMapLink.class.php
@@ -184,7 +184,7 @@ function __construct() {
// $this->a_offset = 'C';
// $this->b_offset = 'C';
- // $this->targets = array();
+ // $this->targets = [];
}
function Reset(&$newowner) {
@@ -274,7 +274,7 @@ function DrawComments($image, $col, $widths) {
}
if ($comment != '') {
- // print "\n\n----------------------------------------------------------------\nComment $dir for ".$this->name."\n";;
+ // print "\n\n----------------------------------------------------------------\nComment $dir for ".$this->name."\n";
[$textlength, $textheight] = $this->owner->myimagestringsize($this->commentfont, $comment);
@@ -631,7 +631,7 @@ function WriteConfig() {
['duplex', 'DUPLEX', CONFIG_TYPE_LITERAL],
['commentstyle', 'COMMENTSTYLE', CONFIG_TYPE_LITERAL],
['labelboxstyle', 'BWSTYLE', CONFIG_TYPE_LITERAL],
- // array('usescale', 'USESCALE', CONFIG_TYPE_LITERAL),
+ // ['usescale', 'USESCALE', CONFIG_TYPE_LITERAL],
['bwfont', 'BWFONT', CONFIG_TYPE_LITERAL],
['commentfont', 'COMMENTFONT', CONFIG_TYPE_LITERAL],
diff --git a/lib/WeatherMapNode.class.php b/lib/WeatherMapNode.class.php
index abc7f5b..01aca14 100644
--- a/lib/WeatherMapNode.class.php
+++ b/lib/WeatherMapNode.class.php
@@ -549,7 +549,7 @@ function pre_render($image, &$map) {
$map->nodes[$this->name]->width = imagesx($icon_im);
$map->nodes[$this->name]->height = imagesy($icon_im);
- // $map->imap->addArea("Rectangle", "NODE:" . $this->name . ':0', '', array($icon_x1, $icon_y1, $icon_x2, $icon_y2));
+ // $map->imap->addArea("Rectangle", "NODE:" . $this->name . ':0', '', [$icon_x1, $icon_y1, $icon_x2, $icon_y2]);
$map->nodes[$this->name]->boundingboxes[] = [$icon_x1, $icon_y1, $icon_x2, $icon_y2];
}
}
@@ -577,7 +577,7 @@ function pre_render($image, &$map) {
$label_y2 += ($this->labeloffsety + $dy);
if ($this->label != '') {
- // $map->imap->addArea("Rectangle", "NODE:" . $this->name .':1', '', array($label_x1, $label_y1, $label_x2, $label_y2));
+ // $map->imap->addArea("Rectangle", "NODE:" . $this->name .':1', '', [$label_x1, $label_y1, $label_x2, $label_y2]);
$map->nodes[$this->name]->boundingboxes[] = [$label_x1, $label_y1, $label_x2, $label_y2];
}
@@ -796,7 +796,7 @@ function WriteConfig() {
// $field = 'zorder'; $keyword = 'ZORDER';
$basic_params = [
- // array('template','TEMPLATE',CONFIG_TYPE_LITERAL),
+ // ['template','TEMPLATE',CONFIG_TYPE_LITERAL],
['label', 'LABEL', CONFIG_TYPE_LITERAL],
['zorder', 'ZORDER', CONFIG_TYPE_LITERAL],
['labeloffset', 'LABELOFFSET', CONFIG_TYPE_LITERAL],
diff --git a/lib/datasources/WeatherMapDataSource_dsstats.php b/lib/datasources/WeatherMapDataSource_dsstats.php
index 2b232ec..836a816 100644
--- a/lib/datasources/WeatherMapDataSource_dsstats.php
+++ b/lib/datasources/WeatherMapDataSource_dsstats.php
@@ -104,7 +104,7 @@ function Recognise($targetstring) {
/**
* Actually read data from a data source, and return it
- * returns a 3-part array (invalue, outvalue and datavalid time_t)
+ * returns a 3-part [invalue, outvalue and datavalid time_t]
* invalue and outvalue should be -1,-1 if there is no valid data
* data_time is intended to allow more informed graphing in the future
* @param mixed $targetstring
diff --git a/lib/datasources/WeatherMapDataSource_fping.php b/lib/datasources/WeatherMapDataSource_fping.php
index 8ddb3cb..44518dd 100644
--- a/lib/datasources/WeatherMapDataSource_fping.php
+++ b/lib/datasources/WeatherMapDataSource_fping.php
@@ -91,15 +91,18 @@ function ReadData($targetstring, &$map, &$item) {
if (preg_match('/^fping:(\S+)$/', $targetstring, $matches)) {
$target = $matches[1];
- $pattern = "/^$target\s:";
+ if (is_executable($this->fping_cmd)) {
+ /* Validate before exec: allow IPv4/IPv6 addresses (including zone IDs with %),
+ * hostnames (underscore-containing per RFC 2181), and bracketed IPv6 literals.
+ * Shell metacharacters in $target would otherwise reach popen() directly. */
+ if (!preg_match('/^[a-zA-Z0-9._\-:%\[\]]+$/', $target)) {
+ wm_warn("FPing ReadData: rejected target with illegal characters (" . json_encode($target) . ") [WMFPING04]");
- for ($i = 0; $i < $ping_count; $i++) {
- $pattern .= '\s(\S+)';
- }
+ return ([-1, -1, 0]);
+ }
- $pattern .= '/';
+ $pattern = '/^' . preg_quote($target, '/') . '\s:';
- if (is_executable($this->fping_cmd)) {
$command = cacti_escapeshellarg($this->fping_cmd) . ' -t100 -r1 -p20 -u -C ' . (int) $ping_count . ' -i10 -q ' . cacti_escapeshellarg($target) . ' 2>&1'; // nosemgrep: php.lang.security.exec-use.exec-use -- fping_cmd is admin-configured; target validated against fping: pattern
wm_debug("Running $command");
diff --git a/lib/datasources/WeatherMapDataSource_rrd.php b/lib/datasources/WeatherMapDataSource_rrd.php
index 3b9d9a6..27069bf 100644
--- a/lib/datasources/WeatherMapDataSource_rrd.php
+++ b/lib/datasources/WeatherMapDataSource_rrd.php
@@ -474,7 +474,7 @@ function wmrrd_read_from_real_rrdtool($rrdfile, $cf, $start, $end, $dsnames, &$d
wm_debug("RRD ReadData: Headings are: $headings");
if ((in_array($dsnames[IN], $heads, true) || $dsnames[IN] == '-') &&
- (in_array($dsnames[OUT],$heads, true) || $dsnames[OUT] == '-')) {
+ (in_array($dsnames[OUT], $heads, true) || $dsnames[OUT] == '-')) {
// deal with the data, starting with the last line of output
$rlines = array_reverse($lines);
@@ -546,7 +546,7 @@ function wmrrd_read_from_real_rrdtool($rrdfile, $cf, $start, $end, $dsnames, &$d
/**
* Actually read data from a data source, and return it
- * returns a 3-part array (invalue, outvalue and datavalid time_t)
+ * returns a 3-part [invalue, outvalue and datavalid time_t]
* invalue and outvalue should be -1,-1 if there is no valid data
* data_time is intended to allow more informed graphing in the future
* @param mixed $targetstring
diff --git a/lib/editor.inc.php b/lib/editor.inc.php
index 6dc3f62..99cf2cb 100644
--- a/lib/editor.inc.php
+++ b/lib/editor.inc.php
@@ -190,6 +190,18 @@ function wm_editor_validate_one_of($input,$valid = [],$case_sensitive = false) {
return false;
}
+function wm_editor_sanitize_action($action, $valid = []) {
+ if ($action === '') {
+ return '';
+ }
+
+ if (!wm_editor_validate_one_of($action, $valid, true)) {
+ return '';
+ }
+
+ return $action;
+}
+
// Labels for Nodes, Links and Scales shouldn't have spaces in
function wm_editor_sanitize_name($str) {
return str_replace([' '], '', $str);
@@ -242,6 +254,11 @@ function wm_editor_sanitize_conffile($filename) {
$filename = '';
}
+ // Defense-in-depth: reject Windows path separators to prevent traversal on Windows hosts.
+ if (strstr($filename, '\\') !== false) {
+ $filename = '';
+ }
+
return $filename;
}
diff --git a/lib/poller-common.php b/lib/poller-common.php
index 591a32f..f8e7720 100644
--- a/lib/poller-common.php
+++ b/lib/poller-common.php
@@ -84,7 +84,7 @@ function weathermap_check_cron($time, $string) {
$lt = localtime($time, true);
- [$minute, $hour, $wday, $day, $month] = preg_split('/\s+/', $string);
+ [$minute, $hour, $day, $month, $wday] = preg_split('/\s+/', $string);
$matched = true;
@@ -224,7 +224,7 @@ function weathermap_repair_maps() {
$old = $mydir . $objfile;
$new = $mydir . 'images/objects/' . basename($objfile);
- if (is_writable($mydir . $bgfile)) {
+ if (is_writable($mydir . $objfile)) {
if (is_writeable($old) && is_writeable(dirname($new)) && is_writeable($new)) {
if (rename($old, $new)) {
$line = "\tICON images/objects/" . basename($objfile);
@@ -445,7 +445,7 @@ function weathermap_run_maps($mydir, $force = false, $maps = []) {
if (file_exists($tempfile)) {
// Don't try and delete a non-existent file (first run)
if (file_exists($imagefile)) {
- unlink($imagefile);
+ unlink($imagefile); // nosemgrep: php.lang.security.unlink-use.unlink-use -- path derived from plugin output dir, not user input
}
rename($tempfile, $imagefile);
diff --git a/weathermap-cacti-plugin-editor.php b/weathermap-cacti-plugin-editor.php
index 9a7e478..dc70e97 100644
--- a/weathermap-cacti-plugin-editor.php
+++ b/weathermap-cacti-plugin-editor.php
@@ -86,7 +86,15 @@
set_default_action('');
if (isset_request_var('action')) {
- $action = get_nfilter_request_var('action');
+ $action = wm_editor_sanitize_action(get_nfilter_request_var('action'), [
+ 'graphs', 'datasources', 'newmap', 'newmapcopy', 'font_samples', 'draw',
+ 'show_config', 'fetch_config', 'set_link_config', 'set_node_config',
+ 'set_node_properties', 'set_link_properties', 'set_map_properties',
+ 'set_map_style', 'add_link2', 'place_legend', 'place_stamp', 'via_link',
+ 'move_node', 'link_tidy', 'retidy', 'retidy_all', 'untidy',
+ 'delete_link', 'add_node', 'editor_settings', 'delete_node',
+ 'clone_node', 'load_area_data', 'load_map_javascript', 'nothing'
+ ]);
}
if (isset_request_var('mapname')) {
@@ -945,4 +953,3 @@