Seditio Source
Root |
./othercms/ips_4.3.4/applications/core/modules/front/system/settings.php
<?php
/**
 * @brief        User CP Controller
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @since        10 Jun 2013
 */
 
namespace IPS\core\modules\front\system;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
   
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
    exit;
}

/**
 * User CP Controller
 */
class _settings extends \IPS\Dispatcher\Controller
{

   
/**
     * Execute
     *
     * @return    void
     */
   
public function execute()
    {
       
/* Only logged in members */
       
if ( !\IPS\Member::loggedIn()->member_id and !in_array( \IPS\Request::i()->do, array( 'mfarecovery', 'mfarecoveryvalidate' ) ) )
        {
            \
IPS\Output::i()->error( 'no_module_permission_guest', '2C122/1', 403, '' );
        }

        \
IPS\Output::i()->sidebar['enabled'] = FALSE;
       
parent::execute();
    }

   
/**
     * Settings
     *
     * @return    void
     */
   
protected function manage()
    {
       
/* Work out output */
       
$area = \IPS\Request::i()->area ?: 'overview';
        if (
method_exists( $this, "_{$area}" ) )
        {
           
$output = call_user_func( array( $this, "_{$area}" ) );
        }
       
       
/* Display */
       
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('settings');
        \
IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('settings') );
        if ( !\
IPS\Request::i()->isAjax() )
        {
            if ( \
IPS\Request::i()->service )
            {
               
$area = "{$area}_" . \IPS\Request::i()->service;
            }
           
            \
IPS\Output::i()->cssFiles    = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/settings.css' ) );
           
            if ( \
IPS\Theme::i()->settings['responsive'] )
            {
                \
IPS\Output::i()->cssFiles    = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/settings_responsive.css' ) );
            }
           
            if (
$output )
            {
                \
IPS\Output::i()->output .= $this->_wrapOutputInTemplate( $area, $output );
            }
        }
        elseif (
$output )
        {
            \
IPS\Output::i()->output .= $output;
        }
    }
   
   
/**
     * Wrap output in template
     *
     * @param    string    $area    Active area
     * @param    string    $output    Output
     * @return    string
     */
   
protected function _wrapOutputInTemplate( $area, $output )
    {
       
/* What can we do? */
       
$canChangePassword = \IPS\Settings::i()->allow_email_changes == 'redirect';
       
$canConfigureMfa = FALSE;
        if ( \
IPS\Settings::i()->allow_password_changes == 'normal' )
        {
            foreach ( \
IPS\Login::methods() as $method )
            {
                if (
$method->canChangePassword( \IPS\Member::loggedIn() ) )
                {
                   
$canChangePassword = TRUE;
                }
            }
        }
        foreach ( \
IPS\MFA\MFAHandler::handlers() as $handler )
        {
            if (
$handler->isEnabled() and $handler->memberCanUseHandler( \IPS\Member::loggedIn() ) )
            {
               
$canConfigureMfa = TRUE;
                break;
            }
        }

       
$sigLimits = explode( ":", \IPS\Member::loggedIn()->group['g_signature_limits'] );
       
$canChangeSignature = (bool) ( \IPS\Settings::i()->signatures_enabled && !$sigLimits[0]    );
               
       
/* Add login handlers */
       
$loginMethods = \IPS\Login::methods();
       
       
/* Show our own oauth clients? */
       
$showApps = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( array( 'oauth_enabled=1 AND oauth_ucp=1' ) ) )->first();
               
       
/* Return */
       
return \IPS\Theme::i()->getTemplate( 'system' )->settings( $area, $output, \IPS\Settings::i()->allow_email_changes == 'normal', $canChangePassword, \IPS\Member::loggedIn()->group['g_dname_changes'], $canChangeSignature, $loginMethods, $canConfigureMfa, $showApps );
    }
   
   
/**
     * Overview
     *
     * @return    string
     */
   
protected function _overview()
    {
       
$loginMethods = array();
       
$canChangePassword = \IPS\Settings::i()->allow_email_changes == 'redirect';
       
        foreach ( \
IPS\Login::methods() as $method )
        {
            if (
$method->showInUcp( \IPS\Member::loggedIn() ) )
            {
                if (
$method->canProcess( \IPS\Member::loggedIn() ) )
                {
                    try
                    {
                       
$name = $method->userProfileName( \IPS\Member::loggedIn() );
                       
                       
$loginMethods[ $method->id ] = array(
                           
'title'    => $method->_title,
                           
'blurb'    => $name ? \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_headline', FALSE, array( 'sprintf' => array( $name ) ) ) : NULL,
                           
'icon'    => $method->userProfilePhoto( \IPS\Member::loggedIn() )
                        );
                    }
                    catch ( \
IPS\Login\Exception $e )
                    {
                       
$loginMethods[ $method->id ] = array( 'title' => $method->_title, 'blurb' => \IPS\Member::loggedIn()->language()->addToStack('profilesync_reauth_needed') );
                    }
                }
                else
                {
                   
$loginMethods[ $method->id ] = array( 'title' => $method->_title, 'blurb' => \IPS\Member::loggedIn()->language()->addToStack('profilesync_not_synced') );
                }
            }
           
           
            if (
$method->canChangePassword( \IPS\Member::loggedIn() ) )
            {
               
$canChangePassword = TRUE;
            }
        }        
               
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsOverview( $loginMethods, $canChangePassword );
    }
   
   
/**
     * Email
     *
     * @return    string
     */
   
protected function _email()
    {
        if ( \
IPS\Settings::i()->allow_email_changes == 'redirect' )
        {
            \
IPS\Output::i()->redirect( \IPS\Http\Url::external( \IPS\Settings::i()->allow_email_changes_target ) );
        }

        if( \
IPS\Settings::i()->allow_email_changes != 'normal' )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2C122/U', 403, '' );
        }
       
        if( \
IPS\Member::loggedIn()->isAdmin() )
        {
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsEmail();
        }
               
       
$mfaOutput = \IPS\MFA\MFAHandler::accessToArea( 'core', 'EmailChange', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=email', 'front', 'settings_email' ) );
        if (
$mfaOutput )
        {
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
            }
            \
IPS\Output::i()->output = $mfaOutput;
        }
       
       
/* Do we have any pending validation emails? */
       
try
        {
           
$pending = \IPS\Db::i()->select( '*', 'core_validating', array( 'member_id=? AND email_chg=1', \IPS\Member::loggedIn()->member_id ), 'entry_date DESC' )->first();
        }
        catch( \
UnderflowException $e )
        {
           
$pending = null;
        }
       
       
/* Build the form */
       
$form = new \IPS\Helpers\Form;
       
$form->class = 'ipsForm_collapseTablet';
       
$form->addDummy( 'current_email', htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE ) );
       
$form->add( new \IPS\Helpers\Form\Email( 'new_email', '', TRUE, array( 'accountEmail' => \IPS\Member::loggedIn() ) ) );
       
       
/* Handle submissions */
       
