r4910 APCPowerSwitch::getPorts () - change snmpwalk call to snmpwalkoid, fixes #504
[racktables] / wwwroot / inc / ophandlers.php
index e24813f..bf09c69 100644 (file)
@@ -8,7 +8,7 @@ action, for example, to set a name of an object. Each such action often
 requires a set of parameters (e. g. ID of the object and the new name),
 and it is responsibility of each ophandler function to verify, that all
 necessary parameters are provided by the user and have proper values. There
-is a number of helper functions to make such verification simple.
+is a number of helper functions to make such verification simpler.
 
 Errors occuring in ophandlers are typically indicated with exceptions of
 assorted classes. Namely, an "InvalidRequestArgException" class means, that
@@ -16,7 +16,7 @@ at least one of the parameters provided by the user is not acceptable. This
 is a "soft" error, which gets displayed in the standard message area of
 otherwise usual interface. A different case is "InvalidArgException", which
 means, that one of the internal functions detected its argument(s) invalid
-or currupted, and that argument(s) did not come from user's input (and thus
+or corrupted, and that argument(s) did not come from user's input (and thus
 cannot be fixed without fixing a bug in the code). Such "hard" errors don't
 get special early handling and end up in the default catching block. The
 latter may print a detailed stack trace instead of the interface HTML to
@@ -25,7 +25,7 @@ help a developer debug the issue.
 As long as an ophandler makes through its request (extracting arguments,
 performing validation and actually updating records in the database), it
 may queue up messages (often referred to as "green" and "red" bars) by
-means of showError() and showSuccess() function. The messages are not
+means of showError() and showSuccess() functions. The messages are not
 displayed immediately, because successfull ophandlers are expected to
 return only the new URL, where the user will be immediately redirected to
 (it is also possible to return an empty string to mean, that the current
@@ -50,35 +50,36 @@ $opspec_list = array();
 
 $opspec_list['rackspace-edit-addRow'] = array
 (
-       'table' => 'RackRow',
+       'table' => 'Object',
        'action' => 'INSERT',
        'arglist' => array
        (
-               array ('url_argname' => 'name', 'assertion' => 'string')
-       ),
-);
-$opspec_list['rackspace-edit-delete'] = array
-(
-       'table' => 'RackRow',
-       'action' => 'DELETE',
-       'arglist' => array
-       (
-               array ('url_argname' => 'row_id', 'table_colname' => 'id', 'assertion' => 'uint')
+               array ('url_argname' => 'objtype_id', 'assertion' => 'uint'),
+               array ('url_argname' => 'name', 'table_colname' => 'label', 'assertion' => 'string')
        ),
 );
 $opspec_list['rackspace-edit-updateRow'] = array
 (
-       'table' => 'RackRow',
+       'table' => 'Object',
        'action' => 'UPDATE',
        'set_arglist' => array
        (
-               array ('url_argname' => 'name', 'assertion' => 'string')
+               array ('url_argname' => 'name', 'table_colname' => 'label', 'assertion' => 'string')
        ),
        'where_arglist' => array
        (
                array ('url_argname' => 'row_id', 'table_colname' => 'id', 'assertion' => 'uint')
        ),
 );
+$opspec_list['rackspace-edit-deleteRow'] = array
+(
+       'table' => 'Object',
+       'action' => 'DELETE',
+       'arglist' => array
+       (
+               array ('url_argname' => 'row_id', 'table_colname' => 'id', 'assertion' => 'uint')
+       ),
+);
 $opspec_list['object-ports-delPort'] = array
 (
        'table' => 'Port',
@@ -104,7 +105,7 @@ $opspec_list['object-log-del'] = array
        'action' => 'DELETE',
        'arglist' => array
        (
-               array ('url_argname' => 'logid', 'table_colname' => 'id', 'assertion' => 'uint'),
+               array ('url_argname' => 'log_id', 'table_colname' => 'id', 'assertion' => 'uint'),
                array ('url_argname' => 'object_id', 'assertion' => 'uint'),
        ),
 );
@@ -131,7 +132,7 @@ $opspec_list['object-editrspvs-updLB'] = array
        (
                array ('url_argname' => 'vsconfig', 'assertion' => 'string0', 'if_empty' => 'NULL'),
                array ('url_argname' => 'rsconfig', 'assertion' => 'string0', 'if_empty' => 'NULL'),
-               array ('url_argname' => 'prio', 'assertion' => 'uint0', 'if_empty' => 'NULL'),
+               array ('url_argname' => 'prio', 'assertion' => 'string0', 'if_empty' => 'NULL'),
        ),
        'where_arglist' => array
        (
@@ -140,6 +141,27 @@ $opspec_list['object-editrspvs-updLB'] = array
                array ('url_argname' => 'vs_id', 'assertion' => 'uint'),
        ),
 );
+$opspec_list['object-cacti-add'] = array
+(
+       'table' => 'CactiGraph',
+       'action' => 'INSERT',
+       'arglist' => array
+       (
+               array ('url_argname' => 'object_id', 'assertion' => 'uint'),
+               array ('url_argname' => 'graph_id', 'assertion' => 'uint'),
+               array ('url_argname' => 'caption', 'assertion' => 'string0'),
+       ),
+);
+$opspec_list['object-cacti-del'] = array
+(
+       'table' => 'CactiGraph',
+       'action' => 'DELETE',
+       'arglist' => array
+       (
+               array ('url_argname' => 'object_id', 'assertion' => 'uint'),
+               array ('url_argname' => 'graph_id', 'assertion' => 'uint'),
+       ),
+);
 $opspec_list['ipv4net-properties-editRange'] = array
 (
        'table' => 'IPv4Network',
@@ -412,6 +434,7 @@ $opspec_list['vlandomain-vlanlist-del'] = array
                array ('url_argname' => 'vlan_id', 'assertion' => 'vlan'),
        ),
 );
+$opspec_list['vlan-edit-upd'] = // both locations are using the same tableHandler op
 $opspec_list['vlandomain-vlanlist-upd'] = array
 (
        'table' => 'VLANDescription',
@@ -428,80 +451,7 @@ $opspec_list['vlandomain-vlanlist-upd'] = array
        ),
 );
 
-// This function is DEPRECATED. Show messages through showError and showSuccess,
-// you dont need to return anything from an ophandler to redirect user back to the page containing submit form
-function buildWideRedirectURL ($log = NULL, $nextpage = NULL, $nexttab = NULL, $moreArgs = array())
-{
-       global $page, $pageno, $tabno;
-       if ($nextpage === NULL)
-               $nextpage = $pageno;
-       if ($nexttab === NULL)
-               $nexttab = $tabno;
-       $url = "index.php?page=${nextpage}&tab=${nexttab}";
-       if (isset ($page[$nextpage]['bypass']))
-               $url .= '&' . $page[$nextpage]['bypass'] . '=' . $_REQUEST[$page[$nextpage]['bypass']];
-
-       if (count ($moreArgs) > 0)
-               foreach ($moreArgs as $arg => $value)
-                       if (is_array ($value))
-                               foreach ($value as $v)
-                                       $url .= '&' . urlencode ($arg . '[]') . '=' . urlencode ($v);
-                       elseif ($arg != 'module')
-                               $url .= '&' . urlencode ($arg) . '=' . urlencode ($value);
-
-       if (! empty ($log))
-       {
-               if (empty ($_SESSION['log']))
-                       $_SESSION['log'] = $log;
-               elseif ($_SESSION['log']['v'] == $log['v'])
-                       $_SESSION['log'] = array_merge_recursive($log, $_SESSION['log']);
-               elseif ($log['v'] == 1 and $_SESSION['log']['v'] == 2)
-                       foreach ($log['m'] as $msg)
-                               setMessage ($msg['code'], $msg['message'], FALSE);
-               elseif ($log['v'] == 2 and $_SESSION['log']['v'] == 1)
-               {
-                       foreach ($_SESSION['log'] as $msg)
-                       {
-                               if (! is_array ($msg))
-                                       continue;
-                               $new_v2_item = array('c' => '', 'a' => array());
-                               switch ($msg['code'])
-                               {
-                                       case 'error':
-                                               $new_v2_item['c'] = 100;
-                                               break;
-                                       case 'success':
-                                               $new_v2_item['c'] = 0;
-                                               break;
-                                       case 'warning':
-                                               $new_v2_item['c'] = 200;
-                                               break;
-                                       default:
-                                               $new_v2_item['c'] = 300;
-                               }
-                               $new_v2_item['a'][] = $msg['message'];
-                               $log['m'][] = $new_v2_item;
-                       }
-                       $_SESSION['log'] = $log; // substitute v1 log structure with merged v2
-               }
-       }
-       return $url;
-}
-
-// This function is DEPRECATED. Show messages through showError and showSuccess,
-// you dont need to return anything from an ophandler to redirect user back to the page containing submit form
-function buildRedirectURL ($callfunc, $status, $log_args = array(), $nextpage = NULL, $nexttab = NULL, $url_args = array())
-{
-       global $pageno, $tabno, $msgcode;
-       if ($nextpage === NULL)
-               $nextpage = $pageno;
-       if ($nexttab === NULL)
-               $nexttab = $tabno;
-       return buildWideRedirectURL (oneLiner ($msgcode[$callfunc][$status], $log_args), $nextpage, $nexttab, $url_args);
-}
-
 $msgcode['addPortForwarding']['OK'] = 48;
-$msgcode['addPortForwarding']['ERR'] = 100;
 function addPortForwarding ()
 {
        assertUIntArg ('object_id');
@@ -514,7 +464,7 @@ function addPortForwarding ()
        if (!strlen ($remoteport))
                $remoteport = $_REQUEST['localport'];
 
-       $error = newPortForwarding
+       newPortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -525,14 +475,10 @@ function addPortForwarding ()
                $_REQUEST['description']
        );
 
-       if ($error == '')
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($error));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delPortForwarding']['OK'] = 49;
-$msgcode['delPortForwarding']['ERR'] = 111;
 function delPortForwarding ()
 {
        assertUIntArg ('object_id');
@@ -542,7 +488,7 @@ function delPortForwarding ()
        assertUIntArg ('remoteport');
        assertStringArg ('proto');
 
-       $result = deletePortForwarding
+       deletePortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -551,11 +497,10 @@ function delPortForwarding ()
                $_REQUEST['remoteport'],
                $_REQUEST['proto']
        );
-       buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updPortForwarding']['OK'] = 51;
-$msgcode['updPortForwarding']['ERR'] = 109;
 function updPortForwarding ()
 {
        assertUIntArg ('object_id');
@@ -566,7 +511,7 @@ function updPortForwarding ()
        assertStringArg ('proto');
        assertStringArg ('description');
 
-       $result = updatePortForwarding
+       updatePortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -576,7 +521,7 @@ function updPortForwarding ()
                $_REQUEST['proto'],
                $_REQUEST['description']
        );
-       buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addPortForObject']['OK'] = 48;
@@ -593,7 +538,7 @@ function addPortForObject ()
                trim ($_REQUEST['port_label']),
                trim ($_REQUEST['port_l2address'])
        );
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($_REQUEST['port_name']));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['port_name']));
 }
 
 $msgcode['editPortForObject']['OK'] = 6;
@@ -606,11 +551,12 @@ function editPortForObject ()
        genericAssertion ('l2address', 'l2address0');
        genericAssertion ('name', 'string');
        commitUpdatePort ($sic['object_id'], $sic['port_id'], $sic['name'], $sic['port_type_id'], $sic['label'], $sic['l2address'], $sic['reservation_comment']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($_REQUEST['name']));
+       if (array_key_exists ('cable', $_REQUEST))
+               commitUpdatePortLink ($sic['port_id'], $sic['cable']);
+       return showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
 }
 
 $msgcode['linkPortForObject']['OK'] = 8;
-$msgcode['linkPortForObject']['ERR'] = 100;
 function linkPortForObject ()
 {
        assertUIntArg ('port_id');
@@ -618,12 +564,9 @@ function linkPortForObject ()
        assertStringArg ('cable', TRUE);
 
        // FIXME: ensure, that at least one of these ports belongs to the current object
-       $error = linkPorts ($_REQUEST['port_id'], $_REQUEST['remote_port_id'], $_REQUEST['cable']);
-       if ($error != '')
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($error));
-       global $sic;
-       $port_info = getPortInfo ($sic['port_id']);
-       return buildRedirectURL
+       linkPorts ($_REQUEST['port_id'], $_REQUEST['remote_port_id'], $_REQUEST['cable']);
+       $port_info = getPortInfo ($_REQUEST['port_id']);
+       return showFuncMessage
        (
                __FUNCTION__,
                'OK',
@@ -636,7 +579,6 @@ function linkPortForObject ()
 }
 
 $msgcode['addMultiPorts']['OK'] = 10;
