r4910 APCPowerSwitch::getPorts () - change snmpwalk call to snmpwalkoid, fixes #504
[racktables] / wwwroot / inc / ophandlers.php
index e32b450486ddade662bb406db4eda8637d6da673..bf09c69295643258a8792ab233041c6321705385 100644 (file)
@@ -1,8 +1,43 @@
 <?php
 /*
-*
-*  This file is a library of operation handlers for RackTables.
-*
+
+"Ophandler" in RackTables stands for "operation handler", or a function,
+which handles execution of "operation" (in the meaning explained in
+navigation.php). Most of the ophandlers are meant to perform one specific
+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 simpler.
+
+Errors occuring in ophandlers are typically indicated with exceptions of
+assorted classes. Namely, an "InvalidRequestArgException" class means, that
+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 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
+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() 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
+logical location remains the same). The page at the "next" location is
+supposed to translate message buffer into the standard message area.
+
+A very special case of an ophandler is tableHandler(). This generic
+function handles the most trivial actions, which map to a single INSERT,
+UPDATE or DELETE SQL statement with a fixed number of arguments. The rules
+of argument validation and mapping are listed in $opspec_list (operation
+specifications list) array.
+
 */
 
 // This array is deprecated. Please do not add new message constants to it.
@@ -15,64 +50,54 @@ $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['object-ports-delPort'] = array
+$opspec_list['rackspace-edit-deleteRow'] = array
 (
-       'table' => 'Port',
+       'table' => 'Object',
        'action' => 'DELETE',
        'arglist' => array
        (
-               array ('url_argname' => 'port_id', 'table_colname' => 'id', 'assertion' => 'uint'),
-               array ('url_argname' => 'object_id', 'assertion' => 'uint'),
+               array ('url_argname' => 'row_id', 'table_colname' => 'id', 'assertion' => 'uint')
        ),
 );
-$opspec_list['object-ports-deleteAll'] = array
+$opspec_list['object-ports-delPort'] = array
 (
        'table' => 'Port',
        'action' => 'DELETE',
        'arglist' => array
        (
+               array ('url_argname' => 'port_id', 'table_colname' => 'id', 'assertion' => 'uint'),
                array ('url_argname' => 'object_id', 'assertion' => 'uint'),
        ),
 );
-$opspec_list['object-ports-unlinkPort'] = array
+$opspec_list['object-ports-deleteAll'] = array
 (
-       'table' => 'Link',
+       'table' => 'Port',
        'action' => 'DELETE',
        'arglist' => array
        (
-               array ('url_argname' => 'port_id', 'table_colname' => 'porta', 'assertion' => 'uint'),
-               array ('url_argname' => 'port_id', 'table_colname' => 'portb', 'assertion' => 'uint'),
+               array ('url_argname' => 'object_id', 'assertion' => 'uint'),
        ),
-       'conjunction' => 'OR',
 );
 $opspec_list['object-log-del'] = array
 (
@@ -80,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'),
        ),
 );
