r3864 copy recent exceptions-related commits into trunk
authorDenis Ovsienko <infrastation@yandex.ru>
Thu, 24 Jun 2010 16:32:31 +0000 (16:32 +0000)
committerDenis Ovsienko <infrastation@yandex.ru>
Thu, 24 Jun 2010 16:32:31 +0000 (16:32 +0000)
14 files changed:
ajax.php
inc/auth.php
inc/code.php
inc/config.php
inc/database.php
inc/exceptions.php
inc/functions.php
inc/gateways.php
inc/init.php
inc/interface.php
inc/ophandlers.php
index.php
process.php
render_image.php

index 751c17f..928507c 100644 (file)
--- a/ajax.php
+++ b/ajax.php
@@ -28,7 +28,7 @@ ob_end_flush();
 catch (Exception $e)
 {
        ob_end_clean();
-       printException($e);
+       echo "NAK\nRuntime exception";
 }
 
 ?>
index 2e487d9..0932176 100644 (file)
@@ -20,9 +20,9 @@ function authenticate ()
                $user_auth_src,
                $require_local_account;
        if (!isset ($user_auth_src) or !isset ($require_local_account))
-               throw new Exception ('secret.php: either user_auth_src or require_local_account are missing', E_MISCONFIGURED);
+               throw new RackTablesError ('secret.php: either user_auth_src or require_local_account are missing', RackTablesError::MISCONFIGURED);
        if (isset ($_REQUEST['logout']))
-               throw new Exception ('', E_NOT_AUTHENTICATED); // Reset browser credentials cache.
+               throw new RackTablesError ('', RackTablesError::NOT_AUTHENTICATED); // Reset browser credentials cache.
        switch ($user_auth_src)
        {
                case 'database':
@@ -34,7 +34,7 @@ function authenticate ()
                                !isset ($_SERVER['PHP_AUTH_PW']) or
                                !strlen ($_SERVER['PHP_AUTH_PW'])
                        )
-                               throw new Exception ('', E_NOT_AUTHENTICATED);
+                               throw new RackTablesError ('', RackTablesError::NOT_AUTHENTICATED);
                        $remote_username = $_SERVER['PHP_AUTH_USER'];
                        break;
                case 'httpd':
@@ -43,16 +43,16 @@ function authenticate ()
                                !isset ($_SERVER['REMOTE_USER']) or
                                !strlen ($_SERVER['REMOTE_USER'])
                        )
-                               throw new Exception ('The web-server didn\'t authenticate the user, although ought to do.', E_MISCONFIGURED);
+                               throw new RackTablesError ('The web-server didn\'t authenticate the user, although ought to do.', RackTablesError::MISCONFIGURED);
                        $remote_username = $_SERVER['REMOTE_USER'];
                        break;
                default:
-                       throw new Exception ('Invalid authentication source!', E_MISCONFIGURED);
+                       throw new RackTablesError ('Invalid authentication source!', RackTablesError::MISCONFIGURED);
                        die;
        }
        $userinfo = constructUserCell ($remote_username);
        if ($require_local_account and !isset ($userinfo['user_id']))
-               throw new Exception ('', E_NOT_AUTHENTICATED);
+               throw new RackTablesError ('', RackTablesError::NOT_AUTHENTICATED);
        $user_given_tags = $userinfo['etags'];
        $auto_tags = array_merge ($auto_tags, $userinfo['atags']);
        switch (TRUE)
@@ -81,9 +81,9 @@ function authenticate ()
                                (strlen ($ldap_dispname) ? $ldap_dispname : $remote_username); // then one from LDAP
                        return; // success
                default:
-                       throw new Exception ('Invalid authentication source!', E_MISCONFIGURED);
+                       throw new RackTablesError ('Invalid authentication source!', RackTablesError::MISCONFIGURED);
        }
-       throw new Exception ('', E_NOT_AUTHENTICATED);
+       throw new RackTablesError ('', RackTablesError::NOT_AUTHENTICATED);
 }
 
 // Merge accumulated tags into a single chain, add location-specific
@@ -291,7 +291,7 @@ function queryLDAPServer ($username, $password)
                $auth_user_name = $info[0]['dn'];
        }
        else
-               throw new Exception ('LDAP misconfiguration. Cannon build username for authentication.', E_MISCONFIGURED);
+               throw new RackTablesError ('LDAP misconfiguration. Cannon build username for authentication.', RackTablesError::MISCONFIGURED);
        if (array_key_exists ('options', $LDAP_options) and is_array ($LDAP_options['options']))
                foreach ($LDAP_options['options'] as $opt_code => $opt_value)
                        ldap_set_option ($connect, $opt_code, $opt_value);
index 38deca7..36c1ef8 100644 (file)
@@ -869,7 +869,7 @@ function processAdjustmentSentence ($modlist, &$chain)
                                $didChanges = TRUE;
                                break;
                        default: // HCF
-                               throw new Exception ('', E_BAD_RACKCODE);
+                               throw new RackTablesError ('invalid structure', RackTablesError::INTERNAL);
                }
        return $didChanges;
 }
index 587512b..89201b0 100644 (file)
@@ -54,14 +54,6 @@ define ('E_8021Q_VERSION_CONFLICT', 101);
 define ('E_8021Q_PULL_REMOTE_ERROR', 102);
 define ('E_8021Q_PUSH_REMOTE_ERROR', 103);
 define ('E_8021Q_SYNC_DISABLED', 104);
-define ('E_BAD_RACKCODE', 1);
-define ('E_INTERNAL', 2);
-define ('E_DB_WRITE_FAILED', 3);
-define ('E_NOT_AUTHENTICATED', 4);
-define ('E_NOT_AUTHORIZED', 5);
-define ('E_MISCONFIGURED', 6);
-define ('E_GW_FAILURE', 7);
-define ('E_DB_CONSTRAINT', 8);
 define ('VLAN_MIN_ID', 1);
 define ('VLAN_MAX_ID', 4094);
 define ('VLAN_DFL_ID', 1);
