<?php
/*
 * This file is part of MODX Revolution.
 *
 * Copyright (c) MODX, LLC. All Rights Reserved.
 *
 * For complete copyright and license information, see the COPYRIGHT and LICENSE
 * files found in the top-level directory of this distribution.
 */

/**
 * Loads the login screen
 *
 * @package modx
 * @subpackage manager.controllers
 */
class SecurityLoginManagerController extends modManagerController {
    public $loadHeader = false;
    public $loadFooter = false;

    public function initialize() {
        $this->handleLanguageChange();
        return true;
    }
    /**
     * Check for any permissions or requirements to load page
     * @return bool
     */
    public function checkPermissions() {
        return true;
    }

    /**
     * Register custom CSS/JS for the page
     * @return void
     */
    public function loadCustomCssJs() {}

    /**
     * Custom logic code here for setting placeholders, etc
     * @param array $scriptProperties
     * @return mixed
     */
    public function process(array $scriptProperties = array()) {
        $this->handleForgotLoginHash();
        $this->preserveReturnUrl();

        if (!empty($this->scriptProperties)) {
            $this->handlePost();
        }

        /* invoke OnManagerLoginFormPrerender event */
        $eventInfo= $this->modx->invokeEvent('OnManagerLoginFormPrerender');
        $eventInfo= is_array($eventInfo) ? implode("\n", $eventInfo) : (string) $eventInfo;
        $this->setPlaceholder('onManagerLoginFormPrerender', $eventInfo);

        $this->checkForActiveInstallation();
        $this->checkForAllowManagerForgotPassword();

        /* invoke OnManagerLoginFormRender event */
        $eventInfo= $this->modx->invokeEvent('OnManagerLoginFormRender');
        $eventInfo= is_array($eventInfo) ? implode("\n", $eventInfo) : (string) $eventInfo;
        $eventInfo= str_replace('\'','\\\'',$eventInfo);
        $this->setPlaceholder('onManagerLoginFormRender', $eventInfo);
    }

    /**
     * Set the cultureKey for the login page and get the list of languages
     * @return string The loaded cultureKey
     */
    public function handleLanguageChange() {
        $cultureKey = $this->modx->getOption('cultureKey',$_REQUEST,'en');
        if ($cultureKey) {
            $cultureKey = $this->modx->sanitizeString($cultureKey);
            $this->modx->setOption('cultureKey',$cultureKey);
            $this->modx->setOption('manager_language',$cultureKey);
        }
        $this->setPlaceholder('cultureKey',$cultureKey);


        $languages = $this->modx->lexicon->getLanguageList('core');

        $list = array();
        foreach ($languages as $language) {
            $selected = $language == $cultureKey ? ' selected="selected"' : '';
            $list[] = '<option value="'.$language.'"'.$selected.'>'.$language.'</option>';
        }
        $this->setPlaceholder('languages',implode("\n",$list));

        $this->modx->lexicon->load('login');
        $languageString = $this->modx->lexicon('login_language');
        if (empty($languageString) || strcmp($languageString,'login_language') == 0) {
            $this->modx->lexicon->load('en:core:login');
            $languageString = $this->modx->lexicon('login_language',array(),'en');
        }
        $this->setPlaceholder('language_str',$languageString);

        return $cultureKey;
    }

    public function checkForAllowManagerForgotPassword() {
        $allow = $this->modx->getOption('allow_manager_login_forgot_password',null,true);
        if ($allow) {
            $this->setPlaceholder('allow_forgot_password',true);
        }
    }

    /**
     * Handle and sanitize the forgot login hash, if existent
     *
     * @return void
     */
    public function handleForgotLoginHash() {
        if (isset($_GET['modahsh'])) {
            $this->scriptProperties['modahsh'] = $this->modx->sanitizeString($_GET['modahsh']);
            $this->setPlaceholder('modahsh',$this->scriptProperties['modahsh']);
        }
    }

    /**
     * If the user is coming from a specific mgr action, preserve the return URL and redirect post-login
     * @return void
     */
    public function preserveReturnUrl() {
        if (!empty($_SERVER['REQUEST_URI'])) {
            $chars = array("'",'"','(',')',';','>','<','!');
            $returnUrl = str_replace($chars,'',$_SERVER['REQUEST_URI']);
            $this->setPlaceholder('returnUrl',$returnUrl);
        }
    }

    /**
     * Check to see if there's an active installation in process; if so, notify the user.
     * @return void
     */
    public function checkForActiveInstallation() {
        if (isset($this->scriptProperties['installGoingOn'])) {
            $installGoingOn = $this->modx->sanitizeString($this->scriptProperties['installGoingOn']);
        }
        if (isset ($installGoingOn)) {
            switch ($installGoingOn) {
                case 1 : $this->setPlaceholder('login_message',$this->modx->lexicon('login_cancelled_install_in_progress').$this->modx->lexicon('login_message')); break;
                case 2 : $this->setPlaceholder('login_message',$this->modx->lexicon('login_cancelled_site_was_updated').$this->modx->lexicon('login_message')); break;
            }
        }
    }

    /**
     * Handle and sanitize any POST actions that come through
     *
     * @return void
     */
    public function handlePost() {
        $san = array("'",'"','(',')',';','>','<','../');
        foreach ($this->scriptProperties as $k => $v) {
            if (!in_array($k,array('returnUrl'))) {
                $this->scriptProperties[$k] = str_replace($san,'',$v);
            } else {
                $chars = array("'",'"','(',')',';','>','<','!','../');
                $this->scriptProperties[$k] = str_replace($chars,'',$v);
            }
        }

        /* handle login */
        if (!empty($this->scriptProperties['login'])) {
            $this->handleLogin();
        } else if (!empty($this->scriptProperties['forgotlogin']) && $this->modx->getOption('allow_manager_login_forgot_password',null,true)) {
            $this->handleForgotLogin();
        }
        $this->setPlaceholder('_post',$this->scriptProperties);
    }

