steffen: server/kolab-horde-framework/kolab-horde-framework/Auth/Auth Signup.php, NONE, 1.1 application.php, NONE, 1.1 auto.php, NONE, 1.1 composite.php, NONE, 1.1 customsql.php, NONE, 1.1 cyrsql.php, NONE, 1.1 cyrus.php, NONE, 1.1 ftp.php, NONE, 1.1 http.php, NONE, 1.1 imap.php, NONE, 1.1 imsp.php, NONE, 1.1 ipbasic.php, NONE, 1.1 ipmap.php, NONE, 1.1 krb5.php, NONE, 1.1 ldap.php, NONE, 1.1 login.php, NONE, 1.1 mcal.php, NONE, 1.1 pam.php, NONE, 1.1 passwd.php, NONE, 1.1 radius.php, NONE, 1.1 sasl.php, NONE, 1.1 smb.php, NONE, 1.1 sql.php, NONE, 1.1 yahoo.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Fri Oct 14 16:33:06 CEST 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-framework/kolab-horde-framework/Auth/Auth
In directory doto:/tmp/cvs-serv28903/kolab-horde-framework/kolab-horde-framework/Auth/Auth

Added Files:
	Signup.php application.php auto.php composite.php 
	customsql.php cyrsql.php cyrus.php ftp.php http.php imap.php 
	imsp.php ipbasic.php ipmap.php krb5.php ldap.php login.php 
	mcal.php pam.php passwd.php radius.php sasl.php smb.php 
	sql.php yahoo.php 
Log Message:
Separated Horde Framework from kolab-resource-handlers

--- NEW FILE: Signup.php ---
<?php

require_once 'Horde/DataTree.php';
require_once 'Horde/Form.php';
require_once 'Horde/Form/Renderer.php';

/**
 * Auth_Signup:: This class provides an interface to sign up or have
 * new users sign themselves up into the horde installation, depending
 * on how the admin has configured Horde.
 *
 * $Horde: framework/Auth/Auth/Signup.php,v 1.32 2004/04/07 14:43:05 chuck Exp $
 *
 * Copyright 2002-2004 Marko Djukic <marko at oblo.com>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Marko Djukic <marko at oblo.com>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_Signup {

    /**
     * Pointer to a DataTree instance to manage/store signups
     *
     * @var object DataTree $_datatree
     */
    var $_datatree;

    function Auth_Signup()
    {
        global $conf;

        if (!isset($conf['datatree']['driver'])) {
            Horde::fatal(_("You must configure a DataTree backend to use Signups."));
        }
        $driver = $conf['datatree']['driver'];
        $this->_datatree = &DataTree::singleton($driver,
                                                array_merge(Horde::getDriverConfig('datatree', $driver),
                                                            array('group' => 'horde.signup')));
    }

    /**
     * Attempts to return a reference to a concrete Auth_Signup
     * instance. It will only create a new instance if no Auth_Signup
     * instance currently exists.
     *
     * This method must be invoked as: $var = &Auth_Signup::singleton()
     *
     * @return object Signup  The concrete Signup reference, or false on an
     *                        error.
     */
    function &singleton()
    {
        static $signup;

        if (!isset($signup)) {
            $signup = new Auth_Signup();
        }

        return $signup;
    }

    /**
     * Adds a new user to the system and handles any extra fields
     * that may have been compiled, relying on the hooks.php file.
     *
     * @access public
     *
     * @return mixed  PEAR_Error if any errors, otherwise true.
     */
    function addSignup(&$info)
    {
        global $auth, $conf;

        // Perform any preprocessing if requested
        if ($conf['signup']['preprocess']) {
            $info = Horde::callHook('_horde_hook_signup_preprocess', array($info));
            if (is_a($info, 'PEAR_Error')) {
                return $info;
            }
        }

        // Attempt to add the user to the system
        $success = $auth->addUser($info['user_name'], array('password' => $info['password']));
        if (is_a($success, 'PEAR_Error')) {
            return $success;
        }

        // Attempt to add/update any extra data handed in
        if (!empty($info['extra'])) {
            $added = false;
            $added = Horde::callHook('_horde_hook_signup_addextra',
                                     array($info['user_name'], $info['extra']));
            if (!$added || is_a($added, 'PEAR_Error')) {
                Horde::logMessage($added, __FILE__, __LINE__, PEAR_LOG_EMERG);
                Horde::fatal(new PEAR_Error(_("Unable to add extra user information when signing up.")), __FILE__, __LINE__);
            }
        }

        return true;
    }

    /**
     * Queues the user's submitted registration info for later admin
     * approval.
     *
     * @access public
     *
     * @return mixed  PEAR_Error if any errors, otherwise true.
     */
    function &queueSignup(&$info)
    {
        global $auth,$conf;

        // Perform any preprocessing if requested
        if ($conf['signup']['preprocess']) {
            $info = Horde::callHook('_horde_hook_signup_preprocess',
                                    array($info));
            if (is_a($info, 'PEAR_Error')) {
                return $info;
            }
        }

        // Check to see if the username already exists
        if ($auth->exists($info['user_name']) ||
            $this->_datatree->exists($info['user_name'])) {
            return PEAR::raiseError(sprintf(_("Username '%s' already exists."), $info['user_name']));
        }

        // If it's a unique username, go ahead and queue the request
        $signup = $this->newSignup($info['user_name']);
        if (!empty($info['extra'])) {
            $signup->data = array_merge($info['extra'],
                                        array('password' => $info['password'],
                                              'dateReceived' => time()));
        } else {
            $signup->data = array('password' => $info['password'],
                                  'dateReceived' => time());
        }

        if ($conf['signup']['queue']) {
            $result = Horde::callHook('_horde_hook_signup_queued',
                                      array($info['user_name'], $info));
            if (is_a($result, 'PEAR_Error')) {
                return $result;
            }
        }

        return $this->_datatree->add($signup);
    }

    /**
     * Get a user's queued signup information.
     *
     * @access public
     *
     * @param string $username  The username to retrieve the queued info for.
     * @return object DataTreeObject_Signup  The DataTreeObject for the
     *                                       requested signup.
     */
    function getQueuedSignup($username)
    {
        return $this->_datatree->getObject($username, 'DataTreeObject_Signup');
    }

    /**
     * Get the queued information for all pending signups.
     *
     * @access public
     *
     * @return array  An array of DataTreeObject_Signup objects, one for
     *                each signup in the queue.
     */
    function getQueuedSignups()
    {
        $signups = array();
        foreach ($this->_datatree->get(DATATREE_FORMAT_FLAT, -1, true) as $username) {
            if ($username != -1) {
                $signups[] = $this->_datatree->getObject($username);
            }
        }
        return $signups;
    }

    /**
     * Remove a queued signup.
     *
     * @access public
     *
     * @param string $username  The user to remove from the signup queue.
     */
    function removeQueuedSignup($username)
    {
        $this->_datatree->remove($username);
    }

    /**
     * Return a new signup object.
     *
     * @param string $name The signups's name.
     *
     * @return object DataTreeObject_Signup A new signup object.
     */
    function &newSignup($name)
    {
        if (empty($name)) {
            return PEAR::raiseError('Signup names must be non-empty');
        }
        return $ret = &new DataTreeObject_Signup($name);
    }

}

/**
 * Extension of the DataTreeObject class for storing Signup
 * information in the DataTree driver. If you want to store
 * specialized Signup information, you should extend this class
 * instead of extending DataTreeObject directly.
 *
 * @author  Marko Djukic <marko at oblo.com>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class DataTreeObject_Signup extends DataTreeObject {

    /**
     * We want to see queued signups in descending order of receipt.
     * Insert new signups at position 0 and push the rest down.
     * @var integer $order
     */
    var $order = 0;

    /**
     * The DataTreeObject_Signup constructor. Just makes sure to call
     * the parent constructor so that the signup's is is set
     * properly.
     *
     * @param string $id The id of the signup.
     */
    function DataTreeObject_Signup($id)
    {
        parent::DataTreeObject($id);
        if (is_null($this->data)) {
            $this->data = array();
        }
    }

}

/**
 * Horde Signup Form, extending of Horde_Form::
 *
 * Copyright 2003-2004 Marko Djukic <marko at oblo.com>
 *
 * See the enclosed file COPYING for license information (GPL).  If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Marko Djukic <marko at oblo.com>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class HordeSignupForm extends Horde_Form {

    var $_useFormToken = true;

    function HordeSignupForm(&$vars)
    {
        global $registry;

        parent::Horde_Form($vars, sprintf(_("%s Sign Up"), $registry->getParam('name')));

        $this->setButtons(_("Sign up"), true);

        $this->addHidden('', 'url', 'text', false);
        $this->addVariable(_("Choose a username"), 'user_name', 'text', true);
        $this->addVariable(_("Choose a password"), 'password', 'passwordconfirm', true, false, _("type the password twice to confirm"));

        /* Use hooks get any extra fields required in signing up. */
        $extra = Horde::callHook('_horde_hook_signup_getextra');
        if (!is_a($extra, 'PEAR_Error') && !empty($extra)) {
            foreach ($extra as $field_name => $field) {
                $readonly = isset($field['readonly']) ? $field['readonly'] : null;
                $desc = isset($field['desc']) ? $field['desc'] : null;
                $required = isset($field['required']) ? $field['required'] : false;
                $field_params = isset($field['params']) ? $field['params'] : array();

                $this->addVariable($field['label'], 'extra[' . $field_name . ']',
                                   $field['type'], $required, $readonly,
                                   $desc, $field_params);
            }
        }
    }

}

