diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml
index 73630b5..1f325b6 100644
--- a/.github/workflows/plugin-ci-workflow.yml
+++ b/.github/workflows/plugin-ci-workflow.yml
@@ -106,7 +106,7 @@ jobs:
exit 1
- name: Install System Dependencies
- run: sudo apt-get install -y apache2 snmp snmpd rrdtool fping
+ run: sudo apt-get install -y apache2 snmp snmpd rrdtool fping libapache2-mod-php
- name: Start SNMPD Agent and Test
run: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b7db7d..ae48cd4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
--- develop ---
+* security: Use prepared statements for the bulk form actions in notify_lists.php and notify_queue.php
+* security: Bind $graph_id in get_allowed_thresholds() and get_allowed_threshold_logs() instead of interpolating it
+* security: Route rfilter through db_qstr_rlike() where Cacti provides it, and quote it otherwise
+* security: Quote the values substituted into trigger commands
+* security: Escape the page, id and drp_action values printed into hidden inputs
+* security: Remove the eval() calls from the RPN expression evaluator
+* issue: Bulk actions on the Notification Lists page did nothing, because the action allowlist compared an int against strings
+* issue: Bulk writes were discarded on MySQL, where Cacti's db_commit_transaction() never commits
+* issue: An RPN expression dividing zero by zero pushed no result, corrupting the rest of the stack
+* issue: An RPN expression taking the modulo of zero, the square root of a negative, or the log of zero aborted the poller or produced NAN
+* issue: Deleting a notification list left soft-deleted devices pointing at it
* issue#686: Applying a templated threshold to a graph via the wrench icon, creates a duplicate graph
* issue#707: Excessive timeout for row caching prevents data from being updated timely
* issue#710: Fixing Typo in thold_daemons.service File
diff --git a/notify_lists.php b/notify_lists.php
index 016e01d..72c549e 100644
--- a/notify_lists.php
+++ b/notify_lists.php
@@ -146,7 +146,19 @@ function form_actions() {
global $actions, $assoc_actions;
// ================= input validation =================
+ /*
+ * get_filter_request_var() stores the value as an int, so the comparison
+ * has to be made on strings for the strict check to mean anything.
+ */
get_filter_request_var('drp_action');
+
+ $valid_actions = array_map('strval', array_keys($actions + $assoc_actions));
+
+ if (!in_array((string) get_request_var('drp_action'), $valid_actions, true)) {
+ raise_message(40);
+ header('Location: notify_lists.php?header=false');
+ exit;
+ }
// ====================================================
// if we are to save this form, instead of display it
@@ -156,41 +168,54 @@ function form_actions() {
if (isset_request_var('save_list')) {
if ($selected_items != false) {
if (get_request_var('drp_action') == '1') { // delete
- db_execute('DELETE FROM plugin_notification_lists
- WHERE ' . array_to_sql_or($selected_items, 'id'));
-
- db_execute('UPDATE host
- SET thold_send_email = 0
- WHERE thold_send_email = 2
- AND deleted=""
- AND ' . array_to_sql_or($selected_items, 'thold_host_email'));
-
- db_execute('UPDATE host
- SET thold_send_email = 1
- WHERE thold_send_email = 3
- AND deleted=""
- AND ' . array_to_sql_or($selected_items, 'thold_host_email'));
-
- db_execute('UPDATE host
- SET thold_host_email = 0
- AND deleted=""
- WHERE ' . array_to_sql_or($selected_items, 'thold_host_email'));
-
- db_execute('UPDATE thold_data
- SET notify_warning = 0
- WHERE ' . array_to_sql_or($selected_items, 'notify_warning'));
-
- db_execute('UPDATE thold_data
- SET notify_alert = 0
- WHERE ' . array_to_sql_or($selected_items, 'notify_alert'));
-
- db_execute('UPDATE thold_template
- SET notify_warning = 0
- WHERE ' . array_to_sql_or($selected_items, 'notify_warning'));
-
- db_execute('UPDATE thold_template
- SET notify_alert = 0
- WHERE ' . array_to_sql_or($selected_items, 'notify_alert'));
+ /*
+ * Bind positionally on the values: sanitize_unserialize_selected_items()
+ * preserves the submitted array's keys, and a string key would be read
+ * as a named parameter.
+ */
+ $ids = array_map('intval', array_values($selected_items));
+ $placeholders = implode(', ', array_fill(0, cacti_sizeof($ids), '?'));
+
+ /*
+ * Issued as SQL rather than through db_begin_transaction() and
+ * friends: Cacti's db_commit_transaction() gates the commit on
+ * SELECT @@in_transaction, which only MariaDB defines. On MySQL that
+ * query fails, the commit is skipped, and every write here is
+ * discarded when the connection closes.
+ */
+ db_execute('START TRANSACTION');
+
+ /*
+ * The host reset deliberately omits the deleted = "" predicate its
+ * siblings carry: a soft-deleted device that is later restored must
+ * not come back pointing at a list that no longer exists.
+ */
+ $statements = [
+ 'DELETE FROM plugin_notification_lists WHERE id IN (' . $placeholders . ')',
+ 'UPDATE host SET thold_send_email = 0 WHERE thold_send_email = 2 AND deleted = "" AND thold_host_email IN (' . $placeholders . ')',
+ 'UPDATE host SET thold_send_email = 1 WHERE thold_send_email = 3 AND deleted = "" AND thold_host_email IN (' . $placeholders . ')',
+ 'UPDATE host SET thold_host_email = 0 WHERE thold_host_email IN (' . $placeholders . ')',
+ 'UPDATE thold_data SET notify_warning = 0 WHERE notify_warning IN (' . $placeholders . ')',
+ 'UPDATE thold_data SET notify_alert = 0 WHERE notify_alert IN (' . $placeholders . ')',
+ 'UPDATE thold_template SET notify_warning = 0 WHERE notify_warning IN (' . $placeholders . ')',
+ 'UPDATE thold_template SET notify_alert = 0 WHERE notify_alert IN (' . $placeholders . ')'
+ ];
+
+ $ok = true;
+
+ foreach ($statements as $sql) {
+ if (!db_execute_prepared($sql, $ids)) {
+ $ok = false;
+
+ break;
+ }
+ }
+
+ if ($ok) {
+ db_execute('COMMIT');
+ } else {
+ db_execute('ROLLBACK');
+ }
} elseif (get_request_var('drp_action') == '2') { // duplicate
$i = 1;
@@ -237,48 +262,60 @@ function form_actions() {
if (isset_request_var('save_associate')) {
if ($selected_items != false) {
+ get_filter_request_var('id');
get_filter_request_var('notification_action');
+ get_filter_request_var('notification_warning_action');
+ get_filter_request_var('notification_alert_action');
+
+ db_execute('START TRANSACTION');
+
+ $ok = true;
if (get_request_var('drp_action') == '1') { // associate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
// set the notification list
- db_execute('UPDATE host
- SET thold_host_email=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i] . '
- AND deleted=""');
+ $ok = db_execute_prepared('UPDATE host
+ SET thold_host_email = ?
+ WHERE id = ?
+ AND deleted = ""',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// set the global/list election
- db_execute('UPDATE host
- SET thold_send_email=' . get_request_var('notification_action') . '
- WHERE id=' . $selected_items[$i] . '
- AND deleted=""');
+ $ok = db_execute_prepared('UPDATE host
+ SET thold_send_email = ?
+ WHERE id = ?
+ AND deleted = ""',
+ [get_request_var('notification_action'), $selected_items[$i]]) && $ok;
if (get_request_var('notification_warning_action') > 0) {
// clear other settings
if (get_request_var('notification_warning_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_warning=' . get_request_var('id') . '
- WHERE td.host_id=' . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_warning = ?
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_warning_extra=''
- WHERE td.host_id=" . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_warning_extra = \'\'
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_warning=' . get_request_var('id') . '
- WHERE td.host_id=' . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_warning = ?
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
@@ -286,78 +323,100 @@ function form_actions() {
// clear other settings
if (get_request_var('notification_alert_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_alert=' . get_request_var('id') . '
- WHERE td.host_id=' . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_alert = ?
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_extra=''
- WHERE host_id=" . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_extra = \'\'
+ WHERE host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [$selected_items[$i]]) && $ok;
// remove legacy contacts
- db_execute('DELETE pttc
+ $ok = db_execute_prepared('DELETE pttc
FROM plugin_thold_threshold_contact AS pttc
INNER JOIN thold_data AS td
ON pttc.thold_id = td.id
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- WHERE td.host_id=' . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_alert=' . get_request_var('id') . '
- WHERE td.host_id=' . $selected_items[$i] . '
- AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)');
+ SET td.notify_alert = ?
+ WHERE td.host_id = ?
+ AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
+
+ if (!$ok) {
+ break;
+ }
}
} elseif (get_request_var('drp_action') == '2') { // disassociate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
// set the notification list
- db_execute('UPDATE host
- SET thold_host_email=0
- WHERE id=' . $selected_items[$i] . '
- AND deleted=""');
+ $ok = db_execute_prepared('UPDATE host
+ SET thold_host_email = 0
+ WHERE id = ?
+ AND deleted = ""',
+ [$selected_items[$i]]) && $ok;
// set the global/list election
- db_execute('UPDATE host
- SET thold_send_email=' . get_request_var('notification_action') . '
- WHERE id=' . $selected_items[$i] . '
- AND deleted=""');
+ $ok = db_execute_prepared('UPDATE host
+ SET thold_send_email = ?
+ WHERE id = ?
+ AND deleted = ""',
+ [get_request_var('notification_action'), $selected_items[$i]]) && $ok;
if (get_request_var('notification_warning_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
SET td.notify_warning = 0
- WHERE td.host_id=' . $selected_items[$i] . '
+ WHERE td.host_id = ?
AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)
- AND td.notify_warning=' . get_request_var('id'));
+ AND td.notify_warning = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
}
if (get_request_var('notification_alert_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_data AS td
+ $ok = db_execute_prepared('UPDATE thold_data AS td
LEFT JOIN thold_template AS tt
ON td.thold_template_id = tt.id
- SET td.notify_alert=0
- WHERE td.host_id=' . $selected_items[$i] . '
+ SET td.notify_alert = 0
+ WHERE td.host_id = ?
AND (tt.notify_templated = "" OR tt.notify_templated IS NULL)
- AND td.notify_alert=' . get_request_var('id'));
+ AND td.notify_alert = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
+ }
+
+ if (!$ok) {
+ break;
}
}
}
+
+ if ($ok) {
+ db_execute('COMMIT');
+ } else {
+ db_execute('ROLLBACK');
+ }
}
header('Location: notify_lists.php?header=false&action=edit&tab=hosts&id=' . get_request_var('id'));
@@ -366,27 +425,38 @@ function form_actions() {
if (isset_request_var('save_templates')) {
if ($selected_items != false) {
+ get_filter_request_var('id');
get_filter_request_var('notification_action');
+ get_filter_request_var('notification_warning_action');
+ get_filter_request_var('notification_alert_action');
+
+ db_execute('START TRANSACTION');
+
+ $ok = true;
+ $update_template = [];
if (get_request_var('drp_action') == '1') { // associate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
if (get_request_var('notification_warning_action') > 0) {
// clear other settings
if (get_request_var('notification_warning_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_warning=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_warning = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_template
- SET notify_warning_extra=''
- WHERE id=" . $selected_items[$i]);
+ $ok = db_execute_prepared("UPDATE thold_template
+ SET notify_warning_extra = ''
+ WHERE id = ?",
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_warning=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_warning = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
@@ -394,47 +464,74 @@ function form_actions() {
// clear other settings
if (get_request_var('notification_alert_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_alert=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_alert = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_template
- SET notify_extra=''
- WHERE id=" . $selected_items[$i]);
-
- db_execute('DELETE FROM plugin_thold_template_contact
- WHERE template_id=' . $selected_items[$i]);
+ $ok = db_execute_prepared("UPDATE thold_template
+ SET notify_extra = ''
+ WHERE id = ?",
+ [$selected_items[$i]]) && $ok;
+
+ $ok = db_execute_prepared('DELETE FROM plugin_thold_template_contact
+ WHERE template_id = ?',
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_alert=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_alert = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
- thold_template_update_thresholds($selected_items[$i]);
+ $update_template[] = $selected_items[$i];
+
+ if (!$ok) {
+ break;
+ }
}
} elseif (get_request_var('drp_action') == '2') { // disassociate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
if (get_request_var('notification_warning_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_warning=0
- WHERE id=' . $selected_items[$i] . '
- AND notify_warning=' . get_request_var('id'));
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_warning = 0
+ WHERE id = ?
+ AND notify_warning = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
}
if (get_request_var('notification_alert_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_template
- SET notify_alert=0
- WHERE id=' . $selected_items[$i] . '
- AND notify_alert=' . get_request_var('id'));
+ $ok = db_execute_prepared('UPDATE thold_template
+ SET notify_alert = 0
+ WHERE id = ?
+ AND notify_alert = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
+ }
+
+ $update_template[] = $selected_items[$i];
+
+ if (!$ok) {
+ break;
}
+ }
+ }
+
+ if ($ok) {
+ db_execute('COMMIT');
- thold_template_update_thresholds($selected_items[$i]);
+ // Propagate template changes to threshold instances after the
+ // notification assignment is committed so this cascade does not
+ // participate in the transaction boundary.
+ foreach ($update_template as $template_id) {
+ thold_template_update_thresholds($template_id);
}
+ } else {
+ db_execute('ROLLBACK');
}
}
@@ -444,27 +541,37 @@ function form_actions() {
if (isset_request_var('save_tholds')) {
if ($selected_items != false) {
+ get_filter_request_var('id');
get_filter_request_var('notification_action');
+ get_filter_request_var('notification_warning_action');
+ get_filter_request_var('notification_alert_action');
+
+ db_execute('START TRANSACTION');
+
+ $ok = true;
if (get_request_var('drp_action') == '1') { // associate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
if (get_request_var('notification_warning_action') > 0) {
// clear other settings
if (get_request_var('notification_warning_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_warning=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_warning = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_data
- SET notify_warning_extra=''
- WHERE id=" . $selected_items[$i]);
+ $ok = db_execute_prepared("UPDATE thold_data
+ SET notify_warning_extra = ''
+ WHERE id = ?",
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_warning=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_warning = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
@@ -472,43 +579,64 @@ function form_actions() {
// clear other settings
if (get_request_var('notification_alert_action') == 1) {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_alert=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_alert = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
// clear other items
- db_execute("UPDATE thold_data
- SET notify_extra=''
- WHERE id=" . $selected_items[$i]);
-
- db_execute('DELETE FROM plugin_thold_threshold_contact WHERE thold_id=' . $selected_items[$i]);
+ $ok = db_execute_prepared("UPDATE thold_data
+ SET notify_extra = ''
+ WHERE id = ?",
+ [$selected_items[$i]]) && $ok;
+
+ $ok = db_execute_prepared('DELETE FROM plugin_thold_threshold_contact
+ WHERE thold_id = ?',
+ [$selected_items[$i]]) && $ok;
} else {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_alert=' . get_request_var('id') . '
- WHERE id=' . $selected_items[$i]);
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_alert = ?
+ WHERE id = ?',
+ [get_request_var('id'), $selected_items[$i]]) && $ok;
}
}
+
+ if (!$ok) {
+ break;
+ }
}
} elseif (get_request_var('drp_action') == '2') { // disassociate
- for ($i = 0; ($i < count($selected_items)); $i++) {
+ for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
if (get_request_var('notification_warning_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_warning=0
- WHERE id=' . $selected_items[$i] . '
- AND notify_warning=' . get_request_var('id'));
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_warning = 0
+ WHERE id = ?
+ AND notify_warning = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
}
if (get_request_var('notification_alert_action') > 0) {
// set the notification list
- db_execute('UPDATE thold_data
- SET notify_alert=0
- WHERE id=' . $selected_items[$i] . '
- AND notify_alert=' . get_request_var('id'));
+ $ok = db_execute_prepared('UPDATE thold_data
+ SET notify_alert = 0
+ WHERE id = ?
+ AND notify_alert = ?',
+ [$selected_items[$i], get_request_var('id')]) && $ok;
+ }
+
+ if (!$ok) {
+ break;
}
}
}
+
+ if ($ok) {
+ db_execute('COMMIT');
+ } else {
+ db_execute('ROLLBACK');
+ }
}
header('Location: notify_lists.php?header=false&action=edit&tab=tholds&id=' . get_request_var('id'));
@@ -590,7 +718,7 @@ function form_actions() {
-
+
$save_html
";
@@ -665,10 +793,10 @@ function form_actions() {
print "
-
+
-
+
$save_html
";
@@ -743,10 +871,10 @@ function form_actions() {
print "
-
+
-
+
$save_html
";
@@ -828,10 +956,10 @@ function form_actions() {
print "