$values = NULL;
        if ( !
$mfaOutput and $values = $form->values() )
        {
           
$_SESSION['newEmail'] = $values['new_email'];
        }
        if ( isset(
$_SESSION['newEmail'] ) )
        {
           
/* Reauthenticate */
           
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=email', 'front', 'settings_email' ), \IPS\Login::LOGIN_REAUTHENTICATE );
           
           
/* After re-authenticating, change the email */
           
$error = NULL;
            try
            {
                if (
$success = $login->authenticate() )
                {
                   
/* Disable syncing */
                   
$profileSync = \IPS\Member::loggedIn()->profilesync;
                    if ( isset(
$profileSync['email'] ) )
                    {
                        unset(
$profileSync['email'] );
                        \
IPS\Member::loggedIn()->profilesync = $profileSync;
                        \
IPS\Member::loggedIn()->save();
                    }
                           
                   
/* Change the email */
                   
$oldEmail = \IPS\Member::loggedIn()->email;
                    \
IPS\Member::loggedIn()->email = $_SESSION['newEmail'];
                    \
IPS\Member::loggedIn()->save();
                    foreach ( \
IPS\Login::methods() as $method )
                    {
                        try
                        {
                           
$method->changeEmail( \IPS\Member::loggedIn(), $oldEmail, $_SESSION['newEmail'] );
                        }
                        catch( \
BadMethodCallException $e ){}
                    }
                    \
IPS\Member::loggedIn()->logHistory( 'core', 'email_change', array( 'old' => $oldEmail, 'new' => \IPS\Member::loggedIn()->email, 'by' => 'manual' ) );
                    \
IPS\Member::loggedIn()->memberSync( 'onEmailChange', array( $_SESSION['newEmail'], $oldEmail ) );
                    unset(
$_SESSION['newEmail'] );
                   
                   
/* Invalidate sessions except this one */
                   
\IPS\Member::loggedIn()->invalidateSessionsAndLogins( \IPS\Session::i()->id );
                    if( isset( \
IPS\Request::i()->cookie['login_key'] ) )
                    {
                        \
IPS\Member\Device::loadOrCreate( \IPS\Member::loggedIn() )->updateAfterAuthentication( TRUE );
                    }
                               
                   
/* Delete any pending validation emails */
                   
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=? AND email_chg=1', \IPS\Member::loggedIn()->member_id ) );
                               
                   
/* Send a validation email if we need to */
                   
if ( \IPS\Settings::i()->reg_auth_type == 'user' or \IPS\Settings::i()->reg_auth_type == 'admin_user' )
                    {
                       
$vid = \IPS\Login::generateRandomString();
                       
                        \
IPS\Db::i()->insert( 'core_validating', array(
                           
'vid'            => $vid,
                           
'member_id'        => \IPS\Member::loggedIn()->member_id,
                           
'entry_date'    => time(),
                           
'email_chg'        => TRUE,
                           
'ip_address'    => \IPS\Request::i()->ipAddress(),
                           
'prev_email'    => $oldEmail,
                           
'email_sent'    => time(),
                        ) );
       
                        \
IPS\Member::loggedIn()->members_bitoptions['validating'] = TRUE;
                        \
IPS\Member::loggedIn()->save();
                       
                        \
IPS\Email::buildFromTemplate( 'core', 'email_change', array( \IPS\Member::loggedIn(), $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
                                   
                        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
                    }
                   
                   
/* Or just redirect */
                   
else
                    {
                       
/* Send a confirmation email */
                       
\IPS\Email::buildFromTemplate( 'core', 'email_address_changed', array( \IPS\Member::loggedIn(), $oldEmail ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $oldEmail, array(), array(), NULL, NULL, array( 'Reply-To' => \IPS\Settings::i()->email_in ) );
       
                        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=email', 'front', 'settings' ), 'email_changed' );
                    }
                }
            }
            catch ( \
IPS\Login\Exception $e )
            {
               
$error = $e->getMessage();
            }
           
           
/* Otherwise show the reauthenticate form */
           
return \IPS\Theme::i()->getTemplate( 'system' )->settingsEmail( NULL, $login, $error );
           
        }
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsEmail( $form );
    }
   
   
/**
     * Password
     *
     * @return    string
     */
   
protected function _password()
    {
        if ( \
IPS\Settings::i()->allow_password_changes == 'redirect' )
        {
            \
IPS\Output::i()->redirect( \IPS\Http\Url::external( \IPS\Settings::i()->allow_password_changes_target ) );
        }

        if( \
IPS\Settings::i()->allow_password_changes != 'normal' )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2C122/T', 403, '' );
        }
       
        if( \
IPS\Member::loggedIn()->isAdmin() )
        {
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsPassword();
        }
       
       
$mfaOutput = \IPS\MFA\MFAHandler::accessToArea( 'core', 'PasswordChange', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=password', 'front', 'settings_password' ) );
        if (
$mfaOutput )
        {
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
            }
            \
IPS\Output::i()->output = $mfaOutput;
        }
               
       
$form = new \IPS\Helpers\Form;
       
$form->class = 'ipsForm_collapseTablet';
       
$form->add( new \IPS\Helpers\Form\Password( 'current_password', '', TRUE, array( 'validateFor' => \IPS\Member::loggedIn(), 'bypassProfanity' => TRUE ) ) );
       
$form->add( new \IPS\Helpers\Form\Password( 'new_password', '', TRUE, array( 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'bypassProfanity' => TRUE ) ) );
       
$form->add( new \IPS\Helpers\Form\Password( 'confirm_new_password', '', TRUE, array( 'confirm' => 'new_password', 'bypassProfanity' => TRUE ) ) );
       
        if ( !
$mfaOutput and $values = $form->values() )
        {
           
/* Change password */
           
\IPS\Member::loggedIn()->changePassword( $values['new_password'] );

           
/* Invalidate sessions except this one */
           
\IPS\Member::loggedIn()->invalidateSessionsAndLogins( \IPS\Session::i()->id );
            if( isset( \
IPS\Request::i()->cookie['login_key'] ) )
            {
                \
IPS\Member\Device::loadOrCreate( \IPS\Member::loggedIn() )->updateAfterAuthentication( TRUE );
            }
           
           
/* Delete any pending validation emails */
           
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=? AND lost_pass=1', \IPS\Member::loggedIn()->member_id ) );

            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=password', 'front', 'settings' ), 'password_changed' );
        }
       
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsPassword( $form );
    }
   
   
   
/**
     * Devices
     *
     * @return    string
     */
   
protected function _devices()
    {
       
/* Can users manage devices? */
       
if ( !\IPS\Settings::i()->device_management )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2C122/S' );
        }

       
$mfaOutput = \IPS\MFA\MFAHandler::accessToArea( 'core', 'DeviceManagement', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=devices', 'front', 'settings_devices' ) );
        if (
$mfaOutput )
        {
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=devices', 'front', 'settings_devices' ) );
            }
            \
IPS\Output::i()->output = $mfaOutput;
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsDevices( array(), array() );
        }
       
       
$devices = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members_known_devices', array( 'member_id=? AND last_seen>?', \IPS\Member::loggedIn()->member_id, ( new \DateTime )->sub( new \DateInterval( \IPS\Member\Device::LOGIN_KEY_VALIDITY ) )->getTimestamp() ), 'last_seen DESC' ), 'IPS\Member\Device' );

       
$locations = array();
       
$ipAddresses = array();
        foreach (
$devices as $device )
        {
            try
            {
               
$log = \IPS\Db::i()->select( '*', 'core_members_known_ip_addresses', array( 'member_id=? AND device_key=?', \IPS\Member::loggedIn()->member_id, $device->device_key ), 'last_seen DESC' )->first();
            }
            catch ( \
UnderflowException $e )
            {
                continue;
            }
           
            if ( \
IPS\Settings::i()->ipsgeoip )
            {
                if ( !
array_key_exists( $log['ip_address'], $locations ) )
                {
                    try
                    {
                       
$locations[ $log['ip_address'] ] = \IPS\GeoLocation::getByIp( $log['ip_address'] );
                    }
                    catch ( \
Exception $e )
                    {
                       
$locations[ $log['ip_address'] ] = \IPS\Member::loggedIn()->language()->addToStack('unknown');
                    }
                }
               
               
$ipAddresses[ $log['device_key'] ][ $log['ip_address'] ] = array(
                   
'location'    => $locations[ $log['ip_address'] ],
                   
'date'        => $log['last_seen']
                );
            }
            else
            {
               
$ipAddresses[ $log['device_key'] ][ $log['ip_address'] ] = array(
                   
'date'        => $log['last_seen']
                );
            }
        }
       
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsDevices( $devices, $ipAddresses );
    }
   
   