--- NEW FILE: application.php ---
<?php
/**
 * The Auth_application class provides a wrapper around
 * application-provided Horde authentication which fits inside the
 * Horde Auth:: API.
 *
 * Required parameters:
 * ====================
 *   'app'  --  The application which is providing authentication.
 *
 *
 * $Horde: framework/Auth/Auth/application.php,v 1.24 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_application extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new Application authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_application($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set connection parameters.
     *
     * @access private
     *
     * @param array $params  A hash containing connection parameters.
     */
    function _setParams($params)
    {
        global $registry;

        Horde::assertDriverConfig($params, 'auth',
                                  array('app'),
                                  'authentication application');

        if (empty($registry->applications[$params['app']])) {
            Horde::fatal(PEAR::raiseError($params['app'] . ' is not configured in the Horde Registry.'), __FILE__, __LINE__);
        }
        $this->capabilities['list']   = $registry->hasMethod('userlist', $params['app']);
        $this->capabilities['add']    = $registry->hasMethod('adduser', $params['app']);
        $this->capabilities['update'] = $registry->hasMethod('updateuser', $params['app']);
        $this->capabilities['remove'] = $registry->hasMethod('removeuser', $params['app']);

        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        global $registry;

        if (!$registry->hasMethod('authenticate', $this->_params['app'])) {
            Horde::fatal(PEAR::raiseError($this->_params['app'] . ' does not provide an authenticate() method.'), __FILE__, __LINE__);
        }
        return $registry->callByPackage($this->_params['app'], 'authenticate',
                                        array('userId' => $userId,
                                              'credentials' => $credentials,
                                              'params' => $this->_params));
    }

    /**
     * Return the URI of the login screen for this authentication
     * method.
     *
     * @access public
     *
     * @param optional string $app  The application to use.
     * @param optional string $url  The URL to redirect to after login.
     *
     * @return string  The login screen URI.
     */
    function _getLoginScreen($app = 'horde', $url = '')
    {
        return parent::_getLoginScreen($this->_params['app'], $url);
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        if ($this->hasCapability('list')) {
            return $GLOBALS['registry']->callByPackage($this->_params['app'], 'userlist');
        } else {
            return PEAR::raiseError('unsupported');
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId      The userId to add.
     * @param array $credentials  The credentials to use.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        if ($this->hasCapability('add')) {
            return $GLOBALS['registry']->callByPackage($this->_params['app'], 'adduser', array($userId, $credentials));
        } else {
            return PEAR::raiseError('unsupported');
        }
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        if ($this->hasCapability('update')) {
            return $GLOBALS['registry']->callByPackage($this->_params['app'], 'updateuser', array($oldID, $newID, $credentials));
        } else {
            return PEAR::raiseError('unsupported');
        }
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId  The userId to delete.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function removeUser($userId)
    {
        if ($this->hasCapability('remove')) {
            return $GLOBALS['registry']->callByPackage($this->_params['app'], 'removeuser', array($userId));
        } else {
            return PEAR::raiseError('unsupported');
        }
    }

}

--- NEW FILE: auto.php ---
<?php
/**
 * The Auth_auto class transparently logs users in to Horde using ONE
 * username, either defined in the config or defaulting to
 * 'horde_user'. This is only for use in testing or behind a firewall;
 * it should NOT be used on a public, production machine.
 *
 * Optional parameters:
 * ====================
 *   'username'     --  The username to authenticate everyone as.
 *                      DEFAULT: 'horde_user'
 *   'requestuser'  --  If true, allow username to be passed by GET, POST
 *                      or cookie.
 *
 *
 * $Horde: framework/Auth/Auth/auto.php,v 1.11 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 2.2
 * @package Horde_Auth
 */
class Auth_auto extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => true);

    /**
     * Constructs a new Automatic authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters.
     */
    function Auth_auto($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set parameters for the Auth_auto object.
     *
     * @access private
     *
     * @param array $params  Parameters. None currently required,
     *                       'username' is optional.
     */
    function _setParams($params)
    {
        if (!isset($params['username'])) {
            $params['username'] = 'horde_user';
        }
        $this->_params = $params;
    }

    /**
     * Automatic authentication: Set the user
     * allowed IP block.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        $username = (!empty($this->_params['requestuser']) && isset($_REQUEST['username'])) ? 
                     $_REQUEST['username'] : 
                     $this->_params['username'];
        $this->setAuth($username,
                       array('transparent' => 1));
        return true;
    }

}

--- NEW FILE: composite.php ---
<?php
/**
 * The Auth_composite class provides a wrapper around
 * application-provided Horde authentication which fits inside the
 * Horde Auth:: API.
 *
 * Required parameters:
 * ====================
 *
 *
 * $Horde: framework/Auth/Auth/composite.php,v 1.25 2004/03/15 22:36:48 jan Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_composite extends Auth {

    /**
     * Hash containing any instantiated drivers.
     *
     * @var array $_drivers
     */
    var $_drivers = array();

    /**
     * Constructs a new Composite authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_composite($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set connection parameters.
     *
     * @access private
     *
     * @param array $params  A hash containing connection parameters.
     */
    function _setParams($params)
    {
        if (!is_array($params)) {
            Horde::fatal(PEAR::raiseError('No configuration information specified for Composite authentication.'), __FILE__, __LINE__);
        }

        $this->_params = $params;
    }

    /**
     * Return the named parameter for the current auth driver.
     * 
     * @access public
     *
     * @param string $param  The parameter to fetch.
     *
     * @return string  The parameter's value.
     */
    function getParam($param)
    {
        if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($login_driver)) {
            return $this->_drivers[$login_driver]->getParam($param);
        }

        return null;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (($auth_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($auth_driver)) {
            return $this->_drivers[$auth_driver]->authenticate($userId, $credentials);
        }

        if (($auth_driver = Auth::_getDriverByParam('username_switch', $this->_params, array($userId))) &&
            $this->_loadDriver($auth_driver)) {
            return $this->_drivers[$auth_driver]->hasCapability('transparent');
        }

        $this->_setAuthError(AUTH_REASON_FAILED);
        return false;
    }

    /**
     * Query the current Auth object to find out if it supports the
     * given capability.
     *
     * @access public
     *
     * @param string $capability  The capability to test for.
     *
     * @return boolean  Whether or not the capability is supported.
     */
    function hasCapability($capability)
    {
        switch ($capability) {
        case 'add':
        case 'update':
        case 'remove':
        case 'list':
            if (!empty($this->_params['admin_driver']) &&
                $this->_loadDriver($this->_params['admin_driver'])) {
                return $this->_drivers[$this->_params['admin_driver']]->hasCapability($capability);
            } else {
                return false;
            }
            break;

        case 'transparent':
            if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
                $this->_loadDriver($login_driver)) {
                return $this->_drivers[$login_driver]->hasCapability('transparent');
            }
            return false;
            break;

        default:
            return false;
        }
    }

    /**
     * Automatic authentication: Find out if the client matches an
     * allowed IP block.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($login_driver)) {
            return $this->_drivers[$login_driver]->transparent();
        }

        return false;
    }

    /**
     * Return the URI of the login screen for this authentication
     * object.
     *
     * @access public
     *
     * @param optional string $app  The application to use.
     * @param optional string $url  The URL to redirect to after login.
     *
     * @return string  The login screen URI.
     */
    function _getLoginScreen($app = 'horde', $url = '')
    {
        if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($login_driver)) {
            return $this->_drivers[$login_driver]->_getLoginScreen($app, $url);
        } else {
            return parent::_getLoginScreen($app, $url);
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @param string $userId       The userId to add.
     * @param array  $credentials  The credentials to use.
     *
     * @return mixed        True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        if (!empty($this->_params['admin_driver']) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->addUser($userId, $credentials);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Update a set of authentication credentials.
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        if (!empty($this->_params['admin_driver']) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->updateUser($oldID, $newID, $credentials);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @param string $userId  The userId to delete.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function removeUser($userId)
    {
        if (!empty($this->_params['admin_driver']) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->removeUser($userId);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * List all users in the system.
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        if (!empty($this->_params['admin_driver']) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->listUsers();
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Checks if a userId exists in the system.
     *
     * @access public
     *
     * @return boolean  Whether or not the userId already exists.
     */
    function exists($userId)
    {
        if (!empty($this->_params['admin_driver']) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->exists($userId);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Loads one of the drivers in our configuration array, if it
     * isn't already loaded.
     *
     * @access private
     *
     * @param string $driver  The name of the driver to load.
     */
    function _loadDriver($driver)
    {
        if (empty($this->_drivers[$driver])) {
            // This is a bit specialized for Horde::getDriverConfig(),
            // so localize it here:
            global $conf;
            if (!empty($this->_params['drivers'][$driver]['params'])) {
                $params = $this->_params['drivers'][$driver]['params'];
            } elseif (!empty($conf[$driver])) {
                $params = $conf[$driver];
            } else {
                $params = null;
            }

            $this->_drivers[$driver] = &Auth::singleton($this->_params['drivers'][$driver]['driver'], $params);
        }

        return isset($this->_drivers[$driver]);
    }

}

--- NEW FILE: customsql.php ---
<?php

require_once dirname(__FILE__) . '/sql.php';

/**
 * The Auth_customsql class provides a sql implementation of the Horde
 * authentication system with the possibility to set custom-made queries.
 *
 * Required parameters:
 * ====================
 *   'database'  --  The name of the database.
 *   'hostspec'  --  The hostname of the database server.
 *   'password'  --  The password associated with 'username'.
 *   'phptype'   --  The database type (ie. 'pgsql', 'mysql, etc.).
 *   'protocol'  --  The communication protocol ('tcp', 'unix', etc.).
 *   'username'  --  The username with which to connect to the database.
 *
 * Required parameters: (Custom query)
 * ===================================
 * Some special tokens can be used in the sql query. They are replaced
 * at the query stage :
 *
 *   - "\L" will be replaced by the user's login
 *   - "\P" will be replaced by the user's password.
 *   - "\O" will be replaced by the old user's login (required for update)
 *
 *   Eg: "SELECT * FROM users WHERE uid = \L
 *                            AND passwd = \P
 *                            AND billing = 'paid'
 *
 *   'query_auth'    Authenticate the user.       "\L" & "\N"
 *   'query_add'     Add user.                    "\L" & "\N"
 *   'query_update'  Update user.                 "\O", "\L" & "\N"
 *   'query_resetpassword'  Reset password.       "\L", & "\P"
 *   'query_remove'  Remove user.                 "\L"
 *   'query_list'    List user.                   "\L"
 *
 * Optional parameters:
 * ====================
 *   'encryption'      --  The encryption to use to store the password in the
 *                         table (e.g. plain, crypt, md5-hex, md5-base64, smd5,
 *                         sha, ssha).
 *                         DEFAULT: 'md5-hex'
 *   'show_encryption' --  Whether or not to prepend the encryption in the
 *                         password field.
 *                         DEFAULT: 'false'
 *
 * Required by some database implementations:
 * ==========================================
 *   'options'  --  Additional options to pass to the database.
 *   'port'     --  The port on which to connect to the database.
 *   'tty'      --  The TTY on which to connect to the database.
 *
 *
 * $Horde: framework/Auth/Auth/customsql.php,v 1.14 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2002 Ronnie Garcia <ronnie at mk2.net>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Ronnie Garcia <ronnie at mk2.net>
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Joel Vandal <jvandal at infoteck.qc.ca>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_customsql extends Auth_sql {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => true,
                              'update'        => true,
                              'resetpassword' => true,
                              'remove'        => true,
                              'list'          => true,
                              'transparent'   => false);

    /**
     * Constructs a new SQL authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_customsql($params = array())
    {
        $this->_params = $params;

        Horde::assertDriverConfig($params, 'auth',
            array('query_auth'),
            'authentication custom SQL');
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build a custom query, based on the config file. */
        $query = $this->_params['query_auth'];
        $query = str_replace("\L", $this->_db->quote($userId), $query);
        $query = str_replace("\P", $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                               '',
                                                                               $this->_params['encryption'],
                                                                               $this->_params['show_encryption'])), $query);

        $result = $this->_db->query($query);
        if (!is_a($result, 'PEAR_Error')) {
            $row = $result->fetchRow(DB_GETMODE_ASSOC);
            /* If we have at least one returned row, then the user is
             * valid. */
            if (is_array($row)) {
                $result->free();
                return true;
            } else {
                $result->free();
                $this->_setAuthError(AUTH_REASON_BADLOGIN);
                return false;
            }
        } else {
            $this->_setAuthError(AUTH_REASON_FAILED);
            return false;
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId      The userId to add.
     * @param array $credentials  The credentials to add.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        $this->_connect();

        /* Build a custom query, based on the config file. */
        $query = $this->_params['query_add'];
        $query = str_replace("\L", $this->_db->quote($userId), $query);
        $query = str_replace("\P", $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                               '',
                                                                               $this->_params['encryption'],
                                                                               $this->_params['show_encryption'])), $query);

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build a custom query, based on the config file. */
        $query = $this->_params['query_update'];
        $query = str_replace("\O", $this->_db->quote($oldId), $query);
        $query = str_replace("\L", $this->_db->quote($newId), $query);
        $query = str_replace("\P", $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                               '',
                                                                               $this->_params['encryption'],
                                                                               $this->_params['show_encryption'])), $query);

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * Reset a user's password. Used for example when the user does not
     * remember the existing password.
     *
     * @access public
     *
     * @param string $user_id  The user id for which to reset the password.
     *
     * @return mixed  The new passwrd on success or a PEAR_Error object on
     *                failure.
     */
    function resetPassword($user_id)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Get a new random password. */
        $password = Auth::genRandomPassword();

        /* Build the SQL query. */
        $query = $this->_params['query_resetpassword'];
        $query = str_replace("\L", $this->_db->quote($user_id), $query);
        $query = str_replace("\P", $this->_db->quote($this->getCryptedPassword($password,
                                                                               '',
                                                                               $this->_params['encryption'],
                                                                               $this->_params['show_encryption'])), $query);

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $password;
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId  The userId to delete.
     *
     * @return boolean        Success or failure.
     */
    function removeUser($userId)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build a custom query, based on the config file. */
        $query = $this->_params['query_remove'];
        $query = str_replace("\L", $this->_db->quote($userId), $query);

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or false on failure/unsupported.
     */
    function listUsers()
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build a custom query, based on the config file. */
        $query = $this->_params['query_list'];

        $result = $this->_db->getAll($query, null, DB_FETCHMODE_ORDERED);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        /* Loop through and build return array. */
        $users = array();
        foreach ($result as $ar) {
            $users[] = $ar[0];
        }

        return $users;
    }

}

--- NEW FILE: cyrsql.php ---
<?php

require_once dirname(__FILE__) . '/sql.php';