-$msgcode['addMultiPorts']['ERR'] = 123;
 function addMultiPorts ()
 {
        assertStringArg ('format');
@@ -718,7 +660,7 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
                                );
                                break;
                        default:
-                               return buildRedirectURL (__FUNCTION__, 'ERR');
+                               throw new InvalidRequestArgException ('format', $format);
                                break;
                }
        }
@@ -738,7 +680,7 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
                        $updated_count++;
                }
        }
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($added_count, $updated_count, $error_count));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($added_count, $updated_count, $error_count));
 }
 
 $msgcode['addBulkPorts']['OK'] = 82;
@@ -765,11 +707,10 @@ function addBulkPorts ()
                commitAddPort ($object_id, @sprintf($port_name,$c), $port_type_id, @sprintf($port_label,$c), '');
                $added_count++;
        }
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($added_count, $error_count));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($added_count, $error_count));
 }
 
 $msgcode['updIPv4Allocation']['OK'] = 51;
-$msgcode['updIPv4Allocation']['ERR'] = 109;
 function updIPv4Allocation ()
 {
        assertIPv4Arg ('ip');
@@ -777,12 +718,11 @@ function updIPv4Allocation ()
        assertStringArg ('bond_name', TRUE);
        genericAssertion ('bond_type', 'enum/inet4alloc');
 
-       $result = updateBond ($_REQUEST['ip'], $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       return buildRedirectURL (__FUNCTION__, $result === FALSE ? 'ERR' : 'OK');
+       updateBond ($_REQUEST['ip'], $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updIPv6Allocation']['OK'] = 51;
-$msgcode['updIv6PAllocation']['ERR'] = 109;
 function updIPv6Allocation ()
 {
        $ipv6 = assertIPv6Arg ('ip');
@@ -790,34 +730,31 @@ function updIPv6Allocation ()
        assertStringArg ('bond_name', TRUE);
        genericAssertion ('bond_type', 'enum/inet6alloc');
 
-       $result = updateIPv6Bond ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       return buildRedirectURL (__FUNCTION__, $result === FALSE ? 'ERR' : 'OK');
+       updateIPv6Bond ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delIPv4Allocation']['OK'] = 49;
-$msgcode['delIPv4Allocation']['ERR'] = 111;
 function delIPv4Allocation ()
 {
        assertIPv4Arg ('ip');
        assertUIntArg ('object_id');
 
-       $result = unbindIpFromObject ($_REQUEST['ip'], $_REQUEST['object_id']);
-       return buildRedirectURL (__FUNCTION__, $result === FALSE ? 'ERR' : 'OK');
+       unbindIpFromObject ($_REQUEST['ip'], $_REQUEST['object_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delIPv6Allocation']['OK'] = 49;
-$msgcode['delIPv6Allocation']['ERR'] = 111;
 function delIPv6Allocation ()
 {
        assertUIntArg ('object_id');
        $ipv6 = assertIPv6Arg ('ip');
-       $result = unbindIPv6FromObject ($ipv6, $_REQUEST['object_id']);
-       return buildRedirectURL (__FUNCTION__, $result === FALSE ? 'ERR' : 'OK');
+       unbindIPv6FromObject ($ipv6, $_REQUEST['object_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addIPv4Allocation']['OK'] = 48;
 $msgcode['addIPv4Allocation']['ERR1'] = 170;
-$msgcode['addIPv4Allocation']['ERR2'] = 100;
 function addIPv4Allocation ()
 {
        assertIPv4Arg ('ip');
@@ -828,10 +765,9 @@ function addIPv4Allocation ()
        // Strip masklen.
        $ip = preg_replace ('@/[[:digit:]]+$@', '', $_REQUEST['ip']);
        if  (getConfigVar ('IPV4_JAYWALK') != 'yes' and NULL === getIPv4AddressNetworkId ($ip))
-               return buildRedirectURL (__FUNCTION__, 'ERR1', array ($ip));
+               return showFuncMessage (__FUNCTION__, 'ERR1', array ($ip));
        
-       if (FALSE === bindIpToObject ($ip, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']))
-               return buildRedirectURL (__FUNCTION__, 'ERR2', array ($error));
+       bindIpToObject ($ip, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
        $address = getIPv4Address ($ip);
        if ($address['reserved'] == 'yes' or strlen ($address['name']))
        {
@@ -842,12 +778,11 @@ function addIPv4Allocation ()
                        $address['name'] = '';
                updateAddress ($ip, $address['name'], $address['reserved']);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addIPv6Allocation']['OK'] = 48;
 $msgcode['addIPv6Allocation']['ERR1'] = 170;
-$msgcode['addIPv6Allocation']['ERR2'] = 100;
 function addIPv6Allocation ()
 {
        assertUIntArg ('object_id');
@@ -860,10 +795,9 @@ function addIPv6Allocation ()
                throw new InvalidRequestArgException('ip', $_REQUEST['ip'], 'parameter is not a valid ipv6 address');
 
        if  (getConfigVar ('IPV4_JAYWALK') != 'yes' and NULL === getIPv6AddressNetworkId ($ipv6))
-               return buildRedirectURL (__FUNCTION__, 'ERR1', array ($ip));
+               return showFuncMessage (__FUNCTION__, 'ERR1', array ($ip));
 
-       if (FALSE === bindIPv6ToObject ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']))
-               return buildRedirectURL (__FUNCTION__, 'ERR2', array ($error));
+       bindIPv6ToObject ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
        $address = getIPv6Address ($ipv6);
        if ($address['reserved'] == 'yes' or strlen ($address['name']))
        {
@@ -874,52 +808,82 @@ function addIPv6Allocation ()
                        $address['name'] = '';
                updateAddress ($ipv6, $address['name'], $address['reserved']);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
-$msgcode['addIPv4Prefix']['OK'] = 48;
 function addIPv4Prefix ()
 {
        assertStringArg ('range');
        assertStringArg ('name', TRUE);
 
-       $is_bcast = isset ($_REQUEST['is_bcast']) ? $_REQUEST['is_bcast'] : 'off';
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
+       $is_connected = isset ($_REQUEST['is_connected']) ? $_REQUEST['is_connected'] : 'off';
        global $sic;
-       createIPv4Prefix ($_REQUEST['range'], $sic['name'], $is_bcast == 'on', $taglist);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       $vlan_ck = empty ($sic['vlan_ck']) ? NULL : $sic['vlan_ck'];
+       $net_id = createIPv4Prefix ($_REQUEST['range'], $sic['name'], $is_connected == 'on', $taglist, $vlan_ck);
+       showSuccess
+       (
+               'IP network <a href="' .
+               makeHref (array ('page' => 'ipv4net', 'tab' => 'default', 'id' => $net_id)) .
+               '">' . $_REQUEST['range'] . '</a> has been created'
+       );
 }
 
-$msgcode['addIPv6Prefix']['OK'] = 48;
 function addIPv6Prefix ()
 {
        assertStringArg ('range');
        assertStringArg ('name', TRUE);
 
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
+       $is_connected = isset ($_REQUEST['is_connected']) ? ($_REQUEST['is_connected'] == 'on') : FALSE;
        global $sic;
-       createIPv6Prefix ($_REQUEST['range'], $sic['name'], $taglist);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       $vlan_ck = empty ($sic['vlan_ck']) ? NULL : $sic['vlan_ck'];
+       $net_id = createIPv6Prefix ($_REQUEST['range'], $sic['name'], $is_connected, $taglist, $vlan_ck);
+       showSuccess
+       (
+               'IP network <a href="' .
+               makeHref (array ('page' => 'ipv6net', 'tab' => 'default', 'id' => $net_id)) .
+               '">' . $_REQUEST['range'] . '</a> has been created'
+       );
 }
 
 $msgcode['delIPv4Prefix']['OK'] = 49;
 function delIPv4Prefix ()
 {
        assertUIntArg ('id');
+       $netinfo = spotEntity ('ipv4net', $_REQUEST['id']);
+       loadIPv4AddrList ($netinfo);
+       if (! isIPNetworkEmpty ($netinfo))
+               return showError ("There are allocations within prefix, delete forbidden");
+       if (array_key_exists ($netinfo['db_first'], $netinfo['addrlist']))
+               updateAddress ($netinfo['addrlist'][$netinfo['db_first']]['ip'], '', 'no');
+       if (array_key_exists ($netinfo['db_last'], $netinfo['addrlist']))
+               updateAddress ($netinfo['addrlist'][$netinfo['db_last']]['ip'], '', 'no');
        destroyIPv4Prefix ($_REQUEST['id']);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       showFuncMessage (__FUNCTION__, 'OK');
+       global $pageno;
+       if ($pageno == 'ipv4net')
+               return buildRedirectURL ('index', 'default');
 }
 
 $msgcode['delIPv6Prefix']['OK'] = 49;
 function delIPv6Prefix ()
 {
        assertUIntArg ('id');
+       $netinfo = spotEntity ('ipv6net', $_REQUEST['id']);
+       loadIPv6AddrList ($netinfo);
+       if (! isIPNetworkEmpty ($netinfo))
+               return showError ("There are allocations within prefix, delete forbidden");
+       if (array_key_exists ($netinfo['db_first']->getBin(), $netinfo['addrlist']))
+               updateAddress ($netinfo['db_first'], '', 'no');
        destroyIPv6Prefix ($_REQUEST['id']);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       showFuncMessage (__FUNCTION__, 'OK');
+       global $pageno;
+       if ($pageno == 'ipv6net')
+               return buildRedirectURL ('index', 'default');
 }
 
 $msgcode['editAddress']['OK'] = 51;
-$msgcode['editAddress']['ERR'] = 100;
 function editAddress ()
 {
        assertStringArg ('name', TRUE);
@@ -928,15 +892,11 @@ function editAddress ()
                $reserved = $_REQUEST['reserved'];
        else
                $reserved = 'off';
-       $error = updateAddress ($_REQUEST['ip'], $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
-       if ($error != '')
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($error));
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       updateAddress ($_REQUEST['ip'], $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['editv6Address']['OK'] = 51;
-$msgcode['editv6Address']['ERR'] = 100;
 function editv6Address ()
 {
        $ipv6 = assertIPArg ('ip');
@@ -946,15 +906,11 @@ function editv6Address ()
                $reserved = $_REQUEST['reserved'];
        else
                $reserved = 'off';
-       $error = updateAddress ($ipv6, $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
-       if ($error != '')
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($error));
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       updateAddress ($ipv6, $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['createUser']['OK'] = 5;
-$msgcode['createUser']['ERR'] = 102;
 function createUser ()
 {
        assertStringArg ('username');
@@ -962,16 +918,13 @@ function createUser ()
        assertStringArg ('password');
        $username = $_REQUEST['username'];
        $password = sha1 ($_REQUEST['password']);
-       $result = commitCreateUserAccount ($username, $_REQUEST['realname'], $password);
-       if ($result != TRUE)
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($username));
+       commitCreateUserAccount ($username, $_REQUEST['realname'], $password);
        if (isset ($_REQUEST['taglist']))
                produceTagsForLastRecord ('user', $_REQUEST['taglist']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($username));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($username));
 }
 
 $msgcode['updateUser']['OK'] = 6;
-$msgcode['updateUser']['ERR2'] = 104;
 function updateUser ()
 {
        genericAssertion ('user_id', 'uint');
@@ -984,16 +937,14 @@ function updateUser ()
        // Update user password only if provided password is not the same as current password hash.
        if ($new_password != $userinfo['user_password_hash'])
                $new_password = sha1 ($new_password);
-       $result = commitUpdateUserAccount ($_REQUEST['user_id'], $username, $_REQUEST['realname'], $new_password);
-       if ($result !== FALSE)
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($username));
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR2', array ($username));
+       commitUpdateUserAccount ($_REQUEST['user_id'], $username, $_REQUEST['realname'], $new_password);
+       return showFuncMessage (__FUNCTION__, 'OK', array ($username));
 }
 
 $msgcode['updateDictionary']['OK'] = 51;
 function updateDictionary ()
 {
+       global $sic;
        assertUIntArg ('dict_key');
        assertStringArg ('dict_value');
        // this request must be built with chapter_no
@@ -1003,11 +954,11 @@ function updateDictionary ()
                array ('dict_value' => $sic['dict_value']),
                array
                (
-                       'chapter_id' => $sic['chapter_no'],
+                       'chapter_id' => getBypassValue(),
                        'dict_key' => $sic['dict_key'],
                )
        );
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateChapter']['OK'] = 51;
@@ -1015,36 +966,33 @@ function updateChapter ()
 {
        assertUIntArg ('chapter_no');
        assertStringArg ('chapter_name');
+       global $sic;
        usePreparedUpdateBlade
        (
                'Chapter',
                array
                (
-                       'name' => $chapter_name,
+                       'name' => $sic['chapter_name'],
                ),
                array
                (
-                       'id' => $chapter_no,
+                       'id' => $sic['chapter_no'],
                        'sticky' => 'no', // note this constant, it protects system chapters
                )
        );
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delChapter']['OK'] = 49;
-$msgcode['delChapter']['ERR'] = 111;
 function delChapter ()
 {
        assertUIntArg ('chapter_no');
-       if (commitDeleteChapter ($_REQUEST['chapter_no']))
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR');
+       commitDeleteChapter ($_REQUEST['chapter_no']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['supplementAttrMap']['OK'] = 48;
 $msgcode['supplementAttrMap']['ERR1'] = 154;
-$msgcode['supplementAttrMap']['ERR2'] = 110;
 function supplementAttrMap ()
 {
        assertUIntArg ('attr_id');
@@ -1060,14 +1008,12 @@ function supplementAttrMap ()
                }
                catch (InvalidRequestArgException $e)
                {
-                       return buildRedirectURL (__FUNCTION__, 'ERR1', array ('chapter not selected'));
+                       return showFuncMessage (__FUNCTION__, 'ERR1', array ('chapter not selected'));
                }
                $chapter_id = $_REQUEST['chapter_no'];
        }
-       if (commitSupplementAttrMap ($_REQUEST['attr_id'], $_REQUEST['objtype_id'], $chapter_id) !== FALSE)
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
+       commitSupplementAttrMap ($_REQUEST['attr_id'], $_REQUEST['objtype_id'], $chapter_id);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['clearSticker']['OK'] = 49;
@@ -1075,7 +1021,13 @@ function clearSticker ()
 {
        global $sic;
        assertUIntArg ('attr_id');
-       commitUpdateAttrValue (getBypassValue(), $sic['attr_id']);
+       if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $sic['attr_id']))))
+               commitUpdateAttrValue (getBypassValue(), $sic['attr_id']);
+       else
+       {
+               $oldvalues = getAttrValues (getBypassValue());
+               showError ('Permission denied, "' . $oldvalues[$sic['attr_id']]['name'] . '" left unchanged');
+       }
 }
 
 $msgcode['updateObjectAllocation']['OK'] = 63;
@@ -1090,9 +1042,17 @@ function updateObjectAllocation ()
                unset($_POST['page']);
                unset($_POST['tab']);
                unset($_POST['op']);
-               return buildWideRedirectURL (array(), NULL, NULL, array_merge ($_GET, $_POST));
+               return buildRedirectURL (NULL, NULL, $_REQUEST);
        }
        $object_id = getBypassValue();
+       $changecnt = 0;
+       // Get a list of all of this object's parents,
+       // then trim the list to only include parents which are racks
+       $objectParents = getEntityRelatives('parents', 'object', $object_id);
+       $parentRacks = array();
+       foreach ($objectParents as $parentData)
+               if ($parentData['entity_type'] == 'rack')
+                       $parentRacks[] = $parentData['entity_id'];
        $workingRacksData = array();
        foreach ($_REQUEST['rackmulti'] as $cand_id)
        {
@@ -1102,19 +1062,27 @@ function updateObjectAllocation ()
                        amplifyCell ($rackData);
                        $workingRacksData[$cand_id] = $rackData;
                }
+               // It's zero-U mounted to this rack on the form, but not in the DB.  Mount it.
+               if (isset($_REQUEST["zerou_${cand_id}"]) && !in_array($cand_id, $parentRacks))
+               {
+                       $changecnt++;
+                       commitLinkEntities ('rack', $cand_id, 'object', $object_id);
+               }
+               // It's not zero-U mounted to this rack on the form, but it is in the DB.  Unmount it.
+               if (!isset($_REQUEST["zerou_${cand_id}"]) && in_array($cand_id, $parentRacks))
+               {
+                       $changecnt++;
+                       commitUnlinkEntities ('rack', $cand_id, 'object', $object_id);
+               }
        }
 
        foreach ($workingRacksData as &$rd)
                applyObjectMountMask ($rd, $object_id);
 
        $oldMolecule = getMoleculeForObject ($object_id);
-       $changecnt = 0;
-       $log = array();
        foreach ($workingRacksData as $rack_id => $rackData)
        {
-               $logrecord = processGridForm ($rackData, 'F', 'T', $object_id);
-               $log[] = $logrecord;
-               if ($logrecord['code'] == 300)
+               if (! processGridForm ($rackData, 'F', 'T', $object_id))
                        continue;
                $changecnt++;
                // Reload our working copy after form processing.
@@ -1123,24 +1091,24 @@ function updateObjectAllocation ()
                applyObjectMountMask ($rackData, $object_id);
                $workingRacksData[$rack_id] = $rackData;
        }
-       if (!$changecnt)
-               return buildRedirectURL (__FUNCTION__, 'OK', $changecnt);
-       // Log a record.
-       $newMolecule = getMoleculeForObject ($object_id);
-       usePreparedInsertBlade
-       (
-               'MountOperation', 
-               array
+       if ($changecnt)
+       {
+               // Log a record.
+               $newMolecule = getMoleculeForObject ($object_id);
+               usePreparedInsertBlade
                (
-                       'object_id' => $object_id,
-                       'old_molecule_id' => count ($oldMolecule) ? createMolecule ($oldMolecule) : NULL,
-                       'new_molecule_id' => count ($newMolecule) ? createMolecule ($newMolecule) : NULL,
-                       'user_name' => $remote_username,
-                       'comment' => empty ($sic['comment']) ? NULL : $sic['comment'],
-               )
-       );
-       $log[] = array ('code' => 200, 'message' => 'history logged');
-       return buildWideRedirectURL ($log);
+                       'MountOperation', 
+                       array
+                       (
+                               'object_id' => $object_id,
+                               'old_molecule_id' => count ($oldMolecule) ? createMolecule ($oldMolecule) : NULL,
+                               'new_molecule_id' => count ($newMolecule) ? createMolecule ($newMolecule) : NULL,
+                               'user_name' => $remote_username,
+                               'comment' => empty ($sic['comment']) ? NULL : $sic['comment'],
+                       )
+               );
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($changecnt));
 }
 
 $msgcode['updateObject']['OK'] = 51;
@@ -1183,7 +1151,10 @@ function updateObject ()
                # type is a dictionary and it is the "--NOT SET--" value of 0.
                if ($value == '' || ($oldvalues[$attr_id]['type'] == 'dict' && $value == 0))
                {
-                       commitUpdateAttrValue ($object_id, $attr_id);
+                       if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $attr_id))))
+                               commitUpdateAttrValue ($object_id, $attr_id);
+                       else
+                               showError ('Permission denied, "' . $oldvalues[$attr_id]['name'] . '" left unchanged');
                        continue;
                }
 
@@ -1204,7 +1175,10 @@ function updateObject ()
                }
                if ($value === $oldvalue) // ('' == 0), but ('' !== 0)
                        continue;
-               commitUpdateAttrValue ($object_id, $attr_id, $value);
+               if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $attr_id))))
+                       commitUpdateAttrValue ($object_id, $attr_id, $value);
+               else
+                       showError ('Permission denied, "' . $oldvalues[$attr_id]['name'] . '" left unchanged');
        }
        $object = spotEntity ('object', $object_id);
        if ($sic['object_type_id'] != $object['objtype_id'])
@@ -1215,23 +1189,19 @@ function updateObject ()
        }
        // Invalidate thumb cache of all racks objects could occupy.
        foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