/**
     * Secure Account
     *
     * @return    string
     */
   
protected function secureAccount()
    {
       
/* Only logged in members */
       
if ( !\IPS\Member::loggedIn()->member_id )
        {
            \
IPS\Output::i()->error( 'no_module_permission_guest', '2C122/Q', 403, '' );
        }

       
$canChangePassword = FALSE;
        foreach ( \
IPS\Login::methods() as $method )
        {
            if (
$method->canChangePassword( \IPS\Member::loggedIn() ) )
            {
               
$canChangePassword = TRUE;
            }
        }
       
       
$canConfigureMfa = FALSE;
       
$hasConfiguredMfa = FALSE;
        foreach ( \
IPS\MFA\MFAHandler::handlers() as $handler )
        {
            if (
$handler->isEnabled() and $handler->memberCanUseHandler( \IPS\Member::loggedIn() ) )
            {
               
$canConfigureMfa = TRUE;
               
                if (
$handler->memberHasConfiguredHandler( \IPS\Member::loggedIn() ) )
                {
                   
$hasConfiguredMfa = TRUE;
                    break;
                }
            }
        }
               
       
$loginMethods = array();
        foreach ( \
IPS\Login::methods() as $method )
        {
            if (
$method->showInUcp( \IPS\Member::loggedIn() ) )
            {
                if (
$method->canProcess( \IPS\Member::loggedIn() ) )
                {
                    try
                    {
                       
$loginMethods[ $method->id ] = array(
                           
'title'    => $method->_title,
                           
'blurb'    => \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_headline', FALSE, array( 'sprintf' => array( $method->userProfileName( \IPS\Member::loggedIn() ) ) ) ),
                           
'icon'    => $method->userProfilePhoto( \IPS\Member::loggedIn() )
                        );
                    }
                    catch ( \
IPS\Login\Exception $e )
                    {
                       
$loginMethods[ $method->id ] = array( 'title' => $method->_title, 'blurb' => \IPS\Member::loggedIn()->language()->addToStack('profilesync_reauth_needed') );
                    }
                }
            }
        }    
       
       
$oauthApps = \IPS\Db::i()->select( 'COUNT(DISTINCT client_id)', 'core_oauth_server_access_tokens', array( 'member_id=? AND oauth_enabled=1 AND oauth_ucp=1', \IPS\Member::loggedIn()->member_id ) )
            ->
join( 'core_oauth_clients', 'oauth_client_id=client_id' )
            ->
first();
               
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'secure_account' );
        \
IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings', 'front', 'settings' ), \IPS\Member::loggedIn()->language()->addToStack('settings') );
        \
IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('secure_account') );
        \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->settingsSecureAccount( $canChangePassword, $canConfigureMfa, $hasConfiguredMfa, $loginMethods, $oauthApps );
    }
   
   
/**
     * Disable Automatic Login
     *
     * @return    string
     */
   
protected function disableAutomaticLogin()
    {
        \
IPS\Session::i()->csrfCheck();
       
        try
        {
           
$device = \IPS\Member\Device::loadAndAuthenticate( \IPS\Request::i()->device, \IPS\Member::loggedIn() );
           
$device->login_key = NULL;
           
$device->save();
           
            \
IPS\Member::loggedIn()->logHistory( 'core', 'login', array( 'type' => 'logout', 'device' => $device->device_key ) );
           
            \
IPS\Session\Store::i()->deleteByMember( $device->member_id, $device->user_agent, array( \IPS\Session::i()->id ) );
        }
        catch ( \
Exception $e ) { }
               
        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=devices', 'front', 'settings_devices' ) );
    }
   
   
/**
     * MFA
     *
     * @return    string
     */
   
protected function _mfa()
    {
       
/* Validate password */
       
if ( !isset( $_SESSION['passwordValidatedForMfa'] ) )
        {
           
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ), \IPS\Login::LOGIN_REAUTHENTICATE );
           
$error = NULL;
            try
            {
                if (
$success = $login->authenticate() )
                {
                   
$_SESSION['passwordValidatedForMfa'] = TRUE;
                    \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
                }
            }
            catch ( \
IPS\Login\Exception $e )
            {
               
$error = $e->getMessage();
            }
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsMfaPassword( $login, $error );
        }
       
       
/* Do MFA check */
       
$mfaOutput = \IPS\MFA\MFAHandler::accessToArea( 'core', 'SecurityQuestions', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
        if (
$mfaOutput )
        {
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
            }
            \
IPS\Output::i()->output = $mfaOutput;
        }
       
       
/* Show it */
       
$handlers = array();
        foreach ( \
IPS\MFA\MFAHandler::handlers() as $key => $handler )
        {
            if (
$handler->isEnabled() and $handler->memberCanUseHandler( \IPS\Member::loggedIn() ) )
            {
               
$handlers[ $key ] = $handler;
            }
        }
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsMfa( $handlers );
    }
   
   
/**
     * Initial MFA Setup
     *
     * @return    string
     */
   
protected function initialMfa()
    {
       
$ref = \IPS\Http\Url::internal('');
        if ( isset( \
IPS\Request::i()->ref ) )
        {
            try
            {
               
$ref = \IPS\Http\Url\Friendly::createFromString( base64_decode( \IPS\Request::i()->ref ) );
            }
            catch ( \
Exception $e ) { }
        }
       
       
$handlers = array();
        foreach ( \
IPS\MFA\MFAHandler::handlers() as $key => $handler )
        {
            if (
$handler->isEnabled() and $handler->memberCanUseHandler( \IPS\Member::loggedIn() ) )
            {
               
$handlers[ $key ] = $handler;
            }
        }
       
        if ( isset( \
IPS\Request::i()->mfa_setup ) )
        {
            \
IPS\Session::i()->csrfCheck();
           
            foreach (
$handlers as $key => $handler )
            {
                if ( (
count( $handlers ) == 1 ) or $key == \IPS\Request::i()->mfa_method )
                {
                    if (
$handler->configurationScreenSubmit( \IPS\Member::loggedIn() ) )
                    {                            
                       
$_SESSION['MFAAuthenticated'] = time();
                        \
IPS\Output::i()->redirect( $ref );
                    }
                }
            }
        }
       
        foreach (
$handlers as $key => $handler )
        {
            if (
$handler->memberHasConfiguredHandler( \IPS\Member::loggedIn() ) )
            {
                \
IPS\Output::i()->redirect( $ref );
            }
        }

        if ( isset( \
IPS\Request::i()->_mfa ) and \IPS\Request::i()->_mfa == 'optout' )
        {
            \
IPS\Session::i()->csrfCheck();
           
            \
IPS\Member::loggedIn()->members_bitoptions['security_questions_opt_out'] = TRUE;
            \
IPS\Member::loggedIn()->save();
            \
IPS\Member::loggedIn()->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => TRUE ) );
            \
