<?php
/**
 *
 * @category        framework
 * @package         backend login
 * @author          Ryan Djurovich (2004-2009), WebsiteBaker Project
 * @copyright       2009-2012, WebsiteBaker Org. e.V.
 * @link			http://www.websitebaker2.org/
 * @license         http://www.gnu.org/licenses/gpl.html
 * @platform        WebsiteBaker 2.8.x
 * @requirements    PHP 5.2.2 and higher
 * @version         $Id: class.Login.php 2125 2015-06-17 18:42:26Z darkviper $
 * @filesource		$HeadURL: svn://isteam.dynxs.de/wb-archiv/branches/2.8.x/wb/framework/class.Login.php $
 * @lastmodified    $Date: 2015-06-17 20:42:26 +0200 (Wed, 17 Jun 2015) $
 *
 */
define('LOGIN_CLASS_LOADED', true);

// Get WB version
require_once(ADMIN_PATH.'/interface/version.php');

class Login extends admin {

    const PASS_CHARS = '\w!#$%&*+\-.:=?@\|';
    const USER_CHARS = 'a-z0-9&\-.=@_';

    protected $oReg    = null;
    protected $oDb     = null;
    protected $oTrans  = null;
    protected $message = '';

    public function __construct($config_array)
    {
        $this->oReg   = WbAdaptor::getInstance();
        $this->oDb    = $this->oReg->Db;
        $this->oTrans = $this->oReg->Trans;

        parent::__construct();
    // Get configuration values
        while(list($key, $value) = each($config_array)) {
            $this->{(strtolower($key))} = $value;
        }
    // calculate redirect URL
        if(!isset($this->redirect_url)) { $this->redirect_url = ''; }
        $aServerUrl = $this->mb_parse_url($this->oReg->AppUrl);
        $sServerUrl = $_SERVER['SERVER_NAME'];
        $sServerScheme =
            isset($_SERVER['REQUEST_SCHEME'])
            ? $_SERVER['REQUEST_SCHEME']
            : isset($aServerUrl['scheme']) ? $aServerUrl['scheme'] : ' http';
        $sServerPath = $_SERVER['SCRIPT_NAME'];
        // If the url is blank, set it to the default url
        $this->url = $this->get_post('url');
        if (preg_match('/%0d|%0a|\s/i', $this->url)) {
            throw new Exception('Warning: possible intruder detected on login');
        }
        $aUrl = $this->mb_parse_url( $this->url );
        $this->url =
            isset($aRedirecthUrl['host']) &&($sServerUrl == $aUrl['host'])
            ? $this->url
            : $this->oReg->AcpUrl.'start/index.php';
        if ($this->redirect_url!='') {
            $aRedirecthUrl = $this->mb_parse_url( $this->redirect_url );
            $this->redirect_url =
                isset($aRedirecthUrl['host']) &&($sServerUrl==$aRedirecthUrl['host'])
                ? $this->redirect_url
                : $sServerScheme.'://'.$sServerUrl;
            $this->url = $this->redirect_url;
        }
        if(strlen($this->url) < 2) {
            $aDefaultUrl = $this->mb_parse_url( $this->default_url );
            $this->default_url =
                isset($aDefaultUrl['host']) &&($sServerUrl==$aDefaultUrl['host'])
                ? $this->default_url
                : $sServerScheme.'://'.$sServerUrl;
            $this->url = $this->default_url;
        }
    // get username & password and validate it
        $username_fieldname = (string)$this->get_post('username_fieldname');
        $username_fieldname = (preg_match('/^_?[a-z][\w]+$/i', $username_fieldname) ? $username_fieldname : 'username');
        $sUsername = strtolower(trim((string)$this->get_post($username_fieldname)));
        $this->username = (preg_match(
            '/^['.self::USER_CHARS.']{'.$this->min_username_len.','.$this->max_username_len.'}$/is',
            $sUsername
        ) ? $sUsername : '');
        $password_fieldname = (string)$this->get_post('password_fieldname');
        $password_fieldname = (preg_match('/^_?[a-z][\w]+$/i', $password_fieldname) ? $password_fieldname : 'password');

        if ($this->username) {
/** @TODO implement crypting */
            $this->password = md5(trim((string)$this->get_post($password_fieldname)));
            // Figure out if the "remember me" option has been checked
            $this->remember = (@$_POST['remember'] == 'true' ? true : false);
        // try to authenticate
            $bSuccess = false;
            if (!($bSuccess = $this->is_authenticated())) {
                if ($this->is_remembered()) {
                    $sql = 'SELECT * FROM `'.$this->oDb->TablePrefix.'users` '
                         . 'WHERE `user_id`='.$this->get_safe_remember_key();
                    if (($oUsers = $this->oDb->doQuery($sql))) {
                        if (($aUser = $oUsers->fetchArray())) {
                            $this->username = $aUser['username'];
                            $this->password = $aUser['password'];
                            // Check if the user exists (authenticate them)
                            $bSuccess = $this->authenticate();
                        }
                    }
                } else {
                    // Check if the user exists (authenticate them)
                    $bSuccess = $this->authenticate();
                }
            }
            if ($bSuccess) {
                // Authentication successful
                $this->send_header($this->url);
            } else {
                $this->message = $this->oTrans->MESSAGE_LOGIN_AUTHENTICATION_FAILED;
                $this->increase_attemps();
            }
        } else {
            $this->display_login();
        }
    }