/**
 * The Auth_cyrsql class provides a SQL implementation of the Horde
 * authentication system for the Cyrus IMAP server. Most of the
 * functionality is the same as for the SQL class; only what is
 * different overrides the parent class implementations.
 *
 * Required parameters:
 * ====================
 *   'cyradmin'  --  The username of the cyrus administrator.
 *   'cyrpass'   --  The password for the cyrus administrator.
 *   'database'  --  The name of the database.
 *   'hostspec'  --  The hostname of the database server.
 *   'imap_dsn'  --  The full IMAP DSN
 *                   (i.e. {localhost:993/imap/ssl/novalidate-cert}).
 *   'password'  --  The password associated with 'username'.
 *   'phptype'   --  The database type (ie. 'pgsql', 'mysql, etc.).
 *   'protocol'  --  The communication protocol ('tcp', 'unix', etc.).
 *   'username'  --  The username with which to connect to the database.
 *
 * Optional parameters:
 * ====================
 *   'domain_field'    --  If set to anything other than 'none' this is used as
 *                         field name where domain is stored.
 *                         DEFAULT: 'domain_name'
 *   'encryption'      --  The encryption to use to store the password in the
 *                         table (e.g. plain, crypt, md5-hex, md5-base64, smd5,
 *                         sha, ssha).
 *                         DEFAULT: 'md5-hex'
 *   'folders'         --  An array of folders to create under username.
 *                         DEFAULT: NONE
 *   'password_field'  --  The name of the password field in the auth table.
 *                         DEFAULT: 'password'
 *   'quota'           --  The quota (in kilobytes) to grant on the mailbox.
 *                         DEFAULT: NONE
 *   'table'           --  The name of the auth table in 'database'.
 *                         DEFAULT: 'accountuser'
 *   'unixhier'        --  The value of imapd.conf's unixhierarchysep setting.
 *                         Set this to true if the value is true in imapd.conf.
 *   'username_field'  --  The name of the username field in the auth table.
 *                         DEFAULT: 'username'
 *
 * Required by some database implementations:
 * ==========================================
 *   'options'  --  Additional options to pass to the database.
 *   'port'     --  The port on which to connect to the database.
 *   'tty'      --  The TTY on which to connect to the database.
 *
 *
 * The table structure for the auth system is as follows:
 *
 * CREATE TABLE accountuser (
 *     username    VARCHAR(255) BINARY NOT NULL DEFAULT '',
 *     password    VARCHAR(32) BINARY NOT NULL DEFAULT '',
 *     prefix      VARCHAR(50) NOT NULL DEFAULT '',
 *     domain_name VARCHAR(255) NOT NULL DEFAULT '',
 *     UNIQUE KEY username (username)
 * );
 *
 * CREATE TABLE adminuser (
 *     username    VARCHAR(50) BINARY NOT NULL DEFAULT '',
 *     password    VARCHAR(50) BINARY NOT NULL DEFAULT '',
 *     type        INT(11) NOT NULL DEFAULT '0',
 *     SID         VARCHAR(255) NOT NULL DEFAULT '',
 *     home        VARCHAR(255) NOT NULL DEFAULT '',
 *     PRIMARY KEY (username)
 * );
 *
 * CREATE TABLE alias (
 *     alias       VARCHAR(255) NOT NULL DEFAULT '',
 *     dest        LONGTEXT,
 *     username    VARCHAR(50) NOT NULL DEFAULT '',
 *     status      INT(11) NOT NULL DEFAULT '1',
 *     PRIMARY KEY (alias)
 * );
 *
 * CREATE TABLE domain (
 *     domain_name VARCHAR(255) NOT NULL DEFAULT '',
 *     prefix      VARCHAR(50) NOT NULL DEFAULT '',
 *     maxaccounts INT(11) NOT NULL DEFAULT '20',
 *     quota       INT(10) NOT NULL DEFAULT '20000',
 *     transport   VARCHAR(255) NOT NULL DEFAULT 'cyrus',
 *     freenames   ENUM('YES','NO') NOT NULL DEFAULT 'NO',
 *     freeaddress ENUM('YES','NO') NOT NULL DEFAULT 'NO',
 *     PRIMARY KEY (domain_name),
 *     UNIQUE KEY prefix (prefix)
 * );
 *
 * CREATE TABLE domainadmin (
 *     domain_name VARCHAR(255) NOT NULL DEFAULT '',
 *     adminuser   VARCHAR(255) NOT NULL DEFAULT ''
 * );
 *
 * CREATE TABLE search (
 *     search_id   VARCHAR(255) NOT NULL DEFAULT '',
 *     search_sql  TEXT NOT NULL,
 *     perpage     INT(11) NOT NULL DEFAULT '0',
 *     timestamp   TIMESTAMP(14) NOT NULL,
 *     PRIMARY KEY (search_id),
 *     KEY search_id (search_id)
 * );
 *
 * CREATE TABLE virtual (
 *     alias       VARCHAR(255) NOT NULL DEFAULT '',
 *     dest        LONGTEXT,
 *     username    VARCHAR(50) NOT NULL DEFAULT '',
 *     status      INT(11) NOT NULL DEFAULT '1',
 *     KEY alias (alias)
 * );
 *
 * CREATE TABLE log (
 *     id          INT(11) NOT NULL AUTO_INCREMENT,
 *     msg         TEXT NOT NULL,
 *     user        VARCHAR(255) NOT NULL DEFAULT '',
 *     host        VARCHAR(255) NOT NULL DEFAULT '',
 *     time        DATETIME NOT NULL DEFAULT '2000-00-00 00:00:00',
 *     pid         VARCHAR(255) NOT NULL DEFAULT '',
 *     PRIMARY KEY (id)
 * );
 *	       	   	     
 *
 * $Horde: framework/Auth/Auth/cyrsql.php,v 1.29 2004/04/14 15:49:04 eraserhd Exp $
 *
 * Copyright 2002-2004 Ilya <mail at krel.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Ilya <mail at krel.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_cyrsql extends Auth_sql {

    /**
     * Handle for the current IMAP connection.
     *
     * @var resource $_imapStream
     */
    var $_imapStream;

    /**
     * Hierarchy separator to use (e.g., is it user/mailbox or user.mailbox)
     *
     * @var string $_separator
     */
    var $_separator = '.';

    /**
     * Constructor.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_cyrsql($params = array())
    {
        if (!Util::extensionExists('imap')) {
            Horde::fatal(PEAR::raiseError(_("Auth_cyrsql: Required imap extension not found."), __FILE__, __LINE__));
        }
        parent::Auth_sql($params);
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId       The userId to add.
     * @param array  $credentials  The credentials to add.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        $this->_connect();

        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            list($name,$domain)=explode('@',$userId);
            /* Build the SQL query. */
            $query = sprintf('INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s)',
                             $this->_params['table'],
                             $this->_params['username_field'],
                             $this->_params['domain_field'],
                             $this->_params['password_field'],
                             $this->_db->quote($name),
                             $this->_db->quote($domain),
                             $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                         '',
                                                                         $this->_params['encryption'],
                                                                         $this->_params['show_encryption'])));
            $dbresult = $this->_db->query($query);
            $query = sprintf('INSERT INTO virtual (alias, dest, username, status) VALUES (%s, %s, %s, 1)',
                             $this->_db->quote($userId),
                             $this->_db->quote($name),
                             $this->_db->quote($name));
            $dbresult2 = $this->_db->query($query);
        } else {
            $dbresult = parent::addUser($userId, $credentials);
        }
        if (is_a($dbresult, 'PEAR_Error')) {
            return $dbresult;
        }
        if (is_a($dbresult2, 'PEAR_Error')) {
            return $dbresult2;
        }
	
        $name = imap_utf7_encode($name);
        if (@imap_createmailbox($this->_imapStream,
                                imap_utf7_encode($this->_params['imap_dsn'] .
                                                 'user' . $this->_separator . $name))) {
            @array_walk($this->_params['folders'],
                        array($this, '_createSubFolders'), $name);
        } else {
            Horde::logMessage('IMAP mailbox creation for ' . $name . ' failed ',
                              __FILE__, __LINE__, PEAR_LOG_ERR);
            return PEAR::raiseError(sprintf(_("IMAP mailbox creation failed: %s"), imap_last_error()));
        }

        if (isset($this->_params['quota']) && $this->_params['quota'] >= 0) {
            if (!@imap_set_quota($this->_imapStream,
                                 'user' . $this->_separator . $name,
                                 $this->_params['quota'])) {
                return PEAR::raiseError(sprintf(_("IMAP mailbox quota creation failed: %s"), imap_last_error()));
            }
        }


        return true;
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId  The userId to delete.
     *
     * @return boolean        Success or failure.
     */
    function removeUser($userId)
    {
        $this->_connect();

        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            list($name,$domain)=explode('@',$userId);
            /* Build the SQL query. */
            $query = sprintf('DELETE FROM %s WHERE %s = %s and %s = %s',
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_db->quote($name),
                         $this->_params['domain_field'],
                         $this->_db->quote($domain));
            $dbresult = $this->_db->query($query);
            $query = sprintf('DELETE FROM virtual WHERE dest = %s',
                             $this->_db->quote($name));
            $dbresult2 = $this->_db->query($query); 
        } else {
            $dbresult = parent::removeUser($userId);
        }

        if (is_a($dbresult, 'PEAR_Error')) {
            return $dbresult;
        }

        if (is_a($dbresult2, 'PEAR_Error')) {
            return $dbresult2;
        }

        /* Set ACL for mailbox deletion. */
        list($admin)=explode('@',$this->_params['cyradmin']);
        @imap_setacl($this->_imapStream,
                     'user' . $this->_separator . $name,
                     $admin, 'lrswipcda');

        /* Delete IMAP mailbox. */
        $imapresult = @imap_deletemailbox($this->_imapStream,
                                          $this->_params['imap_dsn'] .
                                          'user' . $this->_separator . $name);

        if (!$imapresult) {
            return PEAR::raiseError(sprintf(_("IMAP mailbox deletion failed: %s"), imap_last_error()));
        }

        return true;
    }

    /**
     * Attempts to open connections to the SQL and IMAP servers.
     *
     * @access private
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function _connect()
    {
        if (!$this->_connected) {
            parent::_connect();

            // Reset the $_connected flag; we haven't yet successfully
            // opened everything.
            $this->_connected = false;

            $this->_imapStream = @imap_open($this->_params['imap_dsn'], $this->_params['cyradmin'], $this->_params['cyrpass'], OP_HALFOPEN);
            if (!$this->_imapStream) {
                Horde::fatal(PEAR::raiseError(sprintf(_("Can't connect to IMAP server: %s"), imap_last_error())), __FILE__, __LINE__);
            }

            if (!empty($this->_params['unixhier'])) {
                $this->_separator = '/';
            }
            $this->_connected = true;
        }

        return true;
    }

    /**
     * Disconnect from the SQL and IMAP servers and clean up the
     * connections.
     *
     * @access private
     *
     * @return boolean  True on success, false on failure.
     */
    function _disconnect()
    {
        if ($this->_connected) {
            parent::_disconnect();
            @imap_close($this->_imapStream);
        }

        return true;
    }

    /**
     * Creates all mailboxes supllied in configuration
     *
     * @access private
     */
    function _createSubFolders($value, $key, $userName)
    {
        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            list($name,$domain)=explode('@',$userName);
            @imap_createmailbox($this->_imapStream,
                           	    imap_utf7_encode($this->_params['imap_dsn'] .
                                                 'user' . $this->_separator . $name .
                                                 $this->_separator . $value . '@' . $domain));
        } else {
            @imap_createmailbox($this->_imapStream,
                                imap_utf7_encode($this->_params['imap_dsn'] .
                                                 'user' . $this->_separator . $userName .
                                                 $this->_separator . $value));
        }
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or false on failure/unsupported.
     */
    function listUsers()
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            /* Build the SQL query with domain. */
            $query = sprintf('SELECT %s , %s FROM %s ORDER BY %s',
                             $this->_params['username_field'],
                             $this->_params['domain_field'],
                             $this->_params['table'],
                             $this->_params['username_field']);
        } else {
            /* Build the SQL query without domain. */
            $query = sprintf('SELECT %s FROM %s ORDER BY %s',
                             $this->_params['username_field'],
                             $this->_params['table'],
                             $this->_params['username_field']);
        }

        $result = $this->_db->getAll($query, null, DB_FETCHMODE_ORDERED);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        /* Loop through and build return array. */
        $users = array();
        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            foreach ($result as $ar) {
                $users[] = $ar[0] . '@' . $ar[1];
            }
        } else {
            foreach ($result as $ar) {
                $users[] = $ar[0];
            }
        }

        return $users;
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        if (!empty($this->_params['domain_field']) &&
            ($this->_params['domain_field'] != 'none')){
            list($name,$domain)=explode('@',$oldID);
            /* Build the SQL query with domain. */
            $query = sprintf('UPDATE %s SET %s = %s WHERE %s = %s and %s = %s',
                             $this->_params['table'],
                             $this->_params['password_field'],
                             $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                         '',
                                                                         $this->_params['encryption'],
                                                                         $this->_params['show_encryption'])),
                             $this->_params['username_field'],
                             $this->_db->quote($name),
                             $this->_params['domain_field'],
                             $this->_db->quote($domain));
        } else {
            /* Build the SQL query. */
            $query = sprintf('UPDATE %s SET %s = %s WHERE %s = %s',
                             $this->_params['table'],
                             $this->_params['password_field'],
                             $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                         '',
                                                                         $this->_params['encryption'],
                                                                         $this->_params['show_encryption'])),
                             $this->_params['username_field'],
                             $this->_db->quote($oldID));
        }

        return $this->_db->query($query);
    }

}