IPS\Output::i()->redirect( $ref );
        }
       
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_complete_details');
        \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( '2fa.css', 'core', 'global' ) );
        \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->mfaSetup( $handlers, \IPS\Member::loggedIn(), \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&do=initialMfa', 'front', 'settings' )->setQueryString( 'ref', base64_encode( $ref ) ) );
    }
   
   
/**
     * Enable MFA
     *
     * @return    string
     */
   
protected function enableMfa()
    {
        \
IPS\Session::i()->csrfCheck();
       
       
/* Get the handler */
       
$handlers = \IPS\MFA\MFAHandler::handlers();
       
$key = \IPS\Request::i()->type;
        if ( !isset(
$handlers[ $key ] ) or !$handlers[ $key ]->isEnabled() or !$handlers[ $key ]->memberCanUseHandler( \IPS\Member::loggedIn() ) or \IPS\MFA\MFAHandler::accessToArea( 'core', 'SecurityQuestions', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) ) )
        {
            \
IPS\Output::i()->error( 'node_error', '2C122/M', 404, '' );
        }

       
/* Include the CSS we'll need */
       
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( '2fa.css', 'core', 'global' ) );
       
       
/* Show the list again as the backdrop */
       
\IPS\Output::i()->output = $this->_wrapOutputInTemplate( 'mfa', $this->_mfa() );
               
       
/* Did we just submit it? */
       
if ( isset( \IPS\Request::i()->mfa_setup ) and $handlers[ $key ]->configurationScreenSubmit( \IPS\Member::loggedIn() ) )
        {
           
$_SESSION['MFAAuthenticated'] = time();
           
            \
IPS\Member::loggedIn()->members_bitoptions['security_questions_opt_out'] = FALSE;
            \
IPS\Member::loggedIn()->save();
           
            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
        }
               
       
/* And put the configruation modal over the top */
       
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('settings');
        \
IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'system' )->settingsMfaSetup( $handlers[ $key ]->configurationScreen( \IPS\Member::loggedIn(), FALSE, \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa&do=enableMfa&type=' . $key, 'front', 'settings_mfa' ) ), \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa&do=enableMfa&type=' . $key, 'front', 'settings_mfa' ) );
    }
   
   
/**
     * Disable MFA
     *
     * @return    string
     */
   
protected function disableMfa()
    {
        \
IPS\Session::i()->csrfCheck();
       
       
/* Get the handler */
       
$handlers = \IPS\MFA\MFAHandler::handlers();
       
$key = \IPS\Request::i()->type;
        if ( !isset(
$handlers[ $key ] ) or !$handlers[ $key ]->isEnabled() or !$handlers[ $key ]->memberCanUseHandler( \IPS\Member::loggedIn() ) or \IPS\MFA\MFAHandler::accessToArea( 'core', 'SecurityQuestions', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) ) )
        {
            \
IPS\Output::i()->error( 'node_error', '2C122/N', 404, '' );
        }
               
       
/* Disable it */
       
$handlers[ $key ]->disableHandlerForMember( \IPS\Member::loggedIn() );
        \
IPS\Member::loggedIn()->save();

       
/* If we have now disabled everything, save that we have opted out */
       
if ( \IPS\Settings::i()->mfa_required_groups != '*' and !\IPS\Member::loggedIn()->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) )
        {
           
$enabledHandlers = FALSE;
            foreach (
$handlers as $handler )
            {
                if (
$handler->memberHasConfiguredHandler( \IPS\Member::loggedIn() ) )
                {
                   
$enabledHandlers = TRUE;
                    break;
                }
            }
            if ( !
$enabledHandlers )
            {
                \
IPS\Member::loggedIn()->members_bitoptions['security_questions_opt_out'] = TRUE;
                \
IPS\Member::loggedIn()->save();
            }
        }
       
       
/* Redirect */
       
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
    }
   
   
/**
     * Security Questions
     *
     * @return    string
     */
   
protected function _securityquestions()
    {
       
$handler = new \IPS\MFA\SecurityQuestions\Handler();
       
        if ( !
$handler->isEnabled() )
        {
            \
IPS\Output::i()->error( 'requested_route_404', '2C122/J', 404, '' );
        }
               
       
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=securityquestions', 'front', 'settings_securityquestions' );
        if ( isset( \
IPS\Request::i()->initial ) )
        {
            if ( isset( \
IPS\Request::i()->ref ) )
            {
               
$url = $url->setQueryString( 'ref', \IPS\Request::i()->ref );
            }
                       
            if ( !
$handler->memberCanUseHandler( \IPS\Member::loggedIn() ) or $handler->memberHasConfiguredHandler( \IPS\Member::loggedIn() ) )
            {
               
$ref = NULL;
                if ( isset( \
IPS\Request::i()->ref ) )
                {
                    try
                    {
                       
$ref = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
                        if ( !(
$ref instanceof \IPS\Http\Url\Internal ) )
                        {
                           
$ref = NULL;
                        }
                    }
                    catch ( \
Exception $e ) { }
                }
               
                \
IPS\Output::i()->redirect( $ref ?: \IPS\Http\Url::internal( '' ) );
            }
           
           
$url = $url->setQueryString( 'initial', 1 );
        }
        elseif (
$handler->memberHasConfiguredHandler( \IPS\Member::loggedIn() ) )
        {
            if ( isset( \
IPS\Request::i()->_securityQuestionSetup ) )
            {
                return \
IPS\Theme::i()->getTemplate( 'system', 'core' )->securityQuestionsFinished();
            }
            elseif (
$output = \IPS\MFA\MFAHandler::accessToArea( 'core', 'SecurityQuestions', $url ) )
            {
                return
$output;
            }
        }
       
       
$output = $handler->configurationForm( \IPS\Member::loggedIn(), $url, !isset( \IPS\Request::i()->initial ) );
       
        if ( isset( \
IPS\Request::i()->initial ) )
        {
            \
IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
            \
IPS\Output::i()->sidebar['enabled'] = FALSE;
            \
IPS\Output::i()->output = $output;
            return;
        }
        else
        {
            return
$output;
        }
    }
   
   
/**
     * MFA Email Recovery
     *
     * @return    string
     */
   
protected function mfarecovery()
    {
       
/* Who are we */
       
if ( isset( $_SESSION['processing2FA'] ) )
        {
           
$member = \IPS\Member::load( $_SESSION['processing2FA']['memberId'] );
        }
        else
        {
           
$member = \IPS\Member::loggedIn();
        }
               
       
/* Can we use this? */
       
if ( !$member->member_id or !( ( $member->failed_mfa_attempts >= \IPS\Settings::i()->security_questions_tries and \IPS\Settings::i()->mfa_lockout_behaviour == 'email' ) or in_array( 'email', explode( ',', \IPS\Settings::i()->mfa_forgot_behaviour ) ) ) )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2C122/L', 403, '' );
        }
               
       
/* If we have an existing validation record, we can just reuse it */
       