@@ -70,7 +62,7 @@ function loadConfigDefaults() {
        global $configCache;
        $configCache = loadConfigCache();
        if (!count ($configCache))
-               throw new Exception ('Failed to load configuration from the database.', E_INTERNAL);
+               throw new RackTablesError ('Failed to load configuration from the database.', RackTablesError::INTERNAL);
        foreach ($configCache as $varname => &$row) {
                $row['is_altered'] = 'no';
                if ($row['vartype'] == 'uint') $row['varvalue'] = 0 + $row['varvalue'];
@@ -95,7 +87,7 @@ function alterConfigWithUserPreferences() {
 function isConfigVarChanged($varname, $varvalue) {
        global $configCache;
        if (!isset ($configCache))
-               throw new Exception ('configuration cache is unavailable', E_INTERNAL);
+               throw new RackTablesError ('configuration cache is unavailable', RackTablesError::INTERNAL);
        if ($varname == '')
                throw new InvalidArgException('$varname', $varname, 'Empty variable name');
        if (!isset ($configCache[$varname])) return true;
@@ -111,7 +103,7 @@ function getConfigVar ($varname = '')
        // We assume the only point of cache init, and it is init.php. If it
        // has failed, we don't retry loading.
        if (!isset ($configCache))
-               throw new Exception ('configuration cache is unavailable', E_INTERNAL);
+               throw new RackTablesError ('configuration cache is unavailable', RackTablesError::INTERNAL);
        if ($varname == '')
                throw new InvalidArgException('$varname', $varname, 'Empty variable name');
        if (isset ($configCache[$varname]))
@@ -125,18 +117,18 @@ function setConfigVar ($varname = '', $varvalue = '', $softfail = FALSE)
 {
        global $configCache;
        if (!isset ($configCache))
-               throw new Exception ('configuration cache is unavailable', E_INTERNAL);
+               throw new RackTablesError ('configuration cache is unavailable', RackTablesError::INTERNAL);
        if (!strlen ($varname))
-               throw new InvalidArgException('$varname', $varname, 'Empty variable name');
+               throw new InvalidRequestArgException('$varname', $varname, 'Empty variable name');
        // We don't operate on unknown data.
        if (!isset ($configCache[$varname]))
-               throw new InvalidArgException('$varname', $varname, "Don't know how to handle '${varname}'");
+               throw new InvalidRequestArgException('$varname', $varname, "Don't know how to handle '${varname}'");
        if ($configCache[$varname]['is_hidden'] != 'no')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
        if (!strlen ($varvalue) && $configCache[$varname]['emptyok'] != 'yes')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' is configured to take non-empty value. Perhaps there was a reason to do so.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' is configured to take non-empty value. Perhaps there was a reason to do so.");
        if (strlen ($varvalue) && $configCache[$varname]['vartype'] == 'uint' && (!is_numeric ($varvalue) or $varvalue < 0 ))
-               throw new InvalidArgException('$varname', $varname, "'${varname}' can accept UINT values only");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' can accept UINT values only");
        // Update cache only if the changes went into DB.
        storeConfigVar ($varname, $varvalue);
        $configCache[$varname]['varvalue'] = $varvalue;
@@ -147,20 +139,20 @@ function setUserConfigVar ($varname = '', $varvalue = '')
        global $configCache;
        global $remote_username;
        if (!isset ($configCache))
-               throw new Exception ('configuration cache is unavailable', E_INTERNAL);
+               throw new RackTablesError ('configuration cache is unavailable', RackTablesError::INTERNAL);
        if (!strlen ($varname))
-               throw new InvalidArgException('$varname', $varname, 'Empty variable name');
+               throw new InvalidRequestArgException('$varname', $varname, 'Empty variable name');
        // We don't operate on unknown data.
        if (!isset ($configCache[$varname]))
-               throw new InvalidArgException('$varname', $varname, "Don't know how to handle '${varname}'");
+               throw new InvalidRequestArgException('$varname', $varname, "Don't know how to handle '${varname}'");
        if ($configCache[$varname]['is_userdefined'] != 'yes')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' cannot be changed by user.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' cannot be changed by user.");
        if ($configCache[$varname]['is_hidden'] != 'no')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
        if (!strlen ($varvalue) && $configCache[$varname]['emptyok'] != 'yes')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' is configured to take non-empty value. Perhaps there was a reason to do so.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' is configured to take non-empty value. Perhaps there was a reason to do so.");
        if (strlen ($varvalue) && $configCache[$varname]['vartype'] == 'uint' && (!is_numeric ($varvalue) or $varvalue < 0 ))
-               throw new InvalidArgException('$varname', $varname, "'${varname}' can accept UINT values only");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' can accept UINT values only");
        // Update cache only if the changes went into DB.
        storeUserConfigVar ($remote_username, $varname, $varvalue);
        $configCache[$varname]['varvalue'] = $varvalue;
@@ -171,16 +163,16 @@ function resetUserConfigVar ($varname = '')
        global $configCache;
        global $remote_username;
        if (!isset ($configCache))
-               throw new Exception ('configuration cache is unavailable', E_INTERNAL);
+               throw new RackTablesError ('configuration cache is unavailable', RackTablesError::INTERNAL);
        if (!strlen ($varname))
-               throw new InvalidArgException('$varname', $varname, 'Empty variable name');
+               throw new InvalidRequestArgException('$varname', $varname, 'Empty variable name');
        // We don't operate on unknown data.
        if (!isset ($configCache[$varname]))
-               throw new InvalidArgException('$varname', $varname, "Don't know how to handle '${varname}'");
+               throw new InvalidRequestArgException('$varname', $varname, "Don't know how to handle '${varname}'");
        if ($configCache[$varname]['is_userdefined'] != 'yes')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' cannot be changed by user.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' cannot be changed by user.");
        if ($configCache[$varname]['is_hidden'] != 'no')
-               throw new InvalidArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
+               throw new InvalidRequestArgException('$varname', $varname, "'${varname}' is a system variable and cannot be changed by user.");
        // Update cache only if the changes went into DB.
        deleteUserConfigVar ($remote_username, $varname);
 }
index 7e5672a..290f658 100644 (file)
@@ -239,7 +239,7 @@ function listCells ($realm, $parent_id = 0)
        }
        global $SQLSchema;
        if (!isset ($SQLSchema[$realm]))
-               throw new RealmNotFoundException ($realm);
+               throw new InvalidArgException ('realm', $realm);
        $SQLinfo = $SQLSchema[$realm];
        $qparams = array ($realm);
        $query = 'SELECT tag_id';
@@ -333,7 +333,7 @@ function spotEntity ($realm, $id)
                return $entityCache['partial'][$realm][$id];
        global $SQLSchema;
        if (!isset ($SQLSchema[$realm]))
-               throw new RealmNotFoundException ($realm);
+               throw new InvalidArgException ('realm', $realm);
        $SQLinfo = $SQLSchema[$realm];
        $query = 'SELECT tag_id';
        foreach ($SQLinfo['columns'] as $alias => $expression)
@@ -2130,7 +2130,7 @@ function convertPDOException ($e)
                $text = 'unknown error code ' . $e->errorInfo[1];
                break;
        }
-       return new Exception ($text, E_DB_CONSTRAINT);
+       return new RTDBConstraintError ($text);
 }
 
 // This is a swiss-knife blade to insert a record into a table.
@@ -3687,7 +3687,7 @@ function add8021QPort ($object_id, $port_name, $port)
                        array ('object_id' => $object_id, 'port_name' => $port_name, 'vlan_mode' => $port['mode'])
                )
        )
