Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/LDAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/LDAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/LDAP.php	(revision 10038)
@@ -0,0 +1,773 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against an LDAP server
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jan Wagner <wagner@netsols.de> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @author     Hugues Peeters <hugues.peeters@claroline.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from LDAP
+ *
+ * This class is heavily based on the DB and File containers. By default it
+ * connects to localhost:389 and searches for uid=$username with the scope
+ * "sub". If no search base is specified, it will try to determine it via
+ * the namingContexts attribute. It takes its parameters in a hash, connects
+ * to the ldap server, binds anonymously, searches for the user, and tries
+ * to bind as the user with the supplied password. When a group was set, it
+ * will look for group membership of the authenticated user. If all goes
+ * well the authentication was successful.
+ *
+ * Parameters:
+ *
+ * host:        localhost (default), ldap.netsols.de or 127.0.0.1
+ * port:        389 (default) or 636 or whereever your server runs
+ * url:         ldap://localhost:389/
+ *              useful for ldaps://, works only with openldap2 ?
+ *              it will be preferred over host and port
+ * version:     LDAP version to use, ususally 2 (default) or 3,
+ *              must be an integer!
+ * referrals:   If set, determines whether the LDAP library automatically
+ *              follows referrals returned by LDAP servers or not. Possible
+ *              values are true (default) or false.
+ * binddn:      If set, searching for user will be done after binding
+ *              as this user, if not set the bind will be anonymous.
+ *              This is reported to make the container work with MS
+ *              Active Directory, but should work with any server that
+ *              is configured this way.
+ *              This has to be a complete dn for now (basedn and
+ *              userdn will not be appended).
+ * bindpw:      The password to use for binding with binddn
+ * basedn:      the base dn of your server
+ * userdn:      gets prepended to basedn when searching for user
+ * userscope:   Scope for user searching: one, sub (default), or base
+ * userattr:    the user attribute to search for (default: uid)
+ * userfilter:  filter that will be added to the search filter
+ *              this way: (&(userattr=username)(userfilter))
+ *              default: (objectClass=posixAccount)
+ * attributes:  array of additional attributes to fetch from entry.
+ *              these will added to auth data and can be retrieved via
+ *              Auth::getAuthData(). An empty array will fetch all attributes,
+ *              array('') will fetch no attributes at all (default)
+ *              If you add 'dn' as a value to this array, the users DN that was
+ *              used for binding will be added to auth data as well.
+ * attrformat:  The returned format of the additional data defined in the
+ *              'attributes' option. Two formats are available.
+ *              LDAP returns data formatted in a
+ *              multidimensional array where each array starts with a
+ *              'count' element providing the number of attributes in the
+ *              entry, or the number of values for attributes. When set
+ *              to this format, the only way to retrieve data from the
+ *              Auth object is by calling getAuthData('attributes').
+ *              AUTH returns data formatted in a
+ *              structure more compliant with other Auth Containers,
+ *              where each attribute element can be directly called by
+ *              getAuthData() method from Auth.
+ *              For compatibily with previous LDAP container versions,
+ *              the default format is LDAP.
+ * groupdn:     gets prepended to basedn when searching for group
+ * groupattr:   the group attribute to search for (default: cn)
+ * groupfilter: filter that will be added to the search filter when
+ *              searching for a group:
+ *              (&(groupattr=group)(memberattr=username)(groupfilter))
+ *              default: (objectClass=groupOfUniqueNames)
+ * memberattr : the attribute of the group object where the user dn
+ *              may be found (default: uniqueMember)
+ * memberisdn:  whether the memberattr is the dn of the user (default)
+ *              or the value of userattr (usually uid)
+ * group:       the name of group to search for
+ * groupscope:  Scope for group searching: one, sub (default), or base
+ * start_tls:   enable/disable the use of START_TLS encrypted connection 
+ *              (default: false)
+ * debug:       Enable/Disable debugging output (default: false)
+ * try_all:     Whether to try all user accounts returned from the search
+ *              or just the first one. (default: false)
+ *
+ * To use this storage container, you have to use the following syntax:
+ *
+ * <?php
+ * ...
+ *
+ * $a1 = new Auth("LDAP", array(
+ *       'host' => 'localhost',
+ *       'port' => '389',
+ *       'version' => 3,
+ *       'basedn' => 'o=netsols,c=de',
+ *       'userattr' => 'uid'
+ *       'binddn' => 'cn=admin,o=netsols,c=de',
+ *       'bindpw' => 'password'));
+ *
+ * $a2 = new Auth('LDAP', array(
+ *       'url' => 'ldaps://ldap.netsols.de',
+ *       'basedn' => 'o=netsols,c=de',
+ *       'userscope' => 'one',
+ *       'userdn' => 'ou=People',
+ *       'groupdn' => 'ou=Groups',
+ *       'groupfilter' => '(objectClass=posixGroup)',
+ *       'memberattr' => 'memberUid',
+ *       'memberisdn' => false,
+ *       'group' => 'admin'
+ *       ));
+ *
+ * $a3 = new Auth('LDAP', array(
+ *       'host' => 'ldap.netsols.de',
+ *       'port' => 389,
+ *       'version' => 3,
+ *       'referrals' => false,
+ *       'basedn' => 'dc=netsols,dc=de',
+ *       'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de',
+ *       'bindpw' => 'password',
+ *       'userattr' => 'samAccountName',
+ *       'userfilter' => '(objectClass=user)',
+ *       'attributes' => array(''),
+ *       'group' => 'testing',
+ *       'groupattr' => 'samAccountName',
+ *       'groupfilter' => '(objectClass=group)',
+ *       'memberattr' => 'member',
+ *       'memberisdn' => true,
+ *       'groupdn' => 'cn=Users',
+ *       'groupscope' => 'one',
+ *       'debug' => true);
+ *
+ * The parameter values have to correspond
+ * to the ones for your LDAP server of course.
+ *
+ * When talking to a Microsoft ActiveDirectory server you have to
+ * use 'samaccountname' as the 'userattr' and follow special rules
+ * to translate the ActiveDirectory directory names into 'basedn'.
+ * The 'basedn' for the default 'Users' folder on an ActiveDirectory
+ * server for the ActiveDirectory Domain (which is not related to
+ * its DNS name) "win2000.example.org" would be:
+ * "CN=Users, DC=win2000, DC=example, DC=org'
+ * where every component of the domain name becomes a DC attribute
+ * of its own. If you want to use a custom users folder you have to
+ * replace "CN=Users" with a sequence of "OU" attributes that specify
+ * the path to your custom folder in reverse order.
+ * So the ActiveDirectory folder
+ *   "win2000.example.org\Custom\Accounts"
+ * would become
+ *   "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org'
+ *
+ * It seems that binding anonymously to an Active Directory
+ * is not allowed, so you have to set binddn and bindpw for
+ * user searching.
+ * 
+ * LDAP Referrals need to be set to false for AD to work sometimes.
+ *
+ * Example a3 shows a full blown and tested example for connection to 
+ * Windows 2000 Active Directory with group mebership checking
+ *
+ * Note also that if you want an encrypted connection to an MS LDAP 
+ * server, then, on your webserver, you must specify 
+ *        TLS_REQCERT never
+ * in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which
+ * may or may not be read depending on your configuration).
+ *
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jan Wagner <wagner@netsols.de>
+ * @author     Adam Ashley <aashley@php.net>
+ * @author     Hugues Peeters <hugues.peeters@claroline.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_LDAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * Connection ID of LDAP Link
+     * @var string
+     */
+    var $conn_id = false;
+
+    // }}}
+
+    // {{{ Auth_Container_LDAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $params, associative hash with host,port,basedn and userattr key
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_LDAP($params)
+    {
+        if (false === extension_loaded('ldap')) {
+            return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded',
+                    41, PEAR_ERROR_DIE);
+        }
+
+        $this->_setDefaults();
+
+        if (is_array($params)) {
+            $this->_parseOptions($params);
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare LDAP connection
+     *
+     * This function checks if we have already opened a connection to
+     * the LDAP server. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a PEAR error object.
+     */
+    function _prepare()
+    {
+        if (!$this->_isValidLink()) {
+            $res = $this->_connect();
+            if (PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to the LDAP server using the global options
+     *
+     * @access private
+     * @return object  Returns a PEAR error object if an error occurs.
+     */
+    function _connect()
+    {
+        // connect
+        if (isset($this->options['url']) && $this->options['url'] != '') {
+            $this->_debug('Connecting with URL', __LINE__);
+            $conn_params = array($this->options['url']);
+        } else {
+            $this->_debug('Connecting with host:port', __LINE__);
+            $conn_params = array($this->options['host'], $this->options['port']);
+        }
+
+        if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) {
+            return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41);
+        }
+        $this->_debug('Successfully connected to server', __LINE__);
+
+        // switch LDAP version
+        if (is_numeric($this->options['version']) && $this->options['version'] > 2) {
+            $this->_debug("Switching to LDAP version {$this->options['version']}", __LINE__);
+            @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']);
+        
+            // start TLS if available
+            if (isset($this->options['start_tls']) && $this->options['start_tls']) {           
+                $this->_debug("Starting TLS session", __LINE__);
+                if (@ldap_start_tls($this->conn_id) === false) {
+                    return PEAR::raiseError('Auth_Container_LDAP: Could not start tls.', 41);
+                }
+            }
+        }
+
+        // switch LDAP referrals
+        if (is_bool($this->options['referrals'])) {
+          $this->_debug("Switching LDAP referrals to " . (($this->options['referrals']) ? 'true' : 'false'), __LINE__);
+          @ldap_set_option($this->conn_id, LDAP_OPT_REFERRALS, $this->options['referrals']);
+        }
+
+        // bind with credentials or anonymously
+        if (strlen($this->options['binddn']) && strlen($this->options['bindpw'])) {
+            $this->_debug('Binding with credentials', __LINE__);
+            $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']);
+        } else {
+            $this->_debug('Binding anonymously', __LINE__);
+            $bind_params = array($this->conn_id);
+        }
+
+        // bind for searching
+        if ((@call_user_func_array('ldap_bind', $bind_params)) === false) {
+            $this->_debug();
+            $this->_disconnect();
+            return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41);
+        }
+        $this->_debug('Binding was successful', __LINE__);
+
+        return true;
+    }
+
+    // }}}
+    // {{{ _disconnect()
+
+    /**
+     * Disconnects (unbinds) from ldap server
+     *
+     * @access private
+     */
+    function _disconnect()
+    {
+        if ($this->_isValidLink()) {
+            $this->_debug('disconnecting from server');
+            @ldap_unbind($this->conn_id);
+        }
+    }
+
+    // }}}
+    // {{{ _getBaseDN()
+
+    /**
+     * Tries to find Basedn via namingContext Attribute
+     *
+     * @access private
+     */
+    function _getBaseDN()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if ($this->options['basedn'] == "" && $this->_isValidLink()) {
+            $this->_debug("basedn not set, searching via namingContexts.", __LINE__);
+
+            $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts"));
+
+            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
+
+                $this->_debug("got result for namingContexts", __LINE__);
+
+                $entry_id = @ldap_first_entry($this->conn_id, $result_id);
+                $attrs = @ldap_get_attributes($this->conn_id, $entry_id);
+                $basedn = $attrs['namingContexts'][0];
+
+                if ($basedn != "") {
+                    $this->_debug("result for namingContexts was $basedn", __LINE__);
+                    $this->options['basedn'] = $basedn;
+                }
+            }
+            @ldap_free_result($result_id);
+        }
+
+        // if base ist still not set, raise error
+        if ($this->options['basedn'] == "") {
+            return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _isValidLink()
+
+    /**
+     * determines whether there is a valid ldap conenction or not
+     *
+     * @accessd private
+     * @return boolean
+     */
+    function _isValidLink()
+    {
+        if (is_resource($this->conn_id)) {
+            if (get_resource_type($this->conn_id) == 'ldap link') {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults()
+    {
+        $this->options['url']         = '';
+        $this->options['host']        = 'localhost';
+        $this->options['port']        = '389';
+        $this->options['version']     = 2;
+        $this->options['referrals']   = true;
+        $this->options['binddn']      = '';
+        $this->options['bindpw']      = '';
+        $this->options['basedn']      = '';
+        $this->options['userdn']      = '';
+        $this->options['userscope']   = 'sub';
+        $this->options['userattr']    = 'uid';
+        $this->options['userfilter']  = '(objectClass=posixAccount)';
+        $this->options['attributes']  = array(''); // no attributes
+     // $this->options['attrformat']  = 'LDAP'; // returns attribute array as PHP LDAP functions return it
+        $this->options['attrformat']  = 'AUTH'; // returns attribute like other Auth containers
+        $this->options['group']       = '';
+        $this->options['groupdn']     = '';
+        $this->options['groupscope']  = 'sub';
+        $this->options['groupattr']   = 'cn';
+        $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)';
+        $this->options['memberattr']  = 'uniqueMember';
+        $this->options['memberisdn']  = true;
+        $this->options['start_tls']   = false;
+        $this->options['debug']       = false;
+        $this->options['try_all']     = false; // Try all user ids returned not just the first one
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        $array = $this->_setV12OptionsToV13($array);
+
+        foreach ($array as $key => $value) {
+            if (array_key_exists($key, $this->options)) {
+                if ($key == 'attributes') {
+                    if (is_array($value)) {
+                        $this->options[$key] = $value;
+                    } else {
+                        $this->options[$key] = explode(',', $value);
+                    }
+                } else {
+                    $this->options[$key] = $value;
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _setV12OptionsToV13()
+
+    /**
+     * Adapt deprecated options from Auth 1.2 LDAP to Auth 1.3 LDAP
+     * 
+     * @author Hugues Peeters <hugues.peeters@claroline.net>
+     * @access private
+     * @param array
+     * @return array
+     */
+    function _setV12OptionsToV13($array)
+    {
+        if (isset($array['useroc']))
+            $array['userfilter'] = "(objectClass=".$array['useroc'].")";
+        if (isset($array['groupoc']))
+            $array['groupfilter'] = "(objectClass=".$array['groupoc'].")";
+        if (isset($array['scope']))
+            $array['userscope'] = $array['scope'];
+
+        return $array;
+    }
+
+    // }}}
+    // {{{ _scope2function()
+
+    /**
+     * Get search function for scope
+     *
+     * @param  string scope
+     * @return string ldap search function
+     */
+    function _scope2function($scope)
+    {
+        switch($scope) {
+        case 'one':
+            $function = 'ldap_list';
+            break;
+        case 'base':
+            $function = 'ldap_read';
+            break;
+        default:
+            $function = 'ldap_search';
+            break;
+        }
+        return $function;
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from LDAP server
+     *
+     * Searches the LDAP server for the given username/password
+     * combination.  Escapes all LDAP meta characters in username
+     * before performing the query.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean
+     */
+    function fetchData($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $err = $this->_getBaseDN();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // UTF8 Encode username for LDAPv3
+        if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) {
+            $this->_debug('UTF8 encoding username for LDAPv3', __LINE__);
+            $username = utf8_encode($username);
+        }
+
+        // make search filter
+        $filter = sprintf('(&(%s=%s)%s)',
+                          $this->options['userattr'],
+                          $this->_quoteFilterString($username),
+                          $this->options['userfilter']);
+
+        // make search base dn
+        $search_basedn = $this->options['userdn'];
+        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
+            $search_basedn .= ',';
+        }
+        $search_basedn .= $this->options['basedn'];
+
+        // attributes
+        $attributes = $this->options['attributes'];
+
+        // make functions params array
+        $func_params = array($this->conn_id, $search_basedn, $filter, $attributes);
+
+        // search function to use
+        $func_name = $this->_scope2function($this->options['userscope']);
+
+        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
+
+        // search
+        if (($result_id = @call_user_func_array($func_name, $func_params)) === false) {
+            $this->_debug('User not found', __LINE__);
+        } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) { // did we get some possible results?
+
+            $this->_debug('User(s) found', __LINE__);
+
+            $first = true;
+            $entry_id = null;
+
+            do {
+                
+                // then get the user dn
+                if ($first) {
+                    $entry_id = @ldap_first_entry($this->conn_id, $result_id);
+                    $first = false;
+                } else {
+                    $entry_id = @ldap_next_entry($this->conn_id, $entry_id);
+                    if ($entry_id === false)
+                        break;
+                }
+                $user_dn  = @ldap_get_dn($this->conn_id, $entry_id);
+
+                // as the dn is not fetched as an attribute, we save it anyway
+                if (is_array($attributes) && in_array('dn', $attributes)) {
+                    $this->_debug('Saving DN to AuthData', __LINE__);
+                    $this->_auth_obj->setAuthData('dn', $user_dn);
+                }
+            
+                // fetch attributes
+                if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) {
+
+                    if (is_array($attributes) && isset($attributes['count']) &&
+                         $attributes['count'] > 0) {
+
+                        // ldap_get_attributes() returns a specific multi dimensional array
+                        // format containing all the attributes and where each array starts
+                        // with a 'count' element providing the number of attributes in the
+                        // entry, or the number of values for attribute. For compatibility
+                        // reasons, it remains the default format returned by LDAP container
+                        // setAuthData().
+                        // The code below optionally returns attributes in another format,
+                        // more compliant with other Auth containers, where each attribute
+                        // element are directly set in the 'authData' list. This option is
+                        // enabled by setting 'attrformat' to
+                        // 'AUTH' in the 'options' array.
+                        // eg. $this->options['attrformat'] = 'AUTH'
+
+                        if ( strtoupper($this->options['attrformat']) == 'AUTH' ) {
+                            $this->_debug('Saving attributes to Auth data in AUTH format', __LINE__);
+                            unset ($attributes['count']);
+                            foreach ($attributes as $attributeName => $attributeValue ) {
+                                if (is_int($attributeName)) continue;
+                                if (is_array($attributeValue) && isset($attributeValue['count'])) {
+                                    unset ($attributeValue['count']);
+                                }
+                                if (count($attributeValue)<=1) $attributeValue = $attributeValue[0];
+                                $this->_auth_obj->setAuthData($attributeName, $attributeValue);
+                            }
+                        }
+                        else
+                        {
+                            $this->_debug('Saving attributes to Auth data in LDAP format', __LINE__);
+                            $this->_auth_obj->setAuthData('attributes', $attributes);
+                        }
+                    }
+                }
+                @ldap_free_result($result_id);
+
+                // need to catch an empty password as openldap seems to return TRUE
+                // if anonymous binding is allowed
+                if ($password != "") {
+                    $this->_debug("Bind as $user_dn", __LINE__);
+
+                    // try binding as this user with the supplied password
+                    if (@ldap_bind($this->conn_id, $user_dn, $password)) {
+                        $this->_debug('Bind successful', __LINE__);
+
+                        // check group if appropiate
+                        if (strlen($this->options['group'])) {
+                            // decide whether memberattr value is a dn or the username
+                            $this->_debug('Checking group membership', __LINE__);
+                            $return = $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username);
+                            $this->_disconnect();
+                            return $return;
+                        } else {
+                            $this->_debug('Authenticated', __LINE__);
+                            $this->_disconnect();
+                            return true; // user authenticated
+                        } // checkGroup
+                    } // bind
+                } // non-empty password
+            } while ($this->options['try_all'] == true); // interate through entries
+        } // get results
+        // default
+        $this->_debug('NOT authenticated!', __LINE__);
+        $this->_disconnect();
+        return false;
+    }
+
+    // }}}
+    // {{{ checkGroup()
+
+    /**
+     * Validate group membership
+     *
+     * Searches the LDAP server for group membership of the
+     * supplied username.  Quotes all LDAP filter meta characters in
+     * the user name before querying the LDAP server.
+     *
+     * @param  string Distinguished Name of the authenticated User
+     * @return boolean
+     */
+    function checkGroup($user)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // make filter
+        $filter = sprintf('(&(%s=%s)(%s=%s)%s)',
+                          $this->options['groupattr'],
+                          $this->options['group'],
+                          $this->options['memberattr'],
+                          $this->_quoteFilterString($user),
+                          $this->options['groupfilter']);
+
+        // make search base dn
+        $search_basedn = $this->options['groupdn'];
+        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
+            $search_basedn .= ',';
+        }
+        $search_basedn .= $this->options['basedn'];
+
+        $func_params = array($this->conn_id, $search_basedn, $filter,
+                             array($this->options['memberattr']));
+        $func_name = $this->_scope2function($this->options['groupscope']);
+
+        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
+
+        // search
+        if (($result_id = @call_user_func_array($func_name, $func_params)) != false) {
+            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
+                @ldap_free_result($result_id);
+                $this->_debug('User is member of group', __LINE__);
+                return true;
+            }
+        }
+        // default
+        $this->_debug('User is NOT member of group', __LINE__);
+        return false;
+    }
+
+    // }}}
+    // {{{ _debug()
+
+    /**
+     * Outputs debugging messages
+     *
+     * @access private
+     * @param string Debugging Message
+     * @param integer Line number
+     */
+    function _debug($msg = '', $line = 0)
+    {
+        if ($this->options['debug'] == true) {
+            if ($msg == '' && $this->_isValidLink()) {
+                $msg = 'LDAP_Error: ' . @ldap_err2str(@ldap_errno($this->_conn_id));
+            }
+            print("$line: $msg <br />");
+        }
+    }
+
+    // }}}
+    // {{{ _quoteFilterString()
+
+    /**
+     * Escapes LDAP filter special characters as defined in RFC 2254.
+     *
+     * @access private
+     * @param string Filter String
+     */
+    function _quoteFilterString($filter_str)
+    {
+        $metas        = array(  '\\',  '*',  '(',  ')',   "\x00");
+        $quoted_metas = array('\\\\', '\*', '\(', '\)', "\\\x00");
+        return str_replace($metas, $quoted_metas, $filter_str);
+    }
+
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/POP3.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/POP3.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/POP3.php	(revision 10038)
@@ -0,0 +1,143 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a POP3 server
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR package for error handling
+ */
+require_once 'PEAR.php';
+/**
+ * Include PEAR Net_POP3 package
+ */
+require_once 'Net/POP3.php';
+
+/**
+ * Storage driver for Authentication on a POP3 server.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_POP3 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * POP3 Server
+     * @var string
+     */
+    var $server='localhost';
+
+    /**
+     * POP3 Server port
+     * @var string
+     */
+    var $port='110';
+
+    /**
+     * POP3 Authentication method
+     *
+     * Prefered POP3 authentication method. Acceptable values:
+     *      Boolean TRUE    - Use Net_POP3's autodetection
+     *      String 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
+     *                      - Attempt this authentication style first
+     *                        then fallback to autodetection.
+     * @var mixed 
+     */
+    var $method=true;
+
+    // }}}
+    // {{{ Auth_Container_POP3() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $server string server or server:port combination
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_POP3($server=null)
+    {
+        if (isset($server)) {
+            if (is_array($server)) {
+                if (isset($server['host'])) {
+                    $this->server = $server['host'];
+                }
+                if (isset($server['port'])) {
+                    $this->port = $server['port'];
+                }
+                if (isset($server['method'])) {
+                    $this->method = $server['method'];
+                }
+            } else {
+                if (strstr($server, ':')) {
+                    $serverparts = explode(':', trim($server));
+                    $this->server = $serverparts[0];
+                    $this->port = $serverparts[1];
+                } else {
+                    $this->server = $server;
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Try to login to the POP3 server
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        $pop3 =& new Net_POP3();
+        $res = $pop3->connect($this->server, $this->port, $this->method);
+        if (!$res) {
+            return $res;
+        }
+        $result = $pop3->login($username, $password);
+        $pop3->disconnect();
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/SAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/SAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/SAP.php	(revision 10038)
@@ -0,0 +1,177 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SAP system using the SAPRFC PHP extension.
+ *
+ * Requires the SAPRFC ext available at http://saprfc.sourceforge.net/
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stoyan Stefanov <ssttoo@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR for error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * Performs authentication against a SAP system using the SAPRFC PHP extension.
+ *
+ * When the option GETSSO2 is TRUE (default)
+ * the Single Sign-On (SSO) ticket is retrieved
+ * and stored as an Auth attribute called 'sap'
+ * in order to be reused for consecutive connections.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stoyan Stefanov <ssttoo@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_SAP extends Auth_Container {
+
+    // {{{ properties
+    
+    /**
+     * @var array Default options
+     */
+    var $options = array(
+        'CLIENT'    => '000',
+        'LANG'      => 'EN',
+        'GETSSO2'   => true,
+    );
+
+    // }}}
+    // {{{ Auth_Container_SAP()
+
+    /**
+     * Class constructor. Checks that required options
+     * are present and that the SAPRFC extension is loaded
+     *
+     * Options that can be passed and their defaults:
+     * <pre>
+     * array(
+     *   'ASHOST' => "",
+     *   'SYSNR'  => "",
+     *   'CLIENT' => "000",
+     *   'GWHOST' =>"",
+     *   'GWSERV' =>"",
+     *   'MSHOST' =>"",
+     *   'R3NAME' =>"",
+     *   'GROUP'  =>"",
+     *   'LANG'   =>"EN",
+     *   'TRACE'  =>"",
+     *   'GETSSO2'=> true
+     * )
+     * </pre>
+     *
+     * @param array array of options.
+     * @return void
+     */
+    function Auth_Container_SAP($options)
+    {
+        $saprfc_loaded = PEAR::loadExtension('saprfc');
+        if (!$saprfc_loaded) {
+            return PEAR::raiseError('Cannot use SAP authentication, '
+                    .'SAPRFC extension not loaded!');
+        }
+        if (empty($options['R3NAME']) && empty($options['ASHOST'])) {
+            return PEAR::raiseError('R3NAME or ASHOST required for authentication');
+        }
+        $this->options = array_merge($this->options, $options);
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Performs username and password check
+     *
+     * @param string Username
+     * @param string Password
+     * @return boolean TRUE on success (valid user), FALSE otherwise
+     */
+    function fetchData($username, $password)
+    {
+        $connection_options = $this->options;
+        $connection_options['USER'] = $username;
+        $connection_options['PASSWD'] = $password;
+        $rfc = saprfc_open($connection_options);
+        if (!$rfc) {
+            $message = "Couldn't connect to the SAP system.";
+            $error = $this->getError();
+            if ($error['message']) {
+                $message .= ': ' . $error['message'];
+            }
+            PEAR::raiseError($message, null, null, null, @$erorr['all']);
+            return false;
+        } else {
+            if (!empty($this->options['GETSSO2'])) {
+                if ($ticket = @saprfc_get_ticket($rfc)) {
+                    $this->options['MYSAPSSO2'] = $ticket;
+                    unset($this->options['GETSSO2']);
+                    $this->_auth_obj->setAuthData('sap', $this->options);
+                } else {
+                    PEAR::raiseError("SSO ticket retrieval failed");
+                }
+            }
+            @saprfc_close($rfc);
+            return true;
+        }
+    
+    }
+
+    // }}}
+    // {{{ getError()
+
+    /**
+     * Retrieves the last error from the SAP connection
+     * and returns it as an array.
+     *
+     * @return array Array of error information
+     */
+    function getError()
+    {
+
+        $error = array();
+        $sap_error = saprfc_error();
+        if (empty($err)) {
+            return $error;
+        }
+        $err = explode("n", $sap_error);
+        foreach ($err AS $line) {
+            $item = split(':', $line);
+            $error[strtolower(trim($item[0]))] = trim($item[1]);
+        }
+        $error['all'] = $sap_error;
+        return $error;
+    }
+
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB2.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB2.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB2.php	(revision 10038)
@@ -0,0 +1,571 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR MDB2
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR MDB2 package
+ */
+require_once 'MDB2.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR MDB2 abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_MDB2 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * MDB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_MDB2() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::MDB2
+     *
+     * @param  string Connection data or MDB2 object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_MDB2($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  mixed DSN string | array | mdb object
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& MDB2::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'MDB2_Driver_Common')) {
+            $this->db = $dsn;
+        } elseif (is_object($dsn) && MDB2::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->code);
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+
+        }
+
+        if (MDB2::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->code);
+        }
+        
+        if ($this->options['auto_quote']) {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table'], true);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol'], true);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol'], true);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+                
+        return true;
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a MDB error object.
+     */
+    function _prepare()
+    {
+        if (is_subclass_of($this->db, 'MDB2_Driver_Common')) {
+            return true;
+        }
+        return $this->_connect($this->options['dsn']);
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a MDB_result object or MDB_OK on success, a MDB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->exec($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field, true);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields'], true);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+        $query = sprintf("SELECT %s FROM %s WHERE %s = %s",
+                         $sql_from,
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
+        if (MDB2::isError($res) || PEAR::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashing
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] =
+                md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']);
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+        }
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static call to setAuthData does not make sense
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed array|PEAR_Error
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf('SELECT %s FROM %s',
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+
+        $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key   .= ', ' . $this->db->quoteIdentifier($key, true);
+                } else {
+                    $additional_key   .= ', ' . $key;
+                }
+                $additional_value .= ', ' . $this->db->quote($value, 'text');
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->quote($username, 'text'),
+                         $this->db->quote($password, 'text'),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->quote($password, 'text'),
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+     * Returns the selected crypt type for this container
+     *
+     * @return string Function used to crypt the password
+     */
+    function getCryptType()
+    {
+        return $this->options['cryptType'];
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/DB.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/DB.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/DB.php	(revision 10038)
@@ -0,0 +1,578 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR DB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR DB
+ */
+require_once 'DB.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR DB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_DB extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * DB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_DB [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Save the initial options passed to the container. Initiation of the DB
+     * connection is no longer performed here and is only done when needed.
+     *
+     * @param  string Connection data or DB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_DB($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  string DSN string
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db = DB::Connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'db_common')) {
+            $this->db = $dsn;
+        } elseif (DB::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->getCode());
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+        }
+
+        if (DB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a DB error object.
+     */
+    function _prepare()
+    {
+        if (!DB::isConnection($this->db)) {
+            $res = $this->_connect($this->options['dsn']);
+            if (DB::isError($res) || PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a DB_result object or DB_OK on success, a DB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->query($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // Find if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = "SELECT ".$sql_from.
+                " FROM ".$this->options['final_table'].
+                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
+
+        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashihg
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]
+                    .$this->_auth_obj->session['loginchallenege']);
+            
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+            
+            //print " Hashed Password [{$res[$this->options['passwordcol']]}]<br/>\n";
+        }
+
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a 
+                // static call to setAuthData does not make sence
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        // Find if db_fields contains a *, if so assume all col are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf("SELECT %s FROM %s",
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+        $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (   isset($this->options['cryptType']) 
+            && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (   isset($this->options['cryptType']) 
+                  && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key .= ', ' . $this->db->quoteIdentifier($key);
+                } else {
+                    $additional_key .= ', ' . $key;
+                }
+                $additional_value .= ", " . $this->db->quoteSmart($value);
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->quoteSmart($username),
+                         $this->db->quoteSmart($password),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quoteSmart($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+           return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+          return true;
+        }
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (   isset($this->options['cryptType']) 
+            && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (   isset($this->options['cryptType']) 
+                  && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->quoteSmart($password),
+                         $this->options['final_usernamecol'],
+                         $this->db->quoteSmart($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+      * Returns the selected crypt type for this container
+      */
+    function getCryptType()
+    {
+        return($this->options['cryptType']);
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/IMAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/IMAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/IMAP.php	(revision 10038)
@@ -0,0 +1,206 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against IMAP servers
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jeroen Houben <jeroen@terena.nl> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class 
+ */
+require_once "Auth/Container.php";
+
+/**
+ * Include PEAR class for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an IMAP server
+ *
+ * This class is based on LDAP containers, but it very simple.
+ * By default it connects to localhost:143
+ * The constructor will first check if the host:port combination is
+ * actually reachable. This behaviour can be disabled.
+ * It then tries to create an IMAP stream (without opening a mailbox)
+ * If you wish to pass extended options to the connections, you may
+ * do so by specifying protocol options.
+ *
+ * To use this storage containers, you have to use the
+ * following syntax:
+ *
+ * <?php
+ * ...
+ * $params = array(
+ * 'host'       => 'mail.example.com',
+ * 'port'       => 143,
+ * );
+ * $myAuth = new Auth('IMAP', $params);
+ * ...
+ *
+ * By default we connect without any protocol options set. However, some
+ * servers require you to connect with the notls or norsh options set.
+ * To do this you need to add the following value to the params array:
+ * 'baseDSN'   => '/imap/notls/norsh'
+ *
+ * To connect to an SSL IMAP server:
+ * 'baseDSN'   => '/imap/ssl'
+ *
+ * To connect to an SSL IMAP server with a self-signed certificate:
+ * 'baseDSN'   => '/imap/ssl/novalidate-cert'
+ *
+ * Further options may be available and can be found on the php site at
+ * http://www.php.net/manual/function.imap-open.php
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jeroen Houben <jeroen@terena.nl>
+ * @author     Cipriano Groenendal <cipri@campai.nl>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_IMAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var array
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_IMAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $params  associative array with host, port, baseDSN, checkServer
+     *                  and userattr key
+     * @return object Returns an error object if something went wrong
+     * @todo Use PEAR Net_IMAP if IMAP extension not loaded
+     */
+    function Auth_Container_IMAP($params)
+    {
+        if (!extension_loaded('imap')) {
+            return PEAR::raiseError('Cannot use IMAP authentication, '
+                    .'IMAP extension not loaded!', 41, PEAR_ERROR_DIE);
+        }
+        $this->_setDefaults();
+
+        // set parameters (if any)
+        if (is_array($params)) {
+            $this->_parseOptions($params);
+        }
+
+        if ($this->options['checkServer']) {
+            $this->_checkServer($this->options['timeout']);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults()
+    {
+        $this->options['host'] = 'localhost';
+        $this->options['port'] = 143;
+        $this->options['baseDSN'] = '';
+        $this->options['checkServer'] = true;
+        $this->options['timeout'] = 20;
+    }
+
+    // }}}
+    // {{{ _checkServer()
+
+    /**
+     * Check if the given server and port are reachable
+     *
+     * @access private
+     */
+    function _checkServer() {
+        $fp = @fsockopen ($this->options['host'], $this->options['port'],
+                          $errno, $errstr, $this->options['timeout']);
+        if (is_resource($fp)) {
+            @fclose($fp);
+        } else {
+            $message = "Error connecting to IMAP server "
+                . $this->options['host']
+                . ":" . $this->options['port'];
+            return PEAR::raiseError($message, 41);
+        }
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            $this->options[$key] = $value;
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Try to open a IMAP stream using $username / $password
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean
+     */
+    function fetchData($username, $password)
+    {
+        $dsn = '{'.$this->options['host'].':'.$this->options['port'].$this->options['baseDSN'].'}';
+        $conn = @imap_open ($dsn, $username, $password, OP_HALFOPEN);
+        if (is_resource($conn)) {
+            $this->activeUser = $username;
+            @imap_close($conn);
+            return true;
+        } else {
+            $this->activeUser = '';
+            return false;
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/vpopmail.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/vpopmail.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/vpopmail.php	(revision 10038)
@@ -0,0 +1,87 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against vpopmail setups
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stanislav Grozev <tacho@orbitel.bg> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from vpopmail
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stanislav Grozev <tacho@orbitel.bg>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_vpopmail extends Auth_Container {
+
+    // {{{ Constructor
+
+    /**
+     * Constructor of the container class
+     *
+     * @return void
+     */
+    function Auth_Container_vpopmail()
+    {
+        if (!extension_loaded('vpopmail')) {
+            return PEAR::raiseError('Cannot use VPOPMail authentication, '
+                    .'VPOPMail extension not loaded!', 41, PEAR_ERROR_DIE);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from vpopmail
+     *
+     * @param   string Username - has to be valid email address
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        $userdata = array();
+        $userdata = preg_split("/@/", $username, 2);
+        $result = @vpopmail_auth_user($userdata[0], $userdata[1], $password);
+
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/PEAR.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/PEAR.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/PEAR.php	(revision 10038)
@@ -0,0 +1,103 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR website
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Yavor Shahpasov <yavo@netsmart.com.cy>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR XML_RPC
+ */
+require_once 'XML/RPC.php';
+
+/**
+ * Storage driver for authenticating against PEAR website
+ *
+ * This driver provides a method for authenticating against the pear.php.net
+ * authentication system.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Yavor Shahpasov <yavo@netsmart.com.cy>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_Pear extends Auth_Container
+{
+
+    // {{{ Auth_Container_Pear() [constructor]
+
+    /**
+     * Constructor
+     *
+     * Currently does nothing
+     * 
+     * @return void
+     */
+    function Auth_Container_Pear()
+    {
+    
+    }
+
+    // }}}
+    // {{{ fetchData()
+    
+    /**
+     * Get user information from pear.php.net
+     *
+     * This function uses the given username and password to authenticate
+     * against the pear.php.net website
+     *
+     * @param string    Username
+     * @param string    Password
+     * @return mixed    Error object or boolean
+     */
+    function fetchData($username, $password)
+    {
+        $rpc = new XML_RPC_Client('/xmlrpc.php', 'pear.php.net');
+        $rpc_message = new XML_RPC_Message("user.info", array(new XML_RPC_Value($username, "string")) );
+        
+        // Error Checking howto ???
+        $result = $rpc->send($rpc_message);
+        $value = $result->value();
+        $userinfo = xml_rpc_decode($value);
+        if ($userinfo['password'] == md5($password)) {
+            $this->activeUser = $userinfo['handle'];
+            foreach ($userinfo as $uk=>$uv) {
+                $this->_auth_obj->setAuthData($uk, $uv);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+    
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/RADIUS.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/RADIUS.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/RADIUS.php	(revision 10038)
@@ -0,0 +1,180 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against RADIUS servers
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR Auth_RADIUS package
+ */
+require_once "Auth/RADIUS.php";
+
+/**
+ * Storage driver for authenticating users against RADIUS servers.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_RADIUS extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Contains a RADIUS object
+     * @var object
+     */
+    var $radius;
+    
+    /**
+     * Contains the authentication type
+     * @var string
+     */
+    var $authtype;    
+
+    // }}}
+    // {{{ Auth_Container_RADIUS() [constructor]
+
+    /**
+     * Constructor of the container class.
+     *
+     * $options can have these keys:
+     * 'servers'    an array containing an array: servername, port,
+     *              sharedsecret, timeout, maxtries
+     * 'configfile' The filename of the configuration file
+     * 'authtype'   The type of authentication, one of: PAP, CHAP_MD5,
+     *              MSCHAPv1, MSCHAPv2, default is PAP
+     *
+     * @param  $options associative array
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_RADIUS($options)
+    {
+        $this->authtype = 'PAP';
+        if (isset($options['authtype'])) {
+            $this->authtype = $options['authtype'];
+        }
+        $classname = 'Auth_RADIUS_' . $this->authtype;
+        if (!class_exists($classname)) {
+            PEAR::raiseError("Unknown Authtype, please use one of: "
+                    ."PAP, CHAP_MD5, MSCHAPv1, MSCHAPv2!", 41, PEAR_ERROR_DIE);
+        }
+        
+        $this->radius = new $classname;
+
+        if (isset($options['configfile'])) {
+            $this->radius->setConfigfile($options['configfile']);
+        }
+
+        $servers = $options['servers'];
+        if (is_array($servers)) {
+            foreach ($servers as $server) {
+                $servername     = $server[0];
+                $port           = isset($server[1]) ? $server[1] : 0;
+                $sharedsecret   = isset($server[2]) ? $server[2] : 'testing123';
+                $timeout        = isset($server[3]) ? $server[3] : 3;
+                $maxtries       = isset($server[4]) ? $server[4] : 3;
+                $this->radius->addServer($servername, $port, $sharedsecret, $timeout, $maxtries);
+            }
+        }
+        
+        if (!$this->radius->start()) {
+            PEAR::raiseError($this->radius->getError(), 41, PEAR_ERROR_DIE);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Authenticate
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return bool   true on success, false on reject
+     */
+    function fetchData($username, $password, $challenge = null)
+    {
+        switch($this->authtype) {
+        case 'CHAP_MD5':
+        case 'MSCHAPv1':
+            if (isset($challenge)) {
+                $this->radius->challenge = $challenge;
+                $this->radius->chapid    = 1;
+                $this->radius->response  = pack('H*', $password);
+            } else {
+                require_once 'Crypt/CHAP.php';
+                $classname = 'Crypt_' . $this->authtype;
+                $crpt = new $classname;
+                $crpt->password = $password;
+                $this->radius->challenge = $crpt->challenge;
+                $this->radius->chapid    = $crpt->chapid;
+                $this->radius->response  = $crpt->challengeResponse();
+                break;
+            }
+
+        case 'MSCHAPv2':
+            require_once 'Crypt/CHAP.php';
+            $crpt = new Crypt_MSCHAPv2;
+            $crpt->username = $username;
+            $crpt->password = $password;
+            $this->radius->challenge     = $crpt->authChallenge;
+            $this->radius->peerChallenge = $crpt->peerChallenge;
+            $this->radius->chapid        = $crpt->chapid;
+            $this->radius->response      = $crpt->challengeResponse();
+            break;
+
+        default:
+            $this->radius->password = $password;
+            break;
+        }
+
+        $this->radius->username = $username;
+
+        $this->radius->putAuthAttributes();
+        $result = $this->radius->send();
+        if (PEAR::isError($result)) {
+            return false;
+        }
+
+        $this->radius->getAttributes();
+//      just for debugging
+//      $this->radius->dumpAttributes();
+
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP5.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP5.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP5.php	(revision 10038)
@@ -0,0 +1,267 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SOAP service using PHP5 SoapClient
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Based upon Auth_Container_SOAP by Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Marcel Oelke <puRe@rednoize.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from SOAP using the PHP5 Builtin SOAP
+ * functions. This is a modification of the SOAP Storage driver from Bruno Pedro
+ * thats using the PEAR SOAP Package.
+ *
+ * This class takes one parameter (options), where
+ * you specify the following fields: 
+ *  * location and uri, or wsdl file
+ *  * method to call on the SOAP service
+ *  * usernamefield, the name of the parameter where the username is supplied
+ *  * passwordfield, the name of the parameter where the password is supplied
+ *  * matchpassword, whether to look for the password in the response from
+ *                   the function call or assume that no errors means user
+ *                   authenticated.
+ *
+ * See http://www.php.net/manual/en/ref.soap.php for further details
+ * on options for the PHP5 SoapClient which are passed through.
+ *
+ * Example usage without WSDL:
+ *
+ * <?php
+ *
+ * $options = array (
+ *       'wsdl'           => NULL,
+ *       'location'       => 'http://your.soap.service/endpoint',
+ *       'uri'            => 'urn:/Your/Namespace',
+ *       'method'         => 'checkAuth',        
+ *       'usernamefield'  => 'username',
+ *       'passwordfield'  => 'password',
+ *       'matchpasswords' => false,          
+ *       '_features' => array (
+ *           'extra_parameter'    => 'example_value',
+ *           'another_parameter'  => 'foobar'
+ *       )
+ *   );
+ *
+ * $auth = new Auth('SOAP5', $options);
+ * $auth->start();
+ *
+ * ?>
+ *
+ * Example usage with WSDL:
+ *
+ * <?php
+ *
+ * $options = array (
+ *       'wsdl'           => 'http://your.soap.service/wsdl',
+ *       'method'         => 'checkAuth',        
+ *       'usernamefield'  => 'username',
+ *       'passwordfield'  => 'password',
+ *       'matchpasswords' => false,          
+ *       '_features' => array (
+ *           'extra_parameter'    => 'example_value',
+ *           'another_parameter'  => 'foobar'
+ *       )
+ *   );
+ *
+ * $auth = new Auth('SOAP5', $options);
+ * $auth->start();
+ *
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Based upon Auth_Container_SOAP by Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Marcel Oelke <puRe@rednoize.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_SOAP5 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Required options for the class
+     * @var array
+     * @access private
+     */
+    var $_requiredOptions = array(
+            'location', 
+            'uri',
+            'method',
+            'usernamefield',
+            'passwordfield',
+            'wsdl',
+            );
+
+    /**
+     * Options for the class
+     * @var array
+     * @access private
+     */
+    var $_options = array();
+
+    /**
+     * Optional SOAP features
+     * @var array
+     * @access private
+     */
+    var $_features = array();
+
+    /**
+     * The SOAP response
+     * @var array
+     * @access public
+     */
+    var $soapResponse = array();
+     
+    // }}}
+    // {{{ Auth_Container_SOAP5()
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $options, associative array with endpoint, namespace, method,
+     *                   usernamefield, passwordfield and optional features
+     */
+    function Auth_Container_SOAP5($options)
+    {
+        $this->_setDefaults();
+
+        foreach ($options as $name => $value) {
+            $this->_options[$name] = $value;
+        }
+
+        if (!empty($this->_options['_features'])) {
+            $this->_features = $this->_options['_features'];
+            unset($this->_options['_features']);
+        }        
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from SOAP service
+     *
+     * Requests the SOAP service for the given username/password
+     * combination.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return mixed Returns the SOAP response or false if something went wrong
+     */
+    function fetchData($username, $password)
+    {        
+        $result = $this->_validateOptions();
+        if (PEAR::isError($result))
+            return $result;
+
+        // create a SOAP client
+        $soapClient = new SoapClient($this->_options["wsdl"], $this->_options);
+        
+        $params = array();        
+        // first, assign the optional features
+        foreach ($this->_features as $fieldName => $fieldValue) {
+            $params[$fieldName] = $fieldValue;
+        }
+        // assign username and password ...
+        $params[$this->_options['usernamefield']] = $username;
+        $params[$this->_options['passwordfield']] = $password;                
+                
+        try {
+            $this->soapResponse = $soapClient->__soapCall($this->_options['method'], $params);
+                        
+            if ($this->_options['matchpasswords']) {
+                // check if passwords match
+                if ($password == $this->soapResponse[$this->_options['passwordfield']]) {
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {                
+                return true;
+            }
+        } catch (SoapFault $e) {
+            return PEAR::raiseError("Error retrieving authentication data. Received SOAP Fault: ".$e->faultstring, $e->faultcode);
+        }        
+    }
+
+    // }}}
+    // {{{ _validateOptions()
+    
+    /**
+     * Validate that the options passed to the container class are enough for us to proceed
+     *
+     * @access private
+     * @param  array
+     */
+    function _validateOptions($array)
+    {
+        if (   (   is_null($this->options['wsdl'])
+                && is_null($this->options['location'])
+                && is_null($this->options['uri']))
+            || (   is_null($this->options['wsdl'])
+                && (   is_null($this->options['location'])
+                    || is_null($this->options['uri'])))) {
+            return PEAR::raiseError('Either a WSDL file or a location/uri pair must be specified.');
+        }
+        if (is_null($this->options['method'])) {
+            return PEAR::raiseError('A method to call on the soap service must be specified.');
+        }
+        return true;
+    }
+    
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['wsdl']           = null;
+        $this->options['location']       = null;
+        $this->options['uri']            = null;
+        $this->options['method']         = null;
+        $this->options['usernamefield']  = 'username';
+        $this->options['passwordfield']  = 'password';
+        $this->options['matchpasswords'] = true;
+    }
+
+    // }}}
+        
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/KADM5.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/KADM5.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/KADM5.php	(revision 10038)
@@ -0,0 +1,170 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for Authentication on a Kerberos V server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Andrew Teixeira <ateixeira@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR for error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * Storage driver for Authentication on a Kerberos V server.
+ *
+ * Available options:
+ * hostname:        The hostname of the kerberos server
+ * realm:           The Kerberos V realm
+ * timeout:         The timeout for checking the server
+ * checkServer:     Set to true to check if the server is running when
+ *                  constructing the object
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Andrew Teixeira <ateixeira@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_KADM5 extends Auth_Container {
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var string
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_KADM5()
+
+    /**
+     * Constructor of the container class
+     *
+     * $options can have these keys:
+     * 'hostname'    The hostname of the kerberos server
+     * 'realm'       The Kerberos V realm
+     * 'timeout'     The timeout for checking the server
+     * 'checkServer' Set to true to check if the server is running when
+     *               constructing the object
+     *
+     * @param  $options associative array
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_KADM5($options) {
+        if (!extension_loaded('kadm5')) {
+            return PEAR::raiseError("Cannot use Kerberos V authentication, KADM5 extension not loaded!", 41, PEAR_ERROR_DIE);
+        }
+        
+        $this->_setDefaults();
+        
+        if (isset($options['hostname'])) {
+            $this->options['hostname'] = $options['hostname'];
+        }
+        if (isset($options['realm'])) {
+            $this->options['realm'] = $options['realm'];
+        }
+        if (isset($options['timeout'])) {
+            $this->options['timeout'] = $options['timeout'];
+        }
+        if (isset($options['checkServer'])) {
+            $this->options['checkServer'] = $options['checkServer'];
+        }
+        
+        if ($this->options['checkServer']) {
+            $this->_checkServer();
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+    
+    /**
+     * Try to login to the KADM5 server
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password) {
+        if ( ($username == NULL) || ($password == NULL) ) {
+            return false;
+        }
+        
+        $server = $this->options['hostname'];
+        $realm = $this->options['realm'];
+        $check = @kadm5_init_with_password($server, $realm, $username, $password);
+        
+        if ($check == false) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+    
+    // }}}
+    // {{{ _setDefaults()
+    
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults() {
+        $this->options['hostname'] = 'localhost';
+        $this->options['realm'] = NULL;
+        $this->options['timeout'] = 10;
+        $this->options['checkServer'] = false;
+    }
+    
+    // }}}
+    // {{{ _checkServer()
+    
+    /**
+     * Check if the given server and port are reachable
+     *
+     * @access private
+     */
+    function _checkServer() {
+        $fp = @fsockopen ($this->options['host'], 88, $errno, $errstr, $this->options['timeout']);
+        if (is_resource($fp)) {
+            @fclose($fp);
+        } else {
+            $message = "Error connecting to Kerberos V server "
+                .$this->options['host'].":".$this->options['port'];
+            return PEAR::raiseError($message, 41, PEAR_ERROR_DIE);
+        }
+    }
+    
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/MDB.php	(revision 10038)
@@ -0,0 +1,573 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR MDB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.3
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR MDB package
+ */
+require_once 'MDB.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR MDB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.3
+ */
+class Auth_Container_MDB extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * MDB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_MDB() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::MDB
+     *
+     * @param  string Connection data or MDB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_MDB($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  mixed DSN string | array | mdb object
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& MDB::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'mdb_common')) {
+            $this->db = $dsn;
+        } elseif (is_object($dsn) && MDB::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->code);
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+
+        }
+
+        if (MDB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->code);
+        }
+
+        if ($this->options['auto_quote']) {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+
+        return true;
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a MDB error object.
+     */
+    function _prepare()
+    {
+        if (is_subclass_of($this->db, 'mdb_common')) {
+            return true;
+        }
+        return $this->_connect($this->options['dsn']);
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a MDB_result object or MDB_OK on success, a MDB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->query($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf("SELECT %s FROM %s WHERE %s = %s",
+                         $sql_from,
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->db->getRow($query, null, null, null, MDB_FETCHMODE_ASSOC);
+
+        if (MDB::isError($res) || PEAR::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashing
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+        
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] =
+                md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']);
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+        }
+        
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static
+                // call to setAuthData does not make sense
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed array|PEAR_Error
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol']
+                .', '.$this->options['final_passwordcol'];
+            
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf('SELECT %s FROM %s',
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+
+        $res = $this->db->getAll($query, null, null, null, MDB_FETCHMODE_ASSOC);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key   .= ', ' . $this->db->quoteIdentifier($key);
+                } else {
+                    $additional_key   .= ', ' . $key;
+                }
+                $additional_value .= ', ' . $this->db->getTextValue($value);
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->getTextValue($username),
+                         $this->db->getTextValue($password),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->getTextValue($password),
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+     * Returns the selected crypt type for this container
+     *
+     * @return string Function used to crypt the password
+     */
+    function getCryptType()
+    {
+        return $this->options['cryptType'];
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/SOAP.php	(revision 10038)
@@ -0,0 +1,228 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SOAP service
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Bruno Pedro <bpedro@co.sapo.pt> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+/**
+ * Include PEAR SOAP_Client
+ */
+require_once 'SOAP/Client.php';
+
+/**
+ * Storage driver for fetching login data from SOAP
+ *
+ * This class takes one parameter (options), where
+ * you specify the following fields: endpoint, namespace,
+ * method, encoding, usernamefield and passwordfield.
+ *
+ * You can use specify features of your SOAP service
+ * by providing its parameters in an associative manner by
+ * using the '_features' array through the options parameter.
+ *
+ * The 'matchpassword' option should be set to false if your
+ * webservice doesn't return (username,password) pairs, but
+ * instead returns error when the login is invalid.
+ *
+ * Example usage:
+ *
+ * <?php
+ *
+ * ...
+ *
+ * $options = array (
+ *             'endpoint' => 'http://your.soap.service/endpoint',
+ *             'namespace' => 'urn:/Your/Namespace',
+ *             'method' => 'get',
+ *             'encoding' => 'UTF-8',
+ *             'usernamefield' => 'login',
+ *             'passwordfield' => 'password',
+ *             'matchpasswords' => false,
+ *             '_features' => array (
+ *                             'example_feature' => 'example_value',
+ *                             'another_example'  => ''
+ *                             )
+ *             );
+ * $auth = new Auth('SOAP', $options, 'loginFunction');
+ * $auth->start();
+ *
+ * ...
+ *
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_SOAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Required options for the class
+     * @var array
+     * @access private
+     */
+    var $_requiredOptions = array(
+            'endpoint',
+            'namespace',
+            'method',
+            'encoding',
+            'usernamefield',
+            'passwordfield',
+            );
+
+    /**
+     * Options for the class
+     * @var array
+     * @access private
+     */
+    var $_options = array();
+
+    /**
+     * Optional SOAP features
+     * @var array
+     * @access private
+     */
+    var $_features = array();
+
+    /**
+     * The SOAP response
+     * @var array
+     * @access public
+     */
+     var $soapResponse = array();
+
+    /**
+     * The SOAP client
+     * @var mixed
+     * @access public
+     */
+     var $soapClient = null;
+
+    // }}}
+    // {{{ Auth_Container_SOAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $options, associative array with endpoint, namespace, method,
+     *                   usernamefield, passwordfield and optional features
+     */
+    function Auth_Container_SOAP($options)
+    {
+        $this->_options = $options;
+        if (!isset($this->_options['matchpasswords'])) {
+            $this->_options['matchpasswords'] = true;
+        }
+        if (!empty($this->_options['_features'])) {
+            $this->_features = $this->_options['_features'];
+            unset($this->_options['_features']);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from SOAP service
+     *
+     * Requests the SOAP service for the given username/password
+     * combination.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return mixed Returns the SOAP response or false if something went wrong
+     */
+    function fetchData($username, $password)
+    {
+        // check if all required options are set
+        if (array_intersect($this->_requiredOptions, array_keys($this->_options)) != $this->_requiredOptions) {
+            return false;
+        } else {
+            // create a SOAP client and set encoding
+            $this->soapClient = new SOAP_Client($this->_options['endpoint']);
+            $this->soapClient->setEncoding($this->_options['encoding']);
+        }
+
+        // set the trace option if requested
+        if (isset($this->_options['trace'])) {
+            $this->soapClient->__options['trace'] = true;
+        }
+
+        // set the timeout option if requested
+        if (isset($this->_options['timeout'])) {
+            $this->soapClient->__options['timeout'] = $this->_options['timeout'];
+        }
+
+        // assign username and password fields
+        $usernameField = new SOAP_Value($this->_options['usernamefield'],'string', $username);
+        $passwordField = new SOAP_Value($this->_options['passwordfield'],'string', $password);
+        $SOAPParams = array($usernameField, $passwordField);
+
+        // assign optional features
+        foreach ($this->_features as $fieldName => $fieldValue) {
+            $SOAPParams[] = new SOAP_Value($fieldName, 'string', $fieldValue);
+        }
+
+        // make SOAP call
+        $this->soapResponse = $this->soapClient->call(
+                $this->_options['method'],
+                $SOAPParams,
+                array('namespace' => $this->_options['namespace'])
+                );
+
+        if (!PEAR::isError($this->soapResponse)) {
+            if ($this->_options['matchpasswords']) {
+                // check if passwords match
+                if ($password == $this->soapResponse->{$this->_options['passwordfield']}) {
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/DBLite.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/DBLite.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/DBLite.php	(revision 10038)
@@ -0,0 +1,298 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Reduced storage driver for use against PEAR DB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR DB package
+ */
+require_once 'DB.php';
+
+/**
+ * A lighter storage driver for fetching login data from a database
+ *
+ * This driver is derived from the DB storage container but
+ * with the user manipulation function removed for smaller file size
+ * by the PEAR DB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_DBLite extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * DB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_DBLite() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::DB
+     *
+     * @param  string Connection data or DB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_DBLite($dsn)
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  string DSN string
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect(&$dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& DB::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, "db_common")) {
+            $this->db =& $dsn;
+        } else {
+            return PEAR::raiseError("Invalid dsn or db object given");
+        }
+
+        if (DB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a DB error object.
+     */
+    function _prepare()
+    {
+        if (!DB::isConnection($this->db)) {
+            $res = $this->_connect($this->options['dsn']);
+            if (DB::isError($res) || PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // Find if db_fields contains a *, if so assume all col are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+        
+        $query = "SELECT ".$sql_from.
+                " FROM ".$this->options['final_table'].
+                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
+        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+        if ($this->verifyPassword(trim($password, "\r\n"),
+                                  trim($res[$this->options['passwordcol']], "\r\n"),
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static call to setAuthData does not make sence
+                if (is_object($this->_auth_obj)) {
+                    $this->_auth_obj->setAuthData($key, $value);
+                } else {
+                    Auth::setAuthData($key, $value);
+                }
+            }
+            $this->activeUser = $res[$this->options['usernamecol']];
+            return true;
+        }
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/SMBPasswd.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/SMBPasswd.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/SMBPasswd.php	(revision 10038)
@@ -0,0 +1,177 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against Samba password files
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.3
+ */
+
+/**
+ * Include PEAR File_SMBPasswd
+ */
+require_once "File/SMBPasswd.php";
+/**
+ * Include Auth_Container Base file
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR class for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an SAMBA smbpasswd file.
+ *
+ * This storage container can handle SAMBA smbpasswd files.
+ *
+ * Example:
+ * $a = new Auth("SMBPasswd", '/usr/local/private/smbpasswd');
+ * $a->start();
+ * if ($a->getAuth()) {
+ *     printf ("AUTH OK<br>\n");
+ *     $a->logout();
+ * }
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @package    Auth
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.3
+ */
+class Auth_Container_SMBPasswd extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * File_SMBPasswd object
+     * @var object
+     */
+    var $pwfile;
+
+    // }}}
+
+    // {{{ Auth_Container_SMBPasswd() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $filename   string filename for a passwd type file
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_SMBPasswd($filename)
+    {
+        $this->pwfile = new File_SMBPasswd($filename,0);
+
+        if (!$this->pwfile->load()) {
+            PEAR::raiseError("Error while reading file contents.", 41, PEAR_ERROR_DIE);
+            return;
+        }
+
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from pwfile
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        return $this->pwfile->verifyAccount($username, $password);
+    }
+
+    // }}}
+    // {{{ listUsers()
+    
+    function listUsers()
+    {
+        return $this->pwfile->getAccounts();
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add a new user to the storage container
+     *
+     * @param string Username
+     * @param string Password
+     * @param array  Additional information
+     *
+     * @return boolean
+     */
+    function addUser($username, $password, $additional = '')
+    {
+        $res = $this->pwfile->addUser($user, $additional['userid'], $pass);
+        if ($res === true) {
+            return $this->pwfile->save();
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @param string Username
+     */
+    function removeUser($username)
+    {
+        $res = $this->pwfile->delUser($username);
+        if ($res === true) {
+            return $this->pwfile->save();
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password 
+     */
+    function changePassword($username, $password)
+    {
+         $res = $this->pwfile->modUser($username, '', $password);
+         if ($res === true) {
+             return $this->pwfile->save();
+         }
+         return $res;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/Array.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/Array.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/Array.php	(revision 10038)
@@ -0,0 +1,159 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a PHP Array
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     georg_1 at have2 dot com
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching authentication data from a PHP Array
+ *
+ * This container takes two options when configuring:
+ *
+ * cryptType:   The crypt used to store the password. Currently recognised
+ *              are: none, md5 and crypt. default: none
+ * users:       A named array of usernames and passwords.
+ *              Ex:
+ *              array(
+ *                  'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest
+ *                  'georg' => 'fc77dba827fcc88e0243404572c51325'  // password georg
+ *              )
+ *
+ * Usage Example:
+ * <?php
+ * $AuthOptions = array(
+ *      'users' => array(
+ *          'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest
+ *          'georg' => 'fc77dba827fcc88e0243404572c51325'  // password georg
+ *      ),
+ *      'cryptType'=>'md5',
+ *  );
+ *
+ * $auth = new Auth("Array", $AuthOptions);
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     georg_1 at have2 dot com
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      File available since Release 1.4.0
+ */
+
+class Auth_Container_Array extends Auth_Container {
+
+    // {{{ properties
+
+    /**
+     * The users and their password to authenticate against
+     *
+     * @var array $users
+     */
+    var $users;
+
+    /**
+     * The cryptType used on the passwords
+     *
+     * @var string $cryptType
+     */
+    var $cryptType = 'none';
+
+    // }}}
+    // {{{ Auth_Container_Array()
+
+    /**
+     * Constructor for Array Container
+     *
+     * @param array $data Options for the container
+     * @return void
+     */
+    function Auth_Container_Array($data)
+    {
+        if (!is_array($data)) {
+            PEAR::raiseError('The options for Auth_Container_Array must be an array');
+        } 
+        if (isset($data['users']) && is_array($data['users'])) {
+            $this->users = $data['users'];
+        } else {
+            $this->users = array();
+            PEAR::raiseError('Auth_Container_Array: no user data found inoptions array');
+        } 
+        if (isset($data['cryptType'])) {
+            $this->cryptType = $data['cryptType'];
+        } 
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from array
+     *
+     * This function uses the given username to fetch the corresponding
+     * login data from the array. If an account that matches the passed
+     * username and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean|PEAR_Error Error object or boolean
+     */
+    function fetchData($user, $pass)
+    {
+        if (   isset($this->users[$user])
+            && $this->verifyPassword($pass, $this->users[$user], $this->cryptType)) {
+            return true;
+        }
+        return false;
+    } 
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users available within the container
+     *
+     * @return array
+     */
+    function listUsers()
+    {
+        $ret = array();
+        foreach ($this->users as $username => $password) {
+            $ret[]['username'] = $username;
+        } 
+        return $ret;
+    } 
+
+    // }}}
+
+} 
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth/Container/File.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth/Container/File.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth/Container/File.php	(revision 10038)
@@ -0,0 +1,305 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a generic password file
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Michael Wallner <mike@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include PEAR File_Passwd package
+ */
+require_once "File/Passwd.php";
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an encrypted password file.
+ *
+ * This storage container can handle CVS pserver style passwd files.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Michael Wallner <mike@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_File extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Path to passwd file
+     * 
+     * @var string
+     */
+    var $pwfile = '';
+
+    /**
+     * Options for container
+     *
+     * @var array
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_File() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  string $filename             path to passwd file
+     * @return object Auth_Container_File   new Auth_Container_File object
+     */
+    function Auth_Container_File($filename) {
+        $this->_setDefaults();
+        
+        // Only file is a valid option here
+        if(is_array($filename)) {
+            $this->pwfile = $filename['file'];
+            $this->_parseOptions($filename);
+        } else {
+            $this->pwfile = $filename;
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Authenticate an user
+     *
+     * @param   string  username
+     * @param   string  password
+     * @return  mixed   boolean|PEAR_Error
+     */
+    function fetchData($user, $pass)
+    {
+        return File_Passwd::staticAuth($this->options['type'], $this->pwfile, $user, $pass);
+    }
+
+    // }}}
+    // {{{ listUsers()
+    
+    /**
+     * List all available users
+     * 
+     * @return   array
+     */
+    function listUsers()
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return array();
+        }
+
+        $users  = $pw_obj->listUser();
+        if (!is_array($users)) {
+            return array();
+        }
+
+        foreach ($users as $key => $value) {
+            $retVal[] = array("username" => $key, 
+                              "password" => $value['passwd'],
+                              "cvsuser"  => $value['system']);
+        }
+
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add a new user to the storage container
+     *
+     * @param string username
+     * @param string password
+     * @param mixed  Additional parameters to File_Password_*::addUser()
+     *
+     * @return boolean
+     */
+    function addUser($user, $pass, $additional='')
+    {
+        $params = array($user, $pass);
+        if (is_array($additional)) {
+            foreach ($additional as $item) {
+                $params[] = $item;
+            }
+        } else {
+            $params[] = $additional;
+        }
+
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = call_user_func_array(array(&$pw_obj, 'addUser'), $params);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @param   string  Username
+     * @return  boolean
+     */
+    function removeUser($user)
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = $pw_obj->delUser($user);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password 
+     */
+    function changePassword($username, $password)
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = $pw_obj->changePasswd($username, $password);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ _load()
+    
+    /**
+     * Load and initialize the File_Passwd object
+     * 
+     * @return  object  File_Passwd_Cvs|PEAR_Error
+     */
+    function &_load()
+    {
+        static $pw_obj;
+        
+        if (!isset($pw_obj)) {
+            $pw_obj = File_Passwd::factory($this->options['type']);
+            if (PEAR::isError($pw_obj)) {
+                return $pw_obj;
+            }
+            
+            $pw_obj->setFile($this->pwfile);
+            
+            $res = $pw_obj->load();
+            if (PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        
+        return $pw_obj;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['type']       = 'Cvs';
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/KADM5.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/KADM5.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/KADM5.php	(revision 10038)
@@ -0,0 +1,170 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for Authentication on a Kerberos V server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Andrew Teixeira <ateixeira@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR for error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * Storage driver for Authentication on a Kerberos V server.
+ *
+ * Available options:
+ * hostname:        The hostname of the kerberos server
+ * realm:           The Kerberos V realm
+ * timeout:         The timeout for checking the server
+ * checkServer:     Set to true to check if the server is running when
+ *                  constructing the object
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Andrew Teixeira <ateixeira@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_KADM5 extends Auth_Container {
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var string
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_KADM5()
+
+    /**
+     * Constructor of the container class
+     *
+     * $options can have these keys:
+     * 'hostname'    The hostname of the kerberos server
+     * 'realm'       The Kerberos V realm
+     * 'timeout'     The timeout for checking the server
+     * 'checkServer' Set to true to check if the server is running when
+     *               constructing the object
+     *
+     * @param  $options associative array
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_KADM5($options) {
+        if (!extension_loaded('kadm5')) {
+            return PEAR::raiseError("Cannot use Kerberos V authentication, KADM5 extension not loaded!", 41, PEAR_ERROR_DIE);
+        }
+        
+        $this->_setDefaults();
+        
+        if (isset($options['hostname'])) {
+            $this->options['hostname'] = $options['hostname'];
+        }
+        if (isset($options['realm'])) {
+            $this->options['realm'] = $options['realm'];
+        }
+        if (isset($options['timeout'])) {
+            $this->options['timeout'] = $options['timeout'];
+        }
+        if (isset($options['checkServer'])) {
+            $this->options['checkServer'] = $options['checkServer'];
+        }
+        
+        if ($this->options['checkServer']) {
+            $this->_checkServer();
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+    
+    /**
+     * Try to login to the KADM5 server
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password) {
+        if ( ($username == NULL) || ($password == NULL) ) {
+            return false;
+        }
+        
+        $server = $this->options['hostname'];
+        $realm = $this->options['realm'];
+        $check = @kadm5_init_with_password($server, $realm, $username, $password);
+        
+        if ($check == false) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+    
+    // }}}
+    // {{{ _setDefaults()
+    
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults() {
+        $this->options['hostname'] = 'localhost';
+        $this->options['realm'] = NULL;
+        $this->options['timeout'] = 10;
+        $this->options['checkServer'] = false;
+    }
+    
+    // }}}
+    // {{{ _checkServer()
+    
+    /**
+     * Check if the given server and port are reachable
+     *
+     * @access private
+     */
+    function _checkServer() {
+        $fp = @fsockopen ($this->options['host'], 88, $errno, $errstr, $this->options['timeout']);
+        if (is_resource($fp)) {
+            @fclose($fp);
+        } else {
+            $message = "Error connecting to Kerberos V server "
+                .$this->options['host'].":".$this->options['port'];
+            return PEAR::raiseError($message, 41, PEAR_ERROR_DIE);
+        }
+    }
+    
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/MDB.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/MDB.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/MDB.php	(revision 10038)
@@ -0,0 +1,573 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR MDB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.3
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR MDB package
+ */
+require_once 'MDB.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR MDB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.3
+ */
+class Auth_Container_MDB extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * MDB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_MDB() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::MDB
+     *
+     * @param  string Connection data or MDB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_MDB($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  mixed DSN string | array | mdb object
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& MDB::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'mdb_common')) {
+            $this->db = $dsn;
+        } elseif (is_object($dsn) && MDB::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->code);
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+
+        }
+
+        if (MDB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->code);
+        }
+
+        if ($this->options['auto_quote']) {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+
+        return true;
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a MDB error object.
+     */
+    function _prepare()
+    {
+        if (is_subclass_of($this->db, 'mdb_common')) {
+            return true;
+        }
+        return $this->_connect($this->options['dsn']);
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a MDB_result object or MDB_OK on success, a MDB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->query($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf("SELECT %s FROM %s WHERE %s = %s",
+                         $sql_from,
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->db->getRow($query, null, null, null, MDB_FETCHMODE_ASSOC);
+
+        if (MDB::isError($res) || PEAR::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashing
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+        
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] =
+                md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']);
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+        }
+        
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static
+                // call to setAuthData does not make sense
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed array|PEAR_Error
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol']
+                .', '.$this->options['final_passwordcol'];
+            
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf('SELECT %s FROM %s',
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+
+        $res = $this->db->getAll($query, null, null, null, MDB_FETCHMODE_ASSOC);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key   .= ', ' . $this->db->quoteIdentifier($key);
+                } else {
+                    $additional_key   .= ', ' . $key;
+                }
+                $additional_value .= ', ' . $this->db->getTextValue($value);
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->getTextValue($username),
+                         $this->db->getTextValue($password),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->getTextValue($password),
+                         $this->options['final_usernamecol'],
+                         $this->db->getTextValue($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+     * Returns the selected crypt type for this container
+     *
+     * @return string Function used to crypt the password
+     */
+    function getCryptType()
+    {
+        return $this->options['cryptType'];
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/SOAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/SOAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/SOAP.php	(revision 10038)
@@ -0,0 +1,228 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SOAP service
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Bruno Pedro <bpedro@co.sapo.pt> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+/**
+ * Include PEAR SOAP_Client
+ */
+require_once 'SOAP/Client.php';
+
+/**
+ * Storage driver for fetching login data from SOAP
+ *
+ * This class takes one parameter (options), where
+ * you specify the following fields: endpoint, namespace,
+ * method, encoding, usernamefield and passwordfield.
+ *
+ * You can use specify features of your SOAP service
+ * by providing its parameters in an associative manner by
+ * using the '_features' array through the options parameter.
+ *
+ * The 'matchpassword' option should be set to false if your
+ * webservice doesn't return (username,password) pairs, but
+ * instead returns error when the login is invalid.
+ *
+ * Example usage:
+ *
+ * <?php
+ *
+ * ...
+ *
+ * $options = array (
+ *             'endpoint' => 'http://your.soap.service/endpoint',
+ *             'namespace' => 'urn:/Your/Namespace',
+ *             'method' => 'get',
+ *             'encoding' => 'UTF-8',
+ *             'usernamefield' => 'login',
+ *             'passwordfield' => 'password',
+ *             'matchpasswords' => false,
+ *             '_features' => array (
+ *                             'example_feature' => 'example_value',
+ *                             'another_example'  => ''
+ *                             )
+ *             );
+ * $auth = new Auth('SOAP', $options, 'loginFunction');
+ * $auth->start();
+ *
+ * ...
+ *
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_SOAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Required options for the class
+     * @var array
+     * @access private
+     */
+    var $_requiredOptions = array(
+            'endpoint',
+            'namespace',
+            'method',
+            'encoding',
+            'usernamefield',
+            'passwordfield',
+            );
+
+    /**
+     * Options for the class
+     * @var array
+     * @access private
+     */
+    var $_options = array();
+
+    /**
+     * Optional SOAP features
+     * @var array
+     * @access private
+     */
+    var $_features = array();
+
+    /**
+     * The SOAP response
+     * @var array
+     * @access public
+     */
+     var $soapResponse = array();
+
+    /**
+     * The SOAP client
+     * @var mixed
+     * @access public
+     */
+     var $soapClient = null;
+
+    // }}}
+    // {{{ Auth_Container_SOAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $options, associative array with endpoint, namespace, method,
+     *                   usernamefield, passwordfield and optional features
+     */
+    function Auth_Container_SOAP($options)
+    {
+        $this->_options = $options;
+        if (!isset($this->_options['matchpasswords'])) {
+            $this->_options['matchpasswords'] = true;
+        }
+        if (!empty($this->_options['_features'])) {
+            $this->_features = $this->_options['_features'];
+            unset($this->_options['_features']);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from SOAP service
+     *
+     * Requests the SOAP service for the given username/password
+     * combination.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return mixed Returns the SOAP response or false if something went wrong
+     */
+    function fetchData($username, $password)
+    {
+        // check if all required options are set
+        if (array_intersect($this->_requiredOptions, array_keys($this->_options)) != $this->_requiredOptions) {
+            return false;
+        } else {
+            // create a SOAP client and set encoding
+            $this->soapClient = new SOAP_Client($this->_options['endpoint']);
+            $this->soapClient->setEncoding($this->_options['encoding']);
+        }
+
+        // set the trace option if requested
+        if (isset($this->_options['trace'])) {
+            $this->soapClient->__options['trace'] = true;
+        }
+
+        // set the timeout option if requested
+        if (isset($this->_options['timeout'])) {
+            $this->soapClient->__options['timeout'] = $this->_options['timeout'];
+        }
+
+        // assign username and password fields
+        $usernameField = new SOAP_Value($this->_options['usernamefield'],'string', $username);
+        $passwordField = new SOAP_Value($this->_options['passwordfield'],'string', $password);
+        $SOAPParams = array($usernameField, $passwordField);
+
+        // assign optional features
+        foreach ($this->_features as $fieldName => $fieldValue) {
+            $SOAPParams[] = new SOAP_Value($fieldName, 'string', $fieldValue);
+        }
+
+        // make SOAP call
+        $this->soapResponse = $this->soapClient->call(
+                $this->_options['method'],
+                $SOAPParams,
+                array('namespace' => $this->_options['namespace'])
+                );
+
+        if (!PEAR::isError($this->soapResponse)) {
+            if ($this->_options['matchpasswords']) {
+                // check if passwords match
+                if ($password == $this->soapResponse->{$this->_options['passwordfield']}) {
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/SMBPasswd.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/SMBPasswd.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/SMBPasswd.php	(revision 10038)
@@ -0,0 +1,177 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against Samba password files
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.3
+ */
+
+/**
+ * Include PEAR File_SMBPasswd
+ */
+require_once "File/SMBPasswd.php";
+/**
+ * Include Auth_Container Base file
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR class for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an SAMBA smbpasswd file.
+ *
+ * This storage container can handle SAMBA smbpasswd files.
+ *
+ * Example:
+ * $a = new Auth("SMBPasswd", '/usr/local/private/smbpasswd');
+ * $a->start();
+ * if ($a->getAuth()) {
+ *     printf ("AUTH OK<br>\n");
+ *     $a->logout();
+ * }
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @package    Auth
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.3
+ */
+class Auth_Container_SMBPasswd extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * File_SMBPasswd object
+     * @var object
+     */
+    var $pwfile;
+
+    // }}}
+
+    // {{{ Auth_Container_SMBPasswd() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $filename   string filename for a passwd type file
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_SMBPasswd($filename)
+    {
+        $this->pwfile = new File_SMBPasswd($filename,0);
+
+        if (!$this->pwfile->load()) {
+            PEAR::raiseError("Error while reading file contents.", 41, PEAR_ERROR_DIE);
+            return;
+        }
+
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from pwfile
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        return $this->pwfile->verifyAccount($username, $password);
+    }
+
+    // }}}
+    // {{{ listUsers()
+    
+    function listUsers()
+    {
+        return $this->pwfile->getAccounts();
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add a new user to the storage container
+     *
+     * @param string Username
+     * @param string Password
+     * @param array  Additional information
+     *
+     * @return boolean
+     */
+    function addUser($username, $password, $additional = '')
+    {
+        $res = $this->pwfile->addUser($user, $additional['userid'], $pass);
+        if ($res === true) {
+            return $this->pwfile->save();
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @param string Username
+     */
+    function removeUser($username)
+    {
+        $res = $this->pwfile->delUser($username);
+        if ($res === true) {
+            return $this->pwfile->save();
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password 
+     */
+    function changePassword($username, $password)
+    {
+         $res = $this->pwfile->modUser($username, '', $password);
+         if ($res === true) {
+             return $this->pwfile->save();
+         }
+         return $res;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/DBLite.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/DBLite.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/DBLite.php	(revision 10038)
@@ -0,0 +1,298 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Reduced storage driver for use against PEAR DB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR DB package
+ */
+require_once 'DB.php';
+
+/**
+ * A lighter storage driver for fetching login data from a database
+ *
+ * This driver is derived from the DB storage container but
+ * with the user manipulation function removed for smaller file size
+ * by the PEAR DB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_DBLite extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * DB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_DBLite() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::DB
+     *
+     * @param  string Connection data or DB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_DBLite($dsn)
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  string DSN string
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect(&$dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& DB::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, "db_common")) {
+            $this->db =& $dsn;
+        } else {
+            return PEAR::raiseError("Invalid dsn or db object given");
+        }
+
+        if (DB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a DB error object.
+     */
+    function _prepare()
+    {
+        if (!DB::isConnection($this->db)) {
+            $res = $this->_connect($this->options['dsn']);
+            if (DB::isError($res) || PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // Find if db_fields contains a *, if so assume all col are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+        
+        $query = "SELECT ".$sql_from.
+                " FROM ".$this->options['final_table'].
+                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
+        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+        if ($this->verifyPassword(trim($password, "\r\n"),
+                                  trim($res[$this->options['passwordcol']], "\r\n"),
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static call to setAuthData does not make sence
+                if (is_object($this->_auth_obj)) {
+                    $this->_auth_obj->setAuthData($key, $value);
+                } else {
+                    Auth::setAuthData($key, $value);
+                }
+            }
+            $this->activeUser = $res[$this->options['usernamecol']];
+            return true;
+        }
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/Array.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/Array.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/Array.php	(revision 10038)
@@ -0,0 +1,159 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a PHP Array
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     georg_1 at have2 dot com
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching authentication data from a PHP Array
+ *
+ * This container takes two options when configuring:
+ *
+ * cryptType:   The crypt used to store the password. Currently recognised
+ *              are: none, md5 and crypt. default: none
+ * users:       A named array of usernames and passwords.
+ *              Ex:
+ *              array(
+ *                  'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest
+ *                  'georg' => 'fc77dba827fcc88e0243404572c51325'  // password georg
+ *              )
+ *
+ * Usage Example:
+ * <?php
+ * $AuthOptions = array(
+ *      'users' => array(
+ *          'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest
+ *          'georg' => 'fc77dba827fcc88e0243404572c51325'  // password georg
+ *      ),
+ *      'cryptType'=>'md5',
+ *  );
+ *
+ * $auth = new Auth("Array", $AuthOptions);
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     georg_1 at have2 dot com
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      File available since Release 1.4.0
+ */
+
+class Auth_Container_Array extends Auth_Container {
+
+    // {{{ properties
+
+    /**
+     * The users and their password to authenticate against
+     *
+     * @var array $users
+     */
+    var $users;
+
+    /**
+     * The cryptType used on the passwords
+     *
+     * @var string $cryptType
+     */
+    var $cryptType = 'none';
+
+    // }}}
+    // {{{ Auth_Container_Array()
+
+    /**
+     * Constructor for Array Container
+     *
+     * @param array $data Options for the container
+     * @return void
+     */
+    function Auth_Container_Array($data)
+    {
+        if (!is_array($data)) {
+            PEAR::raiseError('The options for Auth_Container_Array must be an array');
+        } 
+        if (isset($data['users']) && is_array($data['users'])) {
+            $this->users = $data['users'];
+        } else {
+            $this->users = array();
+            PEAR::raiseError('Auth_Container_Array: no user data found inoptions array');
+        } 
+        if (isset($data['cryptType'])) {
+            $this->cryptType = $data['cryptType'];
+        } 
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from array
+     *
+     * This function uses the given username to fetch the corresponding
+     * login data from the array. If an account that matches the passed
+     * username and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean|PEAR_Error Error object or boolean
+     */
+    function fetchData($user, $pass)
+    {
+        if (   isset($this->users[$user])
+            && $this->verifyPassword($pass, $this->users[$user], $this->cryptType)) {
+            return true;
+        }
+        return false;
+    } 
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users available within the container
+     *
+     * @return array
+     */
+    function listUsers()
+    {
+        $ret = array();
+        foreach ($this->users as $username => $password) {
+            $ret[]['username'] = $username;
+        } 
+        return $ret;
+    } 
+
+    // }}}
+
+} 
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/File.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/File.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/File.php	(revision 10038)
@@ -0,0 +1,305 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a generic password file
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Michael Wallner <mike@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include PEAR File_Passwd package
+ */
+require_once "File/Passwd.php";
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an encrypted password file.
+ *
+ * This storage container can handle CVS pserver style passwd files.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Michael Wallner <mike@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_File extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Path to passwd file
+     * 
+     * @var string
+     */
+    var $pwfile = '';
+
+    /**
+     * Options for container
+     *
+     * @var array
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_File() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  string $filename             path to passwd file
+     * @return object Auth_Container_File   new Auth_Container_File object
+     */
+    function Auth_Container_File($filename) {
+        $this->_setDefaults();
+        
+        // Only file is a valid option here
+        if(is_array($filename)) {
+            $this->pwfile = $filename['file'];
+            $this->_parseOptions($filename);
+        } else {
+            $this->pwfile = $filename;
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Authenticate an user
+     *
+     * @param   string  username
+     * @param   string  password
+     * @return  mixed   boolean|PEAR_Error
+     */
+    function fetchData($user, $pass)
+    {
+        return File_Passwd::staticAuth($this->options['type'], $this->pwfile, $user, $pass);
+    }
+
+    // }}}
+    // {{{ listUsers()
+    
+    /**
+     * List all available users
+     * 
+     * @return   array
+     */
+    function listUsers()
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return array();
+        }
+
+        $users  = $pw_obj->listUser();
+        if (!is_array($users)) {
+            return array();
+        }
+
+        foreach ($users as $key => $value) {
+            $retVal[] = array("username" => $key, 
+                              "password" => $value['passwd'],
+                              "cvsuser"  => $value['system']);
+        }
+
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add a new user to the storage container
+     *
+     * @param string username
+     * @param string password
+     * @param mixed  Additional parameters to File_Password_*::addUser()
+     *
+     * @return boolean
+     */
+    function addUser($user, $pass, $additional='')
+    {
+        $params = array($user, $pass);
+        if (is_array($additional)) {
+            foreach ($additional as $item) {
+                $params[] = $item;
+            }
+        } else {
+            $params[] = $additional;
+        }
+
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = call_user_func_array(array(&$pw_obj, 'addUser'), $params);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @param   string  Username
+     * @return  boolean
+     */
+    function removeUser($user)
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = $pw_obj->delUser($user);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password 
+     */
+    function changePassword($username, $password)
+    {
+        $pw_obj = &$this->_load();
+        if (PEAR::isError($pw_obj)) {
+            return false;
+        }
+        
+        $res = $pw_obj->changePasswd($username, $password);
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        $res = $pw_obj->save();
+        if (PEAR::isError($res)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    // }}}
+    // {{{ _load()
+    
+    /**
+     * Load and initialize the File_Passwd object
+     * 
+     * @return  object  File_Passwd_Cvs|PEAR_Error
+     */
+    function &_load()
+    {
+        static $pw_obj;
+        
+        if (!isset($pw_obj)) {
+            $pw_obj = File_Passwd::factory($this->options['type']);
+            if (PEAR::isError($pw_obj)) {
+                return $pw_obj;
+            }
+            
+            $pw_obj->setFile($this->pwfile);
+            
+            $res = $pw_obj->load();
+            if (PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        
+        return $pw_obj;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['type']       = 'Cvs';
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/LDAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/LDAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/LDAP.php	(revision 10038)
@@ -0,0 +1,773 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against an LDAP server
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jan Wagner <wagner@netsols.de> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @author     Hugues Peeters <hugues.peeters@claroline.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from LDAP
+ *
+ * This class is heavily based on the DB and File containers. By default it
+ * connects to localhost:389 and searches for uid=$username with the scope
+ * "sub". If no search base is specified, it will try to determine it via
+ * the namingContexts attribute. It takes its parameters in a hash, connects
+ * to the ldap server, binds anonymously, searches for the user, and tries
+ * to bind as the user with the supplied password. When a group was set, it
+ * will look for group membership of the authenticated user. If all goes
+ * well the authentication was successful.
+ *
+ * Parameters:
+ *
+ * host:        localhost (default), ldap.netsols.de or 127.0.0.1
+ * port:        389 (default) or 636 or whereever your server runs
+ * url:         ldap://localhost:389/
+ *              useful for ldaps://, works only with openldap2 ?
+ *              it will be preferred over host and port
+ * version:     LDAP version to use, ususally 2 (default) or 3,
+ *              must be an integer!
+ * referrals:   If set, determines whether the LDAP library automatically
+ *              follows referrals returned by LDAP servers or not. Possible
+ *              values are true (default) or false.
+ * binddn:      If set, searching for user will be done after binding
+ *              as this user, if not set the bind will be anonymous.
+ *              This is reported to make the container work with MS
+ *              Active Directory, but should work with any server that
+ *              is configured this way.
+ *              This has to be a complete dn for now (basedn and
+ *              userdn will not be appended).
+ * bindpw:      The password to use for binding with binddn
+ * basedn:      the base dn of your server
+ * userdn:      gets prepended to basedn when searching for user
+ * userscope:   Scope for user searching: one, sub (default), or base
+ * userattr:    the user attribute to search for (default: uid)
+ * userfilter:  filter that will be added to the search filter
+ *              this way: (&(userattr=username)(userfilter))
+ *              default: (objectClass=posixAccount)
+ * attributes:  array of additional attributes to fetch from entry.
+ *              these will added to auth data and can be retrieved via
+ *              Auth::getAuthData(). An empty array will fetch all attributes,
+ *              array('') will fetch no attributes at all (default)
+ *              If you add 'dn' as a value to this array, the users DN that was
+ *              used for binding will be added to auth data as well.
+ * attrformat:  The returned format of the additional data defined in the
+ *              'attributes' option. Two formats are available.
+ *              LDAP returns data formatted in a
+ *              multidimensional array where each array starts with a
+ *              'count' element providing the number of attributes in the
+ *              entry, or the number of values for attributes. When set
+ *              to this format, the only way to retrieve data from the
+ *              Auth object is by calling getAuthData('attributes').
+ *              AUTH returns data formatted in a
+ *              structure more compliant with other Auth Containers,
+ *              where each attribute element can be directly called by
+ *              getAuthData() method from Auth.
+ *              For compatibily with previous LDAP container versions,
+ *              the default format is LDAP.
+ * groupdn:     gets prepended to basedn when searching for group
+ * groupattr:   the group attribute to search for (default: cn)
+ * groupfilter: filter that will be added to the search filter when
+ *              searching for a group:
+ *              (&(groupattr=group)(memberattr=username)(groupfilter))
+ *              default: (objectClass=groupOfUniqueNames)
+ * memberattr : the attribute of the group object where the user dn
+ *              may be found (default: uniqueMember)
+ * memberisdn:  whether the memberattr is the dn of the user (default)
+ *              or the value of userattr (usually uid)
+ * group:       the name of group to search for
+ * groupscope:  Scope for group searching: one, sub (default), or base
+ * start_tls:   enable/disable the use of START_TLS encrypted connection 
+ *              (default: false)
+ * debug:       Enable/Disable debugging output (default: false)
+ * try_all:     Whether to try all user accounts returned from the search
+ *              or just the first one. (default: false)
+ *
+ * To use this storage container, you have to use the following syntax:
+ *
+ * <?php
+ * ...
+ *
+ * $a1 = new Auth("LDAP", array(
+ *       'host' => 'localhost',
+ *       'port' => '389',
+ *       'version' => 3,
+ *       'basedn' => 'o=netsols,c=de',
+ *       'userattr' => 'uid'
+ *       'binddn' => 'cn=admin,o=netsols,c=de',
+ *       'bindpw' => 'password'));
+ *
+ * $a2 = new Auth('LDAP', array(
+ *       'url' => 'ldaps://ldap.netsols.de',
+ *       'basedn' => 'o=netsols,c=de',
+ *       'userscope' => 'one',
+ *       'userdn' => 'ou=People',
+ *       'groupdn' => 'ou=Groups',
+ *       'groupfilter' => '(objectClass=posixGroup)',
+ *       'memberattr' => 'memberUid',
+ *       'memberisdn' => false,
+ *       'group' => 'admin'
+ *       ));
+ *
+ * $a3 = new Auth('LDAP', array(
+ *       'host' => 'ldap.netsols.de',
+ *       'port' => 389,
+ *       'version' => 3,
+ *       'referrals' => false,
+ *       'basedn' => 'dc=netsols,dc=de',
+ *       'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de',
+ *       'bindpw' => 'password',
+ *       'userattr' => 'samAccountName',
+ *       'userfilter' => '(objectClass=user)',
+ *       'attributes' => array(''),
+ *       'group' => 'testing',
+ *       'groupattr' => 'samAccountName',
+ *       'groupfilter' => '(objectClass=group)',
+ *       'memberattr' => 'member',
+ *       'memberisdn' => true,
+ *       'groupdn' => 'cn=Users',
+ *       'groupscope' => 'one',
+ *       'debug' => true);
+ *
+ * The parameter values have to correspond
+ * to the ones for your LDAP server of course.
+ *
+ * When talking to a Microsoft ActiveDirectory server you have to
+ * use 'samaccountname' as the 'userattr' and follow special rules
+ * to translate the ActiveDirectory directory names into 'basedn'.
+ * The 'basedn' for the default 'Users' folder on an ActiveDirectory
+ * server for the ActiveDirectory Domain (which is not related to
+ * its DNS name) "win2000.example.org" would be:
+ * "CN=Users, DC=win2000, DC=example, DC=org'
+ * where every component of the domain name becomes a DC attribute
+ * of its own. If you want to use a custom users folder you have to
+ * replace "CN=Users" with a sequence of "OU" attributes that specify
+ * the path to your custom folder in reverse order.
+ * So the ActiveDirectory folder
+ *   "win2000.example.org\Custom\Accounts"
+ * would become
+ *   "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org'
+ *
+ * It seems that binding anonymously to an Active Directory
+ * is not allowed, so you have to set binddn and bindpw for
+ * user searching.
+ * 
+ * LDAP Referrals need to be set to false for AD to work sometimes.
+ *
+ * Example a3 shows a full blown and tested example for connection to 
+ * Windows 2000 Active Directory with group mebership checking
+ *
+ * Note also that if you want an encrypted connection to an MS LDAP 
+ * server, then, on your webserver, you must specify 
+ *        TLS_REQCERT never
+ * in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which
+ * may or may not be read depending on your configuration).
+ *
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jan Wagner <wagner@netsols.de>
+ * @author     Adam Ashley <aashley@php.net>
+ * @author     Hugues Peeters <hugues.peeters@claroline.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_LDAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * Connection ID of LDAP Link
+     * @var string
+     */
+    var $conn_id = false;
+
+    // }}}
+
+    // {{{ Auth_Container_LDAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $params, associative hash with host,port,basedn and userattr key
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_LDAP($params)
+    {
+        if (false === extension_loaded('ldap')) {
+            return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded',
+                    41, PEAR_ERROR_DIE);
+        }
+
+        $this->_setDefaults();
+
+        if (is_array($params)) {
+            $this->_parseOptions($params);
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare LDAP connection
+     *
+     * This function checks if we have already opened a connection to
+     * the LDAP server. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a PEAR error object.
+     */
+    function _prepare()
+    {
+        if (!$this->_isValidLink()) {
+            $res = $this->_connect();
+            if (PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to the LDAP server using the global options
+     *
+     * @access private
+     * @return object  Returns a PEAR error object if an error occurs.
+     */
+    function _connect()
+    {
+        // connect
+        if (isset($this->options['url']) && $this->options['url'] != '') {
+            $this->_debug('Connecting with URL', __LINE__);
+            $conn_params = array($this->options['url']);
+        } else {
+            $this->_debug('Connecting with host:port', __LINE__);
+            $conn_params = array($this->options['host'], $this->options['port']);
+        }
+
+        if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) {
+            return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41);
+        }
+        $this->_debug('Successfully connected to server', __LINE__);
+
+        // switch LDAP version
+        if (is_numeric($this->options['version']) && $this->options['version'] > 2) {
+            $this->_debug("Switching to LDAP version {$this->options['version']}", __LINE__);
+            @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']);
+        
+            // start TLS if available
+            if (isset($this->options['start_tls']) && $this->options['start_tls']) {           
+                $this->_debug("Starting TLS session", __LINE__);
+                if (@ldap_start_tls($this->conn_id) === false) {
+                    return PEAR::raiseError('Auth_Container_LDAP: Could not start tls.', 41);
+                }
+            }
+        }
+
+        // switch LDAP referrals
+        if (is_bool($this->options['referrals'])) {
+          $this->_debug("Switching LDAP referrals to " . (($this->options['referrals']) ? 'true' : 'false'), __LINE__);
+          @ldap_set_option($this->conn_id, LDAP_OPT_REFERRALS, $this->options['referrals']);
+        }
+
+        // bind with credentials or anonymously
+        if (strlen($this->options['binddn']) && strlen($this->options['bindpw'])) {
+            $this->_debug('Binding with credentials', __LINE__);
+            $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']);
+        } else {
+            $this->_debug('Binding anonymously', __LINE__);
+            $bind_params = array($this->conn_id);
+        }
+
+        // bind for searching
+        if ((@call_user_func_array('ldap_bind', $bind_params)) === false) {
+            $this->_debug();
+            $this->_disconnect();
+            return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41);
+        }
+        $this->_debug('Binding was successful', __LINE__);
+
+        return true;
+    }
+
+    // }}}
+    // {{{ _disconnect()
+
+    /**
+     * Disconnects (unbinds) from ldap server
+     *
+     * @access private
+     */
+    function _disconnect()
+    {
+        if ($this->_isValidLink()) {
+            $this->_debug('disconnecting from server');
+            @ldap_unbind($this->conn_id);
+        }
+    }
+
+    // }}}
+    // {{{ _getBaseDN()
+
+    /**
+     * Tries to find Basedn via namingContext Attribute
+     *
+     * @access private
+     */
+    function _getBaseDN()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if ($this->options['basedn'] == "" && $this->_isValidLink()) {
+            $this->_debug("basedn not set, searching via namingContexts.", __LINE__);
+
+            $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts"));
+
+            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
+
+                $this->_debug("got result for namingContexts", __LINE__);
+
+                $entry_id = @ldap_first_entry($this->conn_id, $result_id);
+                $attrs = @ldap_get_attributes($this->conn_id, $entry_id);
+                $basedn = $attrs['namingContexts'][0];
+
+                if ($basedn != "") {
+                    $this->_debug("result for namingContexts was $basedn", __LINE__);
+                    $this->options['basedn'] = $basedn;
+                }
+            }
+            @ldap_free_result($result_id);
+        }
+
+        // if base ist still not set, raise error
+        if ($this->options['basedn'] == "") {
+            return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _isValidLink()
+
+    /**
+     * determines whether there is a valid ldap conenction or not
+     *
+     * @accessd private
+     * @return boolean
+     */
+    function _isValidLink()
+    {
+        if (is_resource($this->conn_id)) {
+            if (get_resource_type($this->conn_id) == 'ldap link') {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults()
+    {
+        $this->options['url']         = '';
+        $this->options['host']        = 'localhost';
+        $this->options['port']        = '389';
+        $this->options['version']     = 2;
+        $this->options['referrals']   = true;
+        $this->options['binddn']      = '';
+        $this->options['bindpw']      = '';
+        $this->options['basedn']      = '';
+        $this->options['userdn']      = '';
+        $this->options['userscope']   = 'sub';
+        $this->options['userattr']    = 'uid';
+        $this->options['userfilter']  = '(objectClass=posixAccount)';
+        $this->options['attributes']  = array(''); // no attributes
+     // $this->options['attrformat']  = 'LDAP'; // returns attribute array as PHP LDAP functions return it
+        $this->options['attrformat']  = 'AUTH'; // returns attribute like other Auth containers
+        $this->options['group']       = '';
+        $this->options['groupdn']     = '';
+        $this->options['groupscope']  = 'sub';
+        $this->options['groupattr']   = 'cn';
+        $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)';
+        $this->options['memberattr']  = 'uniqueMember';
+        $this->options['memberisdn']  = true;
+        $this->options['start_tls']   = false;
+        $this->options['debug']       = false;
+        $this->options['try_all']     = false; // Try all user ids returned not just the first one
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        $array = $this->_setV12OptionsToV13($array);
+
+        foreach ($array as $key => $value) {
+            if (array_key_exists($key, $this->options)) {
+                if ($key == 'attributes') {
+                    if (is_array($value)) {
+                        $this->options[$key] = $value;
+                    } else {
+                        $this->options[$key] = explode(',', $value);
+                    }
+                } else {
+                    $this->options[$key] = $value;
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _setV12OptionsToV13()
+
+    /**
+     * Adapt deprecated options from Auth 1.2 LDAP to Auth 1.3 LDAP
+     * 
+     * @author Hugues Peeters <hugues.peeters@claroline.net>
+     * @access private
+     * @param array
+     * @return array
+     */
+    function _setV12OptionsToV13($array)
+    {
+        if (isset($array['useroc']))
+            $array['userfilter'] = "(objectClass=".$array['useroc'].")";
+        if (isset($array['groupoc']))
+            $array['groupfilter'] = "(objectClass=".$array['groupoc'].")";
+        if (isset($array['scope']))
+            $array['userscope'] = $array['scope'];
+
+        return $array;
+    }
+
+    // }}}
+    // {{{ _scope2function()
+
+    /**
+     * Get search function for scope
+     *
+     * @param  string scope
+     * @return string ldap search function
+     */
+    function _scope2function($scope)
+    {
+        switch($scope) {
+        case 'one':
+            $function = 'ldap_list';
+            break;
+        case 'base':
+            $function = 'ldap_read';
+            break;
+        default:
+            $function = 'ldap_search';
+            break;
+        }
+        return $function;
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from LDAP server
+     *
+     * Searches the LDAP server for the given username/password
+     * combination.  Escapes all LDAP meta characters in username
+     * before performing the query.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean
+     */
+    function fetchData($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $err = $this->_getBaseDN();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // UTF8 Encode username for LDAPv3
+        if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) {
+            $this->_debug('UTF8 encoding username for LDAPv3', __LINE__);
+            $username = utf8_encode($username);
+        }
+
+        // make search filter
+        $filter = sprintf('(&(%s=%s)%s)',
+                          $this->options['userattr'],
+                          $this->_quoteFilterString($username),
+                          $this->options['userfilter']);
+
+        // make search base dn
+        $search_basedn = $this->options['userdn'];
+        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
+            $search_basedn .= ',';
+        }
+        $search_basedn .= $this->options['basedn'];
+
+        // attributes
+        $attributes = $this->options['attributes'];
+
+        // make functions params array
+        $func_params = array($this->conn_id, $search_basedn, $filter, $attributes);
+
+        // search function to use
+        $func_name = $this->_scope2function($this->options['userscope']);
+
+        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
+
+        // search
+        if (($result_id = @call_user_func_array($func_name, $func_params)) === false) {
+            $this->_debug('User not found', __LINE__);
+        } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) { // did we get some possible results?
+
+            $this->_debug('User(s) found', __LINE__);
+
+            $first = true;
+            $entry_id = null;
+
+            do {
+                
+                // then get the user dn
+                if ($first) {
+                    $entry_id = @ldap_first_entry($this->conn_id, $result_id);
+                    $first = false;
+                } else {
+                    $entry_id = @ldap_next_entry($this->conn_id, $entry_id);
+                    if ($entry_id === false)
+                        break;
+                }
+                $user_dn  = @ldap_get_dn($this->conn_id, $entry_id);
+
+                // as the dn is not fetched as an attribute, we save it anyway
+                if (is_array($attributes) && in_array('dn', $attributes)) {
+                    $this->_debug('Saving DN to AuthData', __LINE__);
+                    $this->_auth_obj->setAuthData('dn', $user_dn);
+                }
+            
+                // fetch attributes
+                if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) {
+
+                    if (is_array($attributes) && isset($attributes['count']) &&
+                         $attributes['count'] > 0) {
+
+                        // ldap_get_attributes() returns a specific multi dimensional array
+                        // format containing all the attributes and where each array starts
+                        // with a 'count' element providing the number of attributes in the
+                        // entry, or the number of values for attribute. For compatibility
+                        // reasons, it remains the default format returned by LDAP container
+                        // setAuthData().
+                        // The code below optionally returns attributes in another format,
+                        // more compliant with other Auth containers, where each attribute
+                        // element are directly set in the 'authData' list. This option is
+                        // enabled by setting 'attrformat' to
+                        // 'AUTH' in the 'options' array.
+                        // eg. $this->options['attrformat'] = 'AUTH'
+
+                        if ( strtoupper($this->options['attrformat']) == 'AUTH' ) {
+                            $this->_debug('Saving attributes to Auth data in AUTH format', __LINE__);
+                            unset ($attributes['count']);
+                            foreach ($attributes as $attributeName => $attributeValue ) {
+                                if (is_int($attributeName)) continue;
+                                if (is_array($attributeValue) && isset($attributeValue['count'])) {
+                                    unset ($attributeValue['count']);
+                                }
+                                if (count($attributeValue)<=1) $attributeValue = $attributeValue[0];
+                                $this->_auth_obj->setAuthData($attributeName, $attributeValue);
+                            }
+                        }
+                        else
+                        {
+                            $this->_debug('Saving attributes to Auth data in LDAP format', __LINE__);
+                            $this->_auth_obj->setAuthData('attributes', $attributes);
+                        }
+                    }
+                }
+                @ldap_free_result($result_id);
+
+                // need to catch an empty password as openldap seems to return TRUE
+                // if anonymous binding is allowed
+                if ($password != "") {
+                    $this->_debug("Bind as $user_dn", __LINE__);
+
+                    // try binding as this user with the supplied password
+                    if (@ldap_bind($this->conn_id, $user_dn, $password)) {
+                        $this->_debug('Bind successful', __LINE__);
+
+                        // check group if appropiate
+                        if (strlen($this->options['group'])) {
+                            // decide whether memberattr value is a dn or the username
+                            $this->_debug('Checking group membership', __LINE__);
+                            $return = $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username);
+                            $this->_disconnect();
+                            return $return;
+                        } else {
+                            $this->_debug('Authenticated', __LINE__);
+                            $this->_disconnect();
+                            return true; // user authenticated
+                        } // checkGroup
+                    } // bind
+                } // non-empty password
+            } while ($this->options['try_all'] == true); // interate through entries
+        } // get results
+        // default
+        $this->_debug('NOT authenticated!', __LINE__);
+        $this->_disconnect();
+        return false;
+    }
+
+    // }}}
+    // {{{ checkGroup()
+
+    /**
+     * Validate group membership
+     *
+     * Searches the LDAP server for group membership of the
+     * supplied username.  Quotes all LDAP filter meta characters in
+     * the user name before querying the LDAP server.
+     *
+     * @param  string Distinguished Name of the authenticated User
+     * @return boolean
+     */
+    function checkGroup($user)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // make filter
+        $filter = sprintf('(&(%s=%s)(%s=%s)%s)',
+                          $this->options['groupattr'],
+                          $this->options['group'],
+                          $this->options['memberattr'],
+                          $this->_quoteFilterString($user),
+                          $this->options['groupfilter']);
+
+        // make search base dn
+        $search_basedn = $this->options['groupdn'];
+        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
+            $search_basedn .= ',';
+        }
+        $search_basedn .= $this->options['basedn'];
+
+        $func_params = array($this->conn_id, $search_basedn, $filter,
+                             array($this->options['memberattr']));
+        $func_name = $this->_scope2function($this->options['groupscope']);
+
+        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
+
+        // search
+        if (($result_id = @call_user_func_array($func_name, $func_params)) != false) {
+            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
+                @ldap_free_result($result_id);
+                $this->_debug('User is member of group', __LINE__);
+                return true;
+            }
+        }
+        // default
+        $this->_debug('User is NOT member of group', __LINE__);
+        return false;
+    }
+
+    // }}}
+    // {{{ _debug()
+
+    /**
+     * Outputs debugging messages
+     *
+     * @access private
+     * @param string Debugging Message
+     * @param integer Line number
+     */
+    function _debug($msg = '', $line = 0)
+    {
+        if ($this->options['debug'] == true) {
+            if ($msg == '' && $this->_isValidLink()) {
+                $msg = 'LDAP_Error: ' . @ldap_err2str(@ldap_errno($this->_conn_id));
+            }
+            print("$line: $msg <br />");
+        }
+    }
+
+    // }}}
+    // {{{ _quoteFilterString()
+
+    /**
+     * Escapes LDAP filter special characters as defined in RFC 2254.
+     *
+     * @access private
+     * @param string Filter String
+     */
+    function _quoteFilterString($filter_str)
+    {
+        $metas        = array(  '\\',  '*',  '(',  ')',   "\x00");
+        $quoted_metas = array('\\\\', '\*', '\(', '\)', "\\\x00");
+        return str_replace($metas, $quoted_metas, $filter_str);
+    }
+
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/POP3.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/POP3.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/POP3.php	(revision 10038)
@@ -0,0 +1,143 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a POP3 server
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stefan Ekman <stekman@sedata.org> 
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR package for error handling
+ */
+require_once 'PEAR.php';
+/**
+ * Include PEAR Net_POP3 package
+ */
+require_once 'Net/POP3.php';
+
+/**
+ * Storage driver for Authentication on a POP3 server.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Mika Tuupola <tuupola@appelsiini.net> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_POP3 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * POP3 Server
+     * @var string
+     */
+    var $server='localhost';
+
+    /**
+     * POP3 Server port
+     * @var string
+     */
+    var $port='110';
+
+    /**
+     * POP3 Authentication method
+     *
+     * Prefered POP3 authentication method. Acceptable values:
+     *      Boolean TRUE    - Use Net_POP3's autodetection
+     *      String 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
+     *                      - Attempt this authentication style first
+     *                        then fallback to autodetection.
+     * @var mixed 
+     */
+    var $method=true;
+
+    // }}}
+    // {{{ Auth_Container_POP3() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $server string server or server:port combination
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_POP3($server=null)
+    {
+        if (isset($server)) {
+            if (is_array($server)) {
+                if (isset($server['host'])) {
+                    $this->server = $server['host'];
+                }
+                if (isset($server['port'])) {
+                    $this->port = $server['port'];
+                }
+                if (isset($server['method'])) {
+                    $this->method = $server['method'];
+                }
+            } else {
+                if (strstr($server, ':')) {
+                    $serverparts = explode(':', trim($server));
+                    $this->server = $serverparts[0];
+                    $this->port = $serverparts[1];
+                } else {
+                    $this->server = $server;
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Try to login to the POP3 server
+     *
+     * @param   string Username
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        $pop3 =& new Net_POP3();
+        $res = $pop3->connect($this->server, $this->port, $this->method);
+        if (!$res) {
+            return $res;
+        }
+        $result = $pop3->login($username, $password);
+        $pop3->disconnect();
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/SAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/SAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/SAP.php	(revision 10038)
@@ -0,0 +1,177 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SAP system using the SAPRFC PHP extension.
+ *
+ * Requires the SAPRFC ext available at http://saprfc.sourceforge.net/
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stoyan Stefanov <ssttoo@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR for error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * Performs authentication against a SAP system using the SAPRFC PHP extension.
+ *
+ * When the option GETSSO2 is TRUE (default)
+ * the Single Sign-On (SSO) ticket is retrieved
+ * and stored as an Auth attribute called 'sap'
+ * in order to be reused for consecutive connections.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stoyan Stefanov <ssttoo@gmail.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_SAP extends Auth_Container {
+
+    // {{{ properties
+    
+    /**
+     * @var array Default options
+     */
+    var $options = array(
+        'CLIENT'    => '000',
+        'LANG'      => 'EN',
+        'GETSSO2'   => true,
+    );
+
+    // }}}
+    // {{{ Auth_Container_SAP()
+
+    /**
+     * Class constructor. Checks that required options
+     * are present and that the SAPRFC extension is loaded
+     *
+     * Options that can be passed and their defaults:
+     * <pre>
+     * array(
+     *   'ASHOST' => "",
+     *   'SYSNR'  => "",
+     *   'CLIENT' => "000",
+     *   'GWHOST' =>"",
+     *   'GWSERV' =>"",
+     *   'MSHOST' =>"",
+     *   'R3NAME' =>"",
+     *   'GROUP'  =>"",
+     *   'LANG'   =>"EN",
+     *   'TRACE'  =>"",
+     *   'GETSSO2'=> true
+     * )
+     * </pre>
+     *
+     * @param array array of options.
+     * @return void
+     */
+    function Auth_Container_SAP($options)
+    {
+        $saprfc_loaded = PEAR::loadExtension('saprfc');
+        if (!$saprfc_loaded) {
+            return PEAR::raiseError('Cannot use SAP authentication, '
+                    .'SAPRFC extension not loaded!');
+        }
+        if (empty($options['R3NAME']) && empty($options['ASHOST'])) {
+            return PEAR::raiseError('R3NAME or ASHOST required for authentication');
+        }
+        $this->options = array_merge($this->options, $options);
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Performs username and password check
+     *
+     * @param string Username
+     * @param string Password
+     * @return boolean TRUE on success (valid user), FALSE otherwise
+     */
+    function fetchData($username, $password)
+    {
+        $connection_options = $this->options;
+        $connection_options['USER'] = $username;
+        $connection_options['PASSWD'] = $password;
+        $rfc = saprfc_open($connection_options);
+        if (!$rfc) {
+            $message = "Couldn't connect to the SAP system.";
+            $error = $this->getError();
+            if ($error['message']) {
+                $message .= ': ' . $error['message'];
+            }
+            PEAR::raiseError($message, null, null, null, @$erorr['all']);
+            return false;
+        } else {
+            if (!empty($this->options['GETSSO2'])) {
+                if ($ticket = @saprfc_get_ticket($rfc)) {
+                    $this->options['MYSAPSSO2'] = $ticket;
+                    unset($this->options['GETSSO2']);
+                    $this->_auth_obj->setAuthData('sap', $this->options);
+                } else {
+                    PEAR::raiseError("SSO ticket retrieval failed");
+                }
+            }
+            @saprfc_close($rfc);
+            return true;
+        }
+    
+    }
+
+    // }}}
+    // {{{ getError()
+
+    /**
+     * Retrieves the last error from the SAP connection
+     * and returns it as an array.
+     *
+     * @return array Array of error information
+     */
+    function getError()
+    {
+
+        $error = array();
+        $sap_error = saprfc_error();
+        if (empty($err)) {
+            return $error;
+        }
+        $err = explode("n", $sap_error);
+        foreach ($err AS $line) {
+            $item = split(':', $line);
+            $error[strtolower(trim($item[0]))] = trim($item[1]);
+        }
+        $error['all'] = $sap_error;
+        return $error;
+    }
+
+    // }}}
+
+}
+
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/MDB2.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/MDB2.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/MDB2.php	(revision 10038)
@@ -0,0 +1,571 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR MDB2
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR MDB2 package
+ */
+require_once 'MDB2.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR MDB2 abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Lorenzo Alberton <l.alberton@quipo.it>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_MDB2 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * MDB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_MDB2() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Initate connection to the database via PEAR::MDB2
+     *
+     * @param  string Connection data or MDB2 object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_MDB2($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  mixed DSN string | array | mdb object
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db =& MDB2::connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'MDB2_Driver_Common')) {
+            $this->db = $dsn;
+        } elseif (is_object($dsn) && MDB2::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->code);
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+
+        }
+
+        if (MDB2::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->code);
+        }
+        
+        if ($this->options['auto_quote']) {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table'], true);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol'], true);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol'], true);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+                
+        return true;
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a MDB error object.
+     */
+    function _prepare()
+    {
+        if (is_subclass_of($this->db, 'MDB2_Driver_Common')) {
+            return true;
+        }
+        return $this->_connect($this->options['dsn']);
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a MDB_result object or MDB_OK on success, a MDB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->exec($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field, true);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields'], true);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+        $query = sprintf("SELECT %s FROM %s WHERE %s = %s",
+                         $sql_from,
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
+        if (MDB2::isError($res) || PEAR::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashing
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] =
+                md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']);
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+        }
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a static call to setAuthData does not make sense
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed array|PEAR_Error
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        //Check if db_fields contains a *, if so assume all columns are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = '*';
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf('SELECT %s FROM %s',
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+
+        $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key   .= ', ' . $this->db->quoteIdentifier($key, true);
+                } else {
+                    $additional_key   .= ', ' . $key;
+                }
+                $additional_value .= ', ' . $this->db->quote($value, 'text');
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->quote($username, 'text'),
+                         $this->db->quote($password, 'text'),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->quote($password, 'text'),
+                         $this->options['final_usernamecol'],
+                         $this->db->quote($username, 'text')
+                         );
+
+        $res = $this->query($query);
+
+        if (MDB2::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->code);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+     * Returns the selected crypt type for this container
+     *
+     * @return string Function used to crypt the password
+     */
+    function getCryptType()
+    {
+        return $this->options['cryptType'];
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/DB.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/DB.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/DB.php	(revision 10038)
@@ -0,0 +1,578 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR DB
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR DB
+ */
+require_once 'DB.php';
+
+/**
+ * Storage driver for fetching login data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR DB abstraction layer to fetch login data.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth_Container_DB extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Additional options for the storage container
+     * @var array
+     */
+    var $options = array();
+
+    /**
+     * DB object
+     * @var object
+     */
+    var $db = null;
+    var $dsn = '';
+
+    /**
+     * User that is currently selected from the DB.
+     * @var string
+     */
+    var $activeUser = '';
+
+    // }}}
+    // {{{ Auth_Container_DB [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * Save the initial options passed to the container. Initiation of the DB
+     * connection is no longer performed here and is only done when needed.
+     *
+     * @param  string Connection data or DB object
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_DB($dsn)
+    {
+        $this->_setDefaults();
+
+        if (is_array($dsn)) {
+            $this->_parseOptions($dsn);
+
+            if (empty($this->options['dsn'])) {
+                PEAR::raiseError('No connection parameters specified!');
+            }
+        } else {
+            $this->options['dsn'] = $dsn;
+        }
+    }
+
+    // }}}
+    // {{{ _connect()
+
+    /**
+     * Connect to database by using the given DSN string
+     *
+     * @access private
+     * @param  string DSN string
+     * @return mixed  Object on error, otherwise bool
+     */
+    function _connect($dsn)
+    {
+        if (is_string($dsn) || is_array($dsn)) {
+            $this->db = DB::Connect($dsn, $this->options['db_options']);
+        } elseif (is_subclass_of($dsn, 'db_common')) {
+            $this->db = $dsn;
+        } elseif (DB::isError($dsn)) {
+            return PEAR::raiseError($dsn->getMessage(), $dsn->getCode());
+        } else {
+            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
+                                    41,
+                                    PEAR_ERROR_RETURN,
+                                    null,
+                                    null
+                                    );
+        }
+
+        if (DB::isError($this->db) || PEAR::isError($this->db)) {
+            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ _prepare()
+
+    /**
+     * Prepare database connection
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     *
+     * @access private
+     * @return mixed True or a DB error object.
+     */
+    function _prepare()
+    {
+        if (!DB::isConnection($this->db)) {
+            $res = $this->_connect($this->options['dsn']);
+            if (DB::isError($res) || PEAR::isError($res)) {
+                return $res;
+            }
+        }
+        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
+            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
+            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
+            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
+        } else {
+            $this->options['final_table'] = $this->options['table'];
+            $this->options['final_usernamecol'] = $this->options['usernamecol'];
+            $this->options['final_passwordcol'] = $this->options['passwordcol'];
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ query()
+
+    /**
+     * Prepare query to the database
+     *
+     * This function checks if we have already opened a connection to
+     * the database. If that's not the case, a new connection is opened.
+     * After that the query is passed to the database.
+     *
+     * @access public
+     * @param  string Query string
+     * @return mixed  a DB_result object or DB_OK on success, a DB
+     *                or PEAR error on failure
+     */
+    function query($query)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return $err;
+        }
+        return $this->db->query($query);
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['table']       = 'auth';
+        $this->options['usernamecol'] = 'username';
+        $this->options['passwordcol'] = 'password';
+        $this->options['dsn']         = '';
+        $this->options['db_fields']   = '';
+        $this->options['cryptType']   = 'md5';
+        $this->options['db_options']  = array();
+        $this->options['auto_quote']  = true;
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->options[$key])) {
+                $this->options[$key] = $value;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _quoteDBFields()
+
+    /**
+     * Quote the db_fields option to avoid the possibility of SQL injection.
+     *
+     * @access private
+     * @return string A properly quoted string that can be concatenated into a
+     * SELECT clause.
+     */
+    function _quoteDBFields()
+    {
+        if (isset($this->options['db_fields'])) {
+            if (is_array($this->options['db_fields'])) {
+                if ($this->options['auto_quote']) {
+                    $fields = array();
+                    foreach ($this->options['db_fields'] as $field) {
+                        $fields[] = $this->db->quoteIdentifier($field);
+                    }
+                    return implode(', ', $fields);
+                } else {
+                    return implode(', ', $this->options['db_fields']);
+                }
+            } else {
+                if (strlen($this->options['db_fields']) > 0) {
+                    if ($this->options['auto_quote']) {
+                        return $this->db->quoteIdentifier($this->options['db_fields']);
+                    } else {
+                        return $this->options['db_fields'];
+                    }
+                }
+            }
+        }
+
+        return '';
+    }
+    
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from database
+     *
+     * This function uses the given username to fetch
+     * the corresponding login data from the database
+     * table. If an account that matches the passed username
+     * and password is found, the function returns true.
+     * Otherwise it returns false.
+     *
+     * @param   string Username
+     * @param   string Password
+     * @param   boolean If true password is secured using a md5 hash
+     *                  the frontend and auth are responsible for making sure the container supports
+     *                  challenge response password authentication
+     * @return  mixed  Error object or boolean
+     */
+    function fetchData($username, $password, $isChallengeResponse=false)
+    {
+        // Prepare for a database query
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        // Find if db_fields contains a *, if so assume all columns are selected
+        if (is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = "SELECT ".$sql_from.
+                " FROM ".$this->options['final_table'].
+                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
+
+        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        }
+
+        if (!is_array($res)) {
+            $this->activeUser = '';
+            return false;
+        }
+
+        // Perform trimming here before the hashihg
+        $password = trim($password, "\r\n");
+        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
+
+        // If using Challenge Response md5 the pass with the secret
+        if ($isChallengeResponse) {
+            $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]
+                    .$this->_auth_obj->session['loginchallenege']);
+            
+            // UGLY cannot avoid without modifying verifyPassword
+            if ($this->options['cryptType'] == 'md5') {
+                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
+            }
+            
+            //print " Hashed Password [{$res[$this->options['passwordcol']]}]<br/>\n";
+        }
+
+        if ($this->verifyPassword($password,
+                                  $res[$this->options['passwordcol']],
+                                  $this->options['cryptType'])) {
+            // Store additional field values in the session
+            foreach ($res as $key => $value) {
+                if ($key == $this->options['passwordcol'] ||
+                    $key == $this->options['usernamecol']) {
+                    continue;
+                }
+                // Use reference to the auth object if exists
+                // This is because the auth session variable can change so a 
+                // static call to setAuthData does not make sence
+                $this->_auth_obj->setAuthData($key, $value);
+            }
+            return true;
+        }
+        $this->activeUser = $res[$this->options['usernamecol']];
+        return false;
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * Returns a list of users from the container
+     *
+     * @return mixed
+     * @access public
+     */
+    function listUsers()
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $retVal = array();
+
+        // Find if db_fields contains a *, if so assume all col are selected
+        if (   is_string($this->options['db_fields'])
+            && strstr($this->options['db_fields'], '*')) {
+            $sql_from = "*";
+        } else {
+            $sql_from = $this->options['final_usernamecol'].
+                ", ".$this->options['final_passwordcol'];
+
+            if (strlen($fields = $this->_quoteDBFields()) > 0) {
+                $sql_from .= ', '.$fields;
+            }
+        }
+
+        $query = sprintf("SELECT %s FROM %s",
+                         $sql_from,
+                         $this->options['final_table']
+                         );
+        $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            foreach ($res as $user) {
+                $user['username'] = $user[$this->options['usernamecol']];
+                $retVal[] = $user;
+            }
+        }
+        return $retVal;
+    }
+
+    // }}}
+    // {{{ addUser()
+
+    /**
+     * Add user to the storage container
+     *
+     * @access public
+     * @param  string Username
+     * @param  string Password
+     * @param  mixed  Additional information that are stored in the DB
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function addUser($username, $password, $additional = "")
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (   isset($this->options['cryptType']) 
+            && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (   isset($this->options['cryptType']) 
+                  && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $additional_key   = '';
+        $additional_value = '';
+
+        if (is_array($additional)) {
+            foreach ($additional as $key => $value) {
+                if ($this->options['auto_quote']) {
+                    $additional_key .= ', ' . $this->db->quoteIdentifier($key);
+                } else {
+                    $additional_key .= ', ' . $key;
+                }
+                $additional_value .= ", " . $this->db->quoteSmart($value);
+            }
+        }
+
+        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->options['final_passwordcol'],
+                         $additional_key,
+                         $this->db->quoteSmart($username),
+                         $this->db->quoteSmart($password),
+                         $additional_value
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ removeUser()
+
+    /**
+     * Remove user from the storage container
+     *
+     * @access public
+     * @param  string Username
+     *
+     * @return mixed True on success, otherwise error object
+     */
+    function removeUser($username)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        $query = sprintf("DELETE FROM %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_usernamecol'],
+                         $this->db->quoteSmart($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+           return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+          return true;
+        }
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @param string Username
+     * @param string The new password (plain text)
+     */
+    function changePassword($username, $password)
+    {
+        $err = $this->_prepare();
+        if ($err !== true) {
+            return PEAR::raiseError($err->getMessage(), $err->getCode());
+        }
+
+        if (   isset($this->options['cryptType']) 
+            && $this->options['cryptType'] == 'none') {
+            $cryptFunction = 'strval';
+        } elseif (   isset($this->options['cryptType']) 
+                  && function_exists($this->options['cryptType'])) {
+            $cryptFunction = $this->options['cryptType'];
+        } else {
+            $cryptFunction = 'md5';
+        }
+
+        $password = $cryptFunction($password);
+
+        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
+                         $this->options['final_table'],
+                         $this->options['final_passwordcol'],
+                         $this->db->quoteSmart($password),
+                         $this->options['final_usernamecol'],
+                         $this->db->quoteSmart($username)
+                         );
+
+        $res = $this->query($query);
+
+        if (DB::isError($res)) {
+            return PEAR::raiseError($res->getMessage(), $res->getCode());
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ supportsChallengeResponse()
+
+    /**
+     * Determine if this container supports
+     * password authentication with challenge response
+     *
+     * @return bool
+     * @access public
+     */
+    function supportsChallengeResponse()
+    {
+        return in_array($this->options['cryptType'], array('md5', 'none', ''));
+    }
+
+    // }}}
+    // {{{ getCryptType()
+
+    /**
+      * Returns the selected crypt type for this container
+      */
+    function getCryptType()
+    {
+        return($this->options['cryptType']);
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/IMAP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/IMAP.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/IMAP.php	(revision 10038)
@@ -0,0 +1,206 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against IMAP servers
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jeroen Houben <jeroen@terena.nl> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class 
+ */
+require_once "Auth/Container.php";
+
+/**
+ * Include PEAR class for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from an IMAP server
+ *
+ * This class is based on LDAP containers, but it very simple.
+ * By default it connects to localhost:143
+ * The constructor will first check if the host:port combination is
+ * actually reachable. This behaviour can be disabled.
+ * It then tries to create an IMAP stream (without opening a mailbox)
+ * If you wish to pass extended options to the connections, you may
+ * do so by specifying protocol options.
+ *
+ * To use this storage containers, you have to use the
+ * following syntax:
+ *
+ * <?php
+ * ...
+ * $params = array(
+ * 'host'       => 'mail.example.com',
+ * 'port'       => 143,
+ * );
+ * $myAuth = new Auth('IMAP', $params);
+ * ...
+ *
+ * By default we connect without any protocol options set. However, some
+ * servers require you to connect with the notls or norsh options set.
+ * To do this you need to add the following value to the params array:
+ * 'baseDSN'   => '/imap/notls/norsh'
+ *
+ * To connect to an SSL IMAP server:
+ * 'baseDSN'   => '/imap/ssl'
+ *
+ * To connect to an SSL IMAP server with a self-signed certificate:
+ * 'baseDSN'   => '/imap/ssl/novalidate-cert'
+ *
+ * Further options may be available and can be found on the php site at
+ * http://www.php.net/manual/function.imap-open.php
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Jeroen Houben <jeroen@terena.nl>
+ * @author     Cipriano Groenendal <cipri@campai.nl>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_IMAP extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Options for the class
+     * @var array
+     */
+    var $options = array();
+
+    // }}}
+    // {{{ Auth_Container_IMAP() [constructor]
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $params  associative array with host, port, baseDSN, checkServer
+     *                  and userattr key
+     * @return object Returns an error object if something went wrong
+     * @todo Use PEAR Net_IMAP if IMAP extension not loaded
+     */
+    function Auth_Container_IMAP($params)
+    {
+        if (!extension_loaded('imap')) {
+            return PEAR::raiseError('Cannot use IMAP authentication, '
+                    .'IMAP extension not loaded!', 41, PEAR_ERROR_DIE);
+        }
+        $this->_setDefaults();
+
+        // set parameters (if any)
+        if (is_array($params)) {
+            $this->_parseOptions($params);
+        }
+
+        if ($this->options['checkServer']) {
+            $this->_checkServer($this->options['timeout']);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     */
+    function _setDefaults()
+    {
+        $this->options['host'] = 'localhost';
+        $this->options['port'] = 143;
+        $this->options['baseDSN'] = '';
+        $this->options['checkServer'] = true;
+        $this->options['timeout'] = 20;
+    }
+
+    // }}}
+    // {{{ _checkServer()
+
+    /**
+     * Check if the given server and port are reachable
+     *
+     * @access private
+     */
+    function _checkServer() {
+        $fp = @fsockopen ($this->options['host'], $this->options['port'],
+                          $errno, $errstr, $this->options['timeout']);
+        if (is_resource($fp)) {
+            @fclose($fp);
+        } else {
+            $message = "Error connecting to IMAP server "
+                . $this->options['host']
+                . ":" . $this->options['port'];
+            return PEAR::raiseError($message, 41);
+        }
+    }
+
+    // }}}
+    // {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @access private
+     * @param  array
+     */
+    function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            $this->options[$key] = $value;
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Try to open a IMAP stream using $username / $password
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return boolean
+     */
+    function fetchData($username, $password)
+    {
+        $dsn = '{'.$this->options['host'].':'.$this->options['port'].$this->options['baseDSN'].'}';
+        $conn = @imap_open ($dsn, $username, $password, OP_HALFOPEN);
+        if (is_resource($conn)) {
+            $this->activeUser = $username;
+            @imap_close($conn);
+            return true;
+        } else {
+            $this->activeUser = '';
+            return false;
+        }
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/vpopmail.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/vpopmail.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/vpopmail.php	(revision 10038)
@@ -0,0 +1,87 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against vpopmail setups
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stanislav Grozev <tacho@orbitel.bg> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from vpopmail
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Stanislav Grozev <tacho@orbitel.bg>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_vpopmail extends Auth_Container {
+
+    // {{{ Constructor
+
+    /**
+     * Constructor of the container class
+     *
+     * @return void
+     */
+    function Auth_Container_vpopmail()
+    {
+        if (!extension_loaded('vpopmail')) {
+            return PEAR::raiseError('Cannot use VPOPMail authentication, '
+                    .'VPOPMail extension not loaded!', 41, PEAR_ERROR_DIE);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Get user information from vpopmail
+     *
+     * @param   string Username - has to be valid email address
+     * @param   string Password
+     * @return  boolean
+     */
+    function fetchData($username, $password)
+    {
+        $userdata = array();
+        $userdata = preg_split("/@/", $username, 2);
+        $result = @vpopmail_auth_user($userdata[0], $userdata[1], $password);
+
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/PEAR.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/PEAR.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/PEAR.php	(revision 10038)
@@ -0,0 +1,103 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against PEAR website
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Yavor Shahpasov <yavo@netsmart.com.cy>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once 'Auth/Container.php';
+/**
+ * Include PEAR XML_RPC
+ */
+require_once 'XML/RPC.php';
+
+/**
+ * Storage driver for authenticating against PEAR website
+ *
+ * This driver provides a method for authenticating against the pear.php.net
+ * authentication system.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Yavor Shahpasov <yavo@netsmart.com.cy>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.3.0
+ */
+class Auth_Container_Pear extends Auth_Container
+{
+
+    // {{{ Auth_Container_Pear() [constructor]
+
+    /**
+     * Constructor
+     *
+     * Currently does nothing
+     * 
+     * @return void
+     */
+    function Auth_Container_Pear()
+    {
+    
+    }
+
+    // }}}
+    // {{{ fetchData()
+    
+    /**
+     * Get user information from pear.php.net
+     *
+     * This function uses the given username and password to authenticate
+     * against the pear.php.net website
+     *
+     * @param string    Username
+     * @param string    Password
+     * @return mixed    Error object or boolean
+     */
+    function fetchData($username, $password)
+    {
+        $rpc = new XML_RPC_Client('/xmlrpc.php', 'pear.php.net');
+        $rpc_message = new XML_RPC_Message("user.info", array(new XML_RPC_Value($username, "string")) );
+        
+        // Error Checking howto ???
+        $result = $rpc->send($rpc_message);
+        $value = $result->value();
+        $userinfo = xml_rpc_decode($value);
+        if ($userinfo['password'] == md5($password)) {
+            $this->activeUser = $userinfo['handle'];
+            foreach ($userinfo as $uk=>$uv) {
+                $this->_auth_obj->setAuthData($uk, $uv);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+    
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/RADIUS.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/RADIUS.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/RADIUS.php	(revision 10038)
@@ -0,0 +1,180 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against RADIUS servers
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com> 
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR Auth_RADIUS package
+ */
+require_once "Auth/RADIUS.php";
+
+/**
+ * Storage driver for authenticating users against RADIUS servers.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Michael Bretterklieber <michael@bretterklieber.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ * @since      Class available since Release 1.2.0
+ */
+class Auth_Container_RADIUS extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Contains a RADIUS object
+     * @var object
+     */
+    var $radius;
+    
+    /**
+     * Contains the authentication type
+     * @var string
+     */
+    var $authtype;    
+
+    // }}}
+    // {{{ Auth_Container_RADIUS() [constructor]
+
+    /**
+     * Constructor of the container class.
+     *
+     * $options can have these keys:
+     * 'servers'    an array containing an array: servername, port,
+     *              sharedsecret, timeout, maxtries
+     * 'configfile' The filename of the configuration file
+     * 'authtype'   The type of authentication, one of: PAP, CHAP_MD5,
+     *              MSCHAPv1, MSCHAPv2, default is PAP
+     *
+     * @param  $options associative array
+     * @return object Returns an error object if something went wrong
+     */
+    function Auth_Container_RADIUS($options)
+    {
+        $this->authtype = 'PAP';
+        if (isset($options['authtype'])) {
+            $this->authtype = $options['authtype'];
+        }
+        $classname = 'Auth_RADIUS_' . $this->authtype;
+        if (!class_exists($classname)) {
+            PEAR::raiseError("Unknown Authtype, please use one of: "
+                    ."PAP, CHAP_MD5, MSCHAPv1, MSCHAPv2!", 41, PEAR_ERROR_DIE);
+        }
+        
+        $this->radius = new $classname;
+
+        if (isset($options['configfile'])) {
+            $this->radius->setConfigfile($options['configfile']);
+        }
+
+        $servers = $options['servers'];
+        if (is_array($servers)) {
+            foreach ($servers as $server) {
+                $servername     = $server[0];
+                $port           = isset($server[1]) ? $server[1] : 0;
+                $sharedsecret   = isset($server[2]) ? $server[2] : 'testing123';
+                $timeout        = isset($server[3]) ? $server[3] : 3;
+                $maxtries       = isset($server[4]) ? $server[4] : 3;
+                $this->radius->addServer($servername, $port, $sharedsecret, $timeout, $maxtries);
+            }
+        }
+        
+        if (!$this->radius->start()) {
+            PEAR::raiseError($this->radius->getError(), 41, PEAR_ERROR_DIE);
+        }
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Authenticate
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return bool   true on success, false on reject
+     */
+    function fetchData($username, $password, $challenge = null)
+    {
+        switch($this->authtype) {
+        case 'CHAP_MD5':
+        case 'MSCHAPv1':
+            if (isset($challenge)) {
+                $this->radius->challenge = $challenge;
+                $this->radius->chapid    = 1;
+                $this->radius->response  = pack('H*', $password);
+            } else {
+                require_once 'Crypt/CHAP.php';
+                $classname = 'Crypt_' . $this->authtype;
+                $crpt = new $classname;
+                $crpt->password = $password;
+                $this->radius->challenge = $crpt->challenge;
+                $this->radius->chapid    = $crpt->chapid;
+                $this->radius->response  = $crpt->challengeResponse();
+                break;
+            }
+
+        case 'MSCHAPv2':
+            require_once 'Crypt/CHAP.php';
+            $crpt = new Crypt_MSCHAPv2;
+            $crpt->username = $username;
+            $crpt->password = $password;
+            $this->radius->challenge     = $crpt->authChallenge;
+            $this->radius->peerChallenge = $crpt->peerChallenge;
+            $this->radius->chapid        = $crpt->chapid;
+            $this->radius->response      = $crpt->challengeResponse();
+            break;
+
+        default:
+            $this->radius->password = $password;
+            break;
+        }
+
+        $this->radius->username = $username;
+
+        $this->radius->putAuthAttributes();
+        $result = $this->radius->send();
+        if (PEAR::isError($result)) {
+            return false;
+        }
+
+        $this->radius->getAttributes();
+//      just for debugging
+//      $this->radius->dumpAttributes();
+
+        return $result;
+    }
+
+    // }}}
+
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Container/SOAP5.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Container/SOAP5.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Container/SOAP5.php	(revision 10038)
@@ -0,0 +1,267 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * Storage driver for use against a SOAP service using PHP5 SoapClient
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Based upon Auth_Container_SOAP by Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Marcel Oelke <puRe@rednoize.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @since      File available since Release 1.4.0
+ */
+
+/**
+ * Include Auth_Container base class
+ */
+require_once "Auth/Container.php";
+/**
+ * Include PEAR package for error handling
+ */
+require_once "PEAR.php";
+
+/**
+ * Storage driver for fetching login data from SOAP using the PHP5 Builtin SOAP
+ * functions. This is a modification of the SOAP Storage driver from Bruno Pedro
+ * thats using the PEAR SOAP Package.
+ *
+ * This class takes one parameter (options), where
+ * you specify the following fields: 
+ *  * location and uri, or wsdl file
+ *  * method to call on the SOAP service
+ *  * usernamefield, the name of the parameter where the username is supplied
+ *  * passwordfield, the name of the parameter where the password is supplied
+ *  * matchpassword, whether to look for the password in the response from
+ *                   the function call or assume that no errors means user
+ *                   authenticated.
+ *
+ * See http://www.php.net/manual/en/ref.soap.php for further details
+ * on options for the PHP5 SoapClient which are passed through.
+ *
+ * Example usage without WSDL:
+ *
+ * <?php
+ *
+ * $options = array (
+ *       'wsdl'           => NULL,
+ *       'location'       => 'http://your.soap.service/endpoint',
+ *       'uri'            => 'urn:/Your/Namespace',
+ *       'method'         => 'checkAuth',        
+ *       'usernamefield'  => 'username',
+ *       'passwordfield'  => 'password',
+ *       'matchpasswords' => false,          
+ *       '_features' => array (
+ *           'extra_parameter'    => 'example_value',
+ *           'another_parameter'  => 'foobar'
+ *       )
+ *   );
+ *
+ * $auth = new Auth('SOAP5', $options);
+ * $auth->start();
+ *
+ * ?>
+ *
+ * Example usage with WSDL:
+ *
+ * <?php
+ *
+ * $options = array (
+ *       'wsdl'           => 'http://your.soap.service/wsdl',
+ *       'method'         => 'checkAuth',        
+ *       'usernamefield'  => 'username',
+ *       'passwordfield'  => 'password',
+ *       'matchpasswords' => false,          
+ *       '_features' => array (
+ *           'extra_parameter'    => 'example_value',
+ *           'another_parameter'  => 'foobar'
+ *       )
+ *   );
+ *
+ * $auth = new Auth('SOAP5', $options);
+ * $auth->start();
+ *
+ * ?>
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Based upon Auth_Container_SOAP by Bruno Pedro <bpedro@co.sapo.pt>
+ * @author     Marcel Oelke <puRe@rednoize.com>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @since      Class available since Release 1.4.0
+ */
+class Auth_Container_SOAP5 extends Auth_Container
+{
+
+    // {{{ properties
+
+    /**
+     * Required options for the class
+     * @var array
+     * @access private
+     */
+    var $_requiredOptions = array(
+            'location', 
+            'uri',
+            'method',
+            'usernamefield',
+            'passwordfield',
+            'wsdl',
+            );
+
+    /**
+     * Options for the class
+     * @var array
+     * @access private
+     */
+    var $_options = array();
+
+    /**
+     * Optional SOAP features
+     * @var array
+     * @access private
+     */
+    var $_features = array();
+
+    /**
+     * The SOAP response
+     * @var array
+     * @access public
+     */
+    var $soapResponse = array();
+     
+    // }}}
+    // {{{ Auth_Container_SOAP5()
+
+    /**
+     * Constructor of the container class
+     *
+     * @param  $options, associative array with endpoint, namespace, method,
+     *                   usernamefield, passwordfield and optional features
+     */
+    function Auth_Container_SOAP5($options)
+    {
+        $this->_setDefaults();
+
+        foreach ($options as $name => $value) {
+            $this->_options[$name] = $value;
+        }
+
+        if (!empty($this->_options['_features'])) {
+            $this->_features = $this->_options['_features'];
+            unset($this->_options['_features']);
+        }        
+    }
+
+    // }}}
+    // {{{ fetchData()
+
+    /**
+     * Fetch data from SOAP service
+     *
+     * Requests the SOAP service for the given username/password
+     * combination.
+     *
+     * @param  string Username
+     * @param  string Password
+     * @return mixed Returns the SOAP response or false if something went wrong
+     */
+    function fetchData($username, $password)
+    {        
+        $result = $this->_validateOptions();
+        if (PEAR::isError($result))
+            return $result;
+
+        // create a SOAP client
+        $soapClient = new SoapClient($this->_options["wsdl"], $this->_options);
+        
+        $params = array();        
+        // first, assign the optional features
+        foreach ($this->_features as $fieldName => $fieldValue) {
+            $params[$fieldName] = $fieldValue;
+        }
+        // assign username and password ...
+        $params[$this->_options['usernamefield']] = $username;
+        $params[$this->_options['passwordfield']] = $password;                
+                
+        try {
+            $this->soapResponse = $soapClient->__soapCall($this->_options['method'], $params);
+                        
+            if ($this->_options['matchpasswords']) {
+                // check if passwords match
+                if ($password == $this->soapResponse[$this->_options['passwordfield']]) {
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {                
+                return true;
+            }
+        } catch (SoapFault $e) {
+            return PEAR::raiseError("Error retrieving authentication data. Received SOAP Fault: ".$e->faultstring, $e->faultcode);
+        }        
+    }
+
+    // }}}
+    // {{{ _validateOptions()
+    
+    /**
+     * Validate that the options passed to the container class are enough for us to proceed
+     *
+     * @access private
+     * @param  array
+     */
+    function _validateOptions($array)
+    {
+        if (   (   is_null($this->options['wsdl'])
+                && is_null($this->options['location'])
+                && is_null($this->options['uri']))
+            || (   is_null($this->options['wsdl'])
+                && (   is_null($this->options['location'])
+                    || is_null($this->options['uri'])))) {
+            return PEAR::raiseError('Either a WSDL file or a location/uri pair must be specified.');
+        }
+        if (is_null($this->options['method'])) {
+            return PEAR::raiseError('A method to call on the soap service must be specified.');
+        }
+        return true;
+    }
+    
+    // }}}
+    // {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    function _setDefaults()
+    {
+        $this->options['wsdl']           = null;
+        $this->options['location']       = null;
+        $this->options['uri']            = null;
+        $this->options['method']         = null;
+        $this->options['usernamefield']  = 'username';
+        $this->options['passwordfield']  = 'password';
+        $this->options['matchpasswords'] = true;
+    }
+
+    // }}}
+        
+}
+?>
Index: temp/trunk/html/test/kakinaka/pear/Auth_HTTP.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth_HTTP.php	(revision 10037)
+++ temp/trunk/html/test/kakinaka/pear/Auth_HTTP.php	(revision 10038)
@@ -22,5 +22,5 @@
 //
 
-require_once "Auth/Auth.php";
+require_once "Auth.php";
 
 define('AUTH_HTTP_NONCE_TIME_LEN', 16);
Index: temp/trunk/html/test/kakinaka/pear/Auth.php
===================================================================
--- temp/trunk/html/test/kakinaka/pear/Auth.php	(revision 10038)
+++ temp/trunk/html/test/kakinaka/pear/Auth.php	(revision 10038)
@@ -0,0 +1,1079 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
+
+/**
+ * The main include file for Auth package
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/Auth
+ */
+
+/**
+ * Returned if session exceeds idle time
+ */
+define('AUTH_IDLED',                    -1);
+/**
+ * Returned if session has expired
+ */
+define('AUTH_EXPIRED',                  -2);
+/** 
+ * Returned if container is unable to authenticate user/password pair
+ */
+define('AUTH_WRONG_LOGIN',              -3);
+/**
+ * Returned if a container method is not supported.
+ */
+define('AUTH_METHOD_NOT_SUPPORTED',     -4);
+/**
+ * Returned if new Advanced security system detects a breach
+ */
+define('AUTH_SECURITY_BREACH',          -5);
+
+/**
+ * PEAR::Auth
+ *
+ * The PEAR::Auth class provides methods for creating an
+ * authentication system using PHP.
+ *
+ * @category   Authentication
+ * @package    Auth
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Adam Ashley <aashley@php.net>
+ * @copyright  2001-2006 The PHP Group
+ * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
+ * @version    Release: 1.4.2  File: $Revision$
+ * @link       http://pear.php.net/package/Auth
+ */
+class Auth {
+
+    // {{{ properties
+
+    /**
+     * 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()
+     */
+    var $expired = false;
+
+    /**
+     * Maximum idletime in seconds
+     *
+     * The difference to $expire is, that the idletime gets
+     * refreshed each time checkAuth() is called. If this
+     * variable is set to 0, idletime is never checked.
+     *
+     * @var integer
+     * @see setIdle(), checkAuth()
+     */
+    var $idle = 0;
+
+    /**
+     * Is the maximum idletime over?
+     *
+     * @var boolean
+     * @see checkAuth()
+     */
+    var $idled = false;
+
+    /**
+     * Storage object
+     *
+     * @var object
+     * @see Auth(), validateLogin()
+     */
+    var $storage = '';
+
+    /**
+     * User-defined function that creates the login screen
+     *
+     * @var string
+     */
+    var $loginFunction = '';
+
+    /**
+     * Should the login form be displayed
+     *
+     * @var   bool
+     * @see   setShowlogin()
+     */
+    var $showLogin = true;
+    
+    /**
+      * Is Login Allowed from this page
+      *
+      * @var  bool
+      * @see setAllowLogin
+      */
+    var $allowLogin = 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 = '';
+
+    /**
+     * Failed Login callback function name
+     *
+     * @var string
+     * @see setFailedLoginCallback()
+     */
+    var $loginFailedCallback = '';
+
+    /**
+     * Logout callback function name
+     *
+     * @var string
+     * @see setLogoutCallback()
+     */
+    var $logoutCallback = '';
+
+    /**
+     * Auth session-array name
+     *
+     * @var string
+     */
+    var $_sessionName = '_authsession';
+
+    /**
+     * Package Version
+     *
+     * @var string
+     */
+    var $version = "@version@";
+
+    /**
+     * Flag to use advanced security
+     * When set extra checks will be made to see if the 
+     * user's IP or useragent have changed across requests. 
+     * Turned off by default to preserve BC.
+     *
+     * @var boolean
+     */     
+    var $advancedsecurity = false;
+
+    /**
+     * Username key in POST array
+     *
+     * @var string
+     */
+    var $_postUsername = 'username';
+
+    /**
+     * Password key in POST array
+     *
+     * @var string
+     */
+    var $_postPassword = 'password';
+
+    /**
+     * Holds a reference to the session auth variable
+     * @var array
+     */
+    var $session;
+
+    /**
+     * Holds a reference to the global server variable
+     * @var array
+     */
+    var $server;
+
+    /**
+     * Holds a reference to the global post variable
+     * @var array
+     */
+    var $post;
+
+    /**
+     * Holds a reference to the global cookie variable
+     * @var array
+     */
+    var $cookie;
+
+    /**
+     * A hash to hold various superglobals as reference
+     * @var array
+     */
+    var $authdata;
+    
+    /**
+      * How many times has checkAuth been called
+      * var int
+      */
+    var $authChecks = 0;
+
+    // }}}
+    // {{{ Auth() [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 Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true)
+    {
+        $this->applyAuthOptions($options);
+
+        // Start the session suppress error if already started
+        if(!session_id()){
+            @session_start();
+            if(!session_id()) {
+                // Throw error
+                include_once 'PEAR.php';
+                PEAR::throwError('Session could not be started by Auth, '
+                        .'possibly headers are already sent, try putting '
+                        .'ob_start in the beginning of your script');
+            }
+        }
+
+        // Make Sure Auth session variable is there
+        if(!isset($_SESSION[$this->_sessionName])) {
+            $_SESSION[$this->_sessionName] = array();
+        }
+
+        // Assign Some globals to internal references, this will replace _importGlobalVariable
+        $this->session =& $_SESSION[$this->_sessionName];
+        $this->server =& $_SERVER;
+        $this->post =& $_POST;
+        $this->cookie =& $_COOKIE;
+
+        if ($loginFunction != '' && is_callable($loginFunction)) {
+            $this->loginFunction = $loginFunction;
+        }
+
+        if (is_bool($showLogin)) {
+            $this->showLogin = $showLogin;
+        }
+
+        if (is_object($storageDriver)) {
+            $this->storage =& $storageDriver;
+            // Pass a reference to auth to the container, ugly but works
+            // this is used by the DB container to use method setAuthData not staticaly.
+            $this->storage->_auth_obj =& $this;
+        } else {
+            // $this->storage = $this->_factory($storageDriver, $options);
+            // 
+            $this->storage_driver = $storageDriver;
+            $this->storage_options =& $options;
+        }
+    }
+
+    // }}}
+    // {{{ applyAuthOptions()
+
+    /**
+      * Set the Auth options 
+      *
+      * Some options which are Auth specific will be applied
+      * the rest will be left for usage by the container
+      * 
+      * @param array    An array of Auth options
+      * @return array   The options which were not applied
+      * @access private
+      */
+    function &applyAuthOptions(&$options)
+    {
+        if(is_array($options)){
+            if (!empty($options['sessionName'])) {
+                $this->_sessionName = $options['sessionName'];
+                unset($options['sessionName']);
+            }
+            if (isset($options['allowLogin'])) {
+                $this->allowLogin = $options['allowLogin'];
+                unset($options['allowLogin']);
+            }
+            if (!empty($options['postUsername'])) {
+                $this->_postUsername = $options['postUsername'];
+                unset($options['postUsername']);
+            }
+            if (!empty($options['postPassword'])) {
+                $this->_postPassword = $options['postPassword'];
+                unset($options['postPassword']);
+            }
+            if (isset($options['advancedsecurity'])) {
+                $this->advancedsecurity = $options['advancedsecurity'];
+                unset($options['advancedsecurity']);
+            }
+        }
+        return($options);
+    }
+
+    // }}}
+    // {{{ _loadStorage()
+    
+    /**
+      * Load Storage Driver if not already loaded
+      *
+      * Suspend storage instantiation to make Auth lighter to use 
+      * for calls which do not require login
+      *
+      * @return bool    True if the conainer is loaded, false if the container
+      *                 is already loaded
+      * @access private
+      */
+    function _loadStorage()
+    {
+        if(!is_object($this->storage)) {
+            $this->storage =& $this->_factory($this->storage_driver, 
+                    $this->storage_options);
+            $this->storage->_auth_obj =& $this;
+            return(true);
+        }
+        return(false);
+    }
+
+    // }}}
+    // {{{ _factory()
+
+    /**
+     * Return a storage driver based on $driver and $options
+     *
+     * @static
+     * @param  string $driver  Type of storage class to return
+     * @param  string $options Optional parameters for the storage class
+     * @return object Object   Storage object
+     * @access private
+     */
+    function &_factory($driver, $options = '')
+    {
+        $storage_class = 'Auth_Container_' . $driver;
+        include_once 'Auth/Container/' . $driver . '.php';
+        $obj =& new $storage_class($options);
+        return $obj;
+    }
+
+    // }}}
+    // {{{ assignData()
+
+    /**
+     * Assign data from login form to internal values
+     *
+     * This function takes the values for username and password
+     * from $HTTP_POST_VARS/$_POST and assigns them to internal variables.
+     * If you wish to use another source apart from $HTTP_POST_VARS/$_POST,
+     * you have to derive this function.
+     *
+     * @global $HTTP_POST_VARS, $_POST
+     * @see    Auth
+     * @return void
+     * @access private
+     */
+    function assignData()
+    {
+        if (   isset($this->post[$this->_postUsername]) 
+            && $this->post[$this->_postUsername] != '') {
+            $this->username = (get_magic_quotes_gpc() == 1 
+                    ? stripslashes($this->post[$this->_postUsername]) 
+                    : $this->post[$this->_postUsername]);
+        }
+        if (   isset($this->post[$this->_postPassword]) 
+            && $this->post[$this->_postPassword] != '') {
+            $this->password = (get_magic_quotes_gpc() == 1 
+                    ? stripslashes($this->post[$this->_postPassword]) 
+                    : $this->post[$this->_postPassword] );
+        }
+    }
+
+    // }}}
+    // {{{ start()
+
+    /**
+     * Start new auth session
+     *
+     * @return void
+     * @access public
+     */
+    function start()
+    {
+        $this->assignData();
+        if (!$this->checkAuth() && $this->allowLogin) {
+            $this->login();
+        }
+    }
+
+    // }}}
+    // {{{ login()
+
+    /**
+     * Login function
+     *
+     * @return void
+     * @access private
+     */
+    function login()
+    {
+        $login_ok = false;
+        $this->_loadStorage();
+        
+        // Check if using challenge response
+        (isset($this->post['authsecret']) && $this->post['authsecret'] == 1) 
+            ? $usingChap = true 
+            : $usingChap = 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, $usingChap)) {
+                $this->session['challengekey'] = md5($this->username.$this->password);
+                $login_ok = true;
+            }
+        }
+
+        if (!empty($this->username) && $login_ok) {
+            $this->setAuth($this->username);
+            if (is_callable($this->loginCallback)) {
+                call_user_func_array($this->loginCallback, array($this->username, &$this));
+            }
+        }
+
+        // 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 (is_callable($this->loginFailedCallback)) {
+                call_user_func_array($this->loginFailedCallback, array($this->username, &$this));
+            }
+        }
+
+        if ((empty($this->username) || !$login_ok) && $this->showLogin) {
+            if (is_callable($this->loginFunction)) {
+                call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this));
+            } else {
+                // BC fix Auth used to use drawLogin for this
+                // call is sub classes implement this
+                if (is_callable(array($this, 'drawLogin'))) {
+                    return $this->drawLogin($this->username, $this);
+                }
+
+                // New Login form
+                include_once 'Auth/Frontend/Html.php';
+                return Auth_Frontend_Html::render($this, $this->username);
+            }
+        } else {
+            return;
+        }
+    }
+
+    // }}}
+    // {{{ setExpire()
+
+    /**
+     * Set the maximum expire time
+     *
+     * @param  integer time in seconds
+     * @param  bool    add time to current expire time or not
+     * @return void
+     * @access public
+     */
+    function setExpire($time, $add = false)
+    {
+        $add ? $this->expire += $time : $this->expire = $time;
+    }
+
+    // }}}
+    // {{{ setIdle()
+
+    /**
+     * Set the maximum idle time
+     *
+     * @param  integer time in seconds
+     * @param  bool    add time to current maximum idle time or not
+     * @return void
+     * @access public
+     */
+    function setIdle($time, $add = false)
+    {
+        $add ? $this->idle += $time : $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.
+     * This will chnage the name of the session variable 
+     * auth uses to store it's data in the session
+     *
+     * @param  string New name for the session
+     * @return void
+     * @access public
+     */
+    function setSessionName($name = 'session')
+    {
+        $this->_sessionName = '_auth_'.$name;
+        $this->session =& $_SESSION[$this->_sessionName];
+    }
+
+    // }}}
+    // {{{ setShowLogin()
+
+    /**
+     * Should the login form be displayed if neccessary?
+     *
+     * @param  bool    show login form or not
+     * @return void
+     * @access public
+     */
+    function setShowLogin($showLogin = true)
+    {
+        $this->showLogin = $showLogin;
+    }
+
+    // }}}
+    // {{{ setAllowLogin()
+
+    /**
+     * Should the login form be displayed if neccessary?
+     *
+     * @param  bool    show login form or not
+     * @return void
+     * @access public
+     */
+    function setAllowLogin($allowLogin = true)
+    {
+        $this->allowLogin = $allowLogin;
+    }
+
+    // }}}
+    // {{{ setLoginCallback()
+    
+    /**
+     * Register a callback function to be called on user login.
+     * The function will receive two parameters, the username and a reference to the auth object.
+     *
+     * @param  string  callback function name
+     * @return void
+     * @see    setLogoutCallback()
+     * @access public
+     */
+    function setLoginCallback($loginCallback)
+    {
+        $this->loginCallback = $loginCallback;
+    }
+
+    // }}}
+    // {{{ setFailedLoginCallback()
+
+    /**
+     * Register a callback function to be called on failed user login.
+     * The function will receive a single parameter, the username and a reference to the auth object.
+     *
+     * @param  string  callback function name
+     * @return void
+     * @access public
+     */
+    function setFailedLoginCallback($loginFailedCallback)
+    {
+        $this->loginFailedCallback = $loginFailedCallback;
+    }
+
+    // }}}
+    // {{{ setLogoutCallback()
+
+    /**
+     * Register a callback function to be called on user logout.
+     * The function will receive three parameters, the username and a reference to the auth object.
+     *
+     * @param  string  callback function name
+     * @return void
+     * @see    setLoginCallback()
+     * @access public
+     */
+    function setLogoutCallback($logoutCallback)
+    {
+        $this->logoutCallback = $logoutCallback;
+    }
+
+    // }}}
+    // {{{ setAuthData()
+
+    /**
+     * Register additional information that is to be stored
+     * in the session.
+     *
+     * @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
+     * @access public
+     */
+    function setAuthData($name, $value, $overwrite = true)
+    {
+        if (!empty($this->session['data'][$name]) && $overwrite == false) {
+            return;
+        }
+        $this->session['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.
+     *
+     * @param  string Name of the data field
+     * @return mixed  Value of the data field.
+     * @access public
+     */
+    function getAuthData($name = null)
+    {
+        if (!isset($this->session['data'])) {
+            return null;
+        }    
+        if(!isset($name)) {
+            return $this->session['data'];
+        }
+        if (isset($name) && isset($this->session['data'][$name])) {
+            return $this->session['data'][$name];
+        }
+        return null;        
+    }
+
+    // }}}
+    // {{{ setAuth()
+
+    /**
+     * Register variable in a session telling that the user
+     * has logged in successfully
+     *
+     * @param  string Username
+     * @return void
+     * @access public
+     */
+    function setAuth($username)
+    {
+    
+        // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 > 
+        session_regenerate_id(true);
+
+        if (!isset($this->session) || !is_array($this->session)) {
+            $this->session = array();
+        }
+
+        if (!isset($this->session['data'])) {
+            $this->session['data'] = array();
+        }
+
+        $this->session['sessionip'] = isset($this->server['REMOTE_ADDR']) 
+            ? $this->server['REMOTE_ADDR'] 
+            : '';
+        $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT']) 
+            ? $this->server['HTTP_USER_AGENT'] 
+            : '';
+
+        // This should be set by the container to something more safe
+        // Like md5(passwd.microtime)
+        if(empty($this->session['challengekey'])) {
+            $this->session['challengekey'] = md5($username.microtime());
+        }
+
+        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
+        setcookie('authchallenge', $this->session['challengecookie']);
+
+        $this->session['registered'] = true;
+        $this->session['username']   = $username;
+        $this->session['timestamp']  = time();
+        $this->session['idle']       = time();
+    }
+
+    // }}}
+    // {{{ setAdvancedSecurity()
+    
+    /**
+      * Enables advanced security checks
+      *
+      * Currently only ip change and useragent change 
+      * are detected
+      * @todo Add challenge cookies - Create a cookie which changes every time 
+      *       and contains some challenge key which the server can verify with
+      *       a session var cookie might need to be crypted (user pass)
+      * @param bool Enable or disable
+      * @return void
+      * @access public
+      */
+    function setAdvancedSecurity($flag=true)
+    {
+        $this->advancedsecurity = $flag;
+    }
+
+    // }}}
+    // {{{ checkAuth()
+
+    /**
+     * Checks if there is a session with valid auth information.
+     *
+     * @access public
+     * @return boolean  Whether or not the user is authenticated.
+     */
+    function checkAuth()
+    {
+        $this->authChecks++;
+        if (isset($this->session)) {
+            // Check if authentication session is expired
+            if (   $this->expire > 0
+                && isset($this->session['timestamp'])
+                && ($this->session['timestamp'] + $this->expire) < time()) {
+                $this->expired = true;
+                $this->status = AUTH_EXPIRED;
+                $this->logout();
+                return false;
+            }
+
+            // Check if maximum idle time is reached
+            if (   $this->idle > 0
+                && isset($this->session['idle']) 
+                && ($this->session['idle'] + $this->idle) < time()) {
+                $this->idled = true;
+                $this->status = AUTH_IDLED;
+                $this->logout();
+                return false;
+            }
+
+            if (   isset($this->session['registered']) 
+                && isset($this->session['username']) 
+                && $this->session['registered'] == true 
+                && $this->session['username'] != '') {
+                Auth::updateIdle();
+
+                if ($this->advancedsecurity) {
+                    
+                    // Only Generate the challenge once
+                    if($this->authChecks == 1) {
+                        $this->session['challengecookieold'] = $this->session['challengecookie'];
+                        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
+                        setcookie('authchallenge', $this->session['challengecookie']);
+                    }
+                    
+                    // Check for ip change
+                    if (   isset($this->server['REMOTE_ADDR']) 
+                        && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
+                        // Check if the IP of the user has changed, if so we 
+                        // assume a man in the middle attack and log him out
+                        $this->expired = true;
+                        $this->status = AUTH_SECURITY_BREACH;
+                        $this->logout();
+                        return false;
+                    }
+                    
+                    // Check for useragent change
+                    if (   isset($this->server['HTTP_USER_AGENT']) 
+                        && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
+                        // Check if the User-Agent of the user has changed, if 
+                        // so we assume a man in the middle attack and log him out
+                        $this->expired = true;
+                        $this->status = AUTH_SECURITY_BREACH;
+                        $this->logout();
+                        return false;
+                    }
+    
+                    // Check challenge cookie here, if challengecookieold is not set 
+                    // this is the first time and check is skipped
+                    // TODO when user open two pages similtaneuly (open in new window,open 
+                    // in tab) auth breach is caused find out a way around that if possible
+                    if (   isset($this->session['challengecookieold']) 
+                        && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
+                        $this->expired = true;
+                        $this->status = AUTH_SECURITY_BREACH;
+                        $this->logout();
+                        $this->login();
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ staticCheckAuth() [static]
+
+    /**
+     * Statically checks if there is a session with valid auth information.
+     *
+     * @access public
+     * @see checkAuth
+     * @return boolean  Whether or not the user is authenticated.
+     * @static
+     */
+    function staticCheckAuth($options = null)
+    {
+        static $staticAuth;
+        if(!isset($staticAuth)) {
+            $staticAuth = new Auth('null', $options);
+        }
+        return $staticAuth->checkAuth();
+    }
+
+    // }}}
+    // {{{ getAuth()
+
+    /**
+     * Has the user been authenticated?
+     *
+     * @access public
+     * @return bool  True if the user is logged in, otherwise false.
+     */
+    function getAuth()
+    {
+        return $this->checkAuth();
+    }
+
+    // }}}
+    // {{{ 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()
+    {
+        if (is_callable($this->logoutCallback)) {
+            call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
+        }
+
+        $this->username = '';
+        $this->password = '';
+        
+        $this->session = null;
+    }
+
+    // }}}
+    // {{{ updateIdle()
+
+    /**
+     * Update the idletime
+     *
+     * @access private
+     * @return void
+     */
+    function updateIdle()
+    {
+        $this->session['idle'] = time();
+    }
+
+    // }}}
+    // {{{ getUsername()
+
+    /**
+     * Get the username
+     *
+     * @return string
+     * @access public
+     */
+    function getUsername()
+    {
+        if (isset($this->session['username'])) {
+            return($this->session['username']);
+        }
+        return('');
+    }
+
+    // }}}
+    // {{{ getStatus()
+
+    /**
+     * Get the current status
+     *
+     * @return string
+     * @access public
+     */
+    function getStatus()
+    {
+        return $this->status;
+    }
+
+    // }}}
+    // {{{ getPostUsernameField()
+    
+    /**
+     * Gets the post varible used for the username
+     * 
+     * @return string
+     * @access public
+     */
+    function getPostUsernameField()
+    {
+        return($this->_postUsername);
+    }
+
+    // }}}
+    // {{{ getPostPasswordField()
+
+    /**
+     * Gets the post varible used for the username
+     * 
+     * @return string
+     * @access public
+     */
+    function getPostPasswordField()
+    {
+        return($this->_postPassword);
+    }
+
+    // }}}
+    // {{{ sessionValidThru()
+
+    /**
+     * Returns the time up to the session is valid
+     *
+     * @access public
+     * @return integer
+     */
+    function sessionValidThru()
+    {
+        if (!isset($this->session['idle'])) {
+            return 0;
+        }
+        if ($this->idle == 0) {
+            return 0;
+        }
+        return ($this->session['idle'] + $this->idle);
+    }
+
+    // }}}
+    // {{{ listUsers()
+
+    /**
+     * List all users that are currently available in the storage
+     * container
+     *
+     * @access public
+     * @return array
+     */
+    function listUsers()
+    {
+        $this->_loadStorage();
+        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 = '')
+    {
+        $this->_loadStorage();
+        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)
+    {
+        $this->_loadStorage();
+        return $this->storage->removeUser($username);
+    }
+
+    // }}}
+    // {{{ changePassword()
+
+    /**
+     * Change password for user in the storage container
+     *
+     * @access public
+     * @param string Username
+     * @param string The new password 
+     * @return mixed True on success, PEAR error object on error
+     *               and AUTH_METHOD_NOT_SUPPORTED otherwise.
+     */
+    function changePassword($username, $password)
+    {
+        $this->_loadStorage();
+        return $this->storage->changePassword($username, $password);
+    }
+
+    // }}}
+
+}
+?>