@@ -107,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
        (
@@ -116,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',
@@ -388,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',
@@ -404,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 (gettype ($value) != 'array')
-                               $url .= '&' . urlencode ($arg) . '=' . urlencode ($value);
-                       else
-                               foreach ($value as $v)
-                                       $url .= '&' . urlencode ($arg . '[]') . '=' . urlencode ($v);
-
-       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');
@@ -490,7 +464,7 @@ function addPortForwarding ()
        if (!strlen ($remoteport))
                $remoteport = $_REQUEST['localport'];
 
-       $error = newPortForwarding
+       newPortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -501,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');
@@ -518,7 +488,7 @@ function delPortForwarding ()
        assertUIntArg ('remoteport');
        assertStringArg ('proto');
 
-       $result = deletePortForwarding
+       deletePortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -527,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');
@@ -542,7 +511,7 @@ function updPortForwarding ()
        assertStringArg ('proto');
        assertStringArg ('description');
 
-       $result = updatePortForwarding
+       updatePortForwarding
        (
                $_REQUEST['object_id'],
                $_REQUEST['localip'],
@@ -552,7 +521,7 @@ function updPortForwarding ()
                $_REQUEST['proto'],
                $_REQUEST['description']
        );
-       buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['addPortForObject']['OK'] = 48;
@@ -569,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;
@@ -582,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');
@@ -594,28 +564,21 @@ 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;
-       $local_port_info = getPortInfo ($sic['port_id']);
-       $remote_port_info = getPortInfo ($sic['remote_port_id']);
-       $remote_object = spotEntity ('object', $remote_port_info['object_id']);
-       return buildRedirectURL
+       linkPorts ($_REQUEST['port_id'], $_REQUEST['remote_port_id'], $_REQUEST['cable']);
+       $port_info = getPortInfo ($_REQUEST['port_id']);
+       return showFuncMessage
        (
                __FUNCTION__,
                'OK',
                array
                (
-                       $local_port_info['name'],
-                       $remote_port_info['name'],
-                       $remote_object['dname'],
+                       formatPortLink ($port_info['id'], $port_info['name'], NULL, NULL),
+                       formatLinkedPort ($port_info),
                )
        );
 }
 
 $msgcode['addMultiPorts']['OK'] = 10;
-$msgcode['addMultiPorts']['ERR'] = 123;
 function addMultiPorts ()
 {
        assertStringArg ('format');
@@ -697,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;
                }
        }
@@ -717,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;
@@ -744,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');
@@ -756,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');
@@ -769,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');
@@ -807,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']))
        {
@@ -821,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');
@@ -839,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']))
        {
@@ -853,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);
@@ -907,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');
@@ -925,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');
@@ -941,18 +918,16 @@ 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');
        assertStringArg ('username');
        assertStringArg ('realname', TRUE);
        assertStringArg ('password');
@@ -962,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
@@ -981,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;
@@ -993,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');
@@ -1038,25 +1008,26 @@ 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;
-$msgcode['clearSticker']['ERR'] = 120;
 function clearSticker ()
 {
+       global $sic;
        assertUIntArg ('attr_id');
-       if (commitResetAttrValue ($_REQUEST['object_id'], $_REQUEST['attr_id']) !== FALSE)
-               return buildRedirectURL (__FUNCTION__, 'OK');
+       if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $sic['attr_id']))))
+               commitUpdateAttrValue (getBypassValue(), $sic['attr_id']);
        else
-               return buildRedirectURL (__FUNCTION__, 'ERR');
+       {
+               $oldvalues = getAttrValues (getBypassValue());
+               showError ('Permission denied, "' . $oldvalues[$sic['attr_id']]['name'] . '" left unchanged');
+       }
 }
 
 $msgcode['updateObjectAllocation']['OK'] = 63;