--- NEW FILE: cyrus.php ---
<?php
/**
 * The Auth_cyrus class provides horde with the ability of administrating
 * a Cyrus mail server authentications against another backend that Horde
 * can update (eg SQL or LDAP).
 *
 * Required values for $params:
 *   'cyradmin'      The username of the cyrus administrator
 *   'cyrpass'       The password for the cyrus administrator
 *   'imap_dsn'      The full IMAP DSN (i.e. {localhost:993/imap/ssl/novalidate-cert} )
 *   'backend'       The complete hash for the Auth_* driver that cyrus
 *                   authenticates against (eg SQL, LDAP).
 *   'separator'     Hierarchy separator to use (e.g., is it user/mailbox or user.mailbox)
 *
 * Optional values:
 *   'unixhier'       The value of imapd.conf's unixhierarchysep setting
 *                    (set this to true if the value is true in imapd.conf)
 *   'folders'        An array of folders to create under username.
 *                    Doesn't create subfolders by default
 *   'quota'          The quota (in kilobytes) to grant on the mailbox.
 *                    Does not establish quota by default.
 *
 * Example Usage:
 *   $conf['auth']['driver'] = 'composite';
 *   $conf['auth']['params']['loginscreen_switch'] = '_horde_select_loginscreen';
 *   $conf['auth']['params']['admin_driver'] = 'cyrus';
 *   $conf['auth']['params']['drivers']['imp'] = array('driver' => 'application',
 *                                                     'params' => array('app' => 'imp'));
 *   $conf['auth']['params']['drivers']['cyrus'] = array('driver' => 'cyrus',
 *                                                       'params' => array('cyradmin' => 'cyrus',
 *                                                                         'cyrpass' => 'password',
 *                                                                         'separator' => '.',
 *                                                                         'imap_dsn' => '{maik.example.com/imap}'));
 *    $conf['auth']['params']['drivers']['cyrus']['params']['backend'] = array('driver' => 'sql',
 *                                                                             'params' => array('phptype' => 'mysql',
 *                                                                                           'hostspec' => 'database.example.com',
 *                                                                                           'protocol' => 'tcp',
 *                                                                                           'username' => 'username',
 *                                                                                           'password' => 'password',
 *                                                                                           'database' => 'mail',
 *                                                                                           'table' => 'accountuser',
 *                                                                                           'encryption' => 'md5-hex',
 *                                                                                           'username_field' => 'username',
 *                                                                                           'password_field' => 'password'));
 *
 *   if (!function_exists('_horde_select_loginscreen')) {
 *       function _horde_select_loginscreen() {
 *           return 'imp';
 *       }
 *   }
 *
 * $Horde: framework/Auth/Auth/cyrus.php,v 1.12 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2002-2004 Ilya <mail at krel.org>
 * Copyright 2003-2004 Mike Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Ilya <mail at krel.org>
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_cyrus extends Auth {

    /**
     * Handle for the current IMAP connection.
     *
     * @var resource $_imapStream
     */
    var $_imapStream;

    /**
     * Flag indicating if the IMAP connection is connected.
     *
     * @var boolean $_connected
     */
    var $_connected;

    /**
     * Pointer to another Auth_ backend that Cyrus
     * authenticates against.
     *
     * @var Auth_*  $_backend
     */
     var $_backend;

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => true,
                              'update'        => true,
                              'resetpassword' => false,
                              'remove'        => true,
                              'list'          => false,
                              'groups'        => false,
                              'transparent'   => false);

    /**
     * Constructor.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_cyrus($params = array())
    {
        $this->_params = $params;

        if (!isset($this->_params['seperator'])) {
            $this->_params['seperator'] = '.';
        }

        if (isset($this->_params['unixhier']) && $this->_params['unixhier'] == true) {
            $this->_params['seperator'] = '/';
        }

        if (!Util::extensionExists('imap')) {
            Horde::fatal(PEAR::raiseError(_("Auth_cyrus: Required imap extension not found."), __FILE__, __LINE__));
        }

        // Create backend instance.
        $this->_backend = &Auth::singleton($this->_params['backend']['driver'], $this->_params['backend']['params']);
        if (is_a($this->_backend, 'PEAR_Error')) {
            return $this->_backend;
        }

        // Check the capabilities of the backend.
        if (!$this->_backend->hasCapability('add') ||
            !$this->_backend->hasCapability('update') ||
            !$this->_backend->hasCapability('remove')) {
            Horde::fatal(PEAR::raiseError(_("Auth_cyrus: Backend does not have required capabilites."), __FILE__, __LINE__));
        }

        $this->capabilities['list'] = $this->_backend->hasCapability('list');
        $this->capabilities['groups'] = $this->_backend->hasCapability('groups');
        $this->capabilities['transparent'] = $this->_backend->hasCapability('transparent');
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId       The userId to add.
     * @param array  $credentials  The credentials to add.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        $this->_connect();

        $res = $this->_backend->addUser($userId, $credentials);
        if (is_a($res, 'PEAR_Error')) {
            return $res;
        }

        $name = imap_utf7_encode($userId);
        if (@imap_createmailbox($this->_imapStream,
                                imap_utf7_encode($this->_params['imap_dsn'] .
                                'user' . $this->_params['separator'] . $name))) {
            if (isset($this->_params['folders']) && is_array($this->_params['folders'])) {
                foreach ($this->_params['folders'] as $folder) {
                    $this->_createSubFolder($name, $folder);
                }
            }
        } else {
            Horde::logMessage('IMAP mailbox creation for ' . $name . ' failed ',
                              __FILE__, __LINE__, PEAR_LOG_ERR);
            return PEAR::raiseError(sprintf(_("IMAP mailbox creation failed: %s"), imap_last_error()));
        }

        if (isset($this->_params['quota']) && $this->_params['quota'] >= 0) {
            if (!@imap_set_quota($this->_imapStream,
                                 'user' . $this->_separator . $name,
                                 $this->_params['quota'])) {
                return PEAR::raiseError(sprintf(_("IMAP mailbox quota creation failed: %s"), imap_last_error()));
            }
        }

        return true;
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId  The userId to delete.
     *
     * @return boolean        Success or failure.
     */
    function removeUser($userId)
    {
        $this->_connect();

        $res = $this->_backend->removeUser($userId);
        if (is_a($res, 'PEAR_Error')) {
            return $res;
        }

        /* Set ACL for mailbox deletion. */
        list($admin) = explode('@', $this->_params['cyradmin']);
        @imap_setacl($this->_imapStream,
                     'user' . $this->_params['separator'] . $userId,
                     $admin, 'lrswipcda');

        /* Delete IMAP mailbox. */
        $imapresult = @imap_deletemailbox($this->_imapStream,
                                          $this->_params['imap_dsn'] .
                                          'user' . $this->_params['separator'] . $userId);

        if (!$imapresult) {
            return PEAR::raiseError(sprintf(_("IMAP mailbox deletion failed: %s"), imap_last_error()));
        }

        return true;
    }

    /**
     * Attempts to open connections to the SQL and IMAP servers.
     *
     * @access private
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function _connect()
    {
        if (!$this->_connected) {

            $this->_imapStream = @imap_open($this->_params['imap_dsn'], $this->_params['cyradmin'],
                                            $this->_params['cyrpass'], OP_HALFOPEN);

            if (!$this->_imapStream) {
                Horde::fatal(new PEAR_Error(sprintf(_("Can't connect to IMAP server: %s"),
                                                    imap_last_error())), __FILE__, __LINE__);
            }

            $this->_connected = true;
        }

        return true;
    }

    /**
     * Disconnect from the IMAP server.
     *
     * @access private
     *
     * @return boolean  True on success, false on failure.
     */
    function _disconnect()
    {
        if ($this->_connected) {
            @imap_close($this->_imapStream);
        }

        return true;
    }

    /**
     * Creates a mailboxes supplied in configuration
     *
     * @access private
     */
    function _createSubFolder($userName, $folderName)
    {
         @imap_createmailbox($this->_imapStream,
                            imap_utf7_encode($this->_params['imap_dsn'] .
                            'user' . $this->_params['separator'] . $userName .
                                     $this->_params['separator'] . $folderName));
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or false on failure/unsupported.
     */
    function listUsers()
    {
        return $this->_backend->listUsers();
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        return $this->_backend->updateUser($oldID, $newID, $credentials);
    }

    /**
     * Return the URI of the login screen for this authentication
     * method.
     *
     * @access public
     *
     * @param optional string $app  The application to use.
     * @param optional string $url  The URL to redirect to after login.
     *
     * @return string  The login screen URI.
     */
    function _getLoginScreen($app = 'horde', $url = '')
    {
        return $this->_backend->_getLoginScreen($app, $url);
    }

    /**
     * Checks if a userId exists in the sistem.
     *
     * @access public
     *
     * @return boolean  Whether or not the userId already exists.
     */
    function exists($userId)
    {
        return $this->_backend->exists($userId);
    }

    /**
     * Automatic authentication: Find out if the client matches an
     * allowed IP block.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        return $this->_backend->transparent();
    }

}

--- NEW FILE: ftp.php ---
<?php
/**
 * The Auth_ftp class provides an FTP implementation of the Horde
 * authentication system.
 *
 * Optional parameters:
 * ====================
 *   'hostspec'  --  The hostname or IP address of the FTP server.
 *                   DEFAULT: 'localhost'
 *   'port'          The server port to connect to.
 *                   DEFAULT: 21
 *
 *
 * $Horde: framework/Auth/Auth/ftp.php,v 1.23 2004/01/28 00:34:00 slusarz Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 * Copyright 1999-2004 Max Kalika <max at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Max Kalika <max at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_ftp extends Auth {

    /**
     * Constructs a new FTP authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_ftp($params = array())
    {
        if (!Util::extensionExists('ftp')) {
            Horde::fatal(PEAR::raiseError(_("Auth_ftp: Required FTP extension not found. Compile PHP with the --enable-ftp switch.")), __FILE__, __LINE__);
        }

        $default_params = array(
            'hostspec' => 'localhost',
            'port' => 21
        );
        $this->_params = array_merge($default_params, $params);
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials. For FTP,
     *                            this must contain a password entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        $ftp = @ftp_connect($this->_params['hostspec'], $this->_params['port']);

        if ($ftp && @ftp_login($ftp, $userId, $credentials['password'])) {
            @ftp_quit($ftp);
            return true;
        } else {
            @ftp_quit($ftp);
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }
    }

}

--- NEW FILE: http.php ---
<?php
/**
 * The Auth_http class transparently logs users in to Horde using
 * already present HTTP authentication headers.
 *
 * The 'encryption' parameter specifies what kind of passwords are in
 * the .htpasswd file. The supported options are 'crypt-des' (standard
 * crypted htpasswd entries) and 'aprmd5'. This information is used if
 * you want to directly authenticate users with this driver, instead
 * of relying on transparent auth.
 *
 * $Horde: framework/Auth/Auth/http.php,v 1.21 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_http extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => true);

    /**
     * Array of usernames and hashed passwords.
     * @var array $_users
     */
    var $_users = array();

    /**
     * Constructs a new HTTP authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters.
     */
    function Auth_http($params = array())
    {
        $this->_params = $params;

        // Default to DES passwords.
        if (empty($this->_params['encryption'])) {
            $this->_params['encryption'] = 'crypt-des';
        }

        if (!empty($this->_params['htpasswd_file'])) {
            $users = @file($this->_params['htpasswd_file']);
            if (is_array($users)) {
                // Enable the list users capability.
                $this->capabilities['list'] = true;

                // Put users into alphabetical order.
                sort($users);

                foreach ($users as $line) {
                    list($user, $pass) = explode(':', $line, 2);
                    $this->_users[trim($user)] = trim($pass);
                }
            }
        }
    }

    /**
     * Find out if a set of login credentials are valid. Only supports
     * htpasswd files with DES passwords right now.
     *
     * @access private
     *
     * @param string $userId       The userId to check.
     * @param array  $credentials  An array of login credentials. For IMAP,
     *                             this must contain a password entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for HTTP authentication.")), __FILE__, __LINE__);
        }

        if (empty($this->_users[$userId])) {
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }

        $hash = $this->getCryptedPassword($credentials['password'], $this->_users[$userId], $this->_params['encryption'], !empty($this->_params['show_encryption']));
        if ($hash == $this->_users[$userId]) {
            return true;
        } else {
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }
    }

    /**
     * Return the URI of the login screen for this authentication
     * object.
     *
     * @access private
     *
     * @param optional string $app  The application to use.
     * @param optional string $url  The URL to redirect to after login.
     *
     * @return string  The login screen URI.
     */
    function _getLoginScreen($app = 'horde', $url = '')
    {
        if (!empty($this->_params['loginScreen'])) {
            if ($url) {
                return Util::addParameter($this->_params['loginScreen'], 'url', $url);
            } else {
                return $this->_params['loginScreen'];
            }
        } else {
            return parent::_getLoginScreen($app, $url);
        }
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        return array_keys($this->_users);
    }

    /**
     * Automatic authentication: Find out if the client has HTTP
     * authentication info present.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        if (!empty($_SERVER['PHP_AUTH_USER']) &&
            !empty($_SERVER['PHP_AUTH_PW'])) {
            $this->setAuth($_SERVER['PHP_AUTH_USER'],
                           array('password' => $_SERVER['PHP_AUTH_PW'],
                                 'transparent' => 1));
            return true;
        }

        $this->_setAuthError(AUTH_REASON_MESSAGE, _("HTTP Authentication not found."));
        return false;
    }

}

--- NEW FILE: imap.php ---
<?php
/**
 * The Auth_imap:: class provides an IMAP implementation of the Horde
 * authentication system.
 *
 * Optional parameters:
 * ====================
 *  'folder'         --  The initial folder / mailbox to open.
 *                       Should be null for 'imap' and 'nntp' protocols.
 *                       DEFAULT: null
 *  'hostspec'       --  The hostname or IP address of the server.
 *                       DEFAULT: 'localhost'
 *  'port'           --  The server port to which we will connect.
 *                       IMAP is generally 143, while IMAP-SSL is generally 993.
 *                       DEFAULT: 143
 *  'protocol'       --  The connection protocol (e.g. 'imap', 'pop3', 'nntp').
 *                       Protocol is one of 'imap/notls' (or only 'imap' if you
 *                       have a c-client version 2000c or older), 'imap/ssl',
 *                       or 'imap/ssl/novalidate-cert' (for a self-signed
 *                       certificate).
 *                       DEFAULT: 'imap'
 *  'admin_user'     --  The name of a user with admin privileges.
 *                       DEFAULT: null
 *  'admin_password' --  The password of the adminstrator.
 *                       DEFAULT: null
 *  'userhierarchy'  --  The hierarchy where user mailboxes are stored.
 *                       DEFAULT: 'user.'
 *  'dsn'            --  The full IMAP connection string.
 *                       If not present, this is built from 'hostspec', 'port'
 *                       and 'protocol' parameters.
 *
 *
 * If setting up as Horde auth handler in conf.php, this is a sample entry:
 *   $conf['auth']['params']['folder'] = 'INBOX';
 *   $conf['auth']['params']['hostspec'] = 'imap.example.com';
 *   $conf['auth']['params']['port'] = 143;
 *   $conf['auth']['params']['protocol'] = 'imap/notls/novalidate-cert';
 *
 * 
 * $Horde: framework/Auth/Auth/imap.php,v 1.26 2004/04/13 23:16:47 jan Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 * Copyright 1999-2004 Gaudenz Steinlin <gaudenz.steinlin at id.unibe.ch>
 * Copyright 2004 Jan Schneider <jan at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Gaudenz Steinlin <gaudenz.steinlin at id.unibe.ch>
 * @author  Jan Schneider <jan at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_imap extends Auth {

    /**
     * Constructs a new IMAP authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_imap($params = array())
    {
        if (!Util::extensionExists('imap')) {
            Horde::fatal(PEAR::raiseError(_("Auth_imap: Required IMAP extension not found."), __FILE__, __LINE__));
        }

        $default_params = array(
            'folder' => null,
            'hostspec' => 'localhost',
            'port' => '143',
            'protocol' => 'imap',
            'userhierarchy' => 'user.'
        );
        $this->_params = array_merge($default_params, $params);

        if (!empty($this->_params['admin_user'])) {
            $this->capabilities['add'] = true;
            $this->capabilities['remove'] = true;
            $this->capabilities['list'] = true;
        }

        /* Create DSN string. */
        if (!isset($this->_params['dsn'])) {
            $this->_params['dsn'] = sprintf('{%s:%d/%s}', 
                                            $this->_params['hostspec'],
                                            $this->_params['port'],
                                            $this->_params['protocol']);
            if (!empty($this->_params['folder'])) {
                $this->_params['dsn'] .= $this->_params['folder'];
            }
        }
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials. For IMAP,
     *                            this must contain a password entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for IMAP authentication.")), __FILE__, __LINE__);
        }

        $imap = @imap_open($this->_params['dsn'], $userId,
                           $credentials['password'], OP_HALFOPEN);

        if ($imap) {
            @imap_close($imap);
            return true;
        } else {
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @param string $userId       The userId to add.
     * @param array  $credentials  The credentials to use.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        require_once 'Horde/IMAP/Admin.php';
        $imap = &new IMAP_Admin($this->_params);
        return $imap->addMailbox(String::convertCharset($userId, NLS::getCharset(), 'utf7-imap'));
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @param string $userId  The userId to delete.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function removeUser($userId)
    {
        require_once 'Horde/IMAP/Admin.php';
        $imap = &new IMAP_Admin($this->_params);
        return $imap->removeMailbox(String::convertCharset($userId, NLS::getCharset(), 'utf7-imap'));
    }

    /**
     * List all users in the system.
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        require_once 'Horde/IMAP/Admin.php';
        $imap = &new IMAP_Admin($this->_params);
        return $imap->listMailboxes();
    }

}

--- NEW FILE: imsp.php ---
<?php
/**
 * The Auth_imsp class provides basic authentication against an IMSP server.
 * This will be most benificial if already using an IMSP based prefernece system or
 * IMSP based addressbook system
 *
 * $Horde: framework/Auth/Auth/imsp.php,v 1.2 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2004 Michael Rubinsky <mike at theupstairsroom.com>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Rubinsky <mike at theupstairsroom.com>
 * @version $Revision: 1.1 $
 * @package Horde_Auth
 */
class Auth_imsp extends Auth{

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);
    /**
     * Constructor function. Creates new Auth_imsp object.
     *
     * @access public
     * @param optional array $params A hash containing parameters.
     */
    function Auth_imsp($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Private authentication function.
     *
     * @access private
     * @param string $userID Username for IMSP server.
     * @param array $credentials Hash containing 'password' element.
     * @return boolean True on success / False on failure.
     */
    function _authenticate($userID, $credentials)
    {
        require_once 'Net/IMSP/Auth.php';

        $params = array();
        $params['username'] = $userID;
        $params['password'] = $credentials['password'];
        $params['server'] = $this->_params['server'];
        $params['port'] = $this->_params['port'];
        $imsp = &Net_IMSP_Auth::singleton($this->_params['auth_method']);
        if (is_a($imsp, 'PEAR_Error')){
            return $imsp;
        }

        $result = $imsp->authenticate($params, false);
        if (is_a($result, 'PEAR_Error')){
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        } else {
            return true;
        }
    }

    /**
     * Checks the params array and sets default values.
     *
     * @access private
     * @param array $params Hash containing IMSP parameters.
     */
    function _setParams($params)
    {
        if (empty($params['auth_method'])){
            $params['auth_method'] = 'plaintext';
        }

        $this->_params = $params;
    }

}

--- NEW FILE: ipbasic.php ---
<?php
/**
 * The Auth_ipbasic class provides access control based on CIDR masks
 * (client IP addresses). It is not meant for user-based systems, but
 * for times when you want a block of IPs to be able to access a site,
 * and that access is simply on/off - no preferences, etc. If you need
 * more sophisticated IP-based authentication, you should look at the
 * Auth_ipmap class which lets you map IP blocks to specific
 * usernames.
 *
 * Parameters:
 *   'blocks'     An array of CIDR masks which are allowed access.
 *
 * $Horde: framework/Auth/Auth/ipbasic.php,v 1.20 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_ipbasic extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => true);

    /**
     * Constructs a new Basic IP authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters.
     */
    function Auth_ipbasic($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set parameters for the Auth_ipbasic object.
     *
     * @access private
     *
     * @param array $params  Should contain 'blocks', an array of CIDR masks.
     */
    function _setParams($params)
    {
        if (empty($params['blocks'])) {
            $params['blocks'] = array();
        } elseif (!is_array($params['blocks'])) {
            $params['blocks'] = array($params['blocks']);
        }

        $this->_params = $params;
    }

    /**
     * Automatic authentication: Find out if the client matches an
     * allowed IP block.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        if (!isset($_SERVER['REMOTE_ADDR'])) {
            $this->_setAuthError(AUTH_REASON_MESSAGE, _("IP Address not available."));
            return false;
        }

        $client = $_SERVER['REMOTE_ADDR'];
        foreach ($this->_params['blocks'] as $cidr) {
            if ($this->_addressWithinCIDR($client, $cidr)) {
                $this->setAuth($cidr, array('transparent' => 1));
                return true;
            }
        }

        $this->_setAuthError(AUTH_REASON_MESSAGE, _("IP Address not within allowed CIDR block."));
        return false;
    }

    /**
     * Determine if an IP address is within a CIDR block.
     *
     * @access private
     *
     * @param string $address  The IP address to check.
     * @param string $cidr     The block (e.g. 192.168.0.0/16) to test against.
     *
     * @return boolean  Whether or not the address matches the mask.
     */
    function _addressWithinCIDR($address, $cidr)
    {
        $address = ip2long($address);
        list($quad, $bits) = explode('/', $cidr);
        $bits = intval($bits);
        $quad = ip2long($quad);

        return (($address >> (32 - $bits)) == ($quad >> (32 - $bits)));
    }

}

--- NEW FILE: ipmap.php ---
<?php

require_once dirname(__FILE__) . '/ipbasic.php';

/**
 * The Auth_ipmap class provides access control based on CIDR masks.
 *
 * Parameters:
 *   NONE
 *
 * $Horde: framework/Auth/Auth/ipmap.php,v 1.13 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_ipmap extends Auth_ipbasic {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => true);

    /**
     * Constructs a new IP-mapping authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters.
     */
    function Auth_ipmap($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set parameters for the Auth_ipbasic object.
     *
     * @access private
     *
     * @param array $params  A hash containing parameter information.
     */
    function _setParams($params)
    {
    }

}

--- NEW FILE: krb5.php ---
<?php
/**
 * The Auth_krb5 class provides an kerberos implementation of the Horde
 * authentication system.
 *
 * This driver requires the 'krb5' PHP extension to be loaded.
 * The module can be downloaded here:
 *   http://www.horde.org/download/php/phpkrb5.tar.gz
 *
 * Required parameters:
 * ====================
 *   NONE. Instead, Kerberos must be correctly configured on your system (e.g.
 *   /etc/krb5.conf) for this class to work correctly.
 *
 *
 * $Horde: framework/Auth/Auth/krb5.php,v 1.21 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 2.2
 * @package Horde_Auth
 */
class Auth_krb5 extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new Kerberos authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_krb5($params = array())
    {
        if (!Util::extensionExists('krb5')) {
            Horde::fatal(PEAR::raiseError(_("Auth_krb5: Required krb5 extension not found."), __FILE__, __LINE__));
        }

        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials.
     *                            For kerberos, this must contain a password
     *                            entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for Kerberos authentication.")), __FILE__, __LINE__);
        }

        $result = krb5_login($userId, $credentials['password']);

        if ($result === KRB5_OK) {
            return true;
        } else {
            if ($result === KRB5_BAD_PASSWORD) {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Bad kerberos password."));
            } elseif ($result === KRB5_BAD_USER) {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Bad kerberos username."));
            } else {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Kerberos server rejected authentication."));
            }
            return false;
        }
    }

}