-               throw new Exception ('', E_DB_WRITE_FAILED);
+               throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
        upd8021QPort ('cached', $object_id, $port_name, $port);
        upd8021QPort ('desired', $object_id, $port_name, $port);
        return 1;
@@ -3709,7 +3709,7 @@ function del8021QPort ($object_id, $port_name)
                        array ('object_id' => $object_id, 'port_name' => $port_name)
                )
        )
-               throw new Exception ('', E_DB_WRITE_FAILED);
+               throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
        return 1;
 }
 
@@ -3732,20 +3732,20 @@ function upd8021QPort ($instance = 'desired', $object_id, $port_name, $port)
                array ($port['mode'], $object_id, $port_name)
        );
        if (FALSE === usePreparedDeleteBlade ($tablemap_8021q[$instance]['pav'], array ('object_id' => $object_id, 'port_name' => $port_name)))
-               throw new Exception ('', E_DB_WRITE_FAILED);
+               throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
        // FIXME: The goal is to INSERT as many rows as there are values in 'allowed' list
        // without wrapping each row with own INSERT (otherwise the SQL connection
        // instantly becomes the bottleneck).
        foreach ($port['allowed'] as $vlan_id)
                if (!usePreparedInsertBlade ($tablemap_8021q[$instance]['pav'], array ('object_id' => $object_id, 'port_name' => $port_name, 'vlan_id' => $vlan_id)))
-                       throw new Exception ('', E_DB_WRITE_FAILED);
+                       throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
        if
        (
                $port['native'] and
                in_array ($port['native'], $port['allowed']) and
                !usePreparedInsertBlade ($tablemap_8021q[$instance]['pnv'], array ('object_id' => $object_id, 'port_name' => $port_name, 'vlan_id' => $port['native']))
        )
-               throw new Exception ('', E_DB_WRITE_FAILED);
+               throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
        return 1;
 }
 
index e969ca7..0076063 100644 (file)
 <?php
 
