<?php
/**
 * $Horde: passwd/main.php,v 1.49 2003/09/16 23:07:42 jan Exp $
 *
 * Copyright 1999-2003 Charles J. Hagenbuch <chuck@horde.org>
 * Copyright 1999-2003 Jon Parise <jon@horde.org>
 * Copyright 2002-2003 Eric Jon Rostetter <eric.rostetter@physics.utexas.edu>
 *
 * See the enclosed file COPYING for license information (GPL).  If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 */

@define('PASSWD_BASE', dirname(__FILE__));
require_once PASSWD_BASE . '/lib/base.php';
require PASSWD_BASE . '/config/backends.php';

// Use a do-while to allow easy breaking if an error is found.
do {
    // Has the user submitted the form yet?
    $submit = Horde::getFormData('submit', false);
    if (!$submit) {
        // No so we don't need to do anything in this loop.
        break;
    }

    // Get the backend details.
    $backend_key = Horde::getFormData('backend', false);
    $driver = $backends[$backend_key]['driver'];
    $params = $backends[$backend_key]['params'];
    $password_policy = isset($backends[$backend_key]['password policy'])
        ? $backends[$backend_key]['password policy']
        : array();

    // Get the username.
    $userid = Horde::getFormData('userid');

    // Check for users that can not change their passwords.
    if (in_array($userid, $conf['user']['refused'])) {
        $notification->push(sprintf(_("You can't change password for user %s"),
                                    $userid), 'horde.error');
        break;
    }

    // We must be passed the old (current) password, or its an error.
    $old_password = Horde::getFormData('oldpassword', false);
    if (!$old_password) {
        $notification->push(_("You must give your current password"),
                            'horde.warning');
        break;
    }

    // See if they entered the new password and verified it.
    $new_password0 = Horde::getFormData('newpassword0', false);
    $new_password1 = Horde::getFormData('newpassword1', false);
    if (!$new_password0) {
        $notification->push(_("You must give your new password"), 'horde.warning');
        break;
    }
    if (!$new_password1) {
        $notification->push(_("You must verify your new password"), 'horde.warning');
        break;
    }
    if ($new_password0 != $new_password1) {
        $notification->push(_("Your new passwords didn't match"), 'horde.warning');
        break;
    }
    if ($new_password0 == $old_password) {
        $notification->push(_("Your new password must be different from your current password"), 'horde.warning');
        break;
    }

    // Check max/min lengths if specified in the backend config.
    if (isset($password_policy['minLength']) &&
        strlen($new_password0) < $password_policy['minLength']) {
        $notification->push(sprintf(_("Your new password must be at least %d characters long!"), $password_policy['minLength']), 'horde.warning');
        break;
    }
    if (isset($password_policy['maxLength']) &&
        strlen($new_password0) > $password_policy['maxLength']) {
        $notification->push(sprintf(_("Your new password is too long; passwords may not be more than %d characters long!"), $password_policy['maxLength']), 'horde.warning');
        break;
    }

    // Disect the password in a localised way.
    $alpha = 0; $alnum = 0; $num = 0; $upper = 0; $lower = 0; $space = 0;
    for ($i = 0; $i < strlen($new_password0); $i++) {
        $char = substr($new_password0, $i, 1);
        if (ctype_lower($char)) {
            $lower++; $alpha++; $alnum++;
        } elseif (ctype_upper($char)) {
            $upper++; $alpha++; $alnum++;
        } elseif (ctype_digit($char)) {
            $num++; $alnum++;
        } elseif (ctype_space($char)) {
            $space++;
        }
    }

    // Check reamaining password policy options.
    if (isset($password_policy['minUpper']) &&
        $password_policy['minUpper'] > $upper) {
        $notification->push(sprintf(_("Your new password must contain at least %d uppercase characters."), $password_policy['minUpper']), 'horde.warning');
        break;
    }
    if (isset($password_policy['minLower']) &&
        $password_policy['minLower'] > $lower) {
        $notification->push(sprintf(_("Your new password must contain at least %d lowercase characters."), $password_policy['minLower']), 'horde.warning');
        break;
    }
    if (isset($password_policy['minNumeric']) &&
        $password_policy['minNumeric'] > $num) {
        $notification->push(sprintf(_("Your new password must contain at least %d numeric characters."), $password_policy['minNumeric']), 'horde.warning');
        break;
    }
    if (isset($password_policy['minAlpha']) &&
        $password_policy['minAlpha'] > $alpha) {
        $notification->push(sprintf(_("Your new password must contain at least %d alphabetic characters."), $password_policy['minAlpha']), 'horde.warning');
        break;
    }
    if (isset($password_policy['minAlphaNum']) &&
        $password_policy['minAlphaNum'] > $alnum) {
        $notification->push(sprintf(_("Your new password must contain at least %d alphanumeric characters."), $password_policy['minAlphaNum']), 'horde.warning');
        break;
    }
    if (isset($password_policy['maxSpace']) &&
        $password_policy['maxSpace'] < $space) {
        if ($pasword_policy['maxSpace'] > 0) {
            $notification->push(sprintf(_("Your new password must contain less than %d whitespace characters."), $password_policy['maxSpace']), 'horde.warning');
        } else {
            $notification->push(_("Your new password must not contain whitespace characters."), 'horde.warning');
        }
        break;
    }

    // Do some simple strength tests, if enabled in the config file.
    if ($conf['password']['strengthtests']) {
        // Check for new==old, pass==user, simple reverse strings, etc.
        if ((strcasecmp($new_password0, $userid) == 0) ||
            (strcasecmp($new_password0, strrev($userid)) == 0) ||
            (strcasecmp($new_password0, $old_password) == 0) ||
            (strcasecmp($new_password0, strrev($old_password)) == 0) ) {
            $notification->push(_("Your new password is too simple to guess. Not changed!"),
                                'horde.warning');
            break;
        }
        // Check for percentages similarity also.  This will catch very simple
        // Things like "password" -> "password2" or "xpasssword"...
        @similar_text($new_password0, $old_password, $percent1);
        @similar_text($new_password0, $userid, $percent2);
        if (($percent1 > 80) || ($percent2 > 80)) {
            $notification->push(_("Your new password is too simple to guess!  Not changed!"),
                                'horde.warning');
            break;
        }
    }

    // Create a Password_Driver instance.

    require_once PASSWD_BASE . '/lib/Driver.php';
    $daemon = &Passwd_Driver::factory($driver, $params);

    if (is_a($daemon, 'PEAR_Error')) {
        $notification->push(_("Password module is not properly configured"),
                            'horde.error');
    } else {
        $backend_userid = $userid;

        if ($conf['hooks']['username']) {
            $backend_userid = Horde::callHook('_passwd_hook_username',
                                              array($userid));
        }

        $res = $daemon->changePassword($backend_userid, $old_password,
                                       $new_password0);

        if (!is_a($res, 'PEAR_Error')) {
            $notification->push(sprintf(_("Password changed on %s."),
                                        $backends[$backend_key]['name']), 'horde.success');
        } else {
            $notification->push(sprintf(_("Failure in changing password on %s: %s"),
                                        $backends[$backend_key]['name'],
                                        $res->getMessage()), 'horde.error');
        }
    }
} while (false);