$sendEmail = TRUE;
        try
        {
           
$existing = \IPS\Db::i()->select( array( 'vid', 'email_sent' ), 'core_validating', array( 'member_id=? AND forgot_security=1', $member->member_id ) )->first();
           
$vid = $existing['vid'];
           
           
/* If we sent an email within the last 15 minutes, don't send another one otherwise someone could be a nuisence */
           
if ( $existing['email_sent'] and $existing['email_sent'] > ( time() - 900 ) )
            {
               
$sendEmail = FALSE;
            }
            else
            {
                \
IPS\Db::i()->update( 'core_validating', array( 'email_sent' => time() ), array( 'vid=?', $vid ) );
            }
        }
        catch ( \
UnderflowException $e )
        {
           
$vid = md5( $member->members_pass_hash . \IPS\Login::generateRandomString() );

            \
IPS\Db::i()->insert( 'core_validating', array(
               
'vid'                 => $vid,
               
'member_id'           => $member->member_id,
               
'entry_date'          => time(),
               
'forgot_security'   => 1,
               
'ip_address'          => \IPS\Request::i()->ipAddress(),
               
'email_sent'          => time(),
            ) );
        }
                   
       
/* Send email */
       
if ( $sendEmail )
        {
            \
IPS\Email::buildFromTemplate( 'core', 'mfaRecovery', array( $member, $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
           
$message = "mfa_recovery_email_sent";
        }
        else
        {
           
$message = "mfa_recovery_email_already_sent";
        }
       
       
/* Show confirmation page with further instructions */
       
\IPS\Output::i()->sidebar['enabled'] = FALSE;
        \
IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('mfa_account_recovery');
        \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->mfaAccountRecovery( $message );
    }
   
   
/**
     * Validate MFA Email Recovery
     *
     * @return    void
     */
   
protected function mfarecoveryvalidate()
    {
       
/* Validate */
       
try
        {
           
$record = \IPS\Db::i()->select( '*', 'core_validating', array( 'vid=? AND member_id=? AND forgot_security=1', \IPS\Request::i()->vid, \IPS\Request::i()->mid ) )->first();
        }
        catch ( \
UnderflowException $e )
        {
            \
IPS\Output::i()->error( 'mfa_recovery_no_validation_key', '2C122/K', 410, '' );
        }
               
       
/* Remove all MFA */
       
$member = \IPS\Member::load( $record['member_id'] );
        foreach ( \
IPS\MFA\MFAHandler::handlers() as $key => $handler )
        {
           
$handler->disableHandlerForMember( $member );
        }
       
$member->failed_mfa_attempts = 0;
       
$member->save();
       
       
/* Delete validating record  */
       
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=? AND forgot_security=1', $member->member_id ) );
       
       
/* Log in if necessary */
       
if ( !\IPS\Member::loggedIn()->member_id and isset( $_SESSION['processing2FA'] ) )
        {
            ( new \
IPS\Login\Success( $member, \IPS\Login\Handler::load( $_SESSION['processing2FA']['handler'] ), $_SESSION['processing2FA']['remember'], $_SESSION['processing2FA']['anonymous'] ) )->process();
        }
       
       
/* Redirect */
       
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
    }
   
   
/**
     * Username
     *
     * @return    string
     */
   
protected function _username()
    {
       
/* Check they have permission to change their username */
       
if( !\IPS\Member::loggedIn()->group['g_dname_changes'] )
        {
            \
IPS\Output::i()->error( 'username_err_nochange', '1C122/4', 403, '' );
        }
               
        if ( \
IPS\Member::loggedIn()->group['g_displayname_unit'] )
        {
            if ( \
IPS\Member::loggedIn()->group['gbw_displayname_unit_type'] )
            {
                if ( \
IPS\Member::loggedIn()->joined->diff( \IPS\DateTime::create() )->days < \IPS\Member::loggedIn()->group['g_displayname_unit'] )
                {
                    \
IPS\Output::i()->error(
                        \
IPS\Member::loggedIn()->language()->addToStack( 'username_err_days', FALSE, array( 'sprintf' => array(
                        \
IPS\Member::loggedIn()->joined->add(
                            new \
DateInterval( 'P' . \IPS\Member::loggedIn()->group['g_displayname_unit'] . 'D' )
                        )->
localeDate()
                        ),
'pluralize' => array( \IPS\Member::loggedIn()->group['g_displayname_unit'] ) ) ),
                   
'1C122/5', 403, '' );
                }
            }
            else
            {
                if ( \
IPS\Member::loggedIn()->member_posts < \IPS\Member::loggedIn()->group['g_displayname_unit'] )
                {
                    \
IPS\Output::i()->error(
                        \
IPS\Member::loggedIn()->language()->addToStack( 'username_err_posts' , FALSE, array( 'sprintf' => array(
                        ( \
IPS\Member::loggedIn()->group['g_displayname_unit'] - \IPS\Member::loggedIn()->member_posts )
                        ),
'pluralize' => array( \IPS\Member::loggedIn()->group['g_displayname_unit'] ) ) ),
                   
'1C122/6', 403, '' );
                }
            }
        }
       
       
/* How many changes */
       
$nameCount = \IPS\Db::i()->select( 'COUNT(*) as count, MIN(log_date) as min_date', 'core_member_history', array(
           
'log_member=? AND log_app=? AND log_type=? AND log_date>?',
            \
IPS\Member::loggedIn()->member_id,
           
'core',
           
'display_name',
            \
IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Member::loggedIn()->group['g_dname_date'] . 'D' ) )->getTimestamp()
        ) )->
first();

        if ( \
IPS\Member::loggedIn()->group['g_dname_changes'] != -1 and $nameCount['count'] >= \IPS\Member::loggedIn()->group['g_dname_changes'] )
        {
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsUsernameLimitReached( \IPS\Member::loggedIn()->language()->addToStack('username_err_limit', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->group['g_dname_date'] ), 'pluralize' => array( \IPS\Member::loggedIn()->group['g_dname_changes'] ) ) ) );
        }
        else
        {
           
/* Build form */
           
$form = new \IPS\Helpers\Form;
           
$form->class = 'ipsForm_collapseTablet';
           
$form->add( new \IPS\Helpers\Form\Text( 'new_username', '', TRUE, array( 'accountUsername' => \IPS\Member::loggedIn() ) ) );
                       
           
/* Handle submissions */
           
if ( $values = $form->values() )
            {
               
/* Disable syncing */
               
$profileSync = \IPS\Member::loggedIn()->profilesync;
                if ( isset(
$profileSync['name'] ) )
                {
                    unset(
$profileSync['name'] );
                    \
IPS\Member::loggedIn()->profilesync = $profileSync;
                    \
IPS\Member::loggedIn()->save();
                }
               
               
/* Save */
               
$oldName = \IPS\Member::loggedIn()->name;
                \
IPS\Member::loggedIn()->name = $values['new_username'];
                \
IPS\Member::loggedIn()->save();
                \
IPS\Member::loggedIn()->logHistory( 'core', 'display_name', array( 'old' => $oldName, 'new' => $values['new_username'], 'by' => 'manual' ) );
               
               
/* Sync with login handlers */
               
foreach ( \IPS\Login::methods() as $method )
                {
                    try
                    {
                       
$method->changeUsername( \IPS\Member::loggedIn(), $oldName, $values['new_username'] );
                    }
                    catch( \
BadMethodCallException $e ){}
                }
               
               
/* Redirect */
               
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=username', 'front', 'settings' ), 'username_changed' );
            }
        }

        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsUsername( $form, $nameCount['count'], \IPS\Member::loggedIn()->group['g_dname_changes'], $nameCount['min_date'] ? \IPS\DateTime::ts( $nameCount['min_date'] ) : \IPS\Member::loggedIn()->joined, \IPS\Member::loggedIn()->group['g_dname_date'] );
    }
   
   
/**
     * Signature
     *
     * @return    string
     */
   