@@ -1071,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 = $_REQUEST['object_id'];
+       $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)
        {
@@ -1083,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.
@@ -1104,69 +1091,76 @@ 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;
-$msgcode['updateObject']['ERR'] = 109;
 function updateObject ()
 {
-       assertUIntArg ('num_attrs', TRUE);
-       assertStringArg ('object_name', TRUE);
-       assertStringArg ('object_label', TRUE);
-       assertStringArg ('object_asset_no', TRUE);
-       if (isset ($_REQUEST['object_has_problems']) and $_REQUEST['object_has_problems'] == 'on')
+       genericAssertion ('num_attrs', 'uint0');
+       genericAssertion ('object_name', 'string0');
+       genericAssertion ('object_label', 'string0');
+       genericAssertion ('object_asset_no', 'string0');
+       genericAssertion ('object_comment', 'string0');
+       genericAssertion ('object_type_id', 'uint');
+       if (array_key_exists ('object_has_problems', $_REQUEST) and $_REQUEST['object_has_problems'] == 'on')
                $has_problems = 'yes';
        else
                $has_problems = 'no';
+       $object_id = getBypassValue();
 
-       if (commitUpdateObject (
-               $_REQUEST['object_id'],
+       global $dbxlink, $sic;
+       $dbxlink->beginTransaction();
+       commitUpdateObject
+       (
+               $object_id,
                $_REQUEST['object_name'],
                $_REQUEST['object_label'],
                $has_problems,
                $_REQUEST['object_asset_no'],
                $_REQUEST['object_comment']
-       ) !== TRUE)
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-
+       );
        // Update optional attributes
-       $oldvalues = getAttrValues ($_REQUEST['object_id']);
-       $result = array();
-       $num_attrs = isset ($_REQUEST['num_attrs']) ? $_REQUEST['num_attrs'] : 0;
-       for ($i = 0; $i < $num_attrs; $i++)
+       $oldvalues = getAttrValues ($object_id);
+       for ($i = 0; $i < $_REQUEST['num_attrs']; $i++)
        {
-               assertUIntArg ("${i}_attr_id");
+               genericAssertion ("${i}_attr_id", 'uint');
                $attr_id = $_REQUEST["${i}_attr_id"];
+               if (! array_key_exists ($attr_id, $oldvalues))
+                       throw new InvalidRequestArgException ('attr_id', $attr_id, 'malformed request');
+               $value = $_REQUEST["${i}_value"];
 
-               // 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))
+               # Delete attribute and move on, when the field is empty or if the field
+               # type is a dictionary and it is the "--NOT SET--" value of 0.
+               if ($value == '' || ($oldvalues[$attr_id]['type'] == 'dict' && $value == 0))
                {
-                       commitResetAttrValue ($_REQUEST['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;
                }
 
                // 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':
@@ -1181,32 +1175,33 @@ function updateObject ()
                }
                if ($value === $oldvalue) // ('' == 0), but ('' !== 0)
                        continue;
-
-               // Note if the queries succeed or not, it determines which page they see.
-               $result[] = commitUpdateAttrValue ($_REQUEST['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'])
+       {
+               if (! array_key_exists ($sic['object_type_id'], getObjectTypeChangeOptions ($object_id)))
+                       throw new InvalidRequestArgException ('new type_id', $sic['object_type_id'], 'incompatible with requested attribute values');
+               usePreparedUpdateBlade ('RackObject', array ('objtype_id' => $sic['object_type_id']), array ('id' => $object_id));
        }
-       if (in_array (FALSE, $result))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-
        // Invalidate thumb cache of all racks objects could occupy.
-       foreach (getResidentRacksData ($_REQUEST['object_id'], FALSE) as $rack_id)
-               usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $rack_id));
-
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
+               usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
+       $dbxlink->commit();
+       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']))
@@ -1224,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()
@@ -1267,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');
@@ -1288,41 +1288,26 @@ 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;
 function resetObject ()
 {
-       $oinfo = spotEntity ('object', $_REQUEST['object_id']);
-
-       $racklist = getResidentRacksData ($_REQUEST['object_id'], FALSE);
-       commitResetObject ($_REQUEST['object_id']);
+       $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;
 function useupPort ()
 {
-       global $sic;
        assertUIntArg ('port_id');
-       usePreparedUpdateBlade
-       (
-               'Port',
-               array
-               (
-                       'reservation_comment' => NULL,
-               ),
-               array
-               (
-                       'object_id' => $sic['object_id'],
-                       'id' => $sic['port_id'],
-               )
-       );
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       commitUpdatePortComment ($_REQUEST['port_id'], '');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['updateUI']['OK'] = 51;
@@ -1343,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;
@@ -1363,7 +1348,7 @@ function saveMyPreferences ()
                        continue;
                setUserConfigVar ($varname, $varvalue);
        }
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['resetMyPreference']['OK'] = 51;
@@ -1371,7 +1356,7 @@ function resetMyPreference ()
 {
        assertStringArg ("varname");
        resetUserConfigVar ($_REQUEST["varname"]);
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       return showFuncMessage (__FUNCTION__, 'OK');
 }
 
 $msgcode['resetUIConfig']['OK'] = 57;
@@ -1440,46 +1425,52 @@ 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');
        setConfigVar ('PORT_EXCLUSION_LISTSRC', '{$typeid_3} or {$typeid_10} or {$typeid_11} or {$typeid_1505} or {$typeid_1506}');
        setConfigVar ('FILTER_RACKLIST_BY_TAGS', 'yes');
-       return buildRedirectURL (__FUNCTION__, 'OK');
+       setConfigVar ('SSH_OBJS_LISTSRC', 'none');
+       setConfigVar ('TELNET_OBJS_LISTSRC', 'none');
+       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 ()
 {
-       assertUIntArg ('pool_id');
-       assertIPv4Arg ('remoteip');
+       global $sic;
+       assertIPv4Arg ('rsip');
        assertStringArg ('rsport', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (!addRStoRSPool (
-               $_REQUEST['pool_id'],
-               $_REQUEST['remoteip'],
+       assertStringArg ('comment', TRUE);
+       addRStoRSPool
+       (
+               getBypassValue(),
+               $_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)
        {
@@ -1491,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 ($_REQUEST['pool_id'], $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 ($_REQUEST['pool_id'], $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 ($_REQUEST['pool_id'], $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 ($_REQUEST['pool_id'], $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');
@@ -1612,100 +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');
-       $pool_id = $_REQUEST['pool_id'];
-       $orig = spotEntity ('ipv4rspool', $pool_id);
-       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;
@@ -1734,27 +1679,21 @@ 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;
-$msgcode['generateAutoPorts']['ERR'] = 142;
 function generateAutoPorts ()
 {
-       global $pageno;
-       $info = spotEntity ('object', $_REQUEST['object_id']);
-       // Navigate away in case of success, stay at the place otherwise.
-       if (executeAutoPorts ($_REQUEST['object_id'], $info['objtype_id']))
-               return buildRedirectURL (__FUNCTION__, 'OK', array(), $pageno, 'ports');
-       else
-               return buildRedirectURL (__FUNCTION__, 'ERR');
+       $object = spotEntity ('object', getBypassValue());
+       executeAutoPorts ($object['id'], $object['objtype_id']);
+       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;
@@ -1763,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;
@@ -1788,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.
@@ -1797,7 +1722,7 @@ function rollTags ()
        // Minimizing the extra chain early, so that tag rebuilder doesn't have to
        // filter out the same tag again and again. It will have own noise to cancel.
        $extrachain = getExplicitTagsOnly (buildTagChainFromIds ($extratags));
-       foreach (listCells ('rack', $_REQUEST['row_id']) as $rack)
+       foreach (listCells ('rack', getBypassValue()) as $rack)
        {
                if (rebuildTagChainForEntity ('rack', $rack['id'], $extrachain))
                        $n_ok++;
@@ -1806,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');
@@ -1844,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;
@@ -1866,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
@@ -1875,7 +1793,6 @@ function setPortVLAN ()
        // for each of the rest.
        $nports = $_REQUEST['portcount'];
        $prefix = 'set ';
-       $log = emptyLog();
        $setcmd = '';
        for ($i = 0; $i < $nports; $i++)
        {
@@ -1886,7 +1803,7 @@ function setPortVLAN ()
                if
                (
                        $_REQUEST['vlanid_' . $i] == $portlist[$i]['vlanid'] ||
-                       $portlist[$i]['vlaind'] == 'TRUNK'
+                       $portlist[$i]['vlanid'] == 'TRUNK'
                )
                        continue;
                $portname = $_REQUEST['portname_' . $i];
@@ -1894,11 +1811,11 @@ function setPortVLAN ()
                $newvlanid = $_REQUEST['vlanid_' . $i];
                if
                (
-                       !permitted (NULL, NULL, NULL, array (array ('tag' => '$fromvlan_' . $oldvlanid))) or
-                       !permitted (NULL, NULL, NULL, array (array ('tag' => '$tovlan_' . $newvlanid)))
+                       !permitted (NULL, NULL, NULL, array (array ('tag' => '$fromvlan_' . $oldvlanid), array ('tag' => '$vlan_' . $oldvlanid))) or
+                       !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;
@@ -1906,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 ($_REQUEST['object_id']);
-       try
-       {
-               gwSendFileToObject ($_REQUEST['object_id'], '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;
@@ -1944,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)
                {
@@ -1967,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 ()
 {
@@ -1988,58 +1901,104 @@ 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'] = 7;
-$msgcode['updateRack']['ERR'] = 109;
+$msgcode['updateRack']['OK'] = 6;
 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);
 
-       global $sic;
-       usePreparedUpdateBlade ('Rack', array ('thumb_data' => NULL), array ('id' => $sic['rack_id']));
-       if (TRUE === commitUpdateRack ($_REQUEST['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');
+       $rack_id = getBypassValue();
+       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 ()
 {
-       $rackData = spotEntity ('rack', $_REQUEST['rack_id']);
+       $rackData = spotEntity ('rack', getBypassValue());
        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 ()
 {
-       $rackData = spotEntity ('rack', $_REQUEST['rack_id']);
+       $rackData = spotEntity ('rack', getBypassValue());
        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');
@@ -2053,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 ($_REQUEST['object_id'], $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
@@ -2076,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;
@@ -2104,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;
@@ -2133,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;
@@ -2155,42 +2115,31 @@ 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;
-$msgcode['replaceFile']['ERR2'] = 207;
-$msgcode['replaceFile']['ERR3'] = 109;
+$msgcode['replaceFile']['ERR2'] = 201;
 function replaceFile ()
 {
-       global $sic;
-
        // Make sure the file can be uploaded
        if (get_cfg_var('file_uploads') != 1)
                throw new RackTablesError ('file uploads not allowed, change "file_uploads" parameter in php.ini', RackTablesError::MISCONFIGURED);
-       $shortInfo = spotEntity ('file', $sic['file_id']);
+       $shortInfo = spotEntity ('file', getBypassValue());
 
-       $fp = fopen($_FILES['file']['tmp_name'], 'rb');
-       if ($fp === FALSE)
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
-       if (FALSE === commitReplaceFile ($sic['file_id'], $fp))
-               return buildRedirectURL (__FUNCTION__, 'ERR3');
-
-       usePreparedExecuteBlade
-       (
-               'UPDATE File SET thumbnail = NULL WHERE id = ?',
-               $sic['file_id']
-       );
+       if (FALSE === $fp = fopen ($_FILES['file']['tmp_name'], 'rb'))
+               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;
@@ -2199,132 +2148,150 @@ 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 ()
 {
        assertStringArg ('mtime_copy');
        assertStringArg ('file_text', TRUE); // it's Ok to save empty
-       $shortInfo = spotEntity ('file', $_REQUEST['file_id']);
+       $shortInfo = spotEntity ('file', getBypassValue());
        if ($shortInfo['mtime'] != $_REQUEST['mtime_copy'])
-               return buildRedirectURL (__FUNCTION__, 'ERR1');
+               return showFuncMessage (__FUNCTION__, 'ERR1');
        global $sic;
-       if (FALSE === commitReplaceFile ($sic['file_id'], $sic['file_text']))
-               return buildRedirectURL (__FUNCTION__, 'ERR2');
-       return buildRedirectURL (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
+       commitReplaceFile ($shortInfo['id'], $sic['file_text']);
+       return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
 }
 
-$msgcode['addPortInterfaceCompat']['OK'] = 48;
-$msgcode['addPortInterfaceCompat']['ERR'] = 110;
-function addPortInterfaceCompat ()
+$msgcode['addIIFOIFCompat']['OK'] = 48;
+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');
 }
 
-$ifcompatpack = array
-(
-       '1000cwdm80' => array (1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216),
-       '1000dwdm80' => array // ITU channels 20~61
-       (
-               1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226,
-               1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236,
-               1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246,
-               1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256,
-               1257, 1258
-       ),
-       '10000dwdm80' => array // same channels for 10GE
-       (
-               1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268,
-               1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278,
-               1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288,
-               1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298,
-               1299, 1300
-       ),
-);
+$msgcode['addIIFOIFCompatPack']['OK'] = 37;
+function addIIFOIFCompatPack ()
+{
+       genericAssertion ('standard', 'enum/wdmstd');
+       genericAssertion ('iif_id', 'iif');
+       global $wdm_packs, $sic;
+       $ngood = 0;
+       foreach ($wdm_packs[$sic['standard']]['oif_ids'] as $oif_id)
+       {
+               commitSupplementPIC ($sic['iif_id'], $oif_id);
+               $ngood++;
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
+}
 
-$msgcode['addPortInterfaceCompatPack']['OK'] = 44;
-function addPortInterfaceCompatPack ()
+$msgcode['delIIFOIFCompatPack']['OK'] = 38;
+function delIIFOIFCompatPack ()
 {
        genericAssertion ('standard', 'enum/wdmstd');
        genericAssertion ('iif_id', 'iif');
-       global $ifcompatpack;
-       $ngood = $nbad = 0;
-       foreach ($ifcompatpack[$_REQUEST['standard']] as $oif_id)
-               if (commitSupplementPIC ($_REQUEST['iif_id'], $oif_id))
-                       $ngood++;
-               else
-                       $nbad++;
-       return buildRedirectURL (__FUNCTION__, 'OK', array ($nbad, $ngood));
+       global $wdm_packs, $sic;
+       $ngood = 0;
+       foreach ($wdm_packs[$sic['standard']]['oif_ids'] as $oif_id)
+       {
+               usePreparedDeleteBlade ('PortInterfaceCompat', array ('iif_id' => $sic['iif_id'], 'oif_id' => $oif_id));
+               $ngood++;
+       }
+       return showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
 }
 
-$msgcode['delPortInterfaceCompatPack']['OK'] = 44;
-$msgcode['delPortInterfaceCompatPack']['ERR'] = 123;
-function delPortInterfaceCompatPack ()
+$msgcode['addOIFCompatPack']['OK'] = 21;
+function addOIFCompatPack ()
 {
-       assertStringArg ('standard');
-       assertUIntArg ('iif_id');
-       global $ifcompatpack, $sic;
-       if (!array_key_exists ($sic['standard'], $ifcompatpack) or !array_key_exists ($sic['iif_id'], getPortIIFOptions()))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       $ngood = $nbad = 0;
-       foreach ($ifcompatpack[$sic['standard']] 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));
+       genericAssertion ('standard', 'enum/wdmstd');
+       global $wdm_packs;
+       $oifs = $wdm_packs[$_REQUEST['standard']]['oif_ids'];
+       foreach ($oifs as $oif_id_1)
+       {
+               $args = $qmarks = array();
+               $query = 'REPLACE INTO PortCompat (type1, type2) VALUES ';
+               foreach ($oifs as $oif_id_2)
+               {
+                       $qmarks[] = '(?, ?)';
+                       $args[] = $oif_id_1;
+                       $args[] = $oif_id_2;
+               }
+               $query .= implode (', ', $qmarks);
+               usePreparedExecuteBlade ($query, $args);
+       }
+       return showFuncMessage (__FUNCTION__, 'OK');
+}
+
+$msgcode['delOIFCompatPack']['OK'] = 21;
+function delOIFCompatPack ()
+{
+       genericAssertion ('standard', 'enum/wdmstd');
+       global $wdm_packs;
+       $oifs = $wdm_packs[$_REQUEST['standard']]['oif_ids'];
+       foreach ($oifs as $oif_id_1)
+               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 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
@@ -2332,7 +2299,7 @@ function createVLANDomain ()
                        'description' => $sic['vdom_descr'],
                )
        );
-       $result = $result and usePreparedInsertBlade
+       usePreparedInsertBlade
        (
                'VLANDescription',
                array
@@ -2343,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;
@@ -2523,23 +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;
-$msgcode['process8021QRecalcRequest']['ERR'] = 157;
 function process8021QRecalcRequest ()
 {
-       global $sic;
-       if (! permitted (NULL, NULL, NULL, array (array ('tag' => '$op_recalc8021Q'))))
-               return buildRedirectURL (__FUNCTION__, 'ERR');
-       $counters = recalc8021QPorts ($sic['object_id']);
+       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;
@@ -2598,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
@@ -2608,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
@@ -2643,28 +2555,27 @@ 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;
-$msgcode['cloneVST']['ERR'] = 179;
 function cloneVST()
 {
        assertUIntArg ('mutex_rev', TRUE);
        assertUIntArg ('from_id');
-       $src_vst = getVLANSwitchTemplate ($_REQUEST['from_id']);
+       $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;
@@ -2706,26 +2617,40 @@ 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;
 function importDPData()
 {
-       global $sic;
+       global $sic, $dbxlink;
        assertUIntArg ('nports');
        $nignored = $ndone = 0;
        $POIFC = getPortOIFCompat();
        for ($i = 0; $i < $sic['nports']; $i++)
                if (array_key_exists ("do_${i}", $sic))
                {
-                       assertUIntArg ("pid1_${i}");
-                       assertUIntArg ("pid2_${i}");
-                       $porta = getPortInfo ($_REQUEST["pid1_${i}"]);
-                       $portb = getPortInfo ($_REQUEST["pid2_${i}"]);
+                       $params = array();
+                       assertStringArg ("ports_${i}");
+                       foreach (explode (',', $_REQUEST["ports_${i}"]) as $item)
+                       {
+                               $pair = explode (':', $item);
+                               if (count ($pair) != 2)
+                                       continue;
+                               $params[$pair[0]] = $pair[1];
+                       }
+                       if (! isset ($params['a_id']) || ! isset ($params['b_id']) ||
+                               ! intval ($params['a_id']) || ! intval ($params['b_id']))
+                               throw new InvalidArgException ("ports_${i}", $_REQUEST["ports_${i}"], "can not unpack port ids");
+                       
+                       $porta = getPortInfo ($params['a_id']);
+                       $portb = getPortInfo ($params['b_id']);
                        if
                        (
                                $porta['linked'] or
@@ -2736,26 +2661,61 @@ function importDPData()
                                $nignored++;
                                continue;
                        }
+                       $oif_a = intval (@$params['a_oif']); // these parameters are optional
+                       $oif_b = intval (@$params['b_oif']);
+                       
+                       $dbxlink->beginTransaction();
+                       if ($oif_a || $oif_b)
+                               try
+                               {
+                                       if ($oif_a)
+                                       {
+                                               commitUpdatePortOIF ($params['a_id'], $oif_a);
+                                               $porta['oif_id'] = $oif_a;
+                                       }
+                                       if ($oif_b)
+                                       {
+                                               commitUpdatePortOIF ($params['b_id'], $oif_b);
+                                               $portb['oif_id'] = $oif_b;
+                                       }
+                               }
+                               catch (RTDatabaseError $e)
+                               {
+                                       $dbxlink->rollBack();
+                                       $nignored++;
+                                       continue;
+                               }
+
                        foreach ($POIFC as $item)
                                if ($item['type1'] == $porta['oif_id'] and $item['type2'] == $portb['oif_id'])
                                {
-                                       linkPorts ($_REQUEST["pid1_${i}"], $_REQUEST["pid2_${i}"]);
+                                       linkPorts ($params['a_id'], $params['b_id']);
                                        $ndone++;
-                                       continue 2; // next port
+                                       $dbxlink->commit();
+                                       continue 2; //next port
                                }
-                       $nignored++;
+                       $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()
@@ -2774,6 +2734,66 @@ function getOpspec()
        return $ret;
 }
 
+function unlinkPort ()
+{
+       assertUIntArg ('port_id');
+       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()
 {
        $opspec = getOpspec();
@@ -2781,7 +2801,7 @@ function tableHandler()
        $columns = array();
        foreach (array ('arglist', 'set_arglist', 'where_arglist') as $listname)
        {
-               if (! is_array ($opspec[$listname]))
+               if (! array_key_exists ($listname, $opspec))
                        continue;
                foreach ($opspec[$listname] as $argspec)
                {
@@ -2813,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
@@ -2827,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);
 }
 
 ?>