<?php
/**
 * Project: 
 * Contenido Content Management System
 * 
 * Description: 
 * Class for handling passwort recovery for backend users. If a user has set his e-mail address, this class
 * generates a new Password for user and submits to his e-mail adress. Submitting a new Password is
 * only possible every 30 minutes Mailsender, Mailsendername and Mailserver are set into system properties.
 * There it is also possible to deactivate this feature.
 * 
 * Requirements: 
 * @con_php_req 5.0
 * 
 *
 * @package    Contenido Backend classes
 * @version    1.1.0
 * @author     Timo Trautmann
 * @copyright  four for business AG <www.4fb.de>
 * @license    http://www.contenido.org/license/LIZENZ.txt
 * @link       http://www.4fb.de
 * @link       http://www.contenido.org
 * @since      file available since 2008-03-20
 * 
 * {@internal 
 *   created 2008-03-20
 *   modified 2008-06-30, Dominik Ziegler, add security fix
 *   modified 2010-05-27, Oliver Lohkemper, check if user activ in handleNewPassword()
 *   modified 2011-02-26, Ortwin Pinke, added temporary pw request behaviour, so user may login with old and/or requested pw
 *
 *   $Id$:
 * }}
 * 
 */

if(!defined('CON_FRAMEWORK')) {
	die('Illegal call');
}


/**
 *
 * Description: Class for handling passwort recovery
 *
 * @author Timo Trautmann
 * @copyright four for business AG <www.4fb.de>
 *
 */
class RequestPassword {
    /**
      * The contenido database object
      *
      * @var object
      * @access private
      */
    var $oDb;

    /**
      * The contenido configuration array
      *
      * @var array
      * @access private
      */
    var $aCfg;

    /**
      * The contenido template object
      *
      * @var object
      * @access private
      */
    var $oTpl;

    /*################################################################*/

    /**
      * Username of user which requests password
      *
      * @var string
      * @access private
      */
    var $sUsername;

    /**
      * E-mail address of user which requests password
      *
      * @var string
      * @access private
      */
    var $sEmail;

    /*################################################################*/

    /**
      * Time in minutes after which user is allowed to request a new password
      *
      * @var integer
      * @access private
      */
    var $iReloadTime;

    /**
      * Length of new passwort, which is generated automatically
      *
      * @var integer
      * @access private
      */
    var $iPassLength;

    /*################################################################*/

    /**
      * Definies if passwort request is enabled or disabled.
      * Default: This feature is enabled
      *
      * @var boolean
      * @access private
      */
    var $bIsEnabled;

    /**
      * E-mail address of the sender
      *
      * @var string
      * @access private
      */
    var $sSendermail;

    /**
      * Name of the sender
      *
      * @var string
      * @access private
      */
    var $sSendername;

    /**
      * Host of mailserver, which sends new password via mail
      *
      * @var string
      * @access private
      */
    var $sMailhost;

    /*################################################################*/
    /*################################################################*/

    /**
      * Constructor of RequestPassword initializes class variables
      *
      * @param  object $oDb - The contenido database object
      * @param  array $aCfg - The contenido configuration array
      * @access public
      */
    function __construct ($oDb, $aCfg) {
        //generate new dbobject, if it does not exist
        if (!is_object($oDb)) {
            $this->oDb = new DB_ConLite();
        } else {
            $this->oDb = $oDb;
        }

        //init class variables
        $this->aCfg = $aCfg;
        $this->oTpl = new Template();
        $this->sUsername = '';
        $this->sEmail = '';

        //set reload to 30 minutes
        $this->iReloadTime = 30;

        //set pass length to 14 chars
        $this->iPassLength = 14;

        //get systemproperty, which definies if password request is enabled (true) or disabled (false) : default to enabled
        $sEnable = getSystemProperty('pw_request', 'enable');
        if ($sEnable == 'false') {
            $this->bIsEnabled = false;
        } else {
            $this->bIsEnabled = true;
        }

        //get systemproperty for senders mail and validate mailadress, if not set use standard sender
        $sSendermail = getSystemProperty('system', 'mail_sender');
        if (preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $sSendermail)) {
            $this->sSendermail = $sSendermail;
        } else {
            $this->sSendermail = 'noreply@contenido-passwordservice.de';
        }