protected function _signature()
    {
       
/* Check they have permission to change their signature */
       
$sigLimits = explode( ":", \IPS\Member::loggedIn()->group['g_signature_limits']);
       
        if( !\
IPS\Settings::i()->signatures_enabled OR $sigLimits[0] )
        {
            \
IPS\Output::i()->error( 'signatures_disabled', '2C122/C', 403, '' );
        }
       
       
/* Check limits */
       
if ( \IPS\Member::loggedIn()->group['g_sig_unit'] )
        {
           
/* Days */
           
if ( \IPS\Member::loggedIn()->group['gbw_sig_unit_type'] )
            {
                if ( \
IPS\Member::loggedIn()->joined->diff( \IPS\DateTime::create() )->days < \IPS\Member::loggedIn()->group['g_sig_unit'] )
                {
                    \
IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->pluralize(
                           
sprintf(
                                    \
IPS\Member::loggedIn()->language()->get('sig_err_days'),
                                    \
IPS\Member::loggedIn()->joined->add(
                                            new \
DateInterval( 'P' . \IPS\Member::loggedIn()->group['g_sig_unit'] . 'D' )
                                    )->
localeDate()
                            ), array( \
IPS\Member::loggedIn()->group['g_sig_unit'] ) ),
                           
'1C122/D', 403, '' );
                }
            }
           
/* Posts */
           
else
            {
                if ( \
IPS\Member::loggedIn()->member_posts < \IPS\Member::loggedIn()->group['g_sig_unit'] )
                {
                    \
IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->pluralize(
                           
sprintf(
                                    \
IPS\Member::loggedIn()->language()->get('sig_err_posts'),
                                    ( \
IPS\Member::loggedIn()->group['g_sig_unit'] - \IPS\Member::loggedIn()->member_posts )
                            ), array( \
IPS\Member::loggedIn()->group['g_sig_unit'] ) ),
                           
'1C122/E', 403, '' );
                }
            }
        }
   
       
/* Build form */
       
$form = new \IPS\Helpers\Form;
       
$form->class = 'ipsForm_collapseTablet';
       
$form->add( new \IPS\Helpers\Form\YesNo( 'view_sigs', \IPS\Member::loggedIn()->members_bitoptions['view_sigs'], FALSE ) );
       
$form->add( new \IPS\Helpers\Form\Editor( 'signature', \IPS\Member::loggedIn()->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "frontsig-" .\IPS\Member::loggedIn()->member_id, 'attachIds' => array( \IPS\Member::loggedIn()->member_id ) ) ) );
       
       
/* Handle submissions */
       
if ( $values = $form->values() )
        {
            if(
$values['signature'] )
            {
               
/* Check Limits */
               
$signature = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' );
               
$signature->loadHTML( \IPS\Xml\DOMDocument::wrapHtml( $values['signature'] ) );
               
               
$errors = array();
               
               
/* Links */
               
if ( is_numeric( $sigLimits[4] ) and ( $signature->getElementsByTagName('a')->length + $signature->getElementsByTagName('iframe')->length ) > $sigLimits[4] )
                {
                   
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_links_exceeded');
                }

               
/* Number of Images */
               
if ( is_numeric( $sigLimits[1] ) and $signature->getElementsByTagName('img')->length > 0 )
                {
                   
$imageCount = 0;
                    foreach (
$signature->getElementsByTagName('img') as $img )
                    {
                        if( !
$img->hasAttribute("data-emoticon") )
                        {
                           
$imageCount++;
                        }
                    }
                    if(
$imageCount > $sigLimits[1] )
                    {
                       
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_images_exceeded');
                    }
                }
               
               
/* Size of images */
               
if ( ( is_numeric( $sigLimits[2] ) and $sigLimits[2] ) or ( is_numeric( $sigLimits[3] ) and $sigLimits[3] ) )
                {
                    foreach (
$signature->getElementsByTagName('img') as $image )
                    {
                       
$attachId    = $image->getAttribute('data-fileid');
                       
$checkSrc    = TRUE;

                        if(
$attachId )
                        {
                            try
                            {
                               
$attachment = \IPS\Db::i()->select( 'attach_location, attach_thumb_location', 'core_attachments', array( 'attach_id=?', $attachId ) )->first();
                               
$imageProperties = \IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] ?: $attachment['attach_location'] )->getImageDimensions();

                               
$checkSrc    = FALSE;
                            }
                            catch( \
UnderflowException $e ){}
                        }

                        if(
$checkSrc )
                        {
                           
$src = $image->getAttribute('src');
                            \
IPS\Output::i()->parseFileObjectUrls( $src );

                           
$imageProperties = @getimagesize( $src );
                           
                           
/* getimagesize failed so let's try to use getimagesizefromstring */
                           
if( !$imageProperties )
                            {
                                try
                                {
                                   
$image = \IPS\Http\Url::external( $src )->request()->get();
                                   
$imageProperties = getimagesizefromstring( $image->content );
                                }
                                catch ( \
IPS\Http\Request\Exception $e ) {}
                            }
                        }
                        else
                        {
                           
$src = (string) \IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->url;
                        }
                       
                        if(
is_array( $imageProperties ) AND count( $imageProperties ) )
                        {
                            if(
$imageProperties[0] > $sigLimits[2] OR $imageProperties[1] > $sigLimits[3] )
                            {
                               
$errors[] = \IPS\Member::loggedIn()->language()->addToStack( 'sig_imagetoobig', FALSE, array( 'sprintf' => array( $src, $sigLimits[2], $sigLimits[3] ) ) );
                            }
                        }
                        else
                        {
                           
$errors[] = \IPS\Member::loggedIn()->language()->addToStack( 'sig_imagenotretrievable', FALSE, array( 'sprintf' => array( $src ) ) );
                        }
                    }
                }
               
               
/* Lines */
               
$preBreaks = 0;
               
               
/* Make sure we are not trying to bypass the limit by using <pre> tags, which will not have <p> or <br> tags in its content */
               
foreach( $signature->getElementsByTagName('pre') AS $pre )
                {
                   
$content = nl2br( trim( $pre->nodeValue ) );
                   
$preBreaks += count( explode( "<br />", $content ) );
                }
               
                if (
is_numeric( $sigLimits[5] ) and ( $signature->getElementsByTagName('p')->length + $signature->getElementsByTagName('br')->length + $preBreaks ) > $sigLimits[5] )
                {
                   
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_lines_exceeded');
                }
            }
           
            if( !empty(
$errors ) )
            {
               
$form->error = \IPS\Member::loggedIn()->language()->addToStack('sig_restrictions_exceeded');
               
$form->elements['']['signature']->error = \IPS\Member::loggedIn()->language()->formatList( $errors );
               
                return \
IPS\Theme::i()->getTemplate( 'system' )->settingsSignature( $form, $sigLimits );
            }
           
            \
IPS\Member::loggedIn()->signature = $values['signature'];
            \
IPS\Member::loggedIn()->members_bitoptions['view_sigs'] = $values['view_sigs'];
           
            \
IPS\Member::loggedIn()->save();
            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=signature', 'front', 'settings' ), 'signature_changed' );
        }

        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsSignature( $form, $sigLimits );
    }
   
   
/**
     * Login Method
     *
     * @return    string
     */
   
protected function _login()
    {
       
/* Load method */
       
try
        {
           
$method = \IPS\Login\Handler::load( \IPS\Request::i()->service );
            if ( !
$method->showInUcp( \IPS\Member::loggedIn() ) )
            {
                throw new \
OutOfRangeException;
            }
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'page_doesnt_exist', '2C122/B', 404, '' );
        }
       
       
