diff --git a/htdocs/includes/pear/Auth/Auth.php b/htdocs/includes/pear/Auth/Auth.php new file mode 100644 index 00000000000..ed129d70a17 --- /dev/null +++ b/htdocs/includes/pear/Auth/Auth.php @@ -0,0 +1,816 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +require_once DOL_DOCUMENT_ROOT."/includes/pear/PEAR.php"; +//require_once "PEAR.php"; + +define("AUTH_IDLED", -1); +define("AUTH_EXPIRED", -2); +define("AUTH_WRONG_LOGIN", -3); + +/** + * PEAR::Auth + * + * The PEAR::Auth class provides methods for creating an + * authentication system using PHP. + * + * @author Martin Jansen + * @package Auth + * @version $Revision$ + */ +class DOLIAuth { + + /** + * Auth lifetime in seconds + * + * If this variable is set to 0, auth never expires + * + * @var integer + * @see setExpire(), checkAuth() + */ + var $expire = 0; + + /** + * Has the auth session expired? + * + * @var bool + * @see checkAuth(), drawLogin() + */ + var $expired = false; + + /** + * Maximum time of idleness in seconds + * + * The difference to $expire is, that the idletime gets + * refreshed each time, checkAuth() is called. If this + * variable is set to 0, idle time is never checked. + * + * @var integer + * @see setIdle(), checkAuth() + */ + var $idle = 0; + + /** + * Is the maximum idletime over? + * + * @var boolean + * @see checkAuth(), drawLogin(); + */ + var $idled = false; + + /** + * Storage object + * + * @var object + * @see Auth(), validateLogin() + */ + var $storage = ""; + + /** + * Function defined by the user, that creates the login screen + * + * @var string + */ + var $loginFunction = ""; + + /** + * Should the login form be displayed? + * + * @var bool + * @see setShowlogin() + */ + var $showLogin = true; + + /** + * Current authentication status + * + * @var string + */ + var $status = ""; + + /** + * Username + * + * @var string + */ + var $username = ""; + + /** + * Password + * + * @var string + */ + var $password = ""; + + /** + * Login callback function name + * + * @var string + * @see setLoginCallback() + */ + var $loginCallback = ""; + + /** + * Logout callback function name + * + * @var string + * @see setLogoutCallback() + */ + var $logoutCallback = ""; + + // {{{ Constructor + + /** + * Constructor + * + * Set up the storage driver. + * + * @param string Type of the storage driver + * @param mixed Additional options for the storage driver + * (example: if you are using DB as the storage + * driver, you have to pass the dsn string here) + * + * @param string Name of the function that creates the login form + * @param boolean Should the login form be displayed if neccessary? + * @return void + */ + function DOLIAuth($storageDriver, $options = "", $loginFunction = "", $showLogin = true) + { + if ($loginFunction != "" && function_exists($loginFunction)) { + $this->loginFunction = $loginFunction; + } + + if (is_bool($showLogin)) { + $this->showLogin = $showLogin; + } + + if (is_object($storageDriver)) { + $this->storage =& $storageDriver; + } + else{ + $this->storage = $this->_factory($storageDriver, $options); + } + } + + // }}} + // {{{ _factory() + + /** + * Return a storage driver based on $driver and $options + * + * @access private + * @static + * @param string $driver Type of storage class to return + * @param string $options Optional parameters for the storage class + * @return object Object Storage object + */ + function _factory($driver, $options = "") + { + $storage_path = "Auth/Container/" . $driver . ".php"; + $storage_class = "Auth_Container_" . $driver; + + require_once $storage_path; + + return new $storage_class($options); + } + + // }}} + // {{{ assignData() + + /** + * Assign data from login form to internal values + * + * This function takes the values for username and password + * from $HTTP_POST_VARS and assigns them to internal variables. + * If you wish to use another source apart from $HTTP_POST_VARS, + * you have to derive this function. + * + * @access private + * @global $HTTP_POST_VARS + * @see Auth + * @return void + */ + function assignData() + { + $post = &$this->_importGlobalVariable("post"); + + if (isset($post['username']) && $post['username'] != "") { + $this->username = (get_magic_quotes_gpc() == 1 ? stripslashes($post['username']) : $post['username']); + } + + if (isset($post['password']) && $post['password'] != "") { + $this->password = (get_magic_quotes_gpc() == 1 ? stripslashes($post['password']) : $post['password'] ); + } + + } + + // }}} + // {{{ start() + + /** + * Start new auth session + * + * @access public + * @return void + */ + function start() + { + $this->assignData(); + + session_start(); + + if (!$this->checkAuth()) { + $this->login(); + } + } + + // }}} + // {{{ login() + + /** + * Login function + * + * @access private + * @return void + */ + function login() + { + $login_ok = false; + + /** + * When the user has already entered a username, + * we have to validate it. + */ + if (!empty($this->username)) { + if (true === $this->storage->fetchData($this->username, $this->password)) { + $login_ok = true; + } + } + + if (!empty($this->username) && $login_ok) { + $this->setAuth($this->username); + if (!empty($this->loginCallback)) { + call_user_func($this->loginCallback,$this->username); + } + } + + /** + * If the login failed or the user entered no username, + * output the login screen again. + */ + if (!empty($this->username) && !$login_ok) { + $this->status = AUTH_WRONG_LOGIN; + } + + if ((empty($this->username) || !$login_ok) && $this->showLogin) { + $this->drawLogin($this->storage->activeUser); + return; + } + } + + // }}} + // {{{ setExpire() + + /** + * Set the maximum expire time + * + * @access public + * @param integer time in seconds + * @param bool add time to current expire time or not + * @return void + */ + function setExpire($time, $add = false) + { + if ($add) { + $this->expire += $time; + } else { + $this->expire = $time; + } + } + + // }}} + // {{{ setIdle() + + /** + * Set the maximum idle time + * + * @access public + * @param integer time in seconds + * @param bool add time to current maximum idle time or not + * @return void + */ + function setIdle($time, $add = false) + { + if ($add) { + $this->idle += $time; + } else { + $this->idle = $time; + } + } + + // }}} + // {{{ setSessionname() + + /** + * Set name of the session to a customized value. + * + * If you are using multiple instances of PEAR::Auth + * on the same domain, you can change the name of + * session per application via this function. + * + * @access public + * @param string New name for the session + * @return void + */ + function setSessionname($name = "PHPSESSID") + { + @session_name($name); + } + + // }}} + // {{{ setShowLogin() + + /** + * Should the login form be displayed if neccessary? + * + * @access public + * @param bool show login form or not + * @return void + */ + function setShowLogin($showLogin = true) + { + $this->showLogin = $showLogin; + } + + /** + * Register a callback function to be called on user login. + * The function will receive a single parameter, the username. + * + * @access public + * @param string callback function name + * @return void + * @see setLogoutCallback() + */ + function setLoginCallback($loginCallback) + { + $this->loginCallback = $loginCallback; + } + + /** + * Register a callback function to be called on user logout. + * The function will receive a single parameter, the username. + * + * @access public + * @param string callback function name + * @return void + * @see setLoginCallback() + */ + function setLogoutCallback($logoutCallback) + { + $this->logoutCallback = $logoutCallback; + } + + // }}} + // {{{ setAuthData() + + /** + * Register additional information that is to be stored + * in the session. + * + * @access public + * @param string Name of the data field + * @param mixed Value of the data field + * @param boolean Should existing data be overwritten? (default + * is true) + * @return void + */ + function setAuthData($name, $value, $overwrite = true) + { + $session = &DOLIAuth::_importGlobalVariable("session"); + + if (!empty($session['auth']['data'][$name]) && $overwrite == false) { + return; + } + $session['auth']['data'][$name] = $value; + } + + // }}} + // {{{ getAuthData() + + /** + * Get additional information that is stored in the session. + * + * If no value for the first parameter is passed, the method will + * return all data that is currently stored. + * + * @access public + * @param string Name of the data field + * @return mixed Value of the data field. + */ + function getAuthData($name = null) + { + $session = &DOLIAuth::_importGlobalVariable("session"); + + if (is_null($name)) { + return $session['auth']['data']; + } + if (isset($session['auth']['data'][$name])) { + return $session['auth']['data'][$name]; + } else { + return null; + } + } + + // }}} + // {{{ setAuth() + + /** + * Register variable in a session telling that the user + * has logged in successfully + * + * @access public + * @param string Username + * @return void + */ + function setAuth($username) + { + $session = &DOLIAuth::_importGlobalVariable("session"); + + if (!isset($session['auth']) && !isset($_SESSION)) { + session_register("auth"); + } + + if (!isset($session['auth']) || !is_array($session['auth'])) { + $session['auth'] = array(); + } + + if(!isset($session['auth']['data'])){ + $session['auth']['data'] = array(); + } + $session['auth']['registered'] = true; + $session['auth']['username'] = $username; + $session['auth']['timestamp'] = time(); + $session['auth']['idle'] = time(); + } + + // }}} + // {{{ checkAuth() + + /** + * Checks if there is a session with valid auth information. + * + * @access private + * @return boolean Whether or not the user is authenticated. + */ + function checkAuth() + { + $session = &$this->_importGlobalVariable("session"); + + if (isset($session['auth'])) { + /** Check if authentication session is expired */ + if ($this->expire > 0 && + isset($session['auth']['timestamp']) && + ($session['auth']['timestamp'] + $this->expire) < time()) { + + $this->logout(); + $this->expired = true; + $this->status = AUTH_EXPIRED; + + return false; + } + + /** Check if maximum idle time is reached */ + if ($this->idle > 0 && + isset($session['auth']['idle']) && + ($session['auth']['idle'] + $this->idle) < time()) { + + $this->logout(); + $this->idled = true; + $this->status = AUTH_IDLED; + + return false; + } + + if (isset($session['auth']['registered']) && + isset($session['auth']['username']) && + $session['auth']['registered'] == true && + $session['auth']['username'] != "") { + + DOLIAuth::updateIdle(); + + return true; + } + } + + return false; + } + + // }}} + // {{{ getAuth() + + /** + * Has the user been authenticated? + * + * @access public + * @return bool True if the user is logged in, otherwise false. + */ + function getAuth() + { + $session = &$this->_importGlobalVariable("session"); + + if (!empty($session) && + (isset($session['auth']['registered']) && + $session['auth']['registered'] === true)) + { + return true; + } else { + return false; + } + } + + // }}} + // {{{ drawLogin() + + /** + * Draw the login form + * + * Normally you will not use this output in your application, + * because you can pass a different function name to the + * constructor. For more information on this, please + * consult the documentation. + * + * @access private + * @param string Username if already entered + * @return void + */ + function drawLogin($username = "") + { + if ($this->loginFunction != "") { + call_user_func($this->loginFunction, $username, $this->status); + } else { + $server = &$this->_importGlobalVariable("server"); + + echo "
\n"; + + if (!empty($this->status) && $this->status == AUTH_EXPIRED) { + echo "Your session expired. Please login again!\n"; + } else if (!empty($this->status) && $this->status == AUTH_IDLED) { + echo "You have been idle for too long. Please login again!\n"; + } else if (!empty ($this->status) && $this->status == AUTH_WRONG_LOGIN) { + echo "Wrong login data!\n"; + } + + DOLIPEAR::raiseError("You are using the built-in login screen of PEAR::Auth.
See the manual for details on how to create your own login function.", null); + + echo "
\n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo " \n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo " \n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo "\n"; + echo "
Login:
Username:
Password:
\n"; + echo "
\n"; + echo "
\n\n"; + } + } + + // }}} + // {{{ logout() + + /** + * Logout function + * + * This function clears any auth tokens in the currently + * active session and executes the logout callback function, + * if any + * + * @access public + * @return void + */ + function logout() + { + $session = &$this->_importGlobalVariable("session"); + + if (!empty($this->logoutCallback)) { + call_user_func($this->logoutCallback, $session['auth']['username']); + } + + $this->username = ""; + $this->password = ""; + + $session['auth'] = array(); + if (isset($_SESSION)) { + unset($session['auth']); + } else { + session_unregister("auth"); + } + } + + // }}} + // {{{ updateIdle() + + /** + * Update the idletime + * + * @access private + * @return void + */ + function updateIdle() + { + $session = &$this->_importGlobalVariable("session"); + $session['auth']['idle'] = time(); + } + + // }}} + // {{{ getUsername() + + /** + * Get the username + * + * @access public + * @return string + */ + function getUsername() + { + $session = &$this->_importGlobalVariable("session"); + if (!isset($session['auth']['username'])) { + return ""; + } + return $session['auth']['username']; + } + + // }}} + // {{{ getStatus() + + /** + * Get the current status + * + * @access public + * @return string + */ + function getStatus() + { + return $this->status; + } + + // }}} + // {{{ sessionValidThru() + + /** + * Returns the time up to the session is valid + * + * @access public + * @return integer + */ + function sessionValidThru() + { + $session = &$this->_importGlobalVariable("session"); + if (!isset($session['auth']['idle'])) { + return 0; + } + return ($session['auth']['idle'] + $this->idle); + } + + // }}} + // {{{ listUsers() + + /** + * List all users that are currently available in the storage + * container + * + * @access public + * @return array + */ + function listUsers() + { + return $this->storage->listUsers(); + } + + // }}} + // {{{ addUser() + + /** + * Add user to the storage container + * + * @access public + * @param string Username + * @param string Password + * @param mixed Additional parameters + * @return mixed True on success, PEAR error object on error + * and AUTH_METHOD_NOT_SUPPORTED otherwise. + */ + function addUser($username, $password, $additional = "") + { + return $this->storage->addUser($username, $password, $additional); + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @access public + * @param string Username + * @return mixed True on success, PEAR error object on error + * and AUTH_METHOD_NOT_SUPPORTED otherwise. + */ + function removeUser($username) + { + return $this->storage->removeUser($username); + } + + // }}} + // {{{ _importGlobalVariable() + + /** + * Import variables from special namespaces. + * + * @access private + * @param string Type of variable (server, session, post) + * @return array + */ + function &_importGlobalVariable($variable) + { + $var = null; + + switch (strtolower($variable)) { + + case "server" : + if (isset($_SERVER)) { + $var = &$_SERVER; + } else { + $var = &$GLOBALS['HTTP_SERVER_VARS']; + } + break; + + case "session" : + if (isset($_SESSION)) { + $var = &$_SESSION; + } else { + $var = &$GLOBALS['HTTP_SESSION_VARS']; + } + break; + + case "post" : + if (isset($_POST)) { + $var = &$_POST; + } else { + $var = &$GLOBALS['HTTP_POST_VARS']; + } + break; + + case "cookie" : + if (isset($_COOKIE)) { + $var = &$_COOKIE; + } else { + $var = &$GLOBALS['HTTP_COOKIE_VARS']; + } + break; + + case "get" : + if (isset($_GET)) { + $var = &$_GET; + } else { + $var = &$GLOBALS['HTTP_GET_VARS']; + } + break; + + default: + break; + + } + + return $var; + } + + // }}} +} +?> diff --git a/htdocs/includes/pear/PEAR.php b/htdocs/includes/pear/PEAR.php new file mode 100644 index 00000000000..db2f702ce0a --- /dev/null +++ b/htdocs/includes/pear/PEAR.php @@ -0,0 +1,968 @@ + | +// | Stig Bakken | +// | Tomas V.V.Cox | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +define('DOLIPEAR_ERROR_RETURN', 1); +define('DOLIPEAR_ERROR_PRINT', 2); +define('DOLIPEAR_ERROR_TRIGGER', 4); +define('DOLIPEAR_ERROR_DIE', 8); +define('DOLIPEAR_ERROR_CALLBACK', 16); +define('DOLIPEAR_ERROR_EXCEPTION', 32); +define('DOLIPEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('DOLIPEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('DOLIPEAR_OS', 'Unix'); // blatant assumption +} + +$GLOBALS['_DOLIPEAR_default_error_mode'] = DOLIPEAR_ERROR_RETURN; +$GLOBALS['_DOLIPEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_DOLIPEAR_destructor_object_list'] = array(); +$GLOBALS['_DOLIPEAR_shutdown_funcs'] = array(); +$GLOBALS['_DOLIPEAR_error_handler_stack'] = array(); + +ini_set('track_errors', true); + +/** + * Base class for other DOLIPEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit DOLIPEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the DOLIPEAR constructor: $this->DOLIPEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new DOLIPEAR_child; + * + * @since PHP 4.0.2 + * @author Stig Bakken + * @see http://pear.php.net/manual/ + */ +class DOLIPEAR +{ + // {{{ properties + + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is DOLIPEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * DOLIPEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'DOLIPEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + // }}} + + // {{{ constructor + + /** + * Constructor. Registers this object in + * $_DOLIPEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to DOLIPEAR_Error. + * @access public + * @return void + */ + function DOLIPEAR($error_class = null) + { + $classname = get_class($this); + if ($this->_debug) { + print "DOLIPEAR constructor called, class=$classname\n"; + } + if ($error_class !== null) { + $this->_error_class = $error_class; + } + while ($classname) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_DOLIPEAR_destructor_object_list; + $_DOLIPEAR_destructor_object_list[] = &$this; + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + // }}} + // {{{ destructor + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _DOLIPEAR() { + if ($this->_debug) { + printf("DOLIPEAR destructor called, class=%s\n", get_class($this)); + } + } + + // }}} + // {{{ getStaticProperty() + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &DOLIPEAR::getStaticProperty('myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + return $properties[$class][$var]; + } + + // }}} + // {{{ registerShutdownFunc() + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + $GLOBALS['_DOLIPEAR_shutdown_funcs'][] = array($func, $args); + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is a DOLIPEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @access public + * @return bool true if parameter is an error + */ + function isError($data, $code = null) + { + if (is_object($data) && (get_class($data) == 'pear_error' || + is_subclass_of($data, 'pear_error'))) { + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } else { + return $data->getCode() == $code; + } + } + return false; + } + + // }}} + // {{{ setErrorHandling() + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * DOLIPEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of DOLIPEAR_ERROR_RETURN, DOLIPEAR_ERROR_PRINT, + * DOLIPEAR_ERROR_TRIGGER, DOLIPEAR_ERROR_DIE, + * DOLIPEAR_ERROR_CALLBACK or DOLIPEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is DOLIPEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is DOLIPEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is DOLIPEAR_ERROR_PRINT or DOLIPEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see DOLIPEAR_ERROR_RETURN + * @see DOLIPEAR_ERROR_PRINT + * @see DOLIPEAR_ERROR_TRIGGER + * @see DOLIPEAR_ERROR_DIE + * @see DOLIPEAR_ERROR_CALLBACK + * @see DOLIPEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + + function setErrorHandling($mode = null, $options = null) + { + if (isset($this)) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_DOLIPEAR_default_error_mode']; + $setoptions = &$GLOBALS['_DOLIPEAR_default_error_options']; + } + + switch ($mode) { + case DOLIPEAR_ERROR_RETURN: + case DOLIPEAR_ERROR_PRINT: + case DOLIPEAR_ERROR_TRIGGER: + case DOLIPEAR_ERROR_DIE: + case DOLIPEAR_ERROR_EXCEPTION: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case DOLIPEAR_ERROR_CALLBACK: + $setmode = $mode; + if ((is_string($options) && function_exists($options)) || + (is_array($options) && method_exists(@$options[0], @$options[1]))) + { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + // }}} + // {{{ expectError() + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * DOLIPEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return sizeof($this->_expected_errors); + } + + // }}} + // {{{ popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + // }}} + // {{{ _checkDelExpect() + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + + foreach ($this->_expected_errors AS $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + return $deleted; + } + + // }}} + // {{{ delExpect() + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; + // we walk through it trying to unset all + // values + foreach($error_code AS $key => $error) { + if ($this->_checkDelExpect($error)) { + $deleted = true; + } else { + $deleted = false; + } + } + return $deleted ? true : DOLIPEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } else { + return DOLIPEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + } else { + // $error_code is empty + return DOLIPEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + } + + // }}} + // {{{ raiseError() + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a DOLIPEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of DOLIPEAR_ERROR_RETURN, DOLIPEAR_ERROR_PRINT, + * DOLIPEAR_ERROR_TRIGGER, DOLIPEAR_ERROR_DIE, + * DOLIPEAR_ERROR_CALLBACK, DOLIPEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is DOLIPEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is DOLIPEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a DOLIPEAR error object + * @see DOLIPEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a DOLIPEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message = $message->getMessage(); + } + + if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp))) { + $mode = DOLIPEAR_ERROR_RETURN; + } + } + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_DOLIPEAR_default_error_mode'])) { + $mode = $GLOBALS['_DOLIPEAR_default_error_mode']; + $options = $GLOBALS['_DOLIPEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'DOLIPEAR_Error'; + } + if ($skipmsg) { + return new $ec($code, $mode, $options, $userinfo); + } else { + return new $ec($message, $code, $mode, $options, $userinfo); + } + } + + // }}} + // {{{ throwError() + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param string $message + * + */ + function &throwError($message = null, + $code = null, + $userinfo = null) + { + if (isset($this) && is_subclass_of($this, 'DOLIPEAR_Error')) { + return $this->raiseError($message, $code, null, null, $userinfo); + } else { + return DOLIPEAR::raiseError($message, $code, null, null, $userinfo); + } + } + + // }}} + // {{{ pushErrorHandling() + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see DOLIPEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_DOLIPEAR_error_handler_stack']; + if (isset($this)) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_DOLIPEAR_default_error_mode']; + $def_options = &$GLOBALS['_DOLIPEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this)) { + $this->setErrorHandling($mode, $options); + } else { + DOLIPEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + // }}} + // {{{ popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see DOLIPEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_DOLIPEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this)) { + $this->setErrorHandling($mode, $options); + } else { + DOLIPEAR::setErrorHandling($mode, $options); + } + return true; + } + + // }}} + // {{{ loadExtension() + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + function loadExtension($ext) + { + if (!extension_loaded($ext)) { + // if either returns true dl() will produce a FATAL error, stop that + if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { + return false; + } + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + return true; + } + + // }}} +} + +// {{{ _DOLIPEAR_call_destructors() + +function _DOLIPEAR_call_destructors() +{ + global $_DOLIPEAR_destructor_object_list; + if (is_array($_DOLIPEAR_destructor_object_list) && + sizeof($_DOLIPEAR_destructor_object_list)) + { + reset($_DOLIPEAR_destructor_object_list); + while (list($k, $objref) = each($_DOLIPEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_DOLIPEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if (is_array($GLOBALS['_DOLIPEAR_shutdown_funcs']) AND !empty($GLOBALS['_DOLIPEAR_shutdown_funcs'])) { + foreach ($GLOBALS['_DOLIPEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +// }}} + +class DOLIPEAR_Error +{ + // {{{ properties + + var $error_message_prefix = ''; + var $mode = DOLIPEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + // }}} + // {{{ constructor + + /** + * DOLIPEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: DOLIPEAR_ERROR_RETURN, + * DOLIPEAR_ERROR_PRINT, DOLIPEAR_ERROR_DIE, DOLIPEAR_ERROR_TRIGGER, + * DOLIPEAR_ERROR_CALLBACK or DOLIPEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * DOLIPEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function DOLIPEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = DOLIPEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + if (function_exists("debug_backtrace")) { + $this->backtrace = debug_backtrace(); + } + if ($mode & DOLIPEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + $this->level = $options; + $this->callback = null; + } + if ($this->mode & DOLIPEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + printf($format, $this->getMessage()); + } + if ($this->mode & DOLIPEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + if ($this->mode & DOLIPEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + if ($this->mode & DOLIPEAR_ERROR_CALLBACK) { + if (is_string($this->callback) && strlen($this->callback)) { + call_user_func($this->callback, $this); + } elseif (is_array($this->callback) && + sizeof($this->callback) == 2 && + is_object($this->callback[0]) && + is_string($this->callback[1]) && + strlen($this->callback[1])) { + call_user_func($this->callback, $this); + } + } + if (DOLIPEAR_ZE2 && $this->mode & DOLIPEAR_ERROR_EXCEPTION) { + eval('throw $this;'); + } + } + + // }}} + // {{{ getMode() + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() { + return $this->mode; + } + + // }}} + // {{{ getCallback() + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() { + return $this->callback; + } + + // }}} + // {{{ getMessage() + + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + + // }}} + // {{{ getCode() + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + // }}} + // {{{ getType() + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + // }}} + // {{{ getUserInfo() + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + // }}} + // {{{ getDebugInfo() + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + // }}} + // {{{ getBacktrace() + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + // }}} + // {{{ addUserInfo() + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + // }}} + // {{{ toString() + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & DOLIPEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = get_class($this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + get_class($this), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & DOLIPEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & DOLIPEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & DOLIPEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & DOLIPEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + get_class($this), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } + + // }}} +} + +register_shutdown_function("_DOLIPEAR_call_destructors"); + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?>