-               usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $rack_id));
+               usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
        $dbxlink->commit();
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 function addMultipleObjects()
 {
-       $log = emptyLog();
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
        $max = getConfigVar ('MASSCOUNT');
        for ($i = 0; $i < $max; $i++)
        {
                if (!isset ($_REQUEST["${i}_object_type_id"]))
-               {
-                       $log = mergeLogs ($log, oneLiner (184, array ($i + 1)));
-                       break;
-               }
+                       return showError ('Submitted form is invalid at line ' . ($i + 1));
 
                // set to empty values for virtual objects
                if (isset ($_REQUEST['virtual_objects']))
@@ -1249,34 +1219,36 @@ function addMultipleObjects()
                // It's better to skip silently, than to print a notice.
                if ($_REQUEST["${i}_object_type_id"] == 0)
                        continue;
-               if (($object_id = commitAddObject
-               (
-                       $name,
-                       $_REQUEST["${i}_object_label"],
-                       $_REQUEST["${i}_object_type_id"],
-                       $_REQUEST["${i}_object_asset_no"],
-                       $taglist
-               )) !== FALSE){
+               try
+               {
+                       $object_id = commitAddObject
+                       (
+                               $name,
+                               $_REQUEST["${i}_object_label"],
+                               $_REQUEST["${i}_object_type_id"],
+                               $_REQUEST["${i}_object_asset_no"],
+                               $taglist
+                       );
                        $info = spotEntity ('object', $object_id);
-                       // FIXME: employ amplifyCell() instead of calling loader functions directly
                        amplifyCell ($info);
-                       $log = mergeLogs ($log, oneLiner (5, array ('<a href="' . makeHref (array ('page' => 'object', 'tab' => 'default', 'object_id' => $object_id)) . '">' . $info['dname'] . '</a>')));
-               }else{
-                       $log = mergeLogs ($log, oneLiner (147, array ($name)));
+                       showSuccess ("added object " . formatPortLink ($info['id'], $info['dname'], NULL, NULL));
+               }
+               catch (RTDatabaseError $e)
+               {
+                       showError ("Error creating object '$name': " . $e->getMessage());
+                       continue;
                }
        }
-       return buildWideRedirectURL ($log);
 }
 
 function addLotOfObjects()
 {
-       $log = emptyLog();
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
        assertUIntArg ('global_type_id', TRUE);
        assertStringArg ('namelist', TRUE);
        $global_type_id = $_REQUEST['global_type_id'];
        if ($global_type_id == 0 or !strlen ($_REQUEST['namelist']))
-               $log = mergeLogs ($log, oneLiner (186));
+               return showError ('Incomplete form has been ignored. Cheers.');
        else
        {
                // The name extractor below was stolen from ophandlers.php:addMultiPorts()
@@ -1292,19 +1264,22 @@ function addLotOfObjects()
                                $names2[] = rtrim ($parts[0]);
                }
                foreach ($names2 as $name)
-                       if (($object_id = commitAddObject ($name, '', $global_type_id, '', $taglist)) !== FALSE)
+                       try
                        {
+                               $object_id = commitAddObject ($name, NULL, $global_type_id, '', $taglist);
                                $info = spotEntity ('object', $object_id);
                                amplifyCell ($info);
-                               $log = mergeLogs ($log, oneLiner (5, array ('<a href="' . makeHref (array ('page' => 'object', 'tab' => 'default', 'object_id' => $object_id)) . '">' . $info['dname'] . '</a>')));
+                               showSuccess ("added object " . formatPortLink ($info['id'], $info['dname'], NULL, NULL));
+                       }
+                       catch (RTDatabaseError $e)
+                       {
+                               showError ("Error creating object '$name': " . $e->getMessage());
+                               continue;
                        }
-                       else
-                               $log = mergeLogs ($log, oneLiner (147, array ($name)));
        }
-       return buildWideRedirectURL ($log);
 }
 
-$msgcode['deleteObject']['OK'] = 6;
+$msgcode['deleteObject']['OK'] = 7;
 function deleteObject ()
 {
        assertUIntArg ('object_id');
@@ -1313,8 +1288,8 @@ function deleteObject ()
        $racklist = getResidentRacksData ($_REQUEST['object_id'], FALSE);
        commitDeleteObject ($_REQUEST['object_id']);
        foreach ($racklist as $rack_id)
-               usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $rack_id));
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($oinfo['dname']));
+               usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($oinfo['dname']));
 }
 
 $msgcode['resetObject']['OK'] = 57;