    // Authenticate the user (check if they exist in the database)
    function authenticate()
    {
        // Get user information
        $loginname = ( preg_match('/^['.self::USER_CHARS.']+$/s',$this->username) ? $this->username : '0');
        $aSettings = array();
        $aSettings['SYSTEM_PERMISSIONS']   = array();
        $aSettings['MODULE_PERMISSIONS']   = array();
        $aSettings['TEMPLATE_PERMISSIONS'] = array();
        $bRetval = false;

        $sql = 'SELECT * FROM `'.$this->oDb->TablePrefix.'users` '
             . 'WHERE `username`=\''.$this->oDb->escapeString($loginname).'\'';
        if (($oUser = $this->oDb->doQuery($sql))) {
            if (($aUser = $oUser->fetchArray())) {
                if (
                    $aUser['password'] == $this->password &&
                    $aUser['active'] == 1
                ) {
                // valide authentcation !!
                    $user_id                   = $aUser['user_id'];
                    $this->user_id             = $user_id;
                    $aSettings['USER_ID']      = $user_id;
                    $aSettings['GROUP_ID']     = $aUser['group_id'];
                    $aSettings['GROUPS_ID']    = $aUser['groups_id'];
                    $aSettings['USERNAME']     = $aUser['username'];
                    $aSettings['DISPLAY_NAME'] = $aUser['display_name'];
                    $aSettings['EMAIL']        = $aUser['email'];
                    $aSettings['HOME_FOLDER']  = $aUser['home_folder'];
                    // Run remember function if needed
                    if($this->remember == true) {
                        $this->remember($this->user_id);
                    }
                    // Set language
                    if($aUser['language'] != '') {
                        $aSettings['LANGUAGE'] = $aUser['language'];
                    }
                    // Set timezone
                    if($aUser['timezone'] != '-72000') {
                        $aSettings['TIMEZONE'] = $aUser['timezone'];
                    } else {
                        // Set a session var so apps can tell user is using default tz
                        $aSettings['USE_DEFAULT_TIMEZONE'] = true;
                    }
                    // Set date format
                    if($aUser['date_format'] != '') {
                        $aSettings['DATE_FORMAT'] = $aUser['date_format'];
                    } else {
                        // Set a session var so apps can tell user is using default date format
                        $aSettings['USE_DEFAULT_DATE_FORMAT'] = true;
                    }
                    // Set time format
                    if($aUser['time_format'] != '') {
                        $aSettings['TIME_FORMAT'] = $aUser['time_format'];
                    } else {
                        // Set a session var so apps can tell user is using default time format
                        $aSettings['USE_DEFAULT_TIME_FORMAT'] = true;
                    }
                    // Get group information
                    $aSettings['GROUP_NAME'] = array();
                    $bOnlyAdminGroup = $this->ami_group_member('1') && (sizeof($aGroupsIds) == 1);
                    $sql = 'SELECT * FROM `'.$this->oDb->TablePrefix.'groups` '
                         . 'WHERE `group_id` IN (\''.$aUser['groups_id'].',0\') '
                         . 'ORDER BY `group_id`';
                    if (($oGroups = $this->oDb->doQuery($sql))) {
                        while (($aGroup = $oGroups->fetchArray())) {
                            $aSettings['GROUP_NAME'][$aGroup['group_id']] = $aGroup['name'];
                        // collect system_permissions (additively)
                            $aSettings['SYSTEM_PERMISSIONS'] = array_merge(
                                $aSettings['SYSTEM_PERMISSIONS'],
                                explode(',', $aGroup['system_permissions'])
                            );
                        // collect module_permission (subtractive)
                            if (!sizeof($aSettings['MODULE_PERMISSIONS'])) {
                                $aSettings['MODULE_PERMISSIONS'] = explode(',', $aGroup['module_permissions']);
                            } else {
                                $aSettings['MODULE_PERMISSIONS'] = array_intersect(
                                    $aSettings['MODULE_PERMISSIONS'],
                                    preg_split('/\s*[,;\|\+]/', $aGroup['module_permissions'], -1, PREG_SPLIT_NO_EMPTY)
                                );
                            }
                        // collect template_permission (subtractive)
                            if (!sizeof($aSettings['TEMPLATE_PERMISSIONS'])) {
                                $aSettings['TEMPLATE_PERMISSIONS'] = explode(',', $aGroup['template_permissions']);
                            } else {
                                $aSettings['TEMPLATE_PERMISSIONS'] = array_intersect(
                                    $aSettings['TEMPLATE_PERMISSIONS'],
                                    preg_split('/\s*[,;\|\+]/', $aGroup['template_permissions'], -1, PREG_SPLIT_NO_EMPTY)
                                );
                            }
                        }
                    }
                    // Update the users table with current ip and timestamp
                    $sRemoteAddress = @$_SERVER['REMOTE_ADDR'] ?: 'unknown';
                    $sql = 'UPDATE `'.$this->oDb->TablePrefix.'users` '
                         . 'SET `login_when`='.time().', '
                         .     '`login_ip`=\''.$sRemoteAddress.'\' '
                         . 'WHERE `user_id`=\''.$user_id.'\'';
                    $this->oDb->doQuery($sql);
                    $bRetval = true;
                }
            }
        }
        // merge settings into $_SESSION and overwrite older one values
        $_SESSION = array_merge($_SESSION, $aSettings);
        // Return if the user exists or not
        return $bRetval;
    }