/* Are we connected? */
       
$blurb = 'profilesync_blurb';
        try
        {
           
$connected = $method->canProcess( \IPS\Member::loggedIn() );
            if (
$connected )
            {
               
$photoUrl = $method->userProfilePhoto( \IPS\Member::loggedIn() );
               
$profileName = $method->userProfileName( \IPS\Member::loggedIn() );
            }
        }
        catch ( \
IPS\Login\Exception $e )
        {
           
$connected = FALSE;
           
$blurb = 'profilesync_expire_blurb';
        }
               
       
/* Are we connected? */
       
if ( $connected )
        {            
           
/* Can we disassociate? */
           
$canDisassociate = FALSE;
            foreach ( \
IPS\Login::methods() as $_method )
            {
                if (
$_method->id != $method->id and $_method->canProcess( \IPS\Member::loggedIn() ) )
                {
                   
$canDisassociate = TRUE;
                    break;
                }
            }
            if (
$canDisassociate and isset( \IPS\Request::i()->disassociate ) )
            {                
                \
IPS\Session::i()->csrfCheck();
               
$method->disassociate();
               
                if (
$method->showInUcp( \IPS\Member::loggedIn() ) )
                {
                    \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ) );
                }
                else
                {
                    \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings', 'front', 'settings' ) );
                }
            }
           
           
/* Show sync options */
           
$form = NULL;
            if (
$syncOptions = $method->syncOptions( \IPS\Member::loggedIn() ) )
            {
               
$form = new \IPS\Helpers\Form( 'sync', 'profilesync_save' );
               
$form->class = 'ipsForm_vertical';
                foreach (
$syncOptions as $option )
                {
                    if (
$option == 'photo' and !\IPS\Member::loggedIn()->group['g_edit_profile'] )
                    {
                        continue;
                    }
                    if (
$option == 'cover' and ( !\IPS\Member::loggedIn()->group['g_edit_profile'] or !\IPS\Member::loggedIn()->group['gbw_allow_upload_bgimage'] ) )
                    {
                        continue;
                    }
                    if (
$option == 'status' and ( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'status' ) ) or !\IPS\core\Statuses\Status::canCreateFromCreateMenu( \IPS\Member::loggedIn() ) or !\IPS\Settings::i()->profile_comments or \IPS\Member::loggedIn()->group['gbw_no_status_update'] ) )
                    {
                        continue;
                    }
                   
                    if (
$option == 'status' )
                    {
                       
$checked = ( isset( \IPS\Member::loggedIn()->profilesync[ $option ] ) and array_key_exists( $method->id, \IPS\Member::loggedIn()->profilesync[ $option ]) );
                    }
                    else
                    {
                       
$checked = ( isset( \IPS\Member::loggedIn()->profilesync[ $option ] ) and  \IPS\Member::loggedIn()->profilesync[ $option ]['handler'] == $method->id );
                    }
                   
$field = new \IPS\Helpers\Form\Checkbox( "profilesync_{$option}", $checked, FALSE, array( 'labelSprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $method->_title ) ) ), NULL, NULL, NULL, "profilesync_{$option}_{$method->id}" );
                    if (
$checked and ( ( $option == 'status' and $error = \IPS\Member::loggedIn()->profilesync[ $option ][ $method->id ]['error'] ) or ( $option != 'status' and $error = \IPS\Member::loggedIn()->profilesync[ $option ]['error'] ) ) )
                    {
                       
$field->description = \IPS\Theme::i()->getTemplate( 'system' )->settingsLoginMethodSynError( $error );
                    }        
                   
$form->add( $field );
                }
                if (
$values = $form->values() )
                {
                   
$profileSync = \IPS\Member::loggedIn()->profilesync;
                   
$changes = array();
                   
                    foreach (
$values as $k => $v )
                    {
                       
$option = mb_substr( $k, 12 );
                        if (
$option === 'status' )
                        {
                            if ( isset( \
IPS\Member::loggedIn()->profilesync[ $option ][ $method->id ] ) )
                            {
                                if ( !
$v )
                                {
                                    unset(
$profileSync[ $option ][ $method->id ] );
                                   
$changes[ $option ] = FALSE;
                                }
                            }
                            else
                            {
                                if (
$v )
                                {
                                   
$profileSync[ $option ][ $method->id ] = array( 'lastsynced' => NULL, 'error' => NULL );
                                   
$changes[ $option ] = TRUE;
                                }
                            }
                        }
                        else
                        {
                            if ( isset( \
IPS\Member::loggedIn()->profilesync[ $option ] ) and  \IPS\Member::loggedIn()->profilesync[ $option ]['handler'] == $method->id )
                            {
                                if ( !
$v )
                                {
                                    unset(
$profileSync[ $option ] );
                                   
$changes[ $option ] = FALSE;
                                }
                            }
                            else
                            {
                                if (
$v )
                                {
                                   
$profileSync[ $option ] = array( 'handler' => $method->id, 'ref' => NULL, 'error' => NULL );
                                   
$changes[ $option ] = TRUE;
                                }
                            }
                        }
                    }
                   
                    if (
count( $changes ) )
                    {
                        \
IPS\Member::loggedIn()->logHistory( 'core', 'social_account', array( 'changed' => $changes, 'handler' => $method->id, 'service' => $method::getTitle() ) );
                    }
                   
                    \
IPS\Member::loggedIn()->profilesync = $profileSync;
                    \
IPS\Member::loggedIn()->save();
                    \
IPS\Member::loggedIn()->profileSync();
                   
                    \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ) );
                }
            }
           
           
$extraPermissions = NULL;
           
$login = NULL;
            if ( isset( \
IPS\Request::i()->scopes ) )
            {
               
$method = \IPS\Login\Handler::findMethod('IPS\Login\Handler\Oauth2\Facebook');
               
$extraPermissions = \IPS\Request::i()->scopes;
               
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id . '&scopes=' . \IPS\Request::i()->scopes, 'front', 'settings_login' ), \IPS\Login::LOGIN_UCP );
               
$login->reauthenticateAs = \IPS\Member::loggedIn();

                try
                {
                    if (
$success = $login->authenticate( $method ) )
                    {                
                        if (
$success->member->member_id === \IPS\Member::loggedIn()->member_id )
                        {
                           
$method->completeLink( \IPS\Member::loggedIn(), NULL );
                            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ) );
                        }
                    }
                }
                catch( \
Exception $ex ) { }
            }
                   
           
/* Display */
           
return \IPS\Theme::i()->getTemplate( 'system' )->settingsLoginMethodOn( $method, $form, $canDisassociate, $photoUrl, $profileName, $extraPermissions, $login );
        }
       
       
/* No - show option to connect */
       
else
        {            
           
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ), \IPS\Login::LOGIN_UCP );
           
$login->reauthenticateAs = \IPS\Member::loggedIn();
           
$error = NULL;
            try
            {
                if (
$success = $login->authenticate( $method ) )
                {                    
                    if (
$success->member->member_id === \IPS\Member::loggedIn()->member_id )
                    {
                       
$method->completeLink( \IPS\Member::loggedIn(), NULL );
                        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ) );
                    }
                    else
                    {
                       
$error = \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_already_associated', FALSE, array( 'sprintf' => array( $method->_title ) ) );
                    }
                }
            }
            catch ( \
IPS\Login\Exception $e )
            {
                if (
$e->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT )
                {
                    if (
$e->member->member_id === \IPS\Member::loggedIn()->member_id )
                    {
                       
$method->completeLink( \IPS\Member::loggedIn(), NULL );
                        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=login&service=' . $method->id, 'front', 'settings_login' ) );
                    }
                    else
                    {
                       
$error = \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_email_exists', FALSE, array( 'sprintf' => array( $method->_title ) ) );
                    }
                }
                else
                {
                   
$error = $e->getMessage();
                }
            }
           
            return \