--- NEW FILE: ldap.php ---
<?php
/**
 * The Auth_ldap class provides an LDAP implementation of the Horde
 * authentication system.
 *
 * Required parameters:
 * ====================
 *   'basedn'       --  The base DN for the LDAP server.
 *   'hostspec'     --  The hostname of the LDAP server.
 *   'uid'          --  The username search key.
 *   'filter'       --  The LDAP formatted search filter to search for users.
 *                      This setting overrides the 'objectclass' method below.
 *   'objectclass'  --  The objectclass filter used to search for users. Can
 *                      be a single objectclass or an array.
 *
 * Optional parameters:
 * ====================
 *   'binddn'       --  The DN used to bind to the LDAP server
 *   'password'     --  The password used to bind to the LDAP server
 *   'version'      --  The version of the LDAP protocol to use.
 *                      DEFAULT: NONE (system default will be used)
 *
 *
 * $Horde: framework/Auth/Auth/ldap.php,v 1.45 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Jon Parise <jon at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jon Parise <jon at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_ldap extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => true,
                              'update'        => true,
                              'resetpassword' => false,
                              'remove'        => true,
                              'list'          => true,
                              'transparent'   => false);

    /**
     * Constructs a new LDAP authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_ldap($params = array())
    {
        if (!Util::extensionExists('ldap')) {
            Horde::fatal(PEAR::raiseError(_("Auth_ldap: Required LDAP extension not found."), __FILE__, __LINE__));
        }

        $this->_setParams($params);
    }

    /**
     * Set configuration parameters
     *
     * @access private
     *
     * @param array $params  A hash containing connection parameters.
     */
    function _setParams($params)
    {
        /* Ensure we've been provided with all of the necessary parameters. */
        Horde::assertDriverConfig($params, 'auth',
            array('hostspec', 'basedn', 'uid'),
            'authentication LDAP');

        $this->_params = $params;
    }

    /**
     * Find out if the given set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId       The userId to check.
     * @param array  $credentials  An array of login credentials.
     *
     * @return boolean  True on success or a PEAR_Error object on failure.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for LDAP authentication.")), __FILE__, __LINE__);
        }

        /* Connect to the LDAP server. */
        $ldap = @ldap_connect($this->_params['hostspec']);
        if (!$ldap) {
            $this->_setAuthError(AUTH_REASON_MESSAGE, _("Failed to connect to LDAP server."));
            return false;
        }

        $this->_setProtocolVersion($ldap);

        if (isset($this->_params['binddn'])) {
            $binddn = $this->_params['binddn'];
            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
            if (!$bind) {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Could not bind to LDAP server."));
                return false;
            }
        }

        /* Search for the user's full DN. */
        $search = @ldap_search($ldap, $this->_params['basedn'],
                               $this->_params['uid'] . '=' . $userId,
                               array($this->_params['uid']));
        if (!$search) {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Could not search the LDAP server."));
                return false;
        }

        $result = @ldap_get_entries($ldap, $search);
        if (is_array($result) && (count($result) > 1)) {
            $dn = $result[0]['dn'];
        } else {
            $this->_setAuthError(AUTH_REASON_MESSAGE, _("Empty result."));
            return false;
        }

        /* Attempt to bind to the LDAP server as the user. */
        $bind = @ldap_bind($ldap, $dn, $credentials['password']);
        if ($bind != false) {
            @ldap_close($ldap);
            return true;
        } else {
            @ldap_close($ldap);
            $this->_setAuthError(AUTH_REASON_FAILED);
            return false;
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId      The userId to add.
     * @param array $credentials  The credentials to be set.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        $ldap = @ldap_connect($this->_params['hostspec']);
        $this->_setProtocolVersion($ldap);

        if (isset($this->_params['binddn'])) {
            $binddn = $this->_params['binddn'];
            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
        } else {
            $bind = @ldap_bind($ldap);
        }

        global $conf;
        if (!empty($conf['hooks']['authldap'])) {
            @include HORDE_BASE . '/config/hooks.php';
            if (function_exists('_horde_hook_authldap')) {
                $entry = call_user_func('_horde_hook_authldap', $userId, $credentials);
                $dn = $entry['dn'];
                // remove the dn entry from the array
                unset($entry['dn']);
            }
        } else {
            // Try this simple default and hope it works.
            $dn = $this->_params['uid'] . '=' . $userId . ',' . $this->_params['basedn'];
            $entry['cn'] = $userId;
            $entry['sn'] = $userId;
            // password not encrypted?
            $entry['userpassword'] = $credentials['password'];
        }
        $success = @ldap_add($ldap, $dn, $entry);

        if (!$success) {
           return PEAR::raiseError(sprintf(_("Auth_ldap: Unable to add user %s. This is what the server said: "), $userId) . ldap_error($ldap), __FILE__, __LINE__);
        }
        return true;
    }

    /**
     * Remove a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId      The userId to add.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function removeUser($userId)
    {
        $ldap = @ldap_connect($this->_params['hostspec']);
        $this->_setProtocolVersion($ldap);

        if (isset($this->_params['binddn'])) {
            $binddn = $this->_params['binddn'];
            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
        } else {
            $bind = @ldap_bind($ldap);
        }

        global $conf;
        if (!empty($conf['hooks']['authldap'])) {
            @include HORDE_BASE . '/config/hooks.php';
            if (function_exists('_horde_hook_authldap')) {
                $entry = call_user_func('_horde_hook_authldap', $userId);
                $dn = $entry['dn'];
            }
        } else {
            // Try this simple default and hope it works
            $dn = $this->_params['uid'] . '=' . $userId . ',' . $this->_params['basedn'];
        }

        $success = @ldap_delete($ldap,$dn);
        if (!$success) {
           return PEAR::raiseError(sprintf(_("Auth_ldap: Unable to remove user %s"), $userId), __FILE__, __LINE__);
        }
        return true;
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID       The old userId.
     * @param string $newID       The new userId.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        $ldap = @ldap_connect($this->_params['hostspec']);
        $this->_setProtocolVersion($ldap);

        if (isset($this->_params['binddn'])) {
            $binddn = $this->_params['binddn'];
            $bind = @ldap_bind($ldap, $binddn, $this->_params['password']);
        } else {
            $bind = @ldap_bind($ldap);
        }

        global $conf;
        if (!empty($conf['hooks']['authldap'])) {
            @include HORDE_BASE . '/config/hooks.php';
            if (function_exists('_horde_hook_authldap')) {
                $entry = call_user_func('_horde_hook_authldap', $oldID);
                $olddn = $entry['dn'];
                $entry = call_user_func('_horde_hook_authldap', $newID);
                $newdn = $entry['dn'];
                unset($entry['dn']);
            }
        } else {
            // Try this simple default and hope it works
            $newdn = $this->_params['uid'] . '=' . $newID . ',' . $this->_params['basedn'];
            $olddn = $this->_params['uid'] . '=' . $oldID . ',' . $this->_params['basedn'];
            $entry['userpassword'] = $credentials['user_pass_2'];
        }
        if ($oldID != $newID) {
            if (LDAP_OPT_PROTOCOL_VERSION == 3) {
                ldap_rename($ldap, $olddn, $newdn, $this->_params['basedn'], true);

                $success = ldap_modify($ldap, $newdn, $entry);
            } else {
                $success = $this->addUser($newID, $entry);
                if ($success) {
                    $success = $this->removeUser($oldID);
                }
            }
        } else {
            $success = ldap_modify($ldap, $newdn, $entry);
        }

        if (!$success) {
            return PEAR::raiseError(sprintf(_("Auth_ldap: Unable to update user %s"), $newID), __FILE__, __LINE__);
        }
        return true;
    }

    /**
     * List Users
     *
     * @access public
     *
     * @return array  List of Users
     */
    function listUsers()
    {
        $ldap = @ldap_connect($this->_params['hostspec']);
        $this->_setProtocolVersion($ldap);

        if (isset($this->_params['binddn'])) {
            $dn = $this->_params['binddn'];
            $bind = @ldap_bind($ldap, $dn, $this->_params['password']);
        } else {
            $bind = @ldap_bind($ldap);
        }
        if (!empty($this->_params['filter'])) {
            $filter = $this->_params['filter'];
        } elseif (!is_array($this->_params['objectclass'])) {
            $filter = 'objectclass=' . $this->_params['objectclass'];
        } else {
            $filter = '';
            foreach ($this->_params['objectclass'] as $objectclass) {
                $filter = '(&' . $filter;
                $filter .= '(objectclass=' . $objectclass . '))';
            }
        }

        $search = ldap_search($ldap, $this->_params['basedn'], $filter);
        $entries = ldap_get_entries($ldap, $search);
        $userlist = array();
        for ($i = 0; $i < $entries['count']; $i++) {
            $userlist[$i] = $entries[$i][$this->_params['uid']][0];
        }

        return $userlist;
    }

    /**
     * Set the LDAP protocol version according to the driver
     * parameters.
     *
     * @param resource &$conn  The LDAP connection to modify.
     */
    function _setProtocolVersion(&$conn)
    {
        /* Set the LDAP protocol version. */
        if (isset($this->_params['version'])) {
            if (!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
                                 $this->_params['version'])) {
                Horde::logMessage(
                    sprintf('Set LDAP protocol version to %d failed: [%d] %s',
                            $this->_params['version'],
                            ldap_errno($conn),
                            ldap_error($conn),
                            __FILE__, __LINE__));
            }
        }
    }

}