-class EntityNotFoundException extends Exception {
-       private $entity;
-       private $id;
-       function __construct($entity, $id)
-       {
-               parent::__construct ("Object '$entity'#'$id' does not exist");
-               $this->entity = $entity;
-               $this->id = $id;
-       }
-       function getEntity()
+// The default approach is to treat an error as fatal, in which case
+// some message is output and the user is left there. Inheriting classes
+// represent more specific cases, some of which can be handled in a
+// "softer" way (see below).
+class RackTablesError extends Exception
+{
+       const INTERNAL = 2;
+       const DB_WRITE_FAILED = 3;
+       const NOT_AUTHENTICATED = 4;
+       const NOT_AUTHORIZED = 5;
+       const MISCONFIGURED = 6;
+       protected final function genHTMLPage ($title, $text)
        {
-               return $this->entity;
+               header ('Content-Type: text/html; charset=UTF-8');
+               echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
+               echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
+               echo "<head><title>${title}</title>";
+               echo "</head><body>${text}</body></html>";
        }
-       function getId()
+       public function dispatch()
        {
-               return $this->id;
+               $msgheader = array
+               (
+                       self::NOT_AUTHENTICATED => 'Not authenticated',
+                       self::MISCONFIGURED => 'Configuration error',
+                       self::INTERNAL => 'Internal error',
+                       self::DB_WRITE_FAILED => 'Database write failed',
+                       self::NOT_AUTHORIZED => 'Permission denied',
+               );
+               $msgbody = array
+               (
+                       self::NOT_AUTHENTICATED => '<h2>This system requires authentication. You should use a username and a password.</h2>',
+                       self::MISCONFIGURED => '<h2>Configuration error</h2><br>' . $this->message,
+                       self::INTERNAL => '<h2>Internal error</h2><br>' . $this->message,
+                       self::DB_WRITE_FAILED => '<h2>Database write failed</h2><br>' . $this->message,
+                       self::NOT_AUTHORIZED => '<h2>Permission denied</h2><br>' . $this->message,
+               );
+               switch ($this->code)
+               {
+               case self::NOT_AUTHENTICATED:
+                       header ('WWW-Authenticate: Basic realm="' . getConfigVar ('enterprise') . ' RackTables access"');
+                       header ("HTTP/1.1 401 Unauthorized");
+               case self::MISCONFIGURED:
+               case self::INTERNAL:
+               case self::DB_WRITE_FAILED:
+               case self::NOT_AUTHORIZED:
+                       $this->genHTMLPage ($msgheader[$this->code], $msgbody[$this->code]);
+                       break;
+               default:
+                       throw new RackTablesError ('Dispatching error, unknown code ' . $this->code, RackTablesError::INTERNAL);
+               }
        }
 }
 
-class RealmNotFoundException extends Exception {
-       private $realm;
-       function __construct($realm)
+// this simplifies construction of RackTablesError, but is never caught
+class EntityNotFoundException extends RackTablesError
+{
+       function __construct($entity, $id)
        {
-               parent::__construct ("Realm '$realm' does not exist");
-               $this->realm = $realm;
+               parent::__construct ("Object '$entity'#'$id' does not exist");
        }
-       function getRealm()
+       public function dispatch()
        {
-               return $this->realm;
+               RackTablesError::genHTMLPage ('Missing record', "<h2>Missing record</h2><br>" . $this->message);
        }
 }
 
-class InvalidArgException extends Exception
+// this simplifies construction of RackTablesError, but is never caught
+class InvalidArgException extends RackTablesError
 {
-       private $name;
-       private $value;
-       private $reason;
        function __construct ($name, $value, $reason=NULL)
        {
                $message = "Argument '${name}' of value '".var_export($value,true)."' is invalid.";
-               if (!is_null($reason)) {
+               if (!is_null($reason))
                        $message .= ' ('.$reason.')';
-               }
-               parent::__construct ($message);
-               $this->name = $name;
-               $this->value = $value;
-       }
-       function getName()
-       {
-               return $this->name;
-       }
-       function getValue()
-       {
-               return $this->value;
+               parent::__construct ($message, parent::INTERNAL);
        }
 }
 
-class InvalidRequestArgException extends Exception
+// this simplifies construction and helps in catching "soft"
+// errors (invalid input from the user)
+class InvalidRequestArgException extends RackTablesError
 {
-       private $name;
-       private $value;
-       private $reason;
        function __construct ($name, $value, $reason=NULL)
        {
                $message = "Request parameter '${name}' of value '".var_export($value,true)."' is invalid.";
-               if (!is_null($reason)) {
+               if (!is_null($reason))
                        $message .= ' ('.$reason.')';
-               }
                parent::__construct ($message);
-               $this->name = $name;
-               $this->value = $value;
        }
-       function getName()
+       public function dispatch()
        {
-               return $this->name;
+               RackTablesError::genHTMLPage ('Assertion failed', '<h2>Assertion failed</h2><br>' . $this->message);
        }
-       function getValue()
+}
+
+// this wraps certain known PDO errors and is caught in process.php
+// as a "soft" error
+class RTDBConstraintError extends RackTablesError
+{
+       public function dispatch()
+       {
+               RackTablesError::genHTMLPage ('Database constraint violation', '<h2>Constraint violation</h2><br>' . $this->message);
+       }
+}
+
+// gateway failure is a common case of a "soft" error, some functions do catch this
+class RTGatewayError extends RackTablesError
+{
+       public function dispatch()
        {
-               return $this->value;
+               RackTablesError::genHTMLPage ('Gateway error', '<h2>Gateway error</h2><br>' . $this->message);
        }
 }
 
@@ -116,20 +149,6 @@ function stringTrace($trace)
        return $ret;
 }
 
-function print404($e)
-{
-       header("HTTP/1.1 404 Not Found");
-       header ('Content-Type: text/html; charset=UTF-8');
-       echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
-       echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
-       echo "<head><title> Exception </title>\n";
-       printPageHeaders();
-       echo '</head> <body>';
-       echo '<h2>Object: '.$e->getEntity().'#'.$e->getId().' not found</h2>';
-       echo '</body></html>';
-
-}
-
 function printPDOException($e)
 {
        header("HTTP/1.1 500 Internal Server Error");
@@ -184,36 +203,8 @@ function printGenericException($e)
 
 function printException($e)
 {
-       if (get_class ($e) == 'Exception')
-               switch ($e->getCode())
-               {
-               case E_NOT_AUTHENTICATED:
-                       header ('WWW-Authenticate: Basic realm="' . getConfigVar ('enterprise') . ' RackTables access"');
-                       header ("HTTP/1.1 401 Unauthorized");
-               case E_MISCONFIGURED:
-               case E_DB_CONSTRAINT:
-                       header ('Content-Type: text/html; charset=UTF-8');
-                       echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
-                       echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
-                       $msgheader = array
-                       (
-                               E_NOT_AUTHENTICATED => 'Not authenticated',
-                               E_MISCONFIGURED => 'Configuration error',
-                               E_DB_CONSTRAINT => 'Constraint violation',
-                       );
-                       $msgbody = array
-                       (
-                               E_NOT_AUTHENTICATED => '<h2>This system requires authentication. You should use a username and a password.</h2>',
-                               E_MISCONFIGURED => '<h2>Configuration error</h2><br>' . $e->getMessage(),
-                               E_DB_CONSTRAINT => '<h2>Constraint violation</h2><br>' . $e->getMessage(),
-                       );
-                       echo '<head><title>' . $msgheader[$e->getCode()] . '</title>';
-                       echo '</head><body>' . $msgbody[$e->getCode()] . '</body></html>';
-                       return;
-               default:
-               }
-       if (get_class($e) == 'EntityNotFoundException')
-               print404($e);
+       if ($e instanceof RackTablesError)
+               $e->dispatch();
        elseif (get_class($e) == 'PDOException')
                printPDOException($e);
        else
index a7d7a52..f214d33 100644 (file)
@@ -1665,7 +1665,7 @@ function iptree_embed (&$node, $pfx)
                return;
        }
        if ($node['mask'] == $pfx['mask'])
-               throw new Exception ('the recurring loop lost control', E_INTERNAL);
+               throw new RackTablesError ('the recurring loop lost control', RackTablesError::INTERNAL);
 
        // split?
        if (!isset ($node['right']))
@@ -1688,7 +1688,7 @@ function iptree_embed (&$node, $pfx)
        elseif (($node['right']['ip_bin'] & binMaskFromDec ($node['right']['mask'])) == ($pfx['ip_bin'] & binMaskFromDec ($node['left']['mask'])))
                $self ($node['right'], $pfx);
        else
-               throw new Exception ('cannot decide between left and right', E_INTERNAL);
+               throw new RackTablesError ('cannot decide between left and right', RackTablesError::INTERNAL);
 }
 
 function treeApplyFunc (&$tree, $func = '', $stopfunc = '')
@@ -2993,7 +2993,7 @@ function exec8021QDeploy ($object_id, $do_push)
        {
                $R = getRunning8021QConfig ($vswitch['object_id']);
        }
-       catch (Exception $e)
+       catch (RTGatewayError $e)
        {
                usePreparedExecuteBlade
                (
@@ -3101,7 +3101,7 @@ function exec8021QDeploy ($object_id, $do_push)
                                        array (E_8021Q_NOERROR, $vswitch['object_id'])
                                );
                        }
-                       catch (Exception $r)
+                       catch (RTGatewayError $r)
                        {
                                usePreparedExecuteBlade
                                (
index 92ddbd9..f5f1dd3 100644 (file)
@@ -86,14 +86,14 @@ function queryGateway ($gwname, $questions)
 
        $retval = proc_close ($gateway);
        if ($retval != 0)
-               throw new Exception ("gateway failed with code ${retval}", E_GW_FAILURE);
+               throw new RTGatewayError ("gateway failed with code ${retval}");
        if (!count ($answers))
-               throw new Exception ('no response from gateway', E_GW_FAILURE);
+               throw new RTGatewayError ('no response from gateway');
        if (count ($answers) != count ($questions))
-               throw new Exception ('protocol violation', E_GW_FAILURE);
+               throw new RTGatewayError ('protocol violation');
        foreach ($answers as $a)
                if (strpos ($a, 'OK!') !== 0)
-                       throw new Exception ("subcommand failed with status: ${a}", E_GW_FAILURE);
+                       throw new RTGatewayError ("subcommand failed with status: ${a}");
        return $answers;
 }
 
@@ -109,9 +109,9 @@ function getSwitchVLANs ($object_id = 0)
        $objectInfo = spotEntity ('object', $object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
        if (count ($endpoints) == 0)
-               throw new Exception ('no management address set', E_GW_FAILURE);
+               throw new RTGatewayError ('no management address set');
        if (count ($endpoints) > 1)
-               throw new Exception ('cannot pick management address', E_GW_FAILURE);
+               throw new RTGatewayError ('cannot pick management address');
        $hwtype = $swtype = 'unknown';
        foreach (getAttrValues ($object_id) as $record)
        {
@@ -130,11 +130,11 @@ function getSwitchVLANs ($object_id = 0)
        );
        $data = queryGateway ('switchvlans', $commands);
        if (strpos ($data[0], 'OK!') !== 0)
-               throw new Exception ("gateway failed with status: ${data[0]}.", E_GW_FAILURE);
+               throw new RTGatewayError ("gateway failed with status: ${data[0]}.");
        // Now we have VLAN list in $data[1] and port list in $data[2]. Let's sort this out.
        $tmp = array_unique (explode (';', substr ($data[1], strlen ('OK!'))));
        if (count ($tmp) == 0)
-               throw new Exception ('gateway returned no records', E_GW_FAILURE);
+               throw new RTGatewayError ('gateway returned no records');
        $vlanlist = array();
        foreach ($tmp as $record)
        {
@@ -149,7 +149,7 @@ function getSwitchVLANs ($object_id = 0)
                $portlist[] = array ('portname' => $portname, 'status' => $status, 'vlanid' => $vlanid);
        }
        if (count ($portlist) == 0)
-               throw new Exception ('gateway returned no records', E_GW_FAILURE);
+               throw new RTGatewayError ('gateway returned no records');
        $maclist = array();
        foreach (explode (';', substr ($data[3], strlen ('OK!'))) as $pair)
        {
@@ -224,7 +224,7 @@ function gwSendFile ($endpoint, $handlername, $filetext = array())
                {
                        foreach ($tmpnames as $name)
                                unlink ($name);
-                       throw new Exception ('failed to write to temporary file', E_GW_FAILURE);
+                       throw new RTGatewayError ('failed to write to temporary file');
                }
                $command .= " ${name}";
        }
@@ -259,9 +259,9 @@ function gwSendFileToObject ($object_id = 0, $handlername, $filetext = '')
        $objectInfo = spotEntity ('object', $object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
        if (count ($endpoints) == 0)
-               throw new Exception ('no management address set', E_GW_FAILURE);
+               throw new RTGatewayError ('no management address set');
        if (count ($endpoints) > 1)
-               throw new Exception ('cannot pick management address', E_GW_FAILURE);
+               throw new RTGatewayError ('cannot pick management address');
        gwSendFile (str_replace (' ', '+', $endpoints[0]), $handlername, array ($filetext));
 }
 
@@ -328,14 +328,14 @@ function getRunning8021QConfig ($object_id)
        // Once there is no default VLAN in the parsed data, it means
        // something else was parsed instead of config text.
        if (!in_array (VLAN_DFL_ID, $ret['vlanlist']))
-               throw new Exception ('communication with device failed', E_GW_FAILURE);
+               throw new RTGatewayError ('communication with device failed');
        return $ret;
 }
 
 function setDevice8021QConfig ($object_id, $pseudocode)
 {
        if ('' == $breed = detectDeviceBreed ($object_id))
-               throw new Exception ('device breed unknown', E_GW_FAILURE);
+               throw new RTGatewayError ('device breed unknown');
        global $gwpushxlator;
        // FIXME: this is a perfect place to log intended changes
        gwDeployDeviceConfig ($object_id, $breed, unix2dos ($gwpushxlator[$breed] ($pseudocode)));
@@ -345,16 +345,16 @@ function gwRetrieveDeviceConfig ($object_id, $command)
 {
        global $gwrxlator;
        if (!array_key_exists ($command, $gwrxlator))
-               throw new Exception ('command unknown', E_GW_FAILURE);
+               throw new RTGatewayError ('command unknown');
        $breed = detectDeviceBreed ($object_id);
        if (!array_key_exists ($breed, $gwrxlator[$command]))
-               throw new Exception ('device breed unknown', E_GW_FAILURE);
+               throw new RTGatewayError ('device breed unknown');
        $objectInfo = spotEntity ('object', $object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
        if (count ($endpoints) == 0)
-               throw new Exception ('no management address set', E_GW_FAILURE);
+               throw new RTGatewayError ('no management address set');
        if (count ($endpoints) > 1)
-               throw new Exception ('cannot pick management address', E_GW_FAILURE);
+               throw new RTGatewayError ('cannot pick management address');
        $endpoint = str_replace (' ', '\ ', str_replace (' ', '+', $endpoints[0]));
        $tmpfilename = tempnam ('', 'RackTables-deviceconfig-');
        $outputlines = queryGateway
@@ -375,15 +375,15 @@ function gwDeployDeviceConfig ($object_id, $breed, $text)
        $objectInfo = spotEntity ('object', $object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
        if (count ($endpoints) == 0)
-               throw new Exception ('no management address set', E_GW_FAILURE);
+               throw new RTGatewayError ('no management address set');
        if (count ($endpoints) > 1)
-               throw new Exception ('cannot pick management address', E_GW_FAILURE);
+               throw new RTGatewayError ('cannot pick management address');
        $endpoint = str_replace (' ', '\ ', str_replace (' ', '+', $endpoints[0]));
        $tmpfilename = tempnam ('', 'RackTables-deviceconfig-');
        if (FALSE === file_put_contents ($tmpfilename, $text))
        {
                unlink ($tmpfilename);
-               throw new Exception ('failed to write to temporary file', E_GW_FAILURE);
+               throw new RTGatewayError ('failed to write to temporary file');
        }
        $outputlines = queryGateway
        (
@@ -1306,13 +1306,13 @@ function xos12Read8021QConfig ($input)
                {
                case (preg_match ('/^create vlan "([[:alnum:]]+)"$/', $line, $matches)):
                        if (!preg_match ('/^VLAN[[:digit:]]+$/', $matches[1]))
-                               throw new Exception ('unsupported VLAN name ' . $matches[1], E_GW_FAILURE);
+                               throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
                        break;
                case (preg_match ('/^configure vlan ([[:alnum:]]+) tag ([[:digit:]]+)$/', $line, $matches)):
                        if (strtolower ($matches[1]) == 'default')
-                               throw new Exception ('default VLAN tag must be 1', E_GW_FAILURE);
+                               throw new RTGatewayError ('default VLAN tag must be 1');
                        if ($matches[1] != 'VLAN' . $matches[2])
-                               throw new Exception ("VLAN name ${matches[1]} does not match its tag ${matches[2]}", E_GW_FAILURE);
+                               throw new RTGatewayError ("VLAN name ${matches[1]} does not match its tag ${matches[2]}");
                        $ret['vlanlist'][] = $matches[2];
                        break;
                case (preg_match ('/^configure vlan ([[:alnum:]]+) add ports (.+) (tagged|untagged) */', $line, $matches)):
@@ -1320,7 +1320,7 @@ function xos12Read8021QConfig ($input)
                        if ($matches[1] == 'Default')
                                $matches[1] = 'VLAN1';
                        if (!preg_match ('/^VLAN([[:digit:]]+)$/', $matches[1], $submatch))
-                               throw new Exception ('unsupported VLAN name ' . $matches[1], E_GW_FAILURE);
+                               throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
                        $vlan_id = $submatch[1];
                        foreach (iosParseVLANString ($matches[2]) as $port_name)
                        {
index ce7d469..10eb29c 100644 (file)
@@ -59,12 +59,12 @@ if (file_exists ('inc/secret.php'))
        require_once 'inc/secret.php';
 else
 {
-       throw new Exception
+       throw new RackTablesError
        (
                "Database connection parameters are read from inc/secret.php file, " .
                "which cannot be found.<br>You probably need to complete the installation " .
                "procedure by following <a href='install.php'>this link</a>.",
-               E_MISCONFIGURED
+               RackTablesError::MISCONFIGURED
        );
 }
 
@@ -75,7 +75,7 @@ try
 }
 catch (PDOException $e)
 {
-       throw new Exception ("Database connection failed:\n\n" . $e->getMessage(), E_INTERNAL);
+       throw new RackTablesError ("Database connection failed:\n\n" . $e->getMessage(), RackTablesError::INTERNAL);
 }
 $dbxlink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 $dbxlink->exec ("set names 'utf8'");
@@ -114,7 +114,7 @@ if ($dbver != CODE_VERSION)
 }
 
 if (!mb_internal_encoding ('UTF-8'))
-       throw new Exception ('Failed setting multibyte string encoding to UTF-8', E_INTERNAL);
+       throw new RackTablesError ('Failed setting multibyte string encoding to UTF-8', RackTablesError::INTERNAL);
 
 loadConfigDefaults();
 
@@ -136,9 +136,10 @@ else
 }
 
 // Depending on the 'result' value the 'load' carries either the
-// parse tree or error message.
+// parse tree or error message. The latter case is a bug, because
+// RackCode saving function was supposed to validate its input.
 if ($rackCode['result'] != 'ACK')
-       throw new Exception ($rackCode['load'], E_BAD_RACKCODE);
+       throw new RackTablesError ($rackCode['load'], RackTablesError::INTERNAL);
 $rackCode = $rackCode['load'];
 // Only call buildPredicateTable() once and save the result, because it will remain
 // constant during one execution for constraints processing.
index e337db7..932fed7 100644 (file)
@@ -1558,7 +1558,7 @@ function showMessageOrError ()
                                104 => array ('code' => 'error', 'format' => "Error updating user account '%s'"),
                                105 => array ('code' => 'error', 'format' => 'default VLAN cannot be changed'),
 // ...
-// ...
+                               107 => array ('code' => 'error', 'format' => 'Assertion failed: %s'),
                                108 => array ('code' => 'error', 'format' => 'Constraint error: %s'),
                                109 => array ('code' => 'error', 'format' => 'Update failed!'),
                                110 => array ('code' => 'error', 'format' => 'Supplement failed!'),
@@ -1944,7 +1944,7 @@ function renderHistory ($object_type, $object_id)
                        $extra = 8;
                        break;
                default:
-                       throw new RealmNotFoundException($object_type);
+                       throw new InvalidArgException ('object_type', $object_type);
        }
        $result = usePreparedSelectBlade ($query, array ($object_id));
        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
@@ -2911,7 +2911,7 @@ function renderSearchResults ()
        if (!strlen ($terms))
                throw new InvalidRequestArgException('q', $_REQUEST['q'], 'Search string cannot be empty.');
        if (!permitted ('depot', 'default'))
-               throw new Exception ('You are not authorized for viewing information about objects.', E_NOT_AUTHORIZED);
+               throw new RackTablesError ('You are not authorized for viewing information about objects.', RackTablesError::NOT_AUTHORIZED);
        $nhits = 0;
        if (preg_match (RE_IP4_ADDR, $terms))
        // Search for IPv4 address.
@@ -5951,7 +5951,7 @@ function renderCell ($cell)
                echo "</td></tr></table>";
                break;
        default:
-               throw new RealmNotFoundException($cell['realm']);
+               throw new InvalidArgException ('realm', $cell['realm']);
        }
 }
 
@@ -7708,14 +7708,10 @@ function renderDiscoveredNeighbors ($object_id)
        {
                $neighbors = sortPortList (gwRetrieveDeviceConfig ($object_id, $opcode_by_tabno[$tabno]));
        }
-       catch (Exception $e)
+       catch (RTGatewayError $e)
        {
-               if ($e->getCode() == E_GW_FAILURE)
-               {
-                       showWarning ($e->getMessage(), __FUNCTION__);
-                       return;
-               }
-               throw $e;
+               showWarning ($e->getMessage(), __FUNCTION__);
+               return;
        }
        $mydevice = spotEntity ('object', $object_id);
        amplifyCell ($mydevice);
index 95c5335..d1ea528 100644 (file)
@@ -952,7 +952,6 @@ function useupPort ()
 }
 
 $msgcode['updateUI']['OK'] = 56;
-$msgcode['updateUI']['ERR'] = 125;
 function updateUI ()
 {
        assertUIntArg ('num_vars');
@@ -967,19 +966,13 @@ function updateUI ()
                // If form value = value in DB, don't bother updating DB
                if (!isConfigVarChanged($varname, $varvalue))
                        continue;
-
-               // Note if the queries succeed or not, it determines which page they see.
-               try {
-                       setConfigVar ($varname, $varvalue, TRUE);
-               } catch (InvalidArgException $e) {
-                       return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
-               }
+               // any exceptions will be handled by process.php
+               setConfigVar ($varname, $varvalue, TRUE);
        }
        return buildRedirectURL (__FUNCTION__, 'OK');
 }
 
 $msgcode['saveMyPreferences']['OK'] = 56;
-$msgcode['saveMyPreferences']['ERR'] = 125;
 function saveMyPreferences ()
 {
        assertUIntArg ('num_vars');
@@ -994,34 +987,19 @@ function saveMyPreferences ()
                // If form value = value in DB, don't bother updating DB
                if (!isConfigVarChanged($varname, $varvalue))
                        continue;
-               // Note if the queries succeed or not, it determines which page they see.
-               try {
-                       setUserConfigVar ($varname, $varvalue);
-               } catch (InvalidArgException $e) {
-                       return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
-               }
+               setUserConfigVar ($varname, $varvalue);
        }
        return buildRedirectURL (__FUNCTION__, 'OK');
 }
 
 $msgcode['resetMyPreference']['OK'] = 56;