@@ -1323,8 +1298,8 @@ function resetObject ()
        $racklist = getResidentRacksData (getBypassValue(), FALSE);
        commitResetObject (getBypassValue());
        foreach ($racklist as $rack_id)
-               usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $rack_id));
-       return buildRedirectURL (__FUNCTION__, 'OK');
+               usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['useupPort']['OK'] = 49;
@@ -1332,7 +1307,7 @@ function useupPort ()
 {
        assertUIntArg ('port_id');
        commitUpdatePortComment ($_REQUEST['port_id'], '');
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateUI']['OK'] = 51;
@@ -1353,7 +1328,7 @@ function updateUI ()
                // any exceptions will be handled by process.php
                setConfigVar ($varname, $varvalue, TRUE);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['saveMyPreferences']['OK'] = 51;
@@ -1373,7 +1348,7 @@ function saveMyPreferences ()
                        continue;
                setUserConfigVar ($varname, $varvalue);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['resetMyPreference']['OK'] = 51;
@@ -1381,7 +1356,7 @@ function resetMyPreference ()
 {
        assertStringArg ("varname");
        resetUserConfigVar ($_REQUEST["varname"]);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['resetUIConfig']['OK'] = 57;
@@ -1450,7 +1425,6 @@ function resetUIConfig()
        setConfigVar ('8021Q_INSTANT_DEPLOY', 'no');
        setConfigVar ('CDP_RUNNERS_LISTSRC', '');
        setConfigVar ('LLDP_RUNNERS_LISTSRC', '');
-       setConfigVar ('HNDP_RUNNERS_LISTSRC', '');
        setConfigVar ('SHRINK_TAG_TREE_ON_CLICK', 'yes');
        setConfigVar ('MAX_UNFILTERED_ENTITIES', '0');
        setConfigVar ('SYNCDOMAIN_MAX_PROCESSES', '0');
@@ -1458,39 +1432,45 @@ function resetUIConfig()
        setConfigVar ('FILTER_RACKLIST_BY_TAGS', 'yes');
        setConfigVar ('SSH_OBJS_LISTSRC', 'none');
        setConfigVar ('TELNET_OBJS_LISTSRC', 'none');
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       setConfigVar ('SYNC_802Q_LISTSRC', '');
+       setConfigVar ('QUICK_LINK_PAGES', '');
+       setConfigVar ('CACTI_LISTSRC', 'false');
+       setConfigVar ('CACTI_URL', '');
+       setConfigVar ('CACTI_USERNAME', '');
+       setConfigVar ('CACTI_USERPASS', '');
+       setConfigVar ('VIRTUAL_OBJ_LISTSRC', '1504,1505,1506,1507');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addRealServer']['OK'] = 48;
-$msgcode['addRealServer']['ERR'] = 110;
 // Add single record.
 function addRealServer ()
 {
-       assertIPv4Arg ('remoteip');
+       global $sic;
+       assertIPv4Arg ('rsip');
        assertStringArg ('rsport', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (!addRStoRSPool (
+       assertStringArg ('comment', TRUE);
+       addRStoRSPool
+       (
                getBypassValue(),
-               $_REQUEST['remoteip'],
+               $_REQUEST['rsip'],
                $_REQUEST['rsport'],
-               getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'),
-               $_REQUEST['rsconfig']
-       ))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+               (isset ($_REQUEST['inservice']) and $_REQUEST['inservice'] == 'on') ? 'yes' : 'no',
+               $sic['rsconfig'],
+               $sic['comment']
+       );
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addRealServers']['OK'] = 37;
 $msgcode['addRealServers']['ERR1'] = 131;
-$msgcode['addRealServers']['ERR2'] = 127;
 // Parse textarea submitted and try adding a real server for each line.
 function addRealServers ()
 {
        assertStringArg ('format');
        assertStringArg ('rawtext');
-       $ngood = $nbad = 0;
-       $rsconfig = '';
+       $ngood = 0;
        // Keep in mind, that the text will have HTML entities (namely '>') escaped.
        foreach (explode ("\n", dos2unix ($_REQUEST['rawtext'])) as $line)
        {
@@ -1502,120 +1482,111 @@ function addRealServers ()
                        case 'ipvs_2': // address and port only
                                if (!preg_match ('/^  -&gt; ([0-9\.]+):([0-9]+) /', $line, $match))
                                        continue;
-                               if (addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), ''))
-                                       $ngood++;
-                               else
-                                       $nbad++;
+                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        case 'ipvs_3': // address, port and weight
                                if (!preg_match ('/^  -&gt; ([0-9\.]+):([0-9]+) +[a-zA-Z]+ +([0-9]+) /', $line, $match))
                                        continue;
-                               if (addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), 'weight ' . $match[3]))
-                                       $ngood++;
-                               else
-                                       $nbad++;
+                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), 'weight ' . $match[3]);
                                break;
                        case 'ssv_2': // IP address and port
                                if (!preg_match ('/^([0-9\.]+) ([0-9]+)$/', $line, $match))
                                        continue;
-                               if (addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), ''))
-                                       $ngood++;
-                               else
-                                       $nbad++;
+                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        case 'ssv_1': // IP address
                                if (!preg_match ('/^([0-9\.]+)$/', $line, $match))
                                        continue;
-                               if (addRStoRSPool (getBypassValue(), $match[1], 0, getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), ''))
-                                       $ngood++;
-                               else
-                                       $nbad++;
+                               addRStoRSPool (getBypassValue(), $match[1], 0, getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        default:
-                               return buildRedirectURL (__FUNCTION__, 'ERR1');
-                               break;
+                               return showFuncMessage (__FUNCTION__, 'ERR1');
                }
+               $ngood++;
        }