--- NEW FILE: login.php ---
<?php
/**
 * The Auth_login:: class provides a system login implementation of
 * the Horde authentication system.
 * This Auth driver is useful if you have a shadow password system
 * where the Auth_passwd driver doesn't work.
 *
 * Optional parameters:
 * ====================
 *   'location'  --  Location of the su binary
 *                   DEFAULT: /bin/su
 *
 *
 * $Horde: framework/Auth/Auth/login.php,v 1.3 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2004 Jan Schneider <jan at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jan Schneider <jan at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_login extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * su binary.
     *
     * @var string $_location
     */
    var $_location = '/bin/su';

    /**
     * List of users that should be excluded from being listed/handled
     * in any way by this driver.
     *
     * @var array $_exclude
     */
    var $_exclude = array('root', 'daemon', 'bin', 'sys', 'sync', 'games',
                          'man', 'lp', 'mail', 'news', 'uucp', 'proxy',
                          'postgres', 'www-data', 'backup', 'operator',
                          'list', 'irc', 'gnats', 'nobody', 'identd',
                          'sshd', 'gdm', 'postfix', 'mysql', 'cyrus', 'ftp');

    /**
     * Constructs a new Login authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_login($params = array())
    {
        $this->_params = $params;
        if (!empty($params['location'])) {
            $this->_location = $params['location'];
        }
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for Login authentication.")), __FILE__, __LINE__);
        }

        $proc = popen($this->_location . ' ' . $userId, 'w');
        if (!is_resource($proc)) {
            return false;
        }
        fwrite($proc, $credentials['password']);
        fwrite($proc, 'exit');
 
        return pclose($proc) === 0;
    }

}

--- NEW FILE: mcal.php ---
<?php
/**
 * The Auth_mcal class provides an MCAL implementation of the Horde
 * authentication system.
 *
 * MCAL Home Page: http://mcal.chek.com/
 *
 * Required parameters:
 * ====================
 *   'calendar'  --  The MCAL calendar name.
 *
 *
 * $Horde: framework/Auth/Auth/mcal.php,v 1.27 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_mcal extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => true,
                              'transparent'   => false);

    /**
     * Constructs a new MCAL authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_mcal($params = array())
    {
        if (!Util::extensionExists('mcal')) {
            Horde::fatal(PEAR::raiseError(_("Auth_mcal: Required MCAL extension not found."), __FILE__, __LINE__));
        }

        if (empty($params['calendar'])) {
            Horde::fatal(PEAR::raiseError(_("No calendar name provided for MCAL authentication.")), __FILE__, __LINE__);
        }

        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials. For MCAL,
     *                            this must contain a password entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        $mcal = @mcal_open($this->_params['calendar'], $userId, $credentials['password']);

        if ($mcal) {
            @mcal_close($mcal);
            return true;
        } else {
            @mcal_close($mcal);
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        $lines = @file('/etc/mpasswd');
        if (!$lines || !is_array($lines)) {
            return PEAR::raiseError('Unable to list users.');
        }

        $users = array();
        foreach ($lines as $line) {
            $users[] = substr($line, 0, strpos($line, ':'));
        }

        return $users;
    }

}

--- NEW FILE: pam.php ---
<?php
/**
 * The Auth_pam:: class provides a PAM-based implementation of the Horde
 * authentication system.
 * 
 * PAM (Pluggable Authentication Modules) is a flexible mechanism for
 * authenticating users.  It has become the standard authentication system for
 * Linux, Solaris and FreeBSD.
 *
 * This implementation requires Chad Cunningham's pam_auth extension:
 *
 *      http://www.math.ohio-state.edu/~ccunning/pam_auth/
 *
 * Optional parameters:
 * ====================
 *   'service'  --  The name of the PAM service to use when authenticating.
 *                  DEFAULT: php
 *
 *
 * $Horde: framework/Auth/Auth/pam.php,v 1.3 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2004 Jon Parise <jon at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jan Parise <jon at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_pam extends Auth
{
    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new PAM authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_pam($params = array())
    {
        $this->_params = $params;
        if (!empty($params['service'])) {
            ini_set('pam_auth.servicename', $params['service']);
        }

        if (!extension_loaded('pam_auth')) {
            dl('pam_auth.so');
        }
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for Login authentication.")), __FILE__, __LINE__);
        }

        if (!extension_loaded('pam_auth')) {
            Horde::fatal(PEAR::raiseError(_("PAM authentication is not available.")), __FILE__, __LINE__);
        }

        if (!pam_auth($userId, $credentials['password'], &$error)) {
            $this->_setAuthError(AUTH_REASON_MESSAGE, $error);
            return false;
        }

        return true;
    }
}

--- NEW FILE: passwd.php ---
<?php
/**
 * The Auth_passwd:: class provides a passwd-file implementation of
 * the Horde authentication system.
 *
 * Optional parameters:
 * ====================
 *   'filename'  --  The passwd file to use.
 *                   DEFAULT: /etc/passwd
 *   'lock'      --  Should we lock the passwd file? (boolean)
 *                   DEFAULT: false
 *
 *
 * $Horde: framework/Auth/Auth/passwd.php,v 1.16 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1997-2004 Rasmus Lerdorf <rasmus at php.net>
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Rasmus Lerdorf <rasmus at php.net>
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_passwd extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => true,
                              'transparent'   => false);

    /**
     * Passwd file.
     *
     * @var string $_filename
     */
    var $_filename = '/etc/passwd';

    /**
     * Hash list of users.
     *
     * @var array $_users
     */
    var $_users;

    /**
     * Filehandle for lockfile.
     *
     * @var integer $_fplock
     */
    var $_fplock;

    /**
     * Locking state.
     *
     * @var boolean $_locked
     */
    var $_locked;

    /**
     * List of users that should be excluded from being listed/handled
     * in any way by this driver.
     *
     * @var array $_exclude
     */
    var $_exclude = array('root', 'daemon', 'bin', 'sys', 'sync', 'games',
                          'man', 'lp', 'mail', 'news', 'uucp', 'proxy',
                          'postgres', 'www-data', 'backup', 'operator',
                          'list', 'irc', 'gnats', 'nobody', 'identd',
                          'sshd', 'gdm', 'postfix', 'mysql', 'cyrus', 'ftp');

    /**
     * Constructs a new Passwd authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_passwd($params = array())
    {
        $this->_params = $params;
        if (!empty($params['filename'])) {
            $this->_filename = $params['filename'];
        }
        
        $this->_fplock = fopen(Horde::getTempDir() . '/passwd.lock', 'w');
        if (!empty($params['lock'])) {
            flock($this->_fplock, LOCK_EX);
            $this->_locked = true;
        }

        $fp = fopen($this->_filename, 'r');
        if (!$fp) {
            return PEAR::raiseError("Couldn't open '" . $this->_filename . "'.");
        }
        while (!feof($fp)) {
            $line = fgets($fp, 128);
            if (!empty($line)) {
                list($user, $pass, $uid, $gid, $info, $home, $shell) = explode(':', $line);
                if (strlen($user) &&
                    !in_array($user, $this->_exclude)) {
                    $this->_users[$user]['password'] = $pass;
                    $this->_users[$user]['uid'] = $uid;
                    $this->_users[$user]['gid'] = $gid;
                    $this->_users[$user]['info'] = $info;
                    $this->_users[$user]['home'] = $home;
                    $this->_users[$user]['shell'] = $shell;
                }
            }
        }
        fclose($fp);
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials. For MCAL,
     *                            this must contain a password entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for PASSWD authentication.")), __FILE__, __LINE__);
        }
        if (isset($this->_users[$userId])) {
            if ($this->_users[$userId]['password'] == crypt($credentials['password'], substr($this->_users[$userId]['password'], 0, 2))) return true;
        }
        return false;
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        return array_keys($this->_users);
    }

    /**
     * Adds a user.
     *
     * @access public
     *
     * @param string $user          New user ID.
     * @param string $pass          Password for new user.
     * @param optional string $cvs  Cvs user id (needed for pserver passwd
     *                              files).
     *
     * @return boolean  Returns true or PEAR_Error if the user already
     *                  exists.
     */
    function addUser($user, $pass, $cvsuser = '')
    {
        if (!isset($this->_users[$user]) && $this->_locked) {
            $this->_users[$user] = crypt($pass);
            $this->_cvs[$user] = $cvsuser;
            return true;
        } else {
            return PEAR::raiseError("Couldn't add user '$user', because the user already exists.");
        }
    }

    /**
     * Modifies a user.
     *
     * @access public
     *
     * @param string $user          User ID.
     * @param string $pass          Password for new user.
     * @param optional string $cvs  Cvs user id (needed for pserver passwd
     *                              files).
     *
     * @return boolean  Returns true or PEAR_Error if the user doesn't
     *                  exists.
     */
    function modUser($user, $pass, $cvsuser = '')
    {
        if (isset($this->_users[$user]) && $this->_locked) {
            $this->_users[$user] = crypt($pass);
            $this->_cvs[$user] = $cvsuser;
            return true;
        } else {
            return PEAR::raiseError("Couldn't modify user '$user', because the user doesn't exist.");
        }
    }

    /**
     * Deletes a user.
     *
     * @access public
     *
     * @param string $user  User ID.
     *
     * @return boolean  Returs true or PEAR_Error if the user doesn't
     *                  exist.
     */
    function delUser($user)
    {
        if (isset($this->_users[$user]) && $this->_locked) {
            unset($this->_users[$user]);
            unset($this->_cvs[$user]);
        } else {
            return PEAR::raiseError("Couldn't delete user '$user', because the user doesn't exist.");
        }
    }

    /**
     * Writes changes to passwd file and unlocks it.
     *
     * @access public           
     */
    function close()
    {
        if ($this->_locked) {
            foreach($this->_users as $user => $pass) {
                if ($this->_users[$user]) {
                    fputs($this->_fplock, "$user:$pass:" . $this->_users[$user] . "\n");
                } else {
                    fputs($this->_fplock, "$user:$pass\n");
                }
            }
            rename($this->_lockfile, $this->_filename);
            flock($this->_fplock, LOCK_UN);
            $this->_locked = false;
            fclose($this->_fplock);
        }
    }

}