-$msgcode['resetMyPreference']['ERR'] = 125;
 function resetMyPreference ()
 {
        assertStringArg ("varname");
-       $varname = $_REQUEST["varname"];
-
-       try {
-               resetUserConfigVar ($varname);
-       } catch (InvalidArgException $e) {
-               return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
-       }
+       resetUserConfigVar ($_REQUEST["varname"]);
        return buildRedirectURL (__FUNCTION__, 'OK');
 }
 
-
-
-
 $msgcode['resetUIConfig']['OK'] = 57;
 function resetUIConfig()
 {
@@ -1674,12 +1652,9 @@ function submitSLBConfig ()
        {
                gwSendFileToObject ($_REQUEST['object_id'], 'slbconfig', html_entity_decode ($newconfig, ENT_QUOTES, 'UTF-8'));
        }
-       catch (Exception $e)
+       catch (RTGatewayError $e)
        {
-               if ($e->getCode() == E_GW_FAILURE)
-                       return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
-               else
-                       throw $e;
+               return buildRedirectURL (__FUNCTION__, 'ERR', array ($e->getMessage()));
        }
        return buildRedirectURL (__FUNCTION__, 'OK', array ('slbconfig'));
 }
@@ -1855,7 +1830,7 @@ function addFileToEntity ()
 {
        global $page, $pageno, $etype_by_pageno;
        if (!isset ($etype_by_pageno[$pageno]) or !isset ($page[$pageno]['bypass']))
-               throw new Exception ('dispatching failure', E_INTERNAL);
+               throw new RackTablesError ('dispatching failure', RackTablesError::INTERNAL);
        $realm = $etype_by_pageno[$pageno];
        $bypass = $page[$pageno]['bypass'];
        assertUIntArg ($bypass);
@@ -1884,7 +1859,7 @@ function linkFileToEntity ()
        assertUIntArg ('file_id');
        global $page, $pageno, $etype_by_pageno;
        if (!isset ($etype_by_pageno[$pageno]) or !isset ($page[$pageno]['bypass']))
-               throw new Exception ('dispatching failure', E_INTERNAL);
+               throw new RackTablesError ('dispatching failure', RackTablesError::INTERNAL);
        $entity_type = $etype_by_pageno[$pageno];
        $bypass_name = $page[$pageno]['bypass'];
        assertUIntArg ($bypass_name);
index 447179f..0bb0ab5 100644 (file)
--- a/index.php
+++ b/index.php
@@ -70,7 +70,7 @@ if (isset ($tabhandler[$pageno][$tabno]))
                                assertStringArg ($page[$pageno]['bypass']);
                                break;
                        default:
-                               throw new Exception ('Dispatching error for bypass parameter', E_INTERNAL);
+                               throw new RackTablesError ('Dispatching error for bypass parameter', RackTablesError::INTERNAL);
                }
                showMessageOrError();
                call_user_func ($tabhandler[$pageno][$tabno], $_REQUEST[$page[$pageno]['bypass']]);
