* Invision Power Services * IP.Board v3.1.0 * Vkontakte Connect Library * Created by Ivan Gavrikov * * * @author $Author: giv $ * @copyright (c) 2010 IBResource, LTD. * @license http://www.ibresource.ru/products/ipboard/ * @package IP.Board * @link http://www.ibresource.ru * @version $Rev: 6242 $ * */ class Vkontakte_Request_Validator { /** * Vkontakte API key * * @var string */ protected $_api_key = ''; /** * Vkontakte API Secret * * @var string */ protected $_secret = ''; /** * VK data * * @var array */ private $_data = array(); /** * Valid vk data names * * @var array */ private $_valid_params = array( 'expire', 'mid', 'secret', 'sid', 'sig' ); /** * Request validity flag * * @var boolean */ private $_valid = true; /** * undocumented function * * @param string $api_key * @param string $secret * @author GiV */ public function __construct( $api_key, $secret ) { $this->_api_key = $api_key; $this->_secret = $secret; $this->_validate_params(); } /** * Get request validity status * * @return boolean */ public function isValid() { return $this->_valid; } /** * Check vkontakte data parameters validity * compare signature of recieved params and * recieved signature * * @return void */ private function _validate_params() { if( isset($_COOKIE[ 'vk_app_' . $this->_api_key ]) ) { $params = explode( '&', $_COOKIE[ 'vk_app_' . $this->_api_key ]); foreach( $params as &$param ) { $param_data = explode( '=', $param ); if( count( $param_data) > 1 ) { if( in_array( $param_data[0], $this->_valid_params ) ) { $this->_data[ $param_data[0] ] = $param_data[1]; if ( 'sig' == $param_data[0] ) { $param = ''; } } } } if( time() > $this->expire ) { $this->_valid = false; } if( $this->sig != $this->_getSignature( $params ) ) { $this->_valid = false; } return; } $this->_valid = false; return; } /** * Make signature for data array * * @param string $data Array * @return string */ private function _getSignature( $data ) { return md5( implode('', $data) . $this->_secret ); } /** * Magic method for setting data value * from request * * @param string $key * @param string $value * @return mixed */ public function __set( $key, $value ) { $this->_data[ $key ] = $value; } /** * Magic method for getting data value * from request * * @param string $key * @return mixed */ public function __get( $key ) { if( isset( $this->_data[ $key ] ) && $this->_valid ) { return $this->_data[ $key ]; } return false; } } /** * Class for working with vkontakte users * * @author $Author: giv $ * @copyright (c) 2010 IBResource, LTD. * @license http://www.ibresource.ru/products/ipboard/ * @package IP.Board * @link http://www.ibresource.ru * @version $Rev: 6242 $ */ class vkontakte_connect { /**#@+ * Registry Object Shortcuts * * @access protected * @var object */ protected $DB; protected $settings; protected $lang; protected $member; protected $memberData; protected $cache; protected $caches; /**#@-*/ /** * IPBs log in handler * * @access private * @var object */ private $_login; /** * User: Token * * @access private * @var string */ private $_token; /** * User: Data * * @access private * @var array */ private $_userData = array(); /** * Construct. * * @access public * @return void */ public function __construct( $registry, $token='' ) { /* Make object */ $this->registry = $registry; $this->_token = $token; $this->DB = $this->registry->DB(); $this->settings =& $this->registry->fetchSettings(); $this->request =& $this->registry->fetchRequest(); $this->lang = $this->registry->getClass('class_localization'); $this->member = $this->registry->member(); $this->memberData =& $this->registry->member()->fetchMemberData(); $this->cache = $this->registry->cache(); $this->caches =& $this->registry->cache()->fetchCaches(); } /** * Show the vkontakte connect page. * * @access public * @return redirect */ public function redirectToConnectPage() { $template = $this->registry->getClass('output')->getTemplate('vkontakte')->showLogInVkFrame( $this->member->form_hash ); print $template; } /** * Completes the connection * * @access public * @return redirect */ public function finishLogin() { $vk = new Vkontakte_Request_Validator( $this->settings['vk_api_id'], $this->settings['vk_secret'] ); if ( $vk->mid ) { /* Got a member linked already? */ $_member = IPSMember::load( $vk->mid, 'all', 'vk_uid' ); if ( $_member['member_id'] ) { /* Check for partial member id */ $pmember = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'members_partial', 'where' => "partial_member_id=" . $_member['member_id'] ) ); if ( $pmember['partial_member_id'] ) { $this->_token = $_REQUEST['key']; $this->DB->update( 'vkontakte_connect', array( 'v_key' => $pmember['partial_date']), "v_key='" . IPSText::md5Clean( $this->_token ) . "'" ); $this->registry->getClass('output')->silentRedirect( $this->settings['base_url'] . 'app=core&module=global§ion=register&do=complete_login&mid='. $_member['member_id'].'&key='.$pmember['partial_date'] ); } else { /* Here, so log us in!! */ $this->_login()->loginWithoutCheckingCredentials( $_member['member_id'], TRUE ); $this->registry->getClass('output')->silentRedirect( $this->settings['base_url'] ); } } else { $this->_token = $_REQUEST['key']; $this->fetchUserData(); if ( $vk->mid !== $this->_userData['id'] ) { throw new Exception( 'CREATION_FAIL' ); } /* From reg, so create new account properly */ $toSave = array( 'core' => array( 'name' => IPSText::convertCharsets( $this->_userData['name'], 'utf-8', IPS_DOC_CHAR_SET ), 'members_display_name' => '', 'members_created_remote' => 1, 'member_group_id' => ( $this->settings['vk_mgid'] ) ? $this->settings['vk_mgid'] : $this->settings['member_group'], 'email' => '', 'vk_uid' => $this->_userData['id']), 'extendedProfile' => array( 'avatar_location' => $this->_userData['photo'], 'avatar_type' => 'vk' ) ); $memberData = IPSMember::create( $toSave, TRUE, FALSE, FALSE ); if ( ! $memberData['member_id'] ) { throw new Exception( 'CREATION_FAIL' ); } $pmember = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'members_partial', 'where' => "partial_member_id=" . $memberData['member_id'] ) ); if ( $pmember['partial_member_id'] ) { $this->DB->update( 'vkontakte_connect', array( 'v_key' => $pmember['partial_date']), "v_key='" . IPSText::md5Clean( $this->_token ) . "'" ); $this->registry->getClass('output')->silentRedirect( $this->settings['base_url'] . 'app=core&module=global§ion=register&do=complete_login&mid='. $memberData['member_id'].'&key='.$pmember['partial_date'] ); } else { throw new Exception( 'CREATION_FAIL' ); } } } } /** * Return user data * * @access public * @return array */ public function fetchUserData() { //echo $this->_token; exit; $connectData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'vkontakte_connect', 'where' => "v_key='" . IPSText::md5Clean( $this->_token ) . "'" ) ); if ( !$connectData['v_key'] ) { /* @todo something more useful here */ throw new Exception( "NO_KEY_FOUND" ); } $this->_userData = array( 'name' => $connectData['v_user'], 'photo' => $connectData['v_data'], 'id' => $connectData['v_id'] ); return $this->_userData; } /** * Finish a log-in connection * WARNING: NO PERMISSION CHECKS ARE PERFORMED IN THIS FUNCTION. * * @access public * @param int Forum ID of original member (member to keep) * @param int Forum ID of linking member (member to remove) * @return boolean */ public function finishNewConnection( $originalId, $newId ) { $this->DB->delete( 'vkontakte_connect', "v_key='" . IPSText::md5Clean( $this->_token ) . "'" ); if ( $originalId AND $newId ) { $original = IPSMember::load( $originalId, 'all' ); $new = IPSMember::load( $newId, 'all' ); if ( $original['member_id'] AND $new['vk_uid'] ) { IPSMember::save( $original['member_id'], array( 'core' => array( 'vk_uid' => $new['vk_uid'] ) ) ); return true; } } return false; } /** * Check if vk user linked to forum account * * @return boolean|array */ public function isLinked() { $vk = new Vkontakte_Request_Validator( $this->settings['vk_api_id'], $this->settings['vk_secret'] ); if ( $vk->mid ) { $_member = IPSMember::load( $vk->mid, 'all', 'vk_uid' ); return $_member['member_id']; } else { return false; } } /** * Make linking local account and current vk user * * @return boolean */ public function linkTo( $mid ) { $vk = new Vkontakte_Request_Validator( $this->settings['vk_api_id'], $this->settings['vk_secret'] ); if ( $vk->mid ) { $isLinked = $this->isLinked(); if( ! $isLinked OR $isLinked !== $mid ) { if ( $isLinked ) IPSMember::remove( $isLinked ); IPSMember::save( $mid, array( 'core' => array( 'vk_uid' => intval($vk->mid) ) ) ); return true; } } return false; } /** * Accessor for the log in functions * * @access public * @return object */ public function _login() { if ( ! is_object( $this->_login ) ) { require_once( IPS_ROOT_PATH . 'sources/handlers/han_login.php' ); $this->_login = new han_login( $this->registry ); $this->_login->init(); } return $this->_login; } }