--- NEW FILE: radius.php ---
<?php
/**
 * The Auth_radius class provides a RADIUS implementation of the Horde
 * authentication system.
 *
 * This class requires the 'radius' PECL extension.
 * RADIUS PECL extension: http://pecl.php.net/package/radius
 *
 * On *nix-y machines, this extension can be installed as follows:
 *   "pear install radius"
 *
 * Then, edit your php.ini file and make sure the following line is present:
 *   For Windows machines:  extension=php_radius.dll
 *   For all others:        extension=radius.so
 *
 * Required parameters:
 * ====================
 *   'host'    --  The RADIUS host to use (IP address or fully qualified
 *                 hostname).
 *   'method'  --  The RADIUS method to use for validating the request.
 *                 Either: 'PAP', 'CHAP_MD5', 'MSCHAPv1', or 'MSCHAPv2'.
 *                 ** CURRENTLY, only 'PAP' is supported. **
 *   'secret'  --  The RADIUS shared secret string for the host.
 *                 The RADIUS protocol ignores all but the leading 128 bytes
 *                 of the shared secret.
 *
 * Optional parameters:
 * ====================
 *   'nas'      --  The RADIUS NAS identifier to use.
 *                  DEFAULT: The value of $_SERVER['HTTP_HOST'] or, if not
 *                           defined, then 'localhost'.
 *   'port'     --  The port to use on the RADIUS server.
 *                  DEFAULT: Whatever the local system identifies as the
 *                           'radius' UDP port
 *   'retries'  --  The maximum number of repeated requests to make before
 *                  giving up.
 *                  DEFAULT: 3
 *   'suffix'   --  The domain name to add to unqualified user names.
 *                  DEFAULT: NONE
 *   'timeout'  --  The timeout for receiving replies from the server (in
 *                  seconds).
 *                  DEFAULT: 3 seconds
 *
 *
 * $Horde: framework/Auth/Auth/radius.php,v 1.23 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_radius extends Auth {

    /** 
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities 
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new RADIUS authentication object.
     *
     * @access public
     *
     * @param array optional $params  A hash containing connection parameters.
     */
    function Auth_radius($params = array())
    {
        if (!Util::extensionExists('radius')) {
            Horde::fatal(PEAR::raiseError('Auth_radius requires the radius PECL extension to be loaded.'), __FILE__, __LINE__);
        }

        $this->_setParams($params);
    }

    /**
     * Set parameters.
     *
     * @access private
     *
     * @param array $params  The parameter hash.
     */
    function _setParams($params)
    {
        /* A RADIUS host is required. */
        if (empty($params['host'])) {
            Horde::fatal(PEAR::raiseError('Auth_radius requires a RADIUS host to connect to.'), __FILE__, __LINE__);
        }

        /* A RADIUS secret string is required. */
        if (empty($params['secret'])) {
            Horde::fatal(PEAR::raiseError('Auth_radius requires a RADIUS secret string.'), __FILE__, __LINE__);
        }

        /* A RADIUS authentication method is required. */
        if (empty($params['method'])) {
            Horde::fatal(PEAR::raiseError('Auth_radius requires a RADIUS authentication method.'), __FILE__, __LINE__);
        }

        /* RADIUS NAS Identifier. */
        if (empty($params['nas'])) {
            $params['nas'] = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'
] : 'localhost';
        }

        /* Suffix to add to unqualified user names. */
        if (empty($params['suffix'])) {
            $params['suffix'] = '';
        }

        /* The RADIUS port to use. */
        if (empty($params['port'])) {
            $params['port'] = 0;
        }

        /* Maximum number of retries. */
        if (empty($params['retries'])) {
            $params['retries'] = 3;
        }

        /* RADIUS timeout. */
        if (empty($params['timeout'])) {
            $params['timeout'] = 3;
        }

        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $username    The userId to check.
     * @param array $credentials  An array of login credentials.
     *                            For radius, this must contain a password
     *                            entry.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($username, $credentials)
    {
        /* Password is required. */
        if (!isset($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("Password required for RADIUS authentication.")), __FILE__, __LINE__);
        }

        $res = radius_auth_open();
        radius_add_server($res, $this->_params['host'], $this->_params['port'], $this->_params['secret'], $this->_params['timeout'], $this->_params['retries']);
        radius_create_request($res, RADIUS_ACCESS_REQUEST);
        radius_put_attr($res, RADIUS_NAS_IDENTIFIER, $this->_params['nas']);
        radius_put_attr($res, RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL);
        radius_put_attr($res, RADIUS_SERVICE_TYPE, RADIUS_FRAMED);
        radius_put_attr($res, RADIUS_FRAMED_PROTOCOL, RADIUS_PPP);
        radius_put_attr($res, RADIUS_CALLING_STATION_ID, isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : '127.0.0.1');

        /* Insert username/password into request. */
        radius_put_attr($res, RADIUS_USER_NAME, $username);
        radius_put_attr($res, RADIUS_USER_PASSWORD, $credentials['password']);

        /* Send request. */
        $success = radius_send_request($res);

        switch ($success) {
        case RADIUS_ACCESS_ACCEPT:
            return true;

        case RADIUS_ACCESS_REJECT:
            $this->_setAuthError(AUTH_REASON_MESSAGE, _("Authentication rejected by RADIUS server."));
            return false;

        default:
            $this->_setAuthError(AUTH_REASON_MESSAGE, radius_strerror($success));
            return false;
        }
    }

}