        //get systemproperty for senders name, if not set use Contenido Backend
        $sSendername = getSystemProperty('system', 'mail_sender_name');
        if ($sSendername != '') {
            $this->sSendername = $sSendername;
        } else {
            $this->sSendername = 'Contenido Backend';
        }

        //get systemproperty for location of mailserver, if not set use localhost
        $sMailhost = getSystemProperty('system', 'mail_host');
        if ($sMailhost != '') {
            $this->sMailhost = $sMailhost;
        } else {
            $this->sMailhost = 'localhost';
        }
    }

    /**
      * Function displays form for password request and sets new password, if password is submitted this function
      * also starts the passwort change an sending process
      *
      * @access public
      */
    function renderForm () {
        //if feature is not enabled, do nothing
        if (!$this->bIsEnabled) {
            return;
        }

        $sMessage = '';

        //if form is sumbitted call function handleNewPassword() and set submitted username to class variable $sUsername
        if (isset($_POST['action']) && $_POST['action'] == 'request_pw') {
            //avoid SQL-Injection, first check if submitted vars are escaped automatically
            $this->sUsername = $_POST['request_username'];

            $sMessage = $this->handleNewPassword();
            //if form is submitted, show corresponding password request layer
            $this->oTpl->set('s', 'JS_CALL', 'showRequestLayer();');
        } else {
            //by default request layer is invisible so da nothing
            $this->oTpl->set('s', 'JS_CALL', '');
        }

        //generate new form
        $oForm = new UI_Form('request_pw', 'index.php', 'post');
        $sRequestUsername = (isset($_POST['request_username']))?stripslashes($_POST['request_username']):'';
        //generate input for username
        $oInputUsername = new cHTMLTextbox('request_username', $sRequestUsername, '', '', 'request_username');
        $oInputUsername->setStyle('width:215px;');

        //set request action and current language
        $oForm->setVar('action', 'request_pw');
        $oForm->setVar('belang', $GLOBALS['belang']);

        //generate submitbutton and fill the form
        $oForm->add('submit', '<input type="image" src="images/submit.gif" alt="'.i18n('Submit').'" title="'.i18n('Submit').'" style="vertical-align:top; margin-top:2px; float:right; margin-right:6px;">');
        $oForm->add('request_username', $oInputUsername->render());
        $this->oTpl->set('s', 'FORM', $oForm->render());
        $this->oTpl->set('s', 'MESSAGE', $sMessage);
        $this->oTpl->set('s', 'LABEL', i18n('Please enter your login').':');
        
        $oButtonHide = new cHTMLImage($this->aCfg["path"]["contenido_fullhtml"]
                .$this->aCfg["path"]["images"]
                ."but_cancel_off.gif");
        $oButtonHide->setStyle("border: none;");
        $oButtonHide->setAlt(i18n("Hide request"));
        
        $this->oTpl->set('s', 'BTN_HIDE', $oButtonHide->render());

        //if handleNewPassword() returns a message, display it
        $this->oTpl->generate($this->aCfg['path']['contenido']
                .$this->aCfg['path']['templates']
                .$this->aCfg['templates']['request_password']);
    }

    /**
      * Function checks password request for errors an delegate request to setNewPassword() if there is no error
      *
      * @access private
      * @param string - contains message for displaying (errors or success message)
      */
    function handleNewPassword () {
        //notification message, which is returned to caller
        $sMessage = '';
        $this->sUsername = stripslashes($this->sUsername);

        //check if requested username exists, also get email and  timestamp when user last requests a new password (last_pw_request)
        $sSql = "SELECT username, last_pw_request, email FROM ".$this->aCfg["tab"]["phplib_auth_user_md5"]."
			     WHERE username = '".$this->oDb->escape($this->sUsername)."'
				 AND ( valid_from <= NOW() OR valid_from = '0000-00-00' OR valid_from = '1000-01-01')
                 AND ( valid_to >= NOW() OR valid_to = '0000-00-00'OR valid_to = '1000-01-01' )";

    	$this->oDb->query($sSql);
    	if ($this->oDb->next_record() && md5($this->sUsername) == md5($this->oDb->f('username'))) {
            //by default user is allowed to request new password
            $bIsAllowed = true;
            $sLast_pw_request = $this->oDb->f('last_pw_request');
            //store users mail adress to class variable
            $this->sEmail = $this->oDb->f('email');

            //check if there is a correct last request date
            if (preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $sLast_pw_request, $aMatches)) {
                $iLastRequest = mktime($aMatches[4], $aMatches[5], $aMatches[6], $aMatches[2], $aMatches[3], $aMatches[1]);
                $iNow = time();

                //check if this last request is longer ago then timelimit.
                if ($iNow-$iLastRequest < (60*$this->iReloadTime)) {
                    //user is not allowed to request new password, he has to wait
                    $bIsAllowed = false;
                    $sMessage = sprintf(i18n('Password requests are allowed every %s minutes.'), $this->iReloadTime);
                }
            }

            //check if syntax of users mail adress is correct and there is no standard mailadress like admin_kunde@IhreSite.de or sysadmin@IhreSite.de
            if ((!preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $this->sEmail) || $this->sEmail == 'sysadmin@IhreSite.de' || $this->sEmail == 'admin_kunde@IhreSite.de') && $bIsAllowed) {
                $bIsAllowed = false;
                //$sMessage = i18n('The requested user has no valid e-mail address. Submitting new password is not possible. Please contact your system- administrator for further support.');
                $sMessage = i18n('No matching data found. Please contact your systemadministrator.');
            }

            //if there are no errors, call function setNewPassword(), else wait a while, then return error message
            if ($bIsAllowed) {
                $this->setNewPassword();
                $sMessage = i18n('New password was submitted to your e-mail address.');
            } else {
                sleep(5);
            }
        } else {
            //slepp a while, then return error message
            //$sMessage = i18n('This user does not exist.');
            $sMessage = i18n('No matching data found. Please contact your systemadministrator.');
            sleep(5);
        }
        return $sMessage;
    }

    /**
      * Function sets new password for user and sets last request time to now
      *
      * @access private
      */
    function setNewPassword () {
        //generate new password, using generatePassword()
        $sPassword = $this->generatePassword();

        //update database entry, set new password and last_pw_request time
        $sSql = "UPDATE ".$this->aCfg["tab"]["phplib_auth_user_md5"]."
                         SET last_pw_request = '".date('Y-m-d H:i:s')."',
                             tmp_pw_request = '".md5($sPassword)."'
			             WHERE username = '".$this->sUsername."'";
        $this->oDb->query($sSql);

        //call function submitMail(), which sends new password to user
        $this->submitMail($sPassword);
    }

   /**
      * Function submits new password to users mail adress
      *
      * @access private
      * @param string $sPassword - the new password
      */
    function submitMail ($sPassword) {
        $sPassword = (string) $sPassword;

        //get translation for mailbody and insert username and new password
        $sMailBody = sprintf(i18n("Dear Contenidouser %s,\n\nYour password to log in Content Management System Contenido is: %s\n\nBest regards\n\nYour Contenido sysadmin"), $this->sUsername, $sPassword);

        //use php mailer class for submitting mail
        $oMail = new PHPMailer();
        //set host of mailserver
        $oMail->Host = $this->sMailhost;
        //it is not a html mail
        $oMail->IsHTML(0);
        //set senders e-mail adress
        $oMail->From = $this->sSendermail;
        //set senders name
        $oMail->FromName = $this->sSendername;

        //set users e mail adress as recipient
        $oMail->AddAddress($this->sEmail, "");
        //set mail subject
        $oMail->Subject = stripslashes (i18n('Your new password for Contenido Backend'));
        //append mail body
        $oMail->Body = $sMailBody;
        //wrap after 1000 chars
        $oMail->WordWrap = 1000;
        //activate mail and send it
        $oMail->IsMail();
        $oMail->Send();
    }

    /**
      * Function generates new password
      *
      * @access private
      * @return string - the new password
      */
    function generatePassword () {
       //possible chars which were used in password
	   $sChars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz123456789";

		$sPw = "";

        //for each character of password choose one from $sChars randomly
		for ($i = 0; $i < $this->iPassLength; $i++) {
			$sPw.= $sChars[rand(0, strlen($sChars))];
		}

		return $sPw;
    }
}
?>