IPS\Theme::i()->getTemplate( 'system' )->settingsLoginMethodOff( $method, $login, $error, $blurb );
        }
    }
   
   
/**
     * Apps
     *
     * @return    string
     */
   
protected function _apps()
    {
       
$apps = array();
       
        foreach ( \
IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ), 'issued DESC' ) as $accessToken )
        {
            try
            {
               
$client = \IPS\Api\OAuthClient::load( $accessToken['client_id'] );
                if ( !
$client->enabled )
                {
                    throw new \
OutOfRangeException;
                }
                if ( !
$client->ucp )
                {
                    continue;
                }
               
                if ( !isset(
$apps[ $client->client_id ] ) )
                {
                   
$apps[ $client->client_id ] = array(
                       
'issued'    => $accessToken['issued'],
                       
'client'    => $client,
                       
'scopes'    => array()
                    );
                }
                else
                {
                    if (
$accessToken['issued'] < $apps[ $client->client_id ]['issued'] )
                    {
                       
$apps[ $client->client_id ]['issued'] = $accessToken['issued'];
                    }
                }
               
               
$scopes = array();
                if (
$accessToken['scope'] and $authorizedScopes = json_decode( $accessToken['scope'] ) )
                {
                   
$availableScopes = json_decode( $client->scopes, TRUE );
                    foreach (
$authorizedScopes as $scope )
                    {
                        if ( isset(
$availableScopes[ $scope ] ) and !isset( $apps[ $client->client_id ]['scopes'][ $scope ] ) )
                        {
                           
$apps[ $client->client_id ]['scopes'][ $scope ] = $availableScopes[ $scope ]['description'];
                        }
                    }
                }
            }
            catch ( \
OutOfRangeException $e ) { }
        }
               
        return \
IPS\Theme::i()->getTemplate( 'system' )->settingsApps( $apps );
    }
       
   
/**
     * Change App Permissions
     *
     * @return    string
     */
   
protected function revokeApp()
    {
        \
IPS\Session::i()->csrfCheck();
       
        try
        {
           
$client = \IPS\Api\OAuthClient::load( \IPS\Request::i()->client_id );
            \
IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=? AND member_id=?', $client->client_id, \IPS\Member::loggedIn()->member_id ) );
            \
IPS\Member::loggedIn()->logHistory( 'core', 'oauth', array( 'type' => 'revoked_access_token', 'client' => $client->client_id ) );
        }
        catch ( \
Exception $e ) { }
               
        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=apps', 'front', 'settings_apps' ) );
    }
   
   
/**
     * Disable All Signatures
     *
     * @return    void
     */
   
protected function toggleSigs()
    {
        if ( !\
IPS\Settings::i()->signatures_enabled )
        {
            \
IPS\Output::i()->error( 'signatures_disabled', '2C122/F', 403, '' );
        }
           
        \
IPS\Session::i()->csrfCheck();
           
        if ( \
IPS\Member::loggedIn()->members_bitoptions['view_sigs'] )
        {
            \
IPS\Member::loggedIn()->members_bitoptions['view_sigs'] = 0;
        }
        else
        {
            \
IPS\Member::loggedIn()->members_bitoptions['view_sigs'] = 1;
        }
       
        \
IPS\Member::loggedIn()->save();
       
        if ( \
IPS\Request::i()->isAjax() )
        {
            \
IPS\Output::i()->json( 'OK' );
        }
       
       
$redirectUrl = ( !empty( $_SERVER['HTTP_REFERER'] ) ) ? \IPS\Http\Url::external( $_SERVER['HTTP_REFERER'] ) : \IPS\Http\Url::internal( "app=core&module=system&controller=settings", 'front', 'settings' );
        \
IPS\Output::i()->redirect( $redirectUrl, 'signature_pref_toggled' );
    }
   
   
/**
     * Dismiss Profile Completion
     *
     * @return    void
     */
   
protected function dismissProfile()
    {
        \
IPS\Session::i()->csrfCheck();
       
        \
IPS\Member::loggedIn()->members_bitoptions['profile_completion_dismissed'] = TRUE;
        \
IPS\Member::loggedIn()->save();
       
        if ( \
IPS\Request::i()->isAjax() )
        {
            \
IPS\Output::i()->json( 'OK' );
        }
        else
        {
           
$redirectUrl = ( !empty( $_SERVER['HTTP_REFERER'] ) ) ? \IPS\Http\Url::external( $_SERVER['HTTP_REFERER'] ) : \IPS\Http\Url::internal( "app=core&module=system&controller=settings", 'front', 'settings' );
            \
IPS\Output::i()->redirect( $redirectUrl );
        }
    }
   
   
/**
     * Completion Wizard
     *
     * @return    void
     */
   
protected function completion()
    {
       
$steps = array();
       
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&do=completion', 'front', 'settings' );

        foreach( \
IPS\Application::allExtensions( 'core', 'ProfileSteps' ) AS $extension )
        {
            if (
method_exists( $extension, 'wizard') AND count( $extension::wizard() ) )
            {
               
$steps = array_merge( $steps, $extension::wizard() );
            }
        }

       
$steps = \IPS\Member\ProfileStep::setOrder( $steps );

       
$steps = array_merge( $steps, array( 'profile_done' => function( $data ) {
            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=settings", 'front', 'settings' ), 'saved' );
        } ) );
       
       
$wizard = new \IPS\Helpers\Wizard( $steps, $url, TRUE, NULL, TRUE );
   
        \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( '2fa.css', 'core', 'global' ) );
        \
IPS\Output::i()->cssFiles    = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/settings.css' ) );
       
        \
IPS\Output::i()->bodyClasses[]            = 'ipsLayout_minimal';
        \
IPS\Output::i()->sidebar['enabled']    = FALSE;
        \
IPS\Output::i()->title                    = \IPS\Member::loggedIn()->language()->addToStack( 'complete_your_profile' );
        \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->completeWizard( $wizard );
    }

   
/**
     * Subscribe to newsletter
     *
     * @return    void
     */
   
protected function newsletterSubscribe()
    {
        \
IPS\Session::i()->csrfCheck();


        \
IPS\Member::loggedIn()->allow_admin_mails = TRUE;
        \
IPS\Member::loggedIn()->save();

        if ( \
IPS\Request::i()->isAjax() )
        {
            \
IPS\Output::i()->json( 'OK' );
        }

       
$ref = \IPS\Request::i()->ref;
        if (
$ref and $ref = @base64_decode( $ref ) )
        {
            try
            {
               
$ref = \IPS\Http\Url::createFromString( $ref );
                if ( !(
$ref instanceof \IPS\Http\Url\Internal ) or $ref->base !== 'front' )
                {
                    throw new \
Exception;
                }
            }
            catch ( \
Exception $e )
            {
               
$ref = \IPS\Http\Url::internal( "app=core&module=system&controller=settings", 'front', 'settings' );
            }
        }
        else
        {
           
$ref = \IPS\Http\Url::internal( "app=core&module=system&controller=settings", 'front', 'settings' );
        }

        \
IPS\Output::i()->redirect( $ref, 'block_newsletter_subscribed' );
    }
}