// Choose the prefered backend from config/backends.php.
foreach ($backends as $key => $current_backend) {
    if (!isset($backend_key) && substr($key, 0, 1) != '_') {
        $backend_key = $key;
    }
    if (Passwd::isPreferredBackend($current_backend)) {
        $backend_key = $key;
        break;
    }
}

// Build the <select> widget for the backends list.
if ($conf['backend']['backend_list'] == 'shown') {
    $backends_list = '';

    foreach ($backends as $key => $current_backend) {
        $sel = ($key == $backend_key) ? ' selected="selected"' : '';
        $backends_list .= "<option value=\"$key\"$sel>";
        $backends_list .= $current_backend['name'] . '</option>';
    }
}

// Extract userid to be shown in the username field.
if (empty($userid)) {
    if ($conf['hooks']['default_username']) {
        $userid = Horde::callHook('_passwd_hook_default_username', array(Auth::getAuth()));
    } else {
        $userid = Auth::getBareAuth();
    }
}    

$js_onLoad = 'setFocus()';
$title = _("Change Password");
require PASSWD_TEMPLATES . '/common-header.inc';
Passwd::menu();
require PASSWD_TEMPLATES . '/main/javascript.inc';
require PASSWD_TEMPLATES . '/main/main.inc';
require PASSWD_TEMPLATES . '/common-footer.inc';
$js_onLoad = 'setFocus()';