    // Increase the count for login attemps
    function increase_attemps()
    {
        $_SESSION['ATTEMPS'] = (isset($_SESSION['ATTEMPS']) ? $_SESSION['ATTEMPS']++ : 0);
        $this->display_login();
    }

    // Function to set a "remembering" cookie for the user - removed
    function remember($user_id)
    {
        return true;
    }

    // Function to check if a user has been remembered - removed
    function is_remembered()
    {
        return false;
    }

    // Display the login screen
    function display_login()
    {
        // If attemps more than allowed, warn the user
        if($this->get_session('ATTEMPS') > $this->max_attemps) {
            $this->warn();
        }
        // Show the login form
        if($this->frontend != true) {
            $template = new Template(dirname($this->correct_theme_source($this->template_file)));
            $template->set_file('page', $this->template_file);
            $template->set_block('page', 'mainBlock', 'main');
            $template->set_var('DISPLAY_REMEMBER_ME', ($this->remember_me_option ? '' : 'display: none;'));
            $template->set_var($this->oTrans->getLangArray());
            $template->set_var(
                array(
                    'TITLE_LOGOUT'           => $this->oTrans->MENU_LOGIN,
                    'TITLE_VIEW'             => $this->oTrans->TEXT_WEBSITE,
                    'SECTION_NAME'           => $this->oTrans->MENU_LOGIN,
                    'SECTION_LOGIN'          => $this->oTrans->MENU_LOGIN,
                    'ACTION_URL'             => $this->login_url,
                    'URL'                    => $this->default_url,
                    'ATTEMPS'                => $this->get_session('ATTEMPS'),
                    'USERNAME'               => $this->username,
                    'USERNAME_FIELDNAME'     => $this->username_fieldname,
                    'PASSWORD_FIELDNAME'     => $this->password_fieldname,
                    'MESSAGE'                => $this->message,
                    'WEBSITE_TITLE'          => $this->oReg->WebsiteTitle,
                    'INTERFACE_DIR_URL'      => $this->oReg->AcpUrl.'interface',
                    'MAX_USERNAME_LEN'       => $this->max_username_len,
                    'MAX_PASSWORD_LEN'       => $this->max_password_len,
                    'ADMIN_URL'              => $this->oReg->AcpUrl,
                    'WB_URL'                 => $this->oReg->AppUrl,
                    'URL_VIEW'               => $this->oReg->AppUrl,
                    'THEME_URL'              => $this->oReg->ThemeUrl,
                    'VERSION'                => $this->oReg->Version,
                    'SP'                     => (isset($this->oReg->Sp) ? $this->oReg->Sp : ''),
                    'REVISION'               => $this->oReg->Revision,
                    'LANGUAGE'               => strtolower($this->oReg->Language),
                    'FORGOTTEN_DETAILS_APP'  => $this->forgotten_details_app,
                    'PAGES_DIRECTORY'        => $this->oReg->PagesDir,
                    'LOGIN_DISPLAY_HIDDEN'   => !$this->is_authenticated() ? 'hidden' : '',
                    'LOGIN_DISPLAY_NONE'     => !$this->is_authenticated() ? 'none' : '',
                    'LOGIN_LINK'             => $_SERVER['SCRIPT_NAME'],
                    'LOGIN_ICON'             => 'login',
                    'START_ICON'             => 'blank',
                    'URL_HELP'               => 'http://wiki.websitebaker.org/',
                )
            );
            $template->set_var('CHARSET', (isset($this->oReg->DefaultCharset) ? $this->oReg->DefaultCharset : 'utf-8'));
            $template->parse('main', 'mainBlock', false);
            $template->pparse('output', 'page');
        }
    }
    // sanities the REMEMBER_KEY cookie to avoid SQL injection
    function get_safe_remember_key()
    {
        $iMatches = 0;
        if (isset($_COOKIE['REMEMBER_KEY'])) {
            $sRetval = preg_replace(
                '/^([0-9]{11})_([0-9a-f]{11})$/i',
                '\1\2',
                $_COOKIE['REMEMBER_KEY'], -1, $iMatches
            );
        }
        return ($iMatches ? $sRetval : '');
    }
    // Warn user that they have had to many login attemps
    function warn()
    {
        $this->send_header($this->warning_url);
        exit;
    }

}