-       if ($nbad == 0 and $ngood > 0)
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($ngood));
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR2', array ($ngood, $nbad));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
 }
 
 $msgcode['addVService']['OK'] = 48;
 function addVService ()
 {
+       global $sic;
        assertIPv4Arg ('vip');
-       assertUIntArg ('vport');
        genericAssertion ('proto', 'enum/ipproto');
        assertStringArg ('name', TRUE);
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
+       if ($_REQUEST['proto'] == 'MARK')
+               $vport = NULL;
+       else
+       {
+               assertUIntArg ('vport');
+               $vport = $_REQUEST['vport'];
+       }
        usePreparedExecuteBlade
        (
                'INSERT INTO IPv4VS (vip, vport, proto, name, vsconfig, rsconfig) VALUES (INET_ATON(?), ?, ?, ?, ?, ?)',
                array
                (
                        $_REQUEST['vip'],
-                       $_REQUEST['vport'],
+                       $vport,
                        $_REQUEST['proto'],
                        !mb_strlen ($_REQUEST['name']) ? NULL : $_REQUEST['name'],
-                       !strlen ($_REQUEST['vsconfig']) ? NULL : $_REQUEST['vsconfig'],
-                       !strlen ($_REQUEST['rsconfig']) ? NULL : $_REQUEST['rsconfig'],
+                       !strlen ($sic['vsconfig']) ? NULL : $sic['vsconfig'],
+                       !strlen ($sic['rsconfig']) ? NULL : $sic['rsconfig'],
                )
        );
        produceTagsForLastRecord ('ipv4vs', isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array());
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['deleteVService']['OK'] = 49;
-$msgcode['deleteVService']['ERR'] = 111;
 function deleteVService ()
 {
        assertUIntArg ('vs_id');
-       if (!commitDeleteVS ($_REQUEST['vs_id']))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       commitDeleteVS ($_REQUEST['vs_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateSLBDefConfig']['OK'] = 43;
-$msgcode['updateSLBDefConfig']['ERR'] = 109;
 function updateSLBDefConfig ()
 {
-       $data = array(
-               'vs' => $_REQUEST['vsconfig'],
-               'rs' => $_REQUEST['rsconfig']
+       global $sic;
+       commitUpdateSLBDefConf
+       (
+               array
+               (
+                       'vs' => $sic['vsconfig'],
+                       'rs' => $sic['rsconfig'],
+               )
        );
-       if (!commitUpdateSLBDefConf ($data))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateRealServer']['OK'] = 51;
-$msgcode['updateRealServer']['ERR'] = 109;
 function updateRealServer ()
 {
+       global $sic;
        assertUIntArg ('rs_id');
        assertIPv4Arg ('rsip');
        assertStringArg ('rsport', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (FALSE === commitUpdateRS (
+       assertStringArg ('comment', TRUE);
+       commitUpdateRS (
                $_REQUEST['rs_id'],
                $_REQUEST['rsip'],
                $_REQUEST['rsport'],
-               $_REQUEST['rsconfig']
-       ))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+               (isset ($_REQUEST['inservice']) and $_REQUEST['inservice'] == 'on') ? 'yes' : 'no',
+               $sic['rsconfig'],
+               $sic['comment']
+       );
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateVService']['OK'] = 51;
-$msgcode['updateVService']['ERR'] = 109;
 function updateVService ()
 {
+       global $sic;
        assertUIntArg ('vs_id');
        assertIPv4Arg ('vip');
        assertUIntArg ('vport');
@@ -1623,99 +1594,63 @@ function updateVService ()
        assertStringArg ('name', TRUE);
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (FALSE === commitUpdateVS (
+       commitUpdateVS (
                $_REQUEST['vs_id'],
                $_REQUEST['vip'],
                $_REQUEST['vport'],
                $_REQUEST['proto'],
                $_REQUEST['name'],
-               $_REQUEST['vsconfig'],
-               $_REQUEST['rsconfig']
-       ))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+               $sic['vsconfig'],
+               $sic['rsconfig']
+       );
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addLoadBalancer']['OK'] = 48;
-$msgcode['addLoadBalancer']['ERR'] = 110;
 function addLoadBalancer ()
 {
+       global $sic;
        assertUIntArg ('pool_id');
        assertUIntArg ('object_id');
        assertUIntArg ('vs_id');
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (! empty($_REQUEST['prio']))
-               assertUIntArg('prio', TRUE);
+       assertStringArg ('prio', TRUE);
 
-       if (!addLBtoRSPool (
+       addLBtoRSPool (
                $_REQUEST['pool_id'],
                $_REQUEST['object_id'],
                $_REQUEST['vs_id'],
-               $_REQUEST['vsconfig'],
-               $_REQUEST['rsconfig'],
+               $sic['vsconfig'],
+               $sic['rsconfig'],
                $_REQUEST['prio']
-       ))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       );
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addRSPool']['OK'] = 48;
 function addRSPool ()
 {
+       global $sic;
        assertStringArg ('name');
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
        commitCreateRSPool
        (
                $_REQUEST['name'],
-               $_REQUEST['vsconfig'],
-               $_REQUEST['rsconfig'],
+               $sic['vsconfig'],
+               $sic['rsconfig'],
                isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array()
        );
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['deleteRSPool']['OK'] = 49;
-$msgcode['deleteRSPool']['ERR'] = 111;
 function deleteRSPool ()
 {
        assertUIntArg ('pool_id');
-       if (commitDeleteRSPool ($_REQUEST['pool_id']) === FALSE)
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK');
-}
-
-$msgcode['updateRSInService']['OK'] = 26;
-$msgcode['updateRSInService']['ERR'] = 141;
-function updateRSInService ()
-{
-       assertUIntArg ('rscount');
-       $orig = spotEntity ('ipv4rspool', getBypassValue());
-       amplifyCell ($orig);
-       $nbad = $ngood = 0;
-       for ($i = 1; $i <= $_REQUEST['rscount']; $i++)
-       {
-               $rs_id = $_REQUEST["rsid_${i}"];
-               if (isset ($_REQUEST["inservice_${i}"]) and $_REQUEST["inservice_${i}"] == 'on')
-                       $newval = 'yes';
-               else
-                       $newval = 'no';
-               if ($newval != $orig['rslist'][$rs_id]['inservice'])
-               {
-                       if (FALSE !== commitSetInService ($rs_id, $newval))
-                               $ngood++;
-                       else
-                               $nbad++;
-               }
-       }
-       if (!$nbad)
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($ngood));
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($nbad, $ngood));
+       commitDeleteRSPool ($_REQUEST['pool_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['importPTRData']['OK'] = 26;
@@ -1744,9 +1679,9 @@ function importPTRData ()
                        $nbad++;
        }
        if (!$nbad)
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($ngood));
+               return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
        else
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($nbad, $ngood));
+               return showFuncMessage (__FUNCTION__, 'ERR', array ($nbad, $ngood));
 }
 
 $msgcode['generateAutoPorts']['OK'] = 21;
@@ -1754,12 +1689,11 @@ function generateAutoPorts ()
 {
        $object = spotEntity ('object', getBypassValue());
        executeAutoPorts ($object['id'], $object['objtype_id']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array(), NULL, 'ports');
+       showFuncMessage (__FUNCTION__, 'OK');
+       return buildRedirectURL (NULL, 'ports');
 }
 
-$msgcode['saveEntityTags']['OK'] = 26;
-$msgcode['saveEntityTags']['ERR1'] = 143;
-// Filter out implicit tags before storing the new tag set.
+$msgcode['saveEntityTags']['OK'] = 43;
 function saveEntityTags ()
 {
        global $pageno, $etype_by_pageno;
@@ -1768,22 +1702,8 @@ function saveEntityTags ()
        $realm = $etype_by_pageno[$pageno];
        $entity_id = getBypassValue();
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
-       // Build a chain from the submitted data, minimize it,
-       // then wipe existing records and store the new set instead.
-       destroyTagsForEntity ($realm, $entity_id);
-       // TODO: these actions are very close to what rebuildTagChainForEntity() does,
-       // so why not use it?
-       $newchain = getExplicitTagsOnly (buildTagChainFromIds ($taglist));
-       $n_succeeds = $n_errors = 0;
-       foreach ($newchain as $taginfo)
-               if (addTagForEntity ($realm, $entity_id, $taginfo['id']))
-                       $n_succeeds++;
-               else
-                       $n_errors++;
-       if ($n_errors)
-               return buildRedirectURL (__FUNCTION__, 'ERR1', array ($n_succeeds, $n_errors));
-       else
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($n_succeeds));
+       rebuildTagChainForEntity ($realm, $entity_id, buildTagChainFromIds ($taglist), TRUE);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['rollTags']['OK'] = 67;
@@ -1793,7 +1713,7 @@ function rollTags ()
        assertStringArg ('sum', TRUE);
        assertUIntArg ('realsum');
        if ($_REQUEST['sum'] != $_REQUEST['realsum'])
-               return buildRedirectURL (__FUNCTION__, 'ERR');
+               return showFuncMessage (__FUNCTION__, 'ERR');
        // Even if the user requested an empty tag list, don't bail out, but process existing
        // tag chains with "zero" extra. This will make sure, that the stuff processed will
        // have its chains refined to "normal" form.
@@ -1811,37 +1731,33 @@ function rollTags ()
                        if (rebuildTagChainForEntity ('object', $object_id, $extrachain))
                                $n_ok++;
        }
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($n_ok));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($n_ok));
 }
 
 $msgcode['changeMyPassword']['OK'] = 51;
 $msgcode['changeMyPassword']['ERR1'] = 150;
 $msgcode['changeMyPassword']['ERR2'] = 151;
 $msgcode['changeMyPassword']['ERR3'] = 152;
-$msgcode['changeMyPassword']['ERR4'] = 153;
 function changeMyPassword ()
 {
        global $remote_username, $user_auth_src;
        if ($user_auth_src != 'database')
-               return buildRedirectURL (__FUNCTION__, 'ERR1');
+               return showFuncMessage (__FUNCTION__, 'ERR1');
        assertStringArg ('oldpassword');
        assertStringArg ('newpassword1');
        assertStringArg ('newpassword2');
        $remote_userid = getUserIDByUsername ($remote_username);
        $userinfo = spotEntity ('user', $remote_userid);
        if ($userinfo['user_password_hash'] != sha1 ($_REQUEST['oldpassword']))
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
+               return showFuncMessage (__FUNCTION__, 'ERR2');
        if ($_REQUEST['newpassword1'] != $_REQUEST['newpassword2'])
-               return buildRedirectURL (__FUNCTION__, 'ERR3');
-       if (FALSE !== commitUpdateUserAccount ($remote_userid, $userinfo['user_name'], $userinfo['user_realname'], sha1 ($_REQUEST['newpassword1'])))
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR4');
+               return showFuncMessage (__FUNCTION__, 'ERR3');
+       commitUpdateUserAccount ($remote_userid, $userinfo['user_name'], $userinfo['user_realname'], sha1 ($_REQUEST['newpassword1']));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['saveRackCode']['OK'] = 43;
 $msgcode['saveRackCode']['ERR1'] = 154;
-$msgcode['saveRackCode']['ERR2'] = 155;
 function saveRackCode ()
 {
        assertStringArg ('rackcode');
@@ -1849,13 +1765,10 @@ function saveRackCode ()
        $newcode = dos2unix ($_REQUEST['rackcode']);
        $parseTree = getRackCode ($newcode);
        if ($parseTree['result'] != 'ACK')
-               return buildRedirectURL (__FUNCTION__, 'ERR1', array ($parseTree['load']));
-       if (FALSE !== saveScript ('RackCode', $newcode))
-       {
-               saveScript ('RackCodeCache', base64_encode (serialize ($parseTree)));
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       }
-       return buildRedirectURL (__FUNCTION__, 'ERR2');
+               return showFuncMessage (__FUNCTION__, 'ERR1', array ($parseTree['load']));
+       saveScript ('RackCode', $newcode);
+       saveScript ('RackCodeCache', base64_encode (serialize ($parseTree)));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['setPortVLAN']['ERR'] = 164;
@@ -1871,7 +1784,7 @@ function setPortVLAN ()
        }
        catch (RTGatewayError $re)
        {
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($re->getMessage()));
+               return showFuncMessage (__FUNCTION__, 'ERR', array ($re->getMessage()));
        }
        list ($vlanlist, $portlist) = $data;
        // Here we just build up 1 set command for the gateway with all of the ports
@@ -1880,7 +1793,6 @@ function setPortVLAN ()
        // for each of the rest.
        $nports = $_REQUEST['portcount'];
        $prefix = 'set ';
-       $log = emptyLog();
        $setcmd = '';
        for ($i = 0; $i < $nports; $i++)
        {
@@ -1903,7 +1815,7 @@ function setPortVLAN ()
                        !permitted (NULL, NULL, NULL, array (array ('tag' => '$tovlan_' . $newvlanid), array ('tag' => '$vlan_' . $newvlanid)))
                )
                {
-                       $log = mergeLogs ($log, oneLiner (159, array ($portname, $oldvlanid, $newvlanid)));
+                       showOneLiner (159, array ($portname, $oldvlanid, $newvlanid));
                        continue;
                }
                $setcmd .= $prefix . $portname . '=' . $newvlanid;
@@ -1911,35 +1823,23 @@ function setPortVLAN ()
        }
        // Feed the gateway and interpret its (non)response.
        if ($setcmd == '')
-               $log = mergeLogs ($log, oneLiner (201));
+               showOneLiner (201);
        else
        {
                try
                {
-                       $log = mergeLogs ($log, setSwitchVLANs ($_REQUEST['object_id'], $setcmd));
+                       setSwitchVLANs ($_REQUEST['object_id'], $setcmd); // shows messages by itself
                }
                catch (RTGatewayError $e)
                {
-                       $log = mergeLogs ($log, oneLiner (164, $e->getMessage()));
+                       showFuncMessage (__FUNCTION__, 'ERR', array ($e->getMessage()));
                }
        }
-       return buildWideRedirectURL ($log);
 }
 
-$msgcode['submitSLBConfig']['OK'] = 66;
-$msgcode['submitSLBConfig']['ERR'] = 164;
 function submitSLBConfig ()
 {
-       $newconfig = buildLVSConfig (getBypassValue());
-       try
-       {
-               gwSendFileToObject (getBypassValue(), 'slbconfig', html_entity_decode ($newconfig, ENT_QUOTES, 'UTF-8'));
-       }
-       catch (RTGatewayError $e)
-       {
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
-       }
-       return buildRedirectURL (__FUNCTION__, 'OK', array ('slbconfig'));
+       showNotice ("You should redefine submitSLBConfig ophandler in your local extension to install SLB config");
 }
 
 $msgcode['addRack']['OK'] = 48;
@@ -1949,19 +1849,25 @@ function addRack ()
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
        if (isset ($_REQUEST['got_data']))
        {
-               assertStringArg ('rack_name');
-               assertUIntArg ('rack_height1');
-               assertStringArg ('rack_comment', TRUE);
-               commitAddRack ($_REQUEST['rack_name'], $_REQUEST['rack_height1'], $_REQUEST['row_id'], $_REQUEST['rack_comment'], $taglist);
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($_REQUEST['rack_name']));
+               assertStringArg ('name');
+               assertUIntArg ('height1');
+               assertStringArg ('asset_no', TRUE);
+               $rack_id = commitAddObject (NULL, $_REQUEST['name'], 1560, $_REQUEST['asset_no'], $taglist);
+
+               // Update the height
+               commitUpdateAttrValue ($rack_id, 27, $_REQUEST['height1']);
+
+               // Link it to the row
+               commitLinkEntities ('object', $_REQUEST['row_id'], 'object', $rack_id);
+
+               return showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
        }
        elseif (isset ($_REQUEST['got_mdata']))
        {
-               assertUIntArg ('rack_height2');
-               assertStringArg ('rack_names', TRUE);
-               $log = emptyLog();
+               assertUIntArg ('height2');
+               assertStringArg ('names', TRUE);
                // copy-and-paste from renderAddMultipleObjectsForm()
-               $names1 = explode ("\n", $_REQUEST['rack_names']);
+               $names1 = explode ("\n", $_REQUEST['names']);
                $names2 = array();
                foreach ($names1 as $line)
                {
@@ -1972,20 +1878,22 @@ function addRack ()
                        else
                                $names2[] = rtrim ($parts[0]);
                }
-               global $msgcode;
                foreach ($names2 as $cname)
                {
-                       commitAddRack ($cname, $_REQUEST['rack_height2'], $_REQUEST['row_id'], '', $taglist);
-                       $log['m'][] = array ('c' => $msgcode[__FUNCTION__]['OK'], 'a' => array ($cname));
+                       $rack_id = commitAddObject (NULL, $cname, 1560, NULL, $taglist);
+
+                       // Update the height
+                       commitUpdateAttrValue ($rack_id, 27, $_REQUEST['height2']);
+
+                       // Link it to the row
+                       commitLinkEntities ('object', $_REQUEST['row_id'], 'object', $rack_id);
                }
-               return buildWideRedirectURL ($log);
        }
        else
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
+               return showFuncMessage (__FUNCTION__, 'ERR2');
 }
 
 $msgcode['deleteRack']['OK'] = 6;
-$msgcode['deleteRack']['ERR'] = 100;
 $msgcode['deleteRack']['ERR1'] = 206;
 function deleteRack ()
 {
@@ -1993,27 +1901,66 @@ function deleteRack ()
        $rackData = spotEntity ('rack', $_REQUEST['rack_id']);
        amplifyCell ($rackData);
        if (count ($rackData['mountedObjects']))
-               return buildRedirectURL (__FUNCTION__, 'ERR1');
-       if (TRUE !== commitDeleteRack ($_REQUEST['rack_id']))
-               return buildRedirectURL (__FUNCTION__, 'ERR', array(), 'rackspace', 'default');
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($rackData['name']), 'rackspace', 'default');
+               return showFuncMessage (__FUNCTION__, 'ERR1');
+       commitDeleteObject ($_REQUEST['rack_id']);
+       showFuncMessage (__FUNCTION__, 'OK', array ($rackData['name']));
+       return buildRedirectURL ('rackspace', 'default');
 }
 
 $msgcode['updateRack']['OK'] = 6;