    /**
     * Handle when a user attempts to log in
     * @return void
     */
    public function handleLogin() {
        $validated = true;

        /** @var modUser $user */
        $user = $this->modx->getObject('modUser',array(
            'username' => $this->scriptProperties['username'],
        ));

        /* first if there's an activation hash, process that */
        if ($user) {
            if (array_key_exists('modahsh', $this->scriptProperties) && !empty($this->scriptProperties['modahsh'])) {
                $activated = $user->activatePassword($this->scriptProperties['modahsh']);
                if ($activated === false) {
                    $this->modx->smarty->assign('error_message',$this->modx->lexicon('login_activation_key_err'));
                    $validated = false;
                }
            }
        }

        if ($validated) {
            /** @var modProcessorResponse $response */
            $response = $this->modx->runProcessor('security/login',$this->scriptProperties);
            if (($response instanceof modProcessorResponse) && !$response->isError()) {
                $url = !empty($this->scriptProperties['returnUrl']) ? $this->scriptProperties['returnUrl'] : $this->modx->getOption('manager_url',null,MODX_MANAGER_URL);
                $url = $this->modx->getOption('url_scheme', null, MODX_URL_SCHEME).$this->modx->getOption('http_host', null, MODX_HTTP_HOST).$url;
                $this->modx->sendRedirect($url);
            } else {
                $errors = $response->getAllErrors();
                $error_message = implode("\n",$errors);
                $this->setPlaceholder('error_message',$error_message);
            }
        }
    }

    /**
     * Handles the action when a user forgets their login
     *
     * @return void
     */
    public function handleForgotLogin() {
        $c = $this->modx->newQuery('modUser');
        $c->select(array('modUser.*','Profile.email','Profile.fullname'));
        $c->innerJoin('modUserProfile','Profile');
        $c->where(array(
            'modUser.username' => $this->scriptProperties['username_reset'],
            'OR:Profile.email:=' => $this->scriptProperties['username_reset'],
        ));
        /** @var modUser $user */
        $user = $this->modx->getObject('modUser',$c);
        if ($user) {
            $activationHash = md5(uniqid(md5($user->get('email') . '/' . $user->get('id')), true));

            $this->modx->getService('registry', 'registry.modRegistry');
            $this->modx->registry->getRegister('user', 'registry.modDbRegister');
            $this->modx->registry->user->connect();
            $this->modx->registry->user->subscribe('/pwd/reset/');
            $this->modx->registry->user->send('/pwd/reset/', array(md5($user->get('username')) => $activationHash), array('ttl' => 86400));

            $newPassword = $user->generatePassword();

            $user->set('cachepwd', $newPassword);
            $user->save();

            /* send activation email */
            $message = $this->modx->getOption('forgot_login_email');
            $placeholders = $user->toArray();
            $placeholders['url_scheme'] = $this->modx->getOption('url_scheme');
            $placeholders['http_host'] = $this->modx->getOption('http_host');
            $placeholders['manager_url'] = $this->modx->getOption('manager_url');
            $placeholders['hash'] = $activationHash;
            $placeholders['password'] = $newPassword;
            // Store previous placeholders
            $ph = $this->modx->placeholders;
            // now set those useful for modParser
            $this->modx->setPlaceholders($placeholders);
            $this->modx->getParser()->processElementTags('', $message, false, false);
            // Then restore previous placeholders to prevent any breakage
            $this->modx->placeholders = $ph;

            $this->modx->getService('mail', 'mail.modPHPMailer');
            $this->modx->mail->set(modMail::MAIL_BODY, $message);
            $this->modx->mail->set(modMail::MAIL_FROM, $this->modx->getOption('emailsender'));
            $this->modx->mail->set(modMail::MAIL_FROM_NAME, $this->modx->getOption('site_name'));
            $this->modx->mail->set(modMail::MAIL_SENDER, $this->modx->getOption('emailsender'));
            $this->modx->mail->set(modMail::MAIL_SUBJECT, $this->modx->getOption('emailsubject'));
            $this->modx->mail->address('to', $user->get('email'),$user->get('fullname'));
            $this->modx->mail->address('reply-to', $this->modx->getOption('emailsender'));
            $this->modx->mail->setHTML(true);
            if (!$this->modx->mail->send()) {
                /* if for some reason error in email, tell user */
                $err = $this->modx->lexicon('error_sending_email_to').$user->get('email');
                $this->modx->log(modX::LOG_LEVEL_ERROR,$err);
                $this->setPlaceholder('error_message',$err);
            } else {
                $this->setPlaceholder('success_message',$this->modx->lexicon('login_password_reset_act_sent'));
            }
            $this->modx->mail->reset();
        } else {
            $this->setPlaceholder('success_message',$this->modx->lexicon('login_user_err_nf_email'));
        }
    }

    /**
     * Return the pagetitle
     *
     * @return string
     */
    public function getPageTitle() {
        return $this->modx->lexicon('login');
    }

    /**
     * Return the location of the template file
     * @return string
     */
    public function getTemplateFile() {
        return 'security/login.tpl';
    }

    /**
     * Specify the language topics to load
     * @return array
     */
    public function getLanguageTopics() {
        return array('login');
    }
}
