<?php
/**
* @brief Converter Login Handler
* @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 15 October 2017
*/
namespace IPS\convert;
/* 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;
}
/**
* Standard Internal Database Login Handler
*/
class _Login extends \IPS\Login\Handler
{
use \IPS\Login\Handler\UsernamePasswordHandler;
/**
* Get title
*
* @return string
*/
public static function getTitle()
{
return 'login_handler_Convert';
}
/**
* Authenticate
*
* @param \IPS\Login $login The login object
* @param string $usernameOrEmail The username or email address provided by the user
* @param string $password The plaintext password provided by the user
* @return \IPS\Member
* @throws \IPS\Login\Exception
*/
public function authenticateUsernamePassword( \IPS\Login $login, $usernameOrEmail, $password )
{
/* Get member(s) */
$where = array();
$params = array();
if ( $this->authType() & \IPS\Login::AUTH_TYPE_USERNAME )
{
$where[] = 'name=?';
$params[] = $usernameOrEmail;
if ( $usernameOrEmail !== \IPS\Request::legacyEscape( $usernameOrEmail ) )
{
$where[] = 'name=?';
$params[] = \IPS\Request::legacyEscape( $usernameOrEmail );
}
}
if ( $this->authType() & \IPS\Login::AUTH_TYPE_EMAIL )
{
$where[] = 'email=?';
$params[] = $usernameOrEmail;
if ( $usernameOrEmail !== \IPS\Request::legacyEscape( $usernameOrEmail ) )
{
$where[] = 'email=?';
$params[] = \IPS\Request::legacyEscape( $usernameOrEmail );
}
}
$members = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array_merge( array( implode( ' OR ', $where ) ), $params ) ), 'IPS\Member' );
/* If we didn't match any, throw an exception */
if ( !count( $members ) )
{
$type = 'username_or_email';
switch ( $this->authType() )
{
case \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL:
$type = 'username_or_email';
break;
case \IPS\Login::AUTH_TYPE_USERNAME:
$type = 'username';
break;
case \IPS\Login::AUTH_TYPE_EMAIL:
$type = 'email_address';
break;
}
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack('login_err_no_account', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $type ) ) ) ), \IPS\Login\Exception::NO_ACCOUNT );
}
/* Table switcher for new converters */
try
{
foreach( $members as $member )
{
if( $this->authenticatePasswordForMember( $member, $password ) )
{
return $member;
}
}
}
catch( \IPS\Db\Exception $e )
{
/* Converter tables no longer exist */
if( $e->getCode() == 1146 )
{
throw new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::INTERNAL_ERROR );
}
}
/* Still here? Throw a password incorrect exception */
throw new \IPS\Login\Exception( 'login_err_bad_password', \IPS\Login\Exception::BAD_PASSWORD, NULL, isset( $member ) ? $member : NULL );
}
/**
* @brief Convert app cache
*/
protected static $_apps;
/**
* Authenticate
*
* @param \IPS\Member $member The member
* @param string $password The plaintext password provided by the user
* @return bool
*/
public function authenticatePasswordForMember( \IPS\Member $member, $password )
{
if( static::$_apps === NULL )
{
static::$_apps = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'convert_apps', array( 'login=?', 1 ) ), 'IPS\convert\App' );
}
foreach( static::$_apps as $app )
{
/* Strip underscores from keys */
$sw = str_replace( "_", "", $app->key );
/* Get converter classname */
try
{
$application = $app->getSource( FALSE, FALSE );
}
/* Converter application class no longer exists, but we want to continue since we may have a login method here */
catch( \InvalidArgumentException $e )
{
$application = NULL;
}
/* Check at least one of the login methods exist */
if ( !method_exists( $this, $sw ) AND ( $application === NULL OR !method_exists( $application, 'login' ) ) )
{
continue;
}
/* We still want to use the parent methods (no sense in recreating them) so copy conv_password_extra to misc */
$member->misc = $member->conv_password_extra;
/* New login method */
if( class_exists( $application ) AND method_exists( $application, 'login' ) )
{
$success = $application::login( $member, $password );
}
/* Deprecated method */
else
{
$success = $this->$sw( $member, $password );
}
unset( $member->misc );
unset( $member->changed['misc'] );
if ( $success )
{
/* Update password and return */
$member->conv_password = NULL;
$member->conv_password_extra = NULL;
$member->setLocalPassword( $password );
$member->save();
$member->memberSync( 'onPassChange', array( $password ) );
return $member;
}
}
return FALSE;
}
/**
* Can this handler process a login for a member?
*
* @return bool
*/
public function canProcess( \IPS\Member $member )
{
return (bool) $member->conv_password;
}
/**
* Can this handler process a password change for a member?
*
* @return bool
*/
public function canChangePassword( \IPS\Member $member )
{
return FALSE;
}
/**
* Change Password
*
* @param \IPS\Member $member The member
* @param string $newPassword New Password
* @return void
*/
public function changePassword( \IPS\Member $member, $newPassword )
{
$member->setLocalPassword( $newPassword );
$member->save();
}
/**
* Show in Account Settings?
*
* @param \IPS\Member|NULL $member The member, or NULL for if it should show generally
* @return bool
*/
public function showInUcp( \IPS\Member $member = NULL )
{
return FALSE;
}
/**
* AEF
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function aef( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, md5( $member->misc . $password ) ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* BBPress Standalone
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function bbpressstandalone( $member, $password )
{
return $this->bbpress( $member, $password );
}
/**
* BBPress
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function bbpress( $member, $password )
{
$success = false;
$password = html_entity_decode( $password );
$hash = $member->conv_password;
if ( \strlen( $hash ) == 32 )
{
$success = ( bool ) ( \IPS\Login::compareHashes( $member->conv_password, md5( $password ) ) );
}
// Nope, not md5.
if ( ! $success )
{
$hashLibrary = new \IPS\convert\Login\HashCryptPrivate;
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$crypt = $hashLibrary->hashCryptPrivate( $password, $hash, $itoa64, 'P' );
if ( $crypt[ 0 ] == '*' )
{
$crypt = crypt( $password, $hash );
}
if ( $crypt == $hash )
{
$success = true;
}
}
// Nope
if ( ! $success )
{
// No - check against WordPress.
// Note to self - perhaps push this to main bbpress method.
$success = \IPS\convert\Software\Core\Wordpress::login( $member, $password );
}
return $success;
}
/**
* BBPress 2.3
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function bbpress23( $member, $password )
{
return $this->bbpress( $member, $password );
}
/**
* Community Server
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function cs( $member, $password )
{
$encodedHashPass = base64_encode( pack( "H*", sha1( base64_decode( $member->misc ) . $password ) ) );
if ( \IPS\Login::compareHashes( $member->conv_password, $encodedHashPass ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* CSAuth
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function csauth( $member, $password )
{
$wsdl = 'https://internal.auth.com/Service.asmx?wsdl';
$dest = 'https://interal.auth.com/Service.asmx';
$single_md5_pass = md5( $password );
try
{
$client = new SoapClient( $wsdl, array( 'trace' => 1 ) );
$client->__setLocation( $dest );
$loginparams = array( 'username' => $member->name, 'password' => $password );
$result = $client->AuthCS( $loginparams );
switch( $result->AuthCSResult )
{
case 'SUCCESS' :
return TRUE;
case 'WRONG_AUTH' :
return FALSE;
default :
return FALSE;
}
}
catch( Exception $ex )
{
return FALSE;
}
}
/**
* Discuz
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function discuz( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $password ) . $member->misc ) ) )
{
return TRUE;
}
return FALSE;
}
/**
* FudForum
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function fudforum( $member, $password )
{
$success = false;
$single_md5_pass = md5( $password );
$hash = $member->conv_password;
if ( \strlen( $hash ) == 40 )
{
$success = ( \IPS\Login::compareHashes( $member->conv_password, sha1( $member->misc . sha1( $password ) ) ) ) ? TRUE : FALSE;
}
else
{
$success = ( \IPS\Login::compareHashes( $member->conv_password, $single_md5_pass ) ) ? TRUE : FALSE;
}
return $success;
}
/**
* FusionBB
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function fusionbb( $member, $password )
{
/* FusionBB Has multiple methods that can be used to check a hash, so we need to cycle through them */
/* md5( md5( salt ) . md5( pass ) ) */
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $member->misc ) . md5( $password ) ) ) )
{
return TRUE;
}
/* md5( md5( salt ) . pass ) */
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $member->misc ) . $password ) ) )
{
return TRUE;
}
/* md5( pass ) */
if ( \IPS\Login::compareHashes( $member->conv_password, md5( $password ) ) )
{
return TRUE;
}
return FALSE;
}
/**
* Ikonboard
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function ikonboard( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, crypt( $password, $member->misc ) ) )
{
return TRUE;
}
else if ( \IPS\Login::compareHashes( $member->conv_password, md5( $password . mb_strtolower( $member->conv_password_extra ) ) ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Kunena
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function kunena( $member, $password )
{
// Kunena authenticates using internal Joomla functions.
// This is required, however, if the member only converts from
// Kunena and not Joomla + Kunena.
return \IPS\convert\Software\Core\Joomla::login( $member, $password );
}
/**
* PHP Legacy (2.x)
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function phpbblegacy( $member, $password )
{
return \IPS\convert\Software\Core\Phpbb::login( $member, $password );
}
/**
* Vbulletin 5.1
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function vb51connect( $member, $password )
{
return \IPS\convert\Software\Core\Vbulletin5::login( $member, $password );
}
/**
* Vbulletin 5
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function vb5connect( $member, $password )
{
return \IPS\convert\Software\Core\Vbulletin5::login( $member, $password );
}
/**
* Vbulletin 3.8
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function vbulletinlegacy( $member, $password )
{
return \IPS\convert\Software\Core\Vbulletin::login( $member, $password );
}
/**
* Vbulletin 3.6
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function vbulletinlegacy36( $member, $password )
{
return \IPS\convert\Software\Core\Vbulletin::login( $member, $password );
}
/**
* SMF Legacy
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function smflegacy( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->name ) . $password ) ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Telligent
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function telligentcs( $member, $password )
{
return $this->cs( $member, $password );
}
/**
* WoltLab 4.x
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function woltlab( $member, $password )
{
$testHash = FALSE;
/* If it's not blowfish, then we don't have a salt for it. */
if ( !preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $member->conv_password ) )
{
$salt = mb_substr( $member->conv_password, 0, 29 );
$testHash = crypt( crypt( $password, $salt ), $salt );
}
if ( $testHash AND \IPS\Login::compareHashes( $member->conv_password, $testHash ) )
{
return TRUE;
}
elseif ( $this->woltlablegacy( $member, $password ) )
{
return TRUE;
}
return FALSE;
}
/**
* WoltLab 3.x
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function woltlablegacy( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, sha1( $member->misc . sha1( $member->misc . sha1( $password ) ) ) ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* PHP Fusion
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function phpfusion( $member, $password )
{
return ( bool ) \IPS\Login::compareHashes( $member->conv_password, md5( md5( $password ) ) );
}
/**
* fluxBB
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function fluxbb( $member, $password )
{
$success = false;
$hash = $member->conv_password;
if ( \strlen( $hash ) == 40 )
{
if ( \IPS\Login::compareHashes( $hash, sha1( $member->misc . sha1( $password ) ) ) )
{
$success = TRUE;
}
elseif ( \IPS\Login::compareHashes( $hash, sha1( $password ) ) )
{
$success = TRUE;
}
}
else
{
$success = ( \IPS\Login::compareHashes( $hash, md5( $password ) ) ) ? TRUE : FALSE;
}
return $success;
}
/**
* Simplepress Forum
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
protected function simplepress( $member, $password )
{
return \IPS\convert\Software\Core\Wordpress::login( $member, $password );
}
}