-$msgcode['updateRack']['ERR'] = 109;
 function updateRack ()
 {
-       assertUIntArg ('rack_row_id');
-       assertUIntArg ('rack_height');
-       assertStringArg ('rack_name');
-       assertStringArg ('rack_comment', TRUE);
+       assertUIntArg ('row_id');
+       assertStringArg ('name');
+       assertUIntArg ('height');
+       $has_problems = (isset ($_REQUEST['has_problems']) and $_REQUEST['has_problems'] == 'on') ? 'yes' : 'no';
+       assertStringArg ('asset_no', TRUE);
+       assertStringArg ('comment', TRUE);
 
        $rack_id = getBypassValue();
-       usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $rack_id));
-       if (TRUE === commitUpdateRack ($rack_id, $_REQUEST['rack_name'], $_REQUEST['rack_height'], $_REQUEST['rack_row_id'], $_REQUEST['rack_comment']))
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($_REQUEST['rack_name']));
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR');
+       usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
+       commitUpdateRack ($rack_id, $_REQUEST['row_id'], $_REQUEST['name'], $_REQUEST['height'], $has_problems, $_REQUEST['asset_no'], $_REQUEST['comment']);
+
+       // Update optional attributes
+       $oldvalues = getAttrValues ($rack_id);
+       $num_attrs = isset ($_REQUEST['num_attrs']) ? $_REQUEST['num_attrs'] : 0;
+       for ($i = 0; $i < $num_attrs; $i++)
+       {
+               assertUIntArg ("${i}_attr_id");
+               $attr_id = $_REQUEST["${i}_attr_id"];
+
+               // Skip the 'height' attribute as it's already handled by commitUpdateRack
+               if ($attr_id == 27)
+                       continue;
+
+               // Field is empty, delete attribute and move on. OR if the field type is a dictionary and it is the --NOT SET-- value of 0
+               if (!strlen ($_REQUEST["${i}_value"]) || ($oldvalues[$attr_id]['type']=='dict' && $_REQUEST["${i}_value"] == 0))
+               {
+                       commitUpdateAttrValue ($rack_id, $attr_id);
+                       continue;
+               }
+
+               // The value could be uint/float, but we don't know ATM. Let SQL
+               // server check this and complain.
+               assertStringArg ("${i}_value");
+               $value = $_REQUEST["${i}_value"];
+               switch ($oldvalues[$attr_id]['type'])
+               {
+                       case 'uint':
+                       case 'float':
+                       case 'string':
+                               $oldvalue = $oldvalues[$attr_id]['value'];
+                               break;
+                       case 'dict':
+                               $oldvalue = $oldvalues[$attr_id]['key'];
+                               break;
+                       default:
+               }
+               if ($value === $oldvalue) // ('' == 0), but ('' !== 0)
+                       continue;
+               commitUpdateAttrValue ($rack_id, $attr_id, $value);
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
 }
 
 function updateRackDesign ()
@@ -2022,8 +1969,10 @@ function updateRackDesign ()
        amplifyCell ($rackData);
        applyRackDesignMask($rackData);
        markupObjectProblems ($rackData);
-       $response = processGridForm ($rackData, 'A', 'F');
-       return buildWideRedirectURL (array($response));
+       if (processGridForm ($rackData, 'A', 'F'))
+               showSuccess ("Saved successfully");
+       else
+               showNotice ("Nothing saved");
 }
 
 function updateRackProblems ()
@@ -2032,19 +1981,24 @@ function updateRackProblems ()
        amplifyCell ($rackData);
        applyRackProblemMask($rackData);
        markupObjectProblems ($rackData);
-       $response = processGridForm ($rackData, 'F', 'U');
-       return buildWideRedirectURL (array($response));
+       if (processGridForm ($rackData, 'F', 'U'))
+               showSuccess ("Saved successfully");
+       else
+               showNotice ("Nothing saved");
 }
 
 function querySNMPData ()
 {
-       assertStringArg ('community', TRUE);
-
+       genericAssertion ('ver', 'uint');
        $snmpsetup = array ();
-       if ($_REQUEST['community'] != '')
-               $snmpsetup['community'] = $_REQUEST['community'];
-       else
+       switch ($_REQUEST['ver'])
        {
+       case 1:
+       case 2:
+               genericAssertion ('community', 'string');
+               $snmpsetup['community'] = $_REQUEST['community'];
+               break;
+       case 3:
                assertStringArg ('sec_name');
                assertStringArg ('sec_level');
                assertStringArg ('auth_protocol');
@@ -2058,19 +2012,22 @@ function querySNMPData ()
                $snmpsetup['auth_passphrase'] = $_REQUEST['auth_passphrase'];
                $snmpsetup['priv_protocol'] = $_REQUEST['priv_protocol'];
                $snmpsetup['priv_passphrase'] = $_REQUEST['priv_passphrase'];
+               break;
+       default:
+               throw new InvalidRequestArgException ('ver', $_REQUEST['ver']);
        }
-       return doSNMPmining (getBypassValue(), $snmpsetup);
+       $snmpsetup['version'] = $_REQUEST['ver'];
+       doSNMPmining (getBypassValue(), $snmpsetup); // shows message by itself
 }
 
 $msgcode['linkEntities']['OK'] = 51;
-$msgcode['linkEntities']['ERR2'] = 109;
 function linkEntities ()
 {
        assertStringArg ('parent_entity_type');
        assertUIntArg ('parent_entity_id');
        assertStringArg ('child_entity_type');
        assertUIntArg ('child_entity_id');
-       $result = usePreparedInsertBlade
+       usePreparedInsertBlade
        (
                'EntityLink',
                array
@@ -2081,17 +2038,15 @@ function linkEntities ()
                        'child_entity_id' => $_REQUEST['child_entity_id'],
                )
        );
-       if ($result === FALSE)
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['unlinkEntities']['OK'] = 49;
-$msgcode['unlinkEntities']['ERR'] = 111;
 function unlinkEntities ()
 {
        assertUIntArg ('link_id');
-       return buildRedirectURL (__FUNCTION__, commitUnlinkEntities ($_REQUEST['link_id']) === FALSE ? 'ERR' : 'OK');
+       commitUnlinkEntitiesByLinkID ($_REQUEST['link_id']);
+       return showFuncMessage (__FUNCTION__,  'OK');
 }
 
 $msgcode['addFileWithoutLink']['OK'] = 5;
@@ -2109,7 +2064,7 @@ function addFileWithoutLink ()
        commitAddFile ($_FILES['file']['name'], $_FILES['file']['type'], $fp, $sic['comment']);
        if (isset ($_REQUEST['taglist']))
                produceTagsForLastRecord ('file', $_REQUEST['taglist']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
 }
 
 $msgcode['addFileToEntity']['OK'] = 5;
@@ -2138,7 +2093,7 @@ function addFileToEntity ()
                        'entity_id' => getBypassValue(),
                )
        );
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
 }
 
 $msgcode['linkFileToEntity']['OK'] = 71;
@@ -2160,7 +2115,7 @@ function linkFileToEntity ()
                        'entity_id' => getBypassValue(),
                )
        );
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($fi['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($fi['name'])));
 }
 
 $msgcode['replaceFile']['OK'] = 7;
@@ -2173,18 +2128,18 @@ function replaceFile ()
        $shortInfo = spotEntity ('file', getBypassValue());
 
        if (FALSE === $fp = fopen ($_FILES['file']['tmp_name'], 'rb'))
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
+               return showFuncMessage (__FUNCTION__, 'ERR2');
        commitReplaceFile ($shortInfo['id'], $fp);
 
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
 }
 
 $msgcode['unlinkFile']['OK'] = 72;