@@ -87,7 +87,7 @@ elseif (isset ($page[$pageno]['handler']))
        $page[$pageno]['handler'] ($tabno);
 }
 else
-       throw new Exception ("Failed to find handler for page '${pageno}', tab '${tabno}'", E_INTERNAL);
+       throw new RackTablesError ("Failed to find handler for page '${pageno}', tab '${tabno}'", RackTablesError::INTERNAL);
 ?>
        </td>
        </tr>
index d3e2bb5..555ba3e 100644 (file)
@@ -12,11 +12,11 @@ $op = $_REQUEST['op'];
 prepareNavigation();
 // FIXME: find a better way to handle this error
 if ($op == 'addFile' && !isset($_FILES['file']['error']))
-       throw new Exception ('File upload error, check upload_max_filesize in php.ini', E_INTERNAL);
+       throw new RackTablesError ('File upload error, check upload_max_filesize in php.ini', RackTablesError::MISCONFIGURED);
 fixContext();
 
 if (!isset ($ophandler[$pageno][$tabno][$op]) or !function_exists ($ophandler[$pageno][$tabno][$op]))
-       throw new Exception ("Invalid navigation data for '${pageno}-${tabno}-${op}'", E_INTERNAL);
+       throw new RackTablesError ("Invalid navigation data for '${pageno}-${tabno}-${op}'", RackTablesError::INTERNAL);
 
 // We have a chance to handle an error before starting HTTP header.
 if (!isset ($delayauth[$pageno][$tabno][$op]) and !permitted())