--- NEW FILE: sasl.php ---
<?php
/**
 * The Auth_sasl:: class provides a SASL-based implementation of the
 * Horde authentication system.
 *
 * SASL is the Simple Authentication and Security Layer (as defined by
 * RFC 2222). It provides a system for adding plugable authenticating
 * support to connection-based protocols.
 *
 * This driver relies on the PECL sasl package:
 *
 *      http://pecl.php.net/package/sasl
 *
 * Optional parameters:
 * ====================
 *   'app'      --  The name of the authenticating application.
 *                  DEFAULT: horde
 *
 *   'service'  --  The name of the SASL service to use when authenticating.
 *                  DEFAULT: php
 *
 *
 * $Horde: framework/Auth/Auth/sasl.php,v 1.4 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 2004 Jon Parise <jon at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jan Parise <jon at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_sasl extends Auth
{
    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new SASL authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_sasl($params = array())
    {
        $this->_params = $params;

        if (!extension_loaded('sasl')) {
            dl('sasl.so');
        }

        $app = (!empty($params['app'])) ? $params['app'] : 'horde';
        sasl_server_init($app);
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for Login authentication.")), __FILE__, __LINE__);
        }

        if (!extension_loaded('sasl')) {
            Horde::fatal(PEAR::raiseError(_("SASL authentication is not available.")), __FILE__, __LINE__);
        }

        $service = (!empty($params['service'])) ? $params['service'] : 'php';
        $conn = sasl_server_new($service);
        if (!is_resource($conn)) {
            Horde::fatal(PEAR::raiseError(_("Failed to create new SASL connection.")), __FILE__, __LINE__);
        }

        if (!sasl_checkpass($conn, $userId, $credentials['password'])) {
            $this->_setAuthError(AUTH_REASON_MESSAGE, sasl_errdetail($conn));
            return false;
        }

        return true;
    }

}

--- NEW FILE: smb.php ---
<?php
/**
 * The Auth_smb class provides an SMB implementation of the Horde
 * authentication system.
 *
 * This module requires the smbauth extension for PHP:
 *   http://www.tekrat.com/smbauth.php
 *
 * At the time of this writing, the extension, and thus this module, only
 * supported authentication against a domain, and pdc and bdc must be
 * non-null and not equal to each other. In other words, to use this module
 * you must have a domain with at least one PDC and one BDC.
 *
 * Required parameters:
 * ====================
 *   'hostspec'  IP, DNS Name, or NetBios Name of the SMB server to
 *               authenticate with.
 *   'domain'    The domain name to authenticate with.
 *
 * Optional parameters:
 * ====================
 *   'group'     Optional group name that the user must be a member of.
 *               Will be ignored if the value passed is a zero length string.
 *
 *
 * $Horde: framework/Auth/Auth/smb.php,v 1.20 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Jon Parise <jon at horde.org>
 * Copyright 2002-2004 Marcus I. Ryan <marcus at riboflavin.net>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jon Parise <jon at horde.org>
 * @author  Marcus I. Ryan <marcus at riboflavin.net>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_smb extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => false,
                              'update'        => false,
                              'resetpassword' => false,
                              'remove'        => false,
                              'list'          => false,
                              'transparent'   => false);

    /**
     * Constructs a new SMB authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_smb($params = array())
    {
        if (!Util::extensionExists('smbauth')) {
            Horde::fatal(PEAR::raiseError(_("Auth_smbauth: Required smbauth extension not found."), __FILE__, __LINE__));
        }

        /* Ensure we've been provided with all of the necessary parameters. */
        Horde::assertDriverConfig($params, 'auth',
            array('hostspec', 'domain'),
            'authentication Samba');

        $this->_params = $params;
    }

    /**
     * Find out if the given set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  An array of login credentials.
     *
     * @return boolean  True on success or a PEAR_Error object on failure.
     */
    function _authenticate($userId, $credentials)
    {
        if (empty($credentials['password'])) {
            Horde::fatal(PEAR::raiseError(_("No password provided for SMB authentication.")), __FILE__, __LINE__);
        }

        /* Authenticate. */
        $rval = validate($this->_params['hostspec'],
                         $this->_params['domain'],
                         empty($this->_params['group']) ? '' : $this->_params['group'],
                         $userId,
                         $credentials['password']);

        if ($rval === 0) {
            return true;
        } else {
            if ($rval === 1) {
                $this->_setAuthError(AUTH_REASON_MESSAGE, _("Failed to connect to SMB server."));
            } else {
                $this->_setAuthError(AUTH_REASON_MESSAGE, err2str());
            }
            return false;
        }
    }

}

--- NEW FILE: sql.php ---
<?php
/**
 * The Auth_sql class provides a SQL implementation of the Horde
 * authentication system.
 *
 * Required parameters:
 * ====================
 *   'database'  --  The name of the database.
 *   'hostspec'  --  The hostname of the database server.
 *   'password'  --  The password associated with 'username'.
 *   'phptype'   --  The database type (ie. 'pgsql', 'mysql, etc.).
 *   'protocol'  --  The communication protocol ('tcp', 'unix', etc.).
 *   'username'  --  The username with which to connect to the database.
 *
 * Optional parameters:
 * ====================
 *   'encryption'      --  The encryption to use to store the password in the
 *                         table (e.g. plain, crypt, md5-hex, md5-base64, smd5,
 *                         sha, ssha, aprmd5).
 *                         DEFAULT: 'md5-hex'
 *   'show_encryption' --  Whether or not to prepend the encryption in the
 *                         password field.
 *                         DEFAULT: 'false'
 *   'password_field'  --  The name of the password field in the auth table.
 *                         DEFAULT: 'user_pass'
 *   'table'           --  The name of the SQL table to use in 'database'.
 *                         DEFAULT: 'horde_users'
 *   'username_field'  --  The name of the username field in the auth table.
 *                         DEFAULT: 'user_uid'
 *
 * Required by some database implementations:
 * ==========================================
 *   'options'  --  Additional options to pass to the database.
 *   'port'     --  The port on which to connect to the database.
 *   'tty'      --  The TTY on which to connect to the database.
 *
 *
 * The table structure for the auth system is as follows:
 *
 * CREATE TABLE horde_users (
 *     user_uid   VARCHAR(255) NOT NULL,
 *     user_pass  VARCHAR(255) NOT NULL,
 *     PRIMARY KEY (user_uid)
 * );
 *
 *
 * If setting up as the Horde auth handler in conf.php, simply configure
 * $conf['sql'].
 *
 *
 * $Horde: framework/Auth/Auth/sql.php,v 1.66 2004/05/25 08:50:11 mdjukic Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_Auth
 */
class Auth_sql extends Auth {

    /**
     * An array of capabilities, so that the driver can report which
     * operations it supports and which it doesn't.
     *
     * @var array $capabilities
     */
    var $capabilities = array('add'           => true,
                              'update'        => true,
                              'resetpassword' => false,
                              'remove'        => true,
                              'list'          => true,
                              'transparent'   => false);

    /**
     * Handle for the current database connection.
     *
     * @var object DB $_db
     */
    var $_db;

    /**
     * Boolean indicating whether or not we're connected to the SQL server.
     *
     * @var boolean $connected
     */
    var $_connected = false;

    /**
     * Constructs a new SQL authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_sql($params = array())
    {
        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId      The userId to check.
     * @param array $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('SELECT %s FROM %s WHERE %s = %s',
                         $this->_params['password_field'],
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_db->quote($userId));

        $result = $this->_db->query($query);
        if (!is_a($result, 'PEAR_Error')) {
            $row = $result->fetchRow(DB_GETMODE_ASSOC);
            if (is_array($row) && $this->_comparePasswords($row[$this->_params['password_field']], $credentials['password'])) {
                $result->free();
                return true;
            } else {
                if (is_array($row)) {
                    $result->free();
                }
                $this->_setAuthError(AUTH_REASON_BADLOGIN);
                return false;
            }
        } else {
            $this->_setAuthError(AUTH_REASON_FAILED);
            return false;
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId      The userId to add.
     * @param array $credentials  The credentials to add.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function addUser($userId, $credentials)
    {
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('INSERT INTO %s (%s, %s) VALUES (%s, %s)',
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_params['password_field'],
                         $this->_db->quote($userId),
                         $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                     '',
                                                                     $this->_params['encryption'],
                                                                     $this->_params['show_encryption'])));

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * Update a set of authentication credentials.
     *
     * @access public
     *
     * @param string $oldID        The old userId.
     * @param string $newID        The new userId.
     * @param array  $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('UPDATE %s SET %s = %s, %s = %s WHERE %s = %s',
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_db->quote($newID),
                         $this->_params['password_field'],
                         $this->_db->quote($this->getCryptedPassword($credentials['password'],
                                                                     '',
                                                                     $this->_params['encryption'],
                                                                     $this->_params['show_encryption'])),
                         $this->_params['username_field'],
                         $this->_db->quote($oldID));

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * Reset a user's password. Used for example when the user does not
     * remember the existing password.
     *
     * @access public
     *
     * @param string $user_id  The user id for which to reset the password.
     *
     * @return mixed  The new passwrd on success or a PEAR_Error object on
     *                failure.
     */
    function resetPassword($user_id)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Get a new random password. */
        $password = Auth::genRandomPassword();

        /* Build the SQL query. */
        $query = sprintf('UPDATE %s SET %s = %s WHERE %s = %s',
                         $this->_params['table'],
                         $this->_params['password_field'],
                         $this->_db->quote($this->getCryptedPassword($password,
                                                                     '',
                                                                     $this->_params['encryption'],
                                                                     $this->_params['show_encryption'])),
                         $this->_params['username_field'],
                         $this->_db->quote($user_id));

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $password;
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @access public
     *
     * @param string $userId  The userId to delete.
     *
     * @return boolean        Success or failure.
     */
    function removeUser($userId)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('DELETE FROM %s WHERE %s = %s',
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_db->quote($userId));

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    /**
     * List all users in the system.
     *
     * @access public
     *
     * @return mixed  The array of userIds, or false on failure/unsupported.
     */
    function listUsers()
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('SELECT %s FROM %s ORDER BY %s',
                         $this->_params['username_field'],
                         $this->_params['table'],
                         $this->_params['username_field']);

        $result = $this->_db->getAll($query, null, DB_FETCHMODE_ORDERED);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        /* Loop through and build return array. */
        $users = array();
        foreach ($result as $ar) {
            $users[] = $ar[0];
        }

        return $users;
    }

    /**
     * Checks if a userId exists in the sistem.
     *
     * @access public
     *
     * @return boolean  Whether or not the userId already exists.
     */
    function exists($userId)
    {
        /* _connect() will die with Horde::fatal() upon failure. */
        $this->_connect();

        /* Build the SQL query. */
        $query = sprintf('SELECT %s FROM %s WHERE %s = %s',
                         $this->_params['username_field'],
                         $this->_params['table'],
                         $this->_params['username_field'],
                         $this->_db->quote($userId));

        return $this->_db->getOne($query);
    }

    /**
     * Compare an encrypted password to a plaintext string to see if
     * they match.
     *
     * @param string $encrypted  The crypted password to compare against.
     * @param string $plaintext  The plaintext password to verify.
     *
     * @return boolean  True if matched, false otherwise.
     */
    function _comparePasswords($encrypted, $plaintext)
    {
        return $encrypted == $this->getCryptedPassword($plaintext,
                                                       $encrypted,
                                                       $this->_params['encryption'],
                                                       $this->_params['show_encryption']);
    }

    /**
     * Attempts to open a connection to the SQL server.
     *
     * @access private
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function _connect()
    {
        if (!$this->_connected) {
            Horde::assertDriverConfig($this->_params, 'auth',
                array('phptype', 'hostspec', 'username', 'database'),
                'authentication SQL');

            if (empty($this->_params['encryption'])) {
                $this->_params['encryption'] = 'md5-hex';
            }
            if (!isset($this->_params['show_encryption'])) {
                $this->_params['show_encryption'] = false;
            }
            if (empty($this->_params['table'])) {
                $this->_params['table'] = 'horde_users';
            }
            if (empty($this->_params['username_field'])) {
                $this->_params['username_field'] = 'user_uid';
            }
            if (empty($this->_params['password_field'])) {
                $this->_params['password_field'] = 'user_pass';
            }

            /* Connect to the SQL server using the supplied
             * parameters. */
            include_once 'DB.php';
            $this->_db = &DB::connect($this->_params,
                                      array('persistent' => !empty($this->_params['persistent'])));
            if (is_a($this->_db, 'PEAR_Error')) {
                Horde::fatal(PEAR::raiseError(_("Unable to connect to SQL server.")), __FILE__, __LINE__);
            }

            /* Enable the "portability" option. */
            $this->_db->setOption('optimize', 'portability');

            $this->_connected = true;
        }

        return true;
    }

}

--- NEW FILE: yahoo.php ---
<?php
/**
 * The Auth_yahoo:: class checks login credentials against Yahoo! mail
 * accounts.
 *
 * $Horde: framework/Auth/Auth/yahoo.php,v 1.13 2004/01/28 00:34:00 slusarz Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see yahoo://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Auth
 */
class Auth_yahoo extends Auth {

    var $_http;

    /**
     * Constructs a new Yahoo authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters.
     */
    function Auth_yahoo($params = array())
    {
        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userId       The userId to check.
     * @param array  $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userId, $credentials)
    {
        require_once 'HTTP/Request.php';

        // Make sure that we have a bare username - strip off anything
        // after (and including) the first @, if there is one.
        $pos = strpos($userId, '@');
        if ($pos !== false) {
            $userId = substr($userId, 0, $pos);
        }

        $options['method'] = HTTP_REQUEST_METHOD_POST;
        $options['timeout'] = 5;
        $options['allowRedirects'] = true;

        $this->_http = &new HTTP_Request('http://login.yahoo.com/config/login', $options);
        $this->_http->addPostData('login', $userId);
        $this->_http->addPostData('passwd', $credentials['password']);

        $result = $this->_http->sendRequest();
        if (is_a($result, 'PEAR_Error')) {
            $result = $result->getMessage();
        } else {
            $result = $this->_http->getResponseBody();
            $cookies = $this->_http->getResponseCookies();
            if (is_array($cookies)) {
                foreach ($cookies as $cookie) {
                    $this->_http->addCookie($cookie['name'], $cookie['value']);
                }
            }
        }

        // This is _such_ a hack, but it works.
        if (!preg_match('|invalid password|i', $result)) {
            return true;
        } else {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_DEBUG);
            $this->_setAuthError(AUTH_REASON_BADLOGIN);
            return false;
        }
    }

}





More information about the commits mailing list