-$msgcode['unlinkFile']['ERR'] = 111;
 function unlinkFile ()
 {
        assertUIntArg ('link_id');
-       return buildRedirectURL (__FUNCTION__, commitUnlinkFile ($_REQUEST['link_id']) === FALSE ? 'ERR' : 'OK');
+       commitUnlinkFile ($_REQUEST['link_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['deleteFile']['OK'] = 7;
@@ -2193,10 +2148,10 @@ function deleteFile ()
        assertUIntArg ('file_id');
        $shortInfo = spotEntity ('file', $_REQUEST['file_id']);
        commitDeleteFile ($_REQUEST['file_id']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
 }
 
-$msgcode['updateFileText']['OK'] = 7;
+$msgcode['updateFileText']['OK'] = 6;
 $msgcode['updateFileText']['ERR1'] = 179;
 $msgcode['updateFileText']['ERR2'] = 155;
 function updateFileText ()
@@ -2205,51 +2160,49 @@ function updateFileText ()
        assertStringArg ('file_text', TRUE); // it's Ok to save empty
        $shortInfo = spotEntity ('file', getBypassValue());
        if ($shortInfo['mtime'] != $_REQUEST['mtime_copy'])
-               return buildRedirectURL (__FUNCTION__, 'ERR1');
+               return showFuncMessage (__FUNCTION__, 'ERR1');
        global $sic;
        commitReplaceFile ($shortInfo['id'], $sic['file_text']);
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
 }
 
 $msgcode['addIIFOIFCompat']['OK'] = 48;
-$msgcode['addIIFOIFCompat']['ERR'] = 110;
 function addIIFOIFCompat ()
 {
        assertUIntArg ('iif_id');
        assertUIntArg ('oif_id');
-       if (commitSupplementPIC ($_REQUEST['iif_id'], $_REQUEST['oif_id']))
-               return buildRedirectURL (__FUNCTION__, 'OK');
-       return buildRedirectURL (__FUNCTION__, 'ERR');
+       commitSupplementPIC ($_REQUEST['iif_id'], $_REQUEST['oif_id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
-$msgcode['addIIFOIFCompatPack']['OK'] = 44;
+$msgcode['addIIFOIFCompatPack']['OK'] = 37;
 function addIIFOIFCompatPack ()
 {
        genericAssertion ('standard', 'enum/wdmstd');
        genericAssertion ('iif_id', 'iif');
        global $wdm_packs, $sic;
-       $ngood = $nbad = 0;
+       $ngood = 0;
        foreach ($wdm_packs[$sic['standard']]['oif_ids'] as $oif_id)
-               if (commitSupplementPIC ($sic['iif_id'], $oif_id))
-                       $ngood++;
-               else
-                       $nbad++;
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($nbad, $ngood));
+       {
+               commitSupplementPIC ($sic['iif_id'], $oif_id);
+               $ngood++;
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
 }
 
-$msgcode['delIIFOIFCompatPack']['OK'] = 44;
+$msgcode['delIIFOIFCompatPack']['OK'] = 38;
 function delIIFOIFCompatPack ()
 {
        genericAssertion ('standard', 'enum/wdmstd');
        genericAssertion ('iif_id', 'iif');
        global $wdm_packs, $sic;
-       $ngood = $nbad = 0;
+       $ngood = 0;
        foreach ($wdm_packs[$sic['standard']]['oif_ids'] as $oif_id)
-               if (usePreparedDeleteBlade ('PortInterfaceCompat', array ('iif_id' => $sic['iif_id'], 'oif_id' => $oif_id)))
-                       $ngood++;
-               else
-                       $nbad++;
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($nbad, $ngood));
+       {
+               usePreparedDeleteBlade ('PortInterfaceCompat', array ('iif_id' => $sic['iif_id'], 'oif_id' => $oif_id));
+               $ngood++;
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
 }
 
 $msgcode['addOIFCompatPack']['OK'] = 21;
@@ -2271,7 +2224,7 @@ function addOIFCompatPack ()
                $query .= implode (', ', $qmarks);
                usePreparedExecuteBlade ($query, $args);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delOIFCompatPack']['OK'] = 21;
@@ -2284,51 +2237,61 @@ function delOIFCompatPack ()
                foreach ($oifs as $oif_id_2)
                        if ($oif_id_1 != $oif_id_2) # leave narrow-band mapping intact
                                usePreparedDeleteBlade ('PortCompat', array ('type1' => $oif_id_1, 'type2' => $oif_id_2));
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['add8021QOrder']['OK'] = 48;
-$msgcode['add8021QOrder']['ERR'] = 110;
 function add8021QOrder ()
 {
        assertUIntArg ('vdom_id');
        assertUIntArg ('object_id');
        assertUIntArg ('vst_id');
-       global $sic;
-       $result = usePreparedExecuteBlade
+       global $sic, $pageno;
+       fixContext();
+       if ($pageno != 'object')
+               spreadContext (spotEntity ('object', $sic['object_id']));
+       if ($pageno != 'vst')
+               spreadContext (spotEntity ('vst', $sic['vst_id']));
+       assertPermission();
+       usePreparedExecuteBlade
        (
                'INSERT INTO VLANSwitch (domain_id, object_id, template_id, last_change, out_of_sync) ' .
                'VALUES (?, ?, ?, NOW(), "yes")',
                array ($sic['vdom_id'], $sic['object_id'], $sic['vst_id'])
        );
-       return buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['del8021QOrder']['OK'] = 49;
-$msgcode['del8021QOrder']['ERR'] = 111;
 function del8021QOrder ()
 {
        assertUIntArg ('object_id');
        assertUIntArg ('vdom_id');
        assertUIntArg ('vst_id');
-       global $sic;
-       $result = usePreparedDeleteBlade ('VLANSwitch', array ('object_id' => $sic['object_id']));
+       global $sic, $pageno;
+       fixContext();
+       if ($pageno != 'object')
+               spreadContext (spotEntity ('object', $sic['object_id']));
+       if ($pageno != 'vst')
+               spreadContext (spotEntity ('vst', $sic['vst_id']));
+       assertPermission();
+       usePreparedDeleteBlade ('VLANSwitch', array ('object_id' => $sic['object_id']));
        $focus_hints = array
        (
                'prev_objid' => $_REQUEST['object_id'],
                'prev_vstid' => $_REQUEST['vst_id'],
                'prev_vdid' => $_REQUEST['vdom_id'],
        );
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR', array(), NULL, NULL, $focus_hints);
+       showFuncMessage (__FUNCTION__, 'OK');
+       return buildRedirectURL (NULL, NULL, $focus_hints);
 }
 
 $msgcode['createVLANDomain']['OK'] = 48;
-$msgcode['createVLANDomain']['ERR'] = 110;
 function createVLANDomain ()
 {
        assertStringArg ('vdom_descr');
        global $sic;
-       $result = usePreparedInsertBlade
+       usePreparedInsertBlade
        (
                'VLANDomain',
                array
@@ -2336,7 +2299,7 @@ function createVLANDomain ()
                        'description' => $sic['vdom_descr'],
                )
        );
-       $result = $result and usePreparedInsertBlade
+       usePreparedInsertBlade
        (
                'VLANDescription',
                array
@@ -2347,177 +2310,119 @@ function createVLANDomain ()
                        'vlan_descr' => 'default',
                )
        );
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['destroyVLANDomain']['OK'] = 49;
-$msgcode['destroyVLANDomain']['ERR'] = 111;
 function destroyVLANDomain ()
 {
        assertUIntArg ('vdom_id');
        global $sic;
-       $result = FALSE !== usePreparedDeleteBlade ('VLANDomain', array ('id' => $sic['vdom_id']));
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       usePreparedDeleteBlade ('VLANDomain', array ('id' => $sic['vdom_id']));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
-$msgcode['save8021QPorts']['OK1'] = 63;
-$msgcode['save8021QPorts']['OK2'] = 41;
-$msgcode['save8021QPorts']['ERR2'] = 109;
 function save8021QPorts ()
 {
-       global $sic, $dbxlink;
+       global $sic;
        assertUIntArg ('mutex_rev', TRUE); // counts from 0
        assertStringArg ('form_mode');
        if ($sic['form_mode'] != 'save' and $sic['form_mode'] != 'duplicate')
                throw new InvalidRequestArgException ('form_mode', $sic['form_mode']);
        $extra = array();
-       $dbxlink->beginTransaction();
-       try
+
+       // prepare the $changes array
+       $changes = array();
+       switch ($sic['form_mode'])
        {
-               if (NULL === $vswitch = getVLANSwitchInfo ($sic['object_id'], 'FOR UPDATE'))
-                       throw new InvalidArgException ('object_id', $object_id, 'VLAN domain is not set for this object');
-               if ($vswitch['mutex_rev'] != $sic['mutex_rev'])
-                       throw new InvalidRequestArgException ('mutex_rev', $sic['mutex_rev'], 'expired form data');
-               $after = $before = apply8021QOrder ($vswitch['template_id'], getStored8021QConfig ($sic['object_id'], 'desired'));
-               $changes = array();
-               switch ($sic['form_mode'])
+       case 'save':
+               assertUIntArg ('nports');
+               if ($sic['nports'] == 1)
                {
-               case 'save':
-                       assertUIntArg ('nports');
-                       if ($sic['nports'] == 1)
-                       {
-                               assertStringArg ('pn_0');
-                               $extra = array ('port_name' => $sic['pn_0']);
-                       }
-                       for ($i = 0; $i < $sic['nports']; $i++)
+                       assertStringArg ('pn_0');
+                       $extra = array ('port_name' => $sic['pn_0']);
+               }
+               for ($i = 0; $i < $sic['nports']; $i++)
+               {
+                       assertStringArg ('pn_' . $i);
+                       assertStringArg ('pm_' . $i);
+                       // An access port only generates form input for its native VLAN,
+                       // which we derive allowed VLAN list from.
+                       $native = isset ($sic['pnv_' . $i]) ? $sic['pnv_' . $i] : 0;
+                       switch ($sic["pm_${i}"])
                        {
-                               assertStringArg ('pn_' . $i);
-                               assertStringArg ('pm_' . $i);
-                               // An access port only generates form input for its native VLAN,
-                               // which we derive allowed VLAN list from.
-                               $native = isset ($sic['pnv_' . $i]) ? $sic['pnv_' . $i] : 0;
-                               switch ($sic["pm_${i}"])
-                               {
-                               case 'trunk':
+                       case 'trunk':
 #                              assertArrayArg ('pav_' . $i);
-                                       $allowed = isset ($sic['pav_' . $i]) ? $sic['pav_' . $i] : array();
-                                       break;
-                               case 'access':
-                                       if ($native == 'same')
-                                               continue 2;
-                                       assertUIntArg ('pnv_' . $i);
-                                       $allowed = array ($native);
-                                       break;
-                               default:
-                                       throw new InvalidRequestArgException ("pm_${i}", $_REQUEST["pm_${i}"], 'unknown port mode');
-                               }
-                               $changes[$sic['pn_' . $i]] = array
-                               (
-                                       'mode' => $sic['pm_' . $i],
-                                       'allowed' => $allowed,
-                                       'native' => $native,
-                               );
+                               $allowed = isset ($sic['pav_' . $i]) ? $sic['pav_' . $i] : array();
+                               break;
+                       case 'access':
+                               if ($native == 'same')
+                                       continue 2;
+                               assertUIntArg ('pnv_' . $i);
+                               $allowed = array ($native);
+                               break;
+                       default:
+                               throw new InvalidRequestArgException ("pm_${i}", $_REQUEST["pm_${i}"], 'unknown port mode');
                        }
-                       break;
-               case 'duplicate':
-                       assertStringArg ('from_port');
-#                      assertArrayArg ('to_ports');
-                       if (!array_key_exists ($sic['from_port'], $before))
-                               throw new InvalidArgException ('from_port', $sic['from_port'], 'this port does not exist');
-                       foreach ($sic['to_ports'] as $tpn)
-                               if (!array_key_exists ($tpn, $before))
-                                       throw new InvalidArgException ('to_ports[]', $tpn, 'this port does not exist');
-                               elseif ($tpn != $sic['from_port'])
-                                       $changes[$tpn] = $before[$sic['from_port']];
-                       break;
-               }
-               $domain_vlanlist = getDomainVLANs ($vswitch['domain_id']);
-               $changes = filter8021QChangeRequests
-               (
-                       $domain_vlanlist,
-                       $before,
-                       apply8021QOrder ($vswitch['template_id'], $changes)
-               );
-               $changes = authorize8021QChangeRequests ($before, $changes);
-               foreach ($changes as $port_name => $port)
-                       $after[$port_name] = $port;
-               $new_uplinks = filter8021QChangeRequests ($domain_vlanlist, $after, produceUplinkPorts ($domain_vlanlist, $after, $vswitch['object_id']));
-               $npulled = replace8021QPorts ('desired', $vswitch['object_id'], $before, $changes);
-               $nsaved_uplinks = replace8021QPorts ('desired', $vswitch['object_id'], $before, $new_uplinks);
-       }
-       catch (Exception $e)
-       {
-               $dbxlink->rollBack();
-               return buildRedirectURL (__FUNCTION__, 'ERR2', array(), NULL, NULL, $extra);
-       }
-       if ($npulled + $nsaved_uplinks)
-               usePreparedExecuteBlade
-               (
-                       'UPDATE VLANSwitch SET mutex_rev=mutex_rev+1, last_change=NOW(), out_of_sync="yes" WHERE object_id=?',
-                       array ($sic['object_id'])
-               );
-       $dbxlink->commit();
-       $log = oneLiner (63, array ($npulled + $nsaved_uplinks));
-       if ($nsaved_uplinks)
-       {
-               initiateUplinksReverb ($vswitch['object_id'], $new_uplinks);
-               $log = mergeLogs ($log, oneLiner (41));
-       }
-       if ($npulled + $nsaved_uplinks > 0 and getConfigVar ('8021Q_INSTANT_DEPLOY') == 'yes')
-       {
-               try
-               {
-                       if (FALSE === $done = exec8021QDeploy ($sic['object_id'], TRUE))
-                               $log = mergeLogs ($log, oneLiner (191));
-                       else
-                               $log = mergeLogs ($log, oneLiner (63, array ($done)));
-               }
-               catch (Exception $e)
-               {
-                       $log = mergeLogs ($log, oneLiner (109));
+                       $changes[$sic['pn_' . $i]] = array
+                       (
+                               'mode' => $sic['pm_' . $i],
+                               'allowed' => $allowed,
+                               'native' => $native,
+                       );
                }
+               break;
+       case 'duplicate':
+               assertStringArg ('from_port');
+#                      assertArrayArg ('to_ports');
+               $before = getStored8021QConfig ($sic['object_id'], 'desired');
+               if (!array_key_exists ($sic['from_port'], $before))
+                       throw new InvalidArgException ('from_port', $sic['from_port'], 'this port does not exist');
+               foreach ($sic['to_ports'] as $tpn)
+                       if (!array_key_exists ($tpn, $before))
+                               throw new InvalidArgException ('to_ports[]', $tpn, 'this port does not exist');
+                       elseif ($tpn != $sic['from_port'])
+                               $changes[$tpn] = $before[$sic['from_port']];
+               break;
        }
-       return buildWideRedirectURL ($log, NULL, NULL, $extra);
+       apply8021qChangeRequest ($sic['object_id'], $changes, TRUE, $sic['mutex_rev']);
+       return buildRedirectURL (NULL, NULL, $extra);
 }
 
 $msgcode['bindVLANtoIPv4']['OK'] = 48;
-$msgcode['bindVLANtoIPv4']['ERR'] = 110;
 function bindVLANtoIPv4 ()
 {
        assertUIntArg ('id'); // network id
        global $sic;
-       $result = commitSupplementVLANIPv4 ($sic['vlan_ck'], $sic['id']);
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       commitSupplementVLANIPv4 ($sic['vlan_ck'], $sic['id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['bindVLANtoIPv6']['OK'] = 48;
-$msgcode['bindVLANtoIPv6']['ERR'] = 110;
 function bindVLANtoIPv6 ()
 {
        assertUIntArg ('id'); // network id
        global $sic;
-       $result = commitSupplementVLANIPv6 ($sic['vlan_ck'], $_REQUEST['id']);
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       commitSupplementVLANIPv6 ($sic['vlan_ck'], $_REQUEST['id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['unbindVLANfromIPv4']['OK'] = 49;
-$msgcode['unbindVLANfromIPv4']['ERR'] = 111;
 function unbindVLANfromIPv4 ()
 {
        assertUIntArg ('id'); // network id
        global $sic;
-       $result = commitReduceVLANIPv4 ($sic['vlan_ck'], $sic['id']);
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       commitReduceVLANIPv4 ($sic['vlan_ck'], $sic['id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['unbindVLANfromIPv6']['OK'] = 49;
-$msgcode['unbindVLANfromIPv6']['ERR'] = 111;
 function unbindVLANfromIPv6 ()
 {
        assertUIntArg ('id'); // network id
        global $sic;
-       $result = commitReduceVLANIPv6 ($sic['vlan_ck'], $sic['id']);
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       commitReduceVLANIPv6 ($sic['vlan_ck'], $sic['id']);
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['process8021QSyncRequest']['OK'] = 63;
@@ -2527,20 +2432,19 @@ function process8021QSyncRequest ()
        // behave depending on current operation: exec8021QPull or exec8021QPush
        global $sic, $op;
        if (FALSE === $done = exec8021QDeploy ($sic['object_id'], $op == 'exec8021QPush'))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($done));
+               return showFuncMessage (__FUNCTION__, 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK', array ($done));
 }
 
 $msgcode['process8021QRecalcRequest']['CHANGED'] = 87;
-$msgcode['process8021QRecalcRequest']['NO_CHANGES'] = 300;
 function process8021QRecalcRequest ()
 {
        assertPermission (NULL, NULL, NULL, array (array ('tag' => '$op_recalc8021Q')));
        $counters = recalc8021QPorts (getBypassValue());
        if ($counters['ports'])
-               return buildRedirectURL (__FUNCTION__, 'CHANGED', array ($counters['ports'], $counters['switches']));
+               return showFuncMessage (__FUNCTION__, 'CHANGED', array ($counters['ports'], $counters['switches']));
        else
-               return buildRedirectURL (__FUNCTION__, 'NO_CHANGES', array ('No changes were made'));
+               return showNotice ('No changes were made');
 }
 
 $msgcode['resolve8021QConflicts']['OK'] = 63;
@@ -2599,9 +2503,15 @@ function resolve8021QConflicts ()
                                if (!same8021QConfigs ($port, $R['portdata'][$port_name]))
                                        throw new InvalidRequestArgException ("port ${port_name}", '(hidden)', 'expired form (switch data has changed)');
                                if ($port['decision'] == 'right') // D wins, frame R by writing value of R to C
-                                       $ndone += upd8021QPort ('cached', $vswitch['object_id'], $port_name, $port);
+                               {
+                                       upd8021QPort ('cached', $vswitch['object_id'], $port_name, $port);
+                                       $ndone++;
+                               }
                                elseif ($port['decision'] == 'left') // R wins, cross D up
-                                       $ndone += upd8021QPort ('cached', $vswitch['object_id'], $port_name, $D[$port_name]);
+                               {
+                                       upd8021QPort ('cached', $vswitch['object_id'], $port_name, $D[$port_name]);
+                                       $ndone++;
+                               }
                                // otherwise there was no decision made
                        }
                        elseif
@@ -2609,34 +2519,35 @@ function resolve8021QConflicts ()
                                $plan[$port_name]['status'] == 'delete_conflict' or
                                $plan[$port_name]['status'] == 'martian_conflict'
                        )
-                       {
-                               if ($port['decision'] == 'left') // confirm deletion of local copy
-                                       $ndone += del8021QPort ($vswitch['object_id'], $port_name);
-                       }
-                       // otherwise ignore a decision, which doesn't address a conflict
+                               if ($port['decision'] == 'left')
+                               {
+                                       // confirm deletion of local copy
+                                       del8021QPort ($vswitch['object_id'], $port_name);
+                                       $ndone++;
+                               }
+                               // otherwise ignore a decision, which doesn't address a conflict
                }
        }
        catch (InvalidRequestArgException $e)
        {
                $dbxlink->rollBack();
-               return buildRedirectURL (__FUNCTION__, 'ERR1');
+               return showFuncMessage (__FUNCTION__, 'ERR1');
        }
        catch (Exception $e)
        {
                $dbxlink->rollBack();
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
+               return showFuncMessage (__FUNCTION__, 'ERR2');
        }
        $dbxlink->commit();
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($ndone));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ndone));
 }
 
 $msgcode['addVLANSwitchTemplate']['OK'] = 48;
-$msgcode['addVLANSwitchTemplate']['ERR'] = 110;
 function addVLANSwitchTemplate()
 {
        assertStringArg ('vst_descr');
        global $sic;
-       $result = usePreparedInsertBlade
+       usePreparedInsertBlade
        (
                'VLANSwitchTemplate',
                array
@@ -2644,17 +2555,16 @@ function addVLANSwitchTemplate()
                        'description' => $sic['vst_descr'],
                )
        );
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['delVLANSwitchTemplate']['OK'] = 49;
-$msgcode['delVLANSwitchTemplate']['ERR'] = 111;
 function delVLANSwitchTemplate()
 {
        assertUIntArg ('vst_id');
        global $sic;
-       $result = FALSE !== usePreparedDeleteBlade ('VLANSwitchTemplate', array ('id' => $sic['vst_id']));
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
+       usePreparedDeleteBlade ('VLANSwitchTemplate', array ('id' => $sic['vst_id']));
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['cloneVST']['OK'] = 48;
@@ -2665,7 +2575,7 @@ function cloneVST()
        $src_vst = spotEntity ('vst', $_REQUEST['from_id']);
        amplifyCell ($src_vst);
        commitUpdateVSTRules (getBypassValue(), $_REQUEST['mutex_rev'], $src_vst['rules']);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updVSTRule']['OK'] = 43;
@@ -2707,10 +2617,13 @@ function updVSTRule()
        {
                // Every case, which is soft-processed in process.php, will have the working copy available for a retry.
                if ($e instanceof InvalidRequestArgException or $e instanceof RTDatabaseError)
+               {
+                       @session_start();
                        $_SESSION['vst_edited'] = $data;
+               }
                throw $e;
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['importDPData']['OK'] = 44;
@@ -2756,11 +2669,15 @@ function importDPData()
                                try
                                {
                                        if ($oif_a)
-                                               if (commitUpdatePortOIF ($params['a_id'], $oif_a))
-                                                       $porta['oif_id'] = $oif_a;
+                                       {
+                                               commitUpdatePortOIF ($params['a_id'], $oif_a);
+                                               $porta['oif_id'] = $oif_a;
+                                       }
                                        if ($oif_b)
-                                               if (commitUpdatePortOIF ($params['b_id'], $oif_b))
-                                                       $portb['oif_id'] = $oif_b;
+                                       {
+                                               commitUpdatePortOIF ($params['b_id'], $oif_b);
+                                               $portb['oif_id'] = $oif_b;
+                                       }
                                }
                                catch (RTDatabaseError $e)
                                {
@@ -2772,30 +2689,33 @@ function importDPData()
                        foreach ($POIFC as $item)
                                if ($item['type1'] == $porta['oif_id'] and $item['type2'] == $portb['oif_id'])
                                {
-                                       $ret = linkPorts ($params['a_id'], $params['b_id']);
-                                       if (empty ($ret))
-                                       {
-                                               $ndone++;
-                                               $dbxlink->commit();
-                                               continue 2; //next port
-                                       }
-                                       else
-                                               $nignored++;
-                                       break;
+                                       linkPorts ($params['a_id'], $params['b_id']);
+                                       $ndone++;
+                                       $dbxlink->commit();
+                                       continue 2; //next port
                                }
                        $dbxlink->rollback();
                }
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($nignored, $ndone));
+       return showFuncMessage (__FUNCTION__, 'OK', array ($nignored, $ndone));
 }
 
-$msgcode['addObjectlog']['OK'] = 0;
 function addObjectlog ()
 {
        assertStringArg ('logentry');
        global $remote_username, $sic;
-       $oi = spotEntity ('object', $sic['object_id']);
-       usePreparedExecuteBlade ('INSERT INTO ObjectLog SET object_id=?, user=?, date=NOW(), content=?', array ($sic['object_id'], $remote_username, $sic['logentry']));
-       return buildRedirectURL (__FUNCTION__, 'OK', array ('Log entry for ' . mkA ($oi['dname'], 'object', $sic['object_id'], 'log') . " added by ${remote_username}"));
+       $object_id = isset($sic['object_id']) ? $sic['object_id'] : $sic['rack_id'];
+       usePreparedExecuteBlade ('INSERT INTO ObjectLog SET object_id=?, user=?, date=NOW(), content=?', array ($object_id, $remote_username, $sic['logentry']));
+       showSuccess ('Log entry added');
+}
+
+function saveQuickLinks()
+{
+       genericAssertion ('page_list', 'array');
+       if (is_array ($_REQUEST['page_list']))
+       {
+               setUserConfigVar ('QUICK_LINK_PAGES', implode(',', $_REQUEST['page_list']));    
+               showSuccess ('Quick links list is saved');
+       }
 }
 
 function getOpspec()
@@ -2817,10 +2737,61 @@ function getOpspec()
 function unlinkPort ()
 {
        assertUIntArg ('port_id');
-       if (commitUnlinkPort ($_REQUEST['port_id']))
-               showSuccess ("Port unlinked successfully");
-       else
-               showError ("Error unlinking port");
+       commitUnlinkPort ($_REQUEST['port_id']);
+       showSuccess ("Port unlinked successfully");
+}
+
+function clearVlan()
+{
+       assertStringArg ('vlan_ck');
+       list ($vdom_id, $vlan_id) = decodeVLANCK ($_REQUEST['vlan_ck']);
+       
+       $n_cleared = 0;
+       foreach (getVLANConfiguredPorts ($_REQUEST['vlan_ck']) as $object_id => $portnames)
+       {
+               $D = getStored8021QConfig ($object_id);
+               $changes = array();
+               foreach ($portnames as $pn)
+               {
+                       $conf = $D[$pn];
+                       $conf['allowed'] = array_diff ($conf['allowed'], array ($vlan_id));
+                       if ($conf['mode'] == 'access')
+                               $conf['mode'] = 'trunk';
+                       if ($conf['native'] == $vlan_id)
+                               $conf['native'] = 0;
+                       $changes[$pn] = $conf;
+               }
+               $n_cleared += apply8021qChangeRequest ($object_id, $changes, FALSE);
+       }
+       if ($n_cleared > 0)
+               showSuccess ("VLAN $vlan_id removed from $n_cleared ports");
+}
+
+function deleteVlan()
+{
+       assertStringArg ('vlan_ck');
+       $confports = getVLANConfiguredPorts ($_REQUEST['vlan_ck']);
+       if (! empty ($confports))
+               throw new RackTablesError ("You can not delete vlan which has assosiated ports");
+       list ($vdom_id, $vlan_id) = decodeVLANCK ($_REQUEST['vlan_ck']);
+       usePreparedDeleteBlade ('VLANDescription', array ('domain_id' => $vdom_id, 'vlan_id' => $vlan_id));
+       showSuccess ("VLAN $vlan_id has been deleted");
+       return buildRedirectURL ('vlandomain', 'default', array ('vdom_id' => $vdom_id));
+}
+
+function cloneRSPool()
+{
+       assertUIntArg ('pool_id');
+       $pool = spotEntity ('ipv4rspool', $_REQUEST['pool_id']);
+       $rs_list = getRSListInPool ($pool['id']);
+       $tagidlist = array();
+       foreach ($pool['etags'] as $taginfo)
+               $tagidlist[] = $taginfo['id'];
+       $new_id = commitCreateRSPool ($pool['name'] . ' (copy)', $pool['vsconfig'], $pool['rsconfig'], $tagidlist);
+       foreach ($rs_list as $rs)
+               addRStoRSPool ($new_id, $rs['rsip'], $rs['rsport'], $rs['inservice'], $rs['rsconfig'], $rs['comment']);
+       showSuccess ("Created a copy of pool <a href='" . makeHref (array ('page' => 'ipv4rspool', 'tab' => 'default', 'pool_id' => $pool['id'])) . "'>${pool['name']}</a>");
+       return buildRedirectURL ('ipv4rspool', 'default', array ('pool_id' => $new_id));
 }
 
 function tableHandler()
@@ -2862,11 +2833,11 @@ function tableHandler()
        switch ($opspec['action'])
        {
        case 'INSERT':
-               $retcode = TRUE === usePreparedInsertBlade ($opspec['table'], $columns['arglist']) ? 48 : 110;
+               usePreparedInsertBlade ($opspec['table'], $columns['arglist']);
                break;
        case 'DELETE':
                $conjunction = array_key_exists ('conjunction', $opspec) ? $opspec['conjunction'] : 'AND';
-               $retcode = FALSE !== usePreparedDeleteBlade ($opspec['table'], $columns['arglist'], $conjunction) ? 49 : 111;
+               usePreparedDeleteBlade ($opspec['table'], $columns['arglist'], $conjunction);
                break;
        case 'UPDATE':
                usePreparedUpdateBlade
@@ -2876,12 +2847,11 @@ function tableHandler()
                        $columns['where_arglist'],
                        array_key_exists ('conjunction', $opspec) ? $opspec['conjunction'] : 'AND'
                );
-               $retcode = 51;
                break;
        default:
                throw new InvalidArgException ('opspec/action', $opspec['action']);
        }
-       return buildWideRedirectURL (oneLiner ($retcode));
+       showOneLiner (51);
 }
 
 ?>