@@ -25,18 +25,27 @@ else
 {
        $location = call_user_func ($ophandler[$pageno][$tabno][$op]);
        if (!strlen ($location))
-               throw new Exception ('Operation handler failed to return its status', E_INTERNAL);
+               throw new RackTablesError ('Operation handler failed to return its status', RackTablesError::INTERNAL);
 }
 header ("Location: " . $location);
 ob_end_flush();
 }
+// "soft" failures only require a short error message
+catch (InvalidRequestArgException $e)
+{
+       ob_end_clean();
+       header ('Location: ' . buildWideRedirectURL (oneLiner (107, array ($e->getMessage()))));
+}
+catch (RTDBConstraintError $e)
+{
+       ob_end_clean();
+       header ('Location: ' . buildWideRedirectURL (oneLiner (108, array ($e->getMessage()))));
+}
+// the rest ends up in a dedicated page
 catch (Exception $e)
 {
        ob_end_clean();
-       if ($e->getCode() == E_DB_CONSTRAINT)
-               header ('Location: ' . buildWideRedirectURL (oneLiner (108, array ($e->getMessage()))));
-       else
-               printException($e);
+       printException ($e);
 }
 
 ?>
index 035c325..8a21856 100644 (file)
@@ -41,11 +41,9 @@ ob_end_flush();
 catch (Exception $e)
 {
        ob_end_clean();
-       printException($e);
+       renderError();
 }
 
-
-
 //------------------------------------------------------------------------
 function renderError ()
 {