<?php
/*
 * system/login/AbstractUser.class.php
 * 
 * CopyRight(C) 2010 Shopformer Development Team. All Right Reserved
 * URL: http://sourceforge.jp/projects/shopformer/
 * 
 * 
 * Mail: m_nakashima@users.sourceforge.jp
 * Auther: Masanori Nakashima
 * Last Update: 2010-06-18
 */
require_once(dirname(dirname(__FILE__))
.DIRECTORY_SEPARATOR.'AbstractData.class.php');
/**
 * システムログインユーザーの為の抽象クラス
 * @package system
 * @subpackage login
 * @category DAO
 * @version 1.2.00
 * @copyright Copyright(c)2011, Shopformer Development Team. <shopformer-dev@lists.sourceforge.jp>
 * @author Masanori Nakashima
 * @access public
 */
class system_login_AbstractUser extends system_AbstractData {
	/** 最終ログイン日時	*/
	var $lastLoginDate;
	/** 最終アクセスユーザーエージェント	*/
	var $userAgent;
	/** 最終アクセスリモートアドレス	*/
	var $remoteAddress;
	/**
	 * コンストラクタ
	 */
	function system_login_AbstractUser() {
	}
	/**
	 * ログインIDからユーザーを読み込む抽象メソッド
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @param string $loginId ログインID
	 * @return boolean 一意のユーザを読み込むことができればtrue
	 */
	function loadLoginUser( & $request, $loginId ) {
		return false;
	}
	/**
	 * ユーザーユニークIDからユーザーを読み込む抽象メソッド
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @param string $loginId ログインID
	 * @return boolean 一意のユーザを読み込むことができればtrue
	 */
	function loadLoginUserByUniqueId( & $request, $email ) {
		return false;
	}
	/**
	 * メールアドレスからユーザーを読み込む抽象メソッド
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @param string $loginId ログインID
	 * @return boolean 一意のユーザを読み込むことができればtrue
	 */
	function loadLoginUserByEmail( & $request, $uniqueId ) {
		return false;
	}
	/**
	 * ログイン履歴を記録する抽象メソッド
	 */
	function registLoginHistory( & $request, $loginResult ) {
		return false;
	}
	/**
	 * ログインIDを取得します
	 * @return string $loginId システム全体で一意のログイン識別記号
	 */
	function getLoginId(){
		return false;
	}
	/**
	 * ログインパスワードを取得します
	 * @return string $loginPassword ログインパスワード
	 */
	function getLoginPassword(){
		return false;
	}
	/**
	 * ログインパスワードを照合します
	 * @param spider_HttpRequest &$request spider_HttpRequestクラスオブジェクトの参照
	 * @param string $inputPassword 入力されたパスワード
	 * @return boolean パスワードが一致したらtrue
	 */
	function confirmPassword( & $request, $inputPassword ) {
		return false;
	}
	/**
	 * ログインパスワードをランダムに再発行します
	 * @param spider_HttpRequest &$request spider_HttpRequestクラスオブジェクトの参照
	 * @return mixed 新しく発行したパスワード文字列。再発行に失敗したらfalse。
	 */
	function renewPassword( & $request ) {
		require_once(DIR_PATH_LIB.DIRECTORY_SEPARATOR.'util'.DIRECTORY_SEPARATOR.'CharUtility.class.php');
		return util_CharUtility::get_rundom_key( 8 );
	}
	/**
	 * 実装メソッド：パスワードをシステム設定された方法で暗号化
	 * 暫定でここに追加。バージョン２ではライブラリ場所を検討すること
	 */
	function encodePassword( & $request, $password, $key='', $type='' ) {
		$encryptObject	= new system_Encrypt($key, $type);
		$pass	= $encryptObject->encode($password, $key);
		return $pass;
	}
	function decodePassword( & $request, $password, $key='', $type='' ) {
		$encryptObject	= new system_Encrypt($key, $type);
		$pass	= $encryptObject->decode($password,$key);
		return $pass;
	}
	/**
	 * ログイン者の表示名称を取得します
	 * @return string $viewName ログイン者の表示名
	 */
	function getUserViewName() {
		return false;
	}
	/**
	 * 最終ログイン日時を設定します
	 * @param $lastLoginDatetime
	 */
	function setLastLoginDate($lastLoginDatetime) {
		$this->lastLoginDate	= $lastLoginDatetime;
		return true;
	}
	/**
	 * 最終ログイン日時を取得します
	 */
	function getLastLoginDate(){
		return $this->lastLoginDate;
	}
	/**
	 * ログイン時のユーザーエージェントを設定します
	 */
	function setLoginUserAgent( $userAgent ) {
		$this->userAgent	= $userAgent;
		return true;
	}
	/**
	 * ログイン時のユーザーエージェントを取得します
	 */
	function getLoginUserAgent() {
		return $this->userAgent;
	}
	/**
	 * ログイン時のリモートアドレスを設定します
	 */
	function setLoginRemoteAddress($remoteAddress) {
		$this->remoteAddress	= $remoteAddress;
		return true;
	}
	/**
	 * ログイン時のリモートアドレスを取得します
	 */
	function getLoginRemoteAddress(){
		return $this->remoteAddress;
	}
	/**
	 * システムグループレベルを取得します
	 * SYSTEM_LOGIN_USER_ADMINISTRATORS 0 管理者グループ
	 * SYSTEM_LOGIN_USER_POWERS パワーユーザーグループ
	 * SYSTEM_LOGIN_USER_USERS 一般ユーザーグループ
	 * SYSTEM_LOGIN_USER_GUEST ゲストユーザー グループ
	 */
	function getLoginGroupLevel(){
		return SYSTEM_LOGIN_USER_GUEST;
	}
	//
	// アクセス権限に関するメソッド
	//
	/**
	 * 指定のパッケージIDを利用する権限があるか確認します
	 */
	function hasPackagePermittion( $packageId ) {
		return false;
	}
	/**
	 * 指定のURIを表示する権限があるか確認します
	 */
	function hasUriPermittion( $uri ) {
		return true;
	}
	//
	// データ扱いの権限に関するメソッド
	//
	/**
	 * 本オブジェクトのデータを渡されたAbstractUserが閲覧できるか確認します
	 * @param &$request spider_HttpRequestオブジェクト
	 * @param $abstractUser system_login_AbstractUser実装クラスインスタンス
	 */
	function canViewData( & $request, $abstractUser ) {
		return true;
	}
	/**
	 * 渡されたAbstractDataオブジェクトに対する編集権限があるか確認します
	 * @param &$request spider_HttpRequestオブジェクト
	 * @param $abstractUser system_login_AbstractUser実装クラスインスタンス
	 */
	function canEditData( & $request, $abstractUser ) {
		return true;
	}
	//
	// データの編集ロックに関するメソッド
	//
	/**
	 * 渡されたAbstractDataオブジェクトに対して編集ロックを取得します。
	 * @param &$request spider_HttpRequestオブジェクト参照
	 * @param $abstractData system_AbstractData実装オブジェクト
	 * @param $lifeTime ロックの有効秒数
	 * @return ロックを取得出来たらtrue
	 */
	function lockData( & $request, $abstractData, $lifeTime=300 ) {
		if( $abstractData->canEditData( $request, $this ) ) {
			// 編集可能なデータならロック識別子を生成する
			$operatorId	= $this->lockCreateOperatorId();
			return $abstractData->lock( $operatorId, $lifeTime );
		}
		return false;
	}
	/**
	 * 渡されたAbstractDataオブジェクトに対して編集ロックを解除します。
	 * @param &$request spider_HttpRequestオブジェクト参照
	 * @param $abstractData system_AbstractData実装オブジェクト
	 * @return ロックを解除出来たらtrue
	 */
	function unlockData( & $request, $abstractData ) {
		if( $abstractData->canEditData( $request, $this ) ) {
			$operatorId	= $this->lockCreateOperatorId();
			return $abstractData->unlock( $operatorId );
		}
		return false;
	}
	/**
	 * 渡されたAbstractDataオブジェクトに対して編集ロックを取得可能か確認します。
	 * @param &$request spider_HttpRequestオブジェクト参照
	 * @param $abstractData system_AbstractData実装オブジェクト
	 * @return ロックを取得できるならtrue/取得できないならfalse
	 */
	function canLockData( & $request, $abstractData ) {
		if( $abstractData->canEditData( $request, $this ) ) {
			$operatorId	= $this->lockCreateOperatorId();
			return $abstractData->canLock( $operatorId );
		}
		return false;
	}
	/**
	 * 本オブジェクトユーザーでロックに使用するロック固有識別子を取得します。
	 * @return ロック固有識別子の文字列
	 */
	function lockCreateOperatorId() {
		return get_class($this)."\r\n"
			.$this->getUniqueId();
	}
	//
	// system_AbstractDataの実装とオーバーライド
	//
	/**
	 * データ保存区分名を取得する抽象メソッドの実装
	 * 固定で'user'とする
	 */
	function getDataClassName(){
		return 'user';
	}
	/**
	 * データの保存区分名を設定する抽象メソッド
	 * 'user'固定の為設定できず常にtrueを返す
	 */
	function setDataClassName( $dataClassName ) {
		return true;
	}
	/**
	 * データ固有のIDを取得する抽象メソッドの実装
	 * ログインIDを返します
	 */
	function getUniqueId(){
		return $this->getLoginId();
	}
	/**
	 * データ保存区分と設定メンバ値に従って本オブジェクトのデータを保存するメソッドのオーバーロード
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @return boolean 保存に成功したらtrue, 失敗したらfalse
	 */
	function save( & $request ) {
		return parent::save( $request, 2, false );
	}
	/**
	 * データ保存区分と固有IDから保存データを本オブジェクトのメンバ値に読み込むメソッドのオーバーロード
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @param string $uniqueId 固有ID
	 * @return boolean 読み込みに成功したらtrue, 失敗したらfalse
	 */
	function load( & $request, $uniqueId ) {
		return parent::load( $request, $uniqueId, 2 );
	}
	/**
	 * データ保存区分と固有IDから保存データを削除するメソッドのオーバーロード
	 * @param object $request spider_HttpRequestクラスのインスタンス
	 * @param string $uniqueId 固有ID
	 * @param integer $explodeIdLength 0以下の場合分割しない
	 * @param boolean $saveSlaveServers trueならスレーブサーバのdataフォルダがマウントされている場合にスレーブサーバも削除する
	 * @return boolean 削除に成功したらtrue, 失敗したらfalse
	 */
	function delete( & $request, $uniqueId ) {
		return parent::delete( $request, $uniqueId, 2, false );
	}
	//
	// パスワードリマインダ関連
	//
	/**
	 * パスワードリマインダメールを送信
	 * @param $request spider_HttpRequestオブジェクト参照
	 * @param $targetAddress 送信ターゲットメールアドレス指定
	 */
	function sendPaswordReminder( & $request, $targetAddress ) {
		if( $newPassword = $this->renewPassword( $request ) ) {
			return $this->sendInformationMail( $request, $targetAddress, 'mail.password',array(),null,$newPassword );
		} else {
			return false;
		}
	}
	/**
	 * このユーザー宛てにメールを送信
	 * @param $request spider_HttpRequestオブジェクト参照
	 * @param $targetAddress 送信ターゲットメールアドレス指定
	 * @param $templateName メールテンプレート名
	 * @param $replaceHash 置き換えワード
	 * @param $bcc ブラインドカーボンコピーアドレス
	 * @return 成功したらtrue、失敗したらfalse
	 */
	function sendInformationMail( & $request, $targetAddress, $templateName, $replaceHash=array(), $bcc=null, $loginPassword='' ) {
		// 宛先確認
		if( strlen(trim($targetAddress)) == 0 ) {
			$request->addLocaledError('system.error.login.user.nomail',SPIDER_LOG_LEVEL_ERROR,array());
			return false;
		} else if( trim($this->email) != trim($targetAddress) ) {
			$request->addLocaledError('system.error.login.user.invalidmail',SPIDER_LOG_LEVEL_ERROR,array());
			return false;
		}
		// メールオブジェクト
		$mailerObject	= $request->getSystemMailer();
		if( false === $mailerObject ) {
			$request->addLocaledError('system.error.login.user.nomailsetting',SPIDER_LOG_LEVEL_ERROR,array());
			return false;
		}
		// メール送信テンプレート情報
		$from	= '';
		$reply	= '';
		$return	= '';
		$mailInformationHash	= $this->getMailInformation( $request, $templateName, $targetAddress );
		if( false === $mailInformationHash ) {
			return;
		} else {
			// 件名と本文の取得
			$mailerObject->subject		= $mailInformationHash['subject'];
			$mailerObject->text_body	= $mailInformationHash['text_body'];
			$mailerObject->html_body	= $mailInformationHash['html_body'];
			// from
			if( isset($mailInformationHash['from']) && strlen($mailInformationHash['from']) > 0 ) {
				$from	= $mailInformationHash['from'];
				if( isset($mailInformationHash['from_name'])
				&& strlen($mailInformationHash['from_name']) > 0 ) {
					$from	= $mailInformationHash['from_name'].' <'.$from.'>';
				}
			} else if( defined('SYSTEM_MAIL_FROM_ADDRESS') ){
				$from	= SYSTEM_MAIL_FROM_ADDRESS;
				if( defined('SYSTEM_MAIL_FROM_NAME') ){
					$from	= SYSTEM_MAIL_FROM_NAME.' <'.$from.'>';
				}
			} else {
				$request->addLocaledError('system.error.login.user.nomailsetting',SPIDER_LOG_LEVEL_ERROR,array());
				return false;
			}
			// reply
			if( isset($mailInformationHash['reply']) && strlen($mailInformationHash['reply']) > 0 ) {
				$reply	= $mailInformationHash['reply'];
			} else if( defined('SYSTEM_MAIL_REPLY_ADDRESS') ){
				$reply	= SYSTEM_MAIL_REPLY_ADDRESS;
			} else {
				$reply	= $from;
			}
			// return
			if( isset($mailInformationHash['return']) && strlen($mailInformationHash['return']) > 0 ) {
				$return	= $mailInformationHash['return'];
			} else if( defined('SYSTEM_MAIL_REPLY_ADDRESS') ){
				$return	= SYSTEM_MAIL_RETURN_ADDRESS;
			} else {
				$return	= $from;
			}
		}
		
		// 置換ワード設定
		$mailerObject->addReplaceWord('url_base',SPIDER_URL_BASE);
		$mailerObject->addReplaceWord('base_url',SPIDER_URL_BASE);
		$mailerObject->addReplaceWord('login_id',$this->getLoginId());
//		$mailerObject->addReplaceWord('login_password',$this->getLoginPassword());
		if( is_null($loginPassword) || strlen($loginPassword) == 0 ) {
			$mailerObject->addReplaceWord('login_password','設定済みパスワード');
		} else {
			$mailerObject->addReplaceWord('login_password',$loginPassword);
		}
		$mailerObject->addReplaceWordByObject( 'user', $this );
		if( is_array($replaceHash) ) {
			foreach( $replaceHash as $repWord => $repValue ) {
				if( is_object($repValue) ) {
					$mailerObject->addReplaceWordByObject( $repWord, $repValue );
				} else if( is_array($repValue) ) {
					$mailerObject->addReplaceWordByHash( $repWord, $repValue );
				} else {
					$mailerObject->addReplaceWord( $repWord, $repValue );
				}
			}
		}
		
		// 送信実行
		$result	= $mailerObject->send( $targetAddress, null, null, $from, $reply, $return );
		if( $result ) {
			return true;
		} else {
			$request->addLocaledError('system.error.login.user.failtomail',SPIDER_LOG_LEVEL_ERROR,array());
			return false;
		}
	}
	/**
	 * 送信メールテンプレート情報を名称から取得
	 * @param $request spider_HttpRequestオブジェクト参照
	 * @param $templateName メールテンプレート名
	 * @return array array('subject'=>[], 'text_body'=>[], 'html_body'=>[], 'from'=>[], 'from_name'=>[], 'reply'=>[], 'return'=>[] )
	 */
	function getMailInformation( & $request, $templateName, $targetAddress ) {
		$infoHash	= array();
		// 定義済みの携帯宛か確認
		$isMobile	= false;
		if( isset($GLOBALS['MOBILE_MAIL_DOMAIN_ARRAY'])
		&& is_array($GLOBALS['MOBILE_MAIL_DOMAIN_ARRAY']) ) {
			foreach( $GLOBALS['MOBILE_MAIL_DOMAIN_ARRAY'] as $fqdn ) {
				$fqdn_regx	= str_replace('.','\\.',$fqdn );
				if( preg_match('/\\@'.$fqdn_regx.'$/',$targetAddress) > 0 ) {
					$isMobile	= true;
					break;
				}
			}
		}
		// /DATA/data/system/の下を優先して読み込む
		$templateFileName	= $templateName.'.default.txt';
		if( $isMobile ) {
			$templateFileName	= $templateName.'.mobile.txt';
		}
		$templateFilePath	= DIR_PATH_DATA
		.DIRECTORY_SEPARATOR.'system'
		.DIRECTORY_SEPARATOR.'tools'
		.DIRECTORY_SEPARATOR.'notify'
		.DIRECTORY_SEPARATOR.$templateFileName;
		if( !file_exists($templateFilePath) ) {
			// ファイルがなければ、デフォルトのテンプレートを読み込む
			$templateFilePath	= dirname(dirname(__FILE__))
			.DIRECTORY_SEPARATOR.'tools'
			.DIRECTORY_SEPARATOR.'notify'
			.DIRECTORY_SEPARATOR.'mailbody'.DIRECTORY_SEPARATOR.$templateFileName;
			if( !file_exists($templateFilePath) ) {
				$request->addLocaledError('system.error.login.user.nomailtemplate',SPIDER_LOG_LEVEL_ERROR,array());
				return false;
			}
		}
		// ファイル内容を読み込み
		if( file_exists( $templateFilePath ) ) {
			if( $lines = @file( $templateFilePath ) ) {
				$infoHash['subject']	= array_shift( $lines );
				$infoHash['subject']	= str_replace("\r\n","",$infoHash['subject']);
				$infoHash['subject']	= str_replace("\r","",$infoHash['subject']);
				$infoHash['subject']	= str_replace("\n","",$infoHash['subject']);
				$infoHash['text_body']	= implode('',$lines);
				$infoHash['text_body']	= str_replace("\r\n","\n",$infoHash['text_body']);
				$infoHash['text_body']	= str_replace("\r","\n",$infoHash['text_body']);
				return $infoHash;
			} else {
				$request->addLocaledError('system.error.login.user.failtoopenmailtemplate',SPIDER_LOG_LEVEL_ERROR,array());
			}
		} else {
			$request->addLocaledError('system.error.login.user.nomailtemplate',SPIDER_LOG_LEVEL_ERROR,array());
		}
		return false;
	}
}
/**
 * 
 */
class system_Encrypt {
	var $CI;
	var $encryptionKey	= '';
	var $_hashType	= 'sha1';
	var $_mcryptExists = FALSE;
	var $_mcryptCipher;
	var $_mcryptMode;
	/**
	 * Constructor
	 * Simply determines whether the mcrypt library exists.
	 */
	function system_Encrypt($encliptionKey=null,$hashType=null){
		$this->_mcryptExists = ( ! function_exists('mcrypt_encrypt')) ? FALSE : TRUE;
		if( !is_null($hashType) && preg_match('/^([mM][dD]5|[sS][hH][aA]1)$/',$hashType) > 0 ){
			$this->_hashType	= $hashType;
		} else {
			$this->_hashType = ( defined('SYSTEM_CRYPT_METHOD')
			&& preg_match('/^[mM][dD]5$/',SYSTEM_CRYPT_METHOD) > 0 ) ? 'md5' : 'sha1';
		}
		if( !is_null($encliptionKey) && strlen($encliptionKey) > 0 ){
			$this->encryptionKey	= $encliptionKey;
		}
	}
	/**
	 * Fetch the encryption key
	 * Returns it as MD5 in order to have an exact-length 128 bit key.
	 * Mcrypt is sensitive to keys that are not the correct length
	 * @access	public
	 * @param	string
	 * @return	string
	 */
	function get_key($key = ''){
		if ($key == ''){
			if ($this->encryptionKey != ''){
				return $this->encryptionKey;
			}
			if( defined('SYSTEM_CRYPT_KEY') && strlen(SYSTEM_CRYPT_KEY)>0){
				$key	= SYSTEM_CRYPT_KEY;
			}
		}
		return md5($key);
	}
	// --------------------------------------------------------------------
	/**
	 * Set the encryption key
	 *
	 * @access	public
	 * @param	string
	 * @return	void
	 */
	function set_key($key = ''){
		$this->encryptionKey = $key;
	}
	// --------------------------------------------------------------------
	/**
	 * Encode
	 *
	 * Encodes the message string using bitwise XOR encoding.
	 * The key is combined with a random hash, and then it
	 * too gets converted using XOR. The whole thing is then run
	 * through mcrypt (if supported) using the randomized key.
	 * The end result is a double-encrypted message string
	 * that is randomized with each call to this function,
	 * even if the supplied message and key are the same.
	 *
	 * @access	public
	 * @param	string	the string to encode
	 * @param	string	the key
	 * @return	string
	 */
	function encode($string, $key = ''){
		$key = $this->get_key($key);
		$enc = $this->_xor_encode($string, $key);
		if ($this->_mcryptExists === TRUE){
			$enc = $this->mcrypt_encode($enc, $key);
		}
		return base64_encode($enc);
	}
	// --------------------------------------------------------------------
	/**
	 * Decode
	 *
	 * Reverses the above process
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function decode($string, $key = ''){
		$key = $this->get_key($key);
		if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string)){
			return FALSE;
		}
		$dec = base64_decode($string);
		if ($this->_mcryptExists === TRUE){
			if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE){
				return FALSE;
			}
		}
		return $this->_xor_decode($dec, $key);
	}
	/**
	 * XOR Encode
	 *
	 * Takes a plain-text string and key as input and generates an
	 * encoded bit-string using XOR
	 *
	 * @access	private
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function _xor_encode($string, $key){
		$rand = '';
		while (strlen($rand) < 32){
			$rand .= mt_rand(0, mt_getrandmax());
		}
		$rand = $this->hash($rand);
		$enc = '';
		for ($i = 0; $i < strlen($string); $i++){
			$enc .= substr($rand, ($i % strlen($rand)), 1).(substr($rand, ($i % strlen($rand)), 1) ^ substr($string, $i, 1));
		}
		return $this->_xor_merge($enc, $key);
	}
	/**
	 * XOR Decode
	 *
	 * Takes an encoded string and key as input and generates the
	 * plain-text original message
	 *
	 * @access	private
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function _xor_decode($string, $key){
		$string = $this->_xor_merge($string, $key);
		$dec = '';
		for ($i = 0; $i < strlen($string); $i++){
			$dec .= (substr($string, $i++, 1) ^ substr($string, $i, 1));
		}
		return $dec;
	}
	/**
	 * XOR key + string Combiner
	 *
	 * Takes a string and key as input and computes the difference using XOR
	 *
	 * @access	private
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function _xor_merge($string, $key){
		$hash = $this->hash($key);
		$str = '';
		for ($i = 0; $i < strlen($string); $i++){
			$str .= substr($string, $i, 1) ^ substr($hash, ($i % strlen($hash)), 1);
		}
		return $str;
	}
	/**
	 * Encrypt using Mcrypt
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function mcrypt_encode($data, $key){
		$init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
		$init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND);
		return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key);
	}
	/**
	 * Decrypt using Mcrypt
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function mcrypt_decode($data, $key){
		$data = $this->_remove_cipher_noise($data, $key);
		$init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
		if ($init_size > strlen($data)){
			return FALSE;
		}
		$init_vect = substr($data, 0, $init_size);
		$data = substr($data, $init_size);
		return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0");
	}
	/**
	 * Adds permuted noise to the IV + encrypted data to protect
	 * against Man-in-the-middle attacks on CBC mode ciphers
	 * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV
	 *
	 * Function description
	 *
	 * @access	private
	 * @param	string
	 * @param	string
	 * @return	string
	 */
	function _add_cipher_noise($data, $key){
		$keyhash = $this->hash($key);
		$keylen = strlen($keyhash);
		$str = '';
		for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j){
			if ($j >= $keylen){
				$j = 0;
			}
			$str .= chr((ord($data[$i]) + ord($keyhash[$j])) % 256);
		}
		return $str;
	}
	/**
	 * Removes permuted noise from the IV + encrypted data, reversing
	 * _add_cipher_noise()
	 *
	 * Function description
	 *
	 * @access	public
	 * @param	type
	 * @return	type
	 */
	function _remove_cipher_noise($data, $key){
		$keyhash = $this->hash($key);
		$keylen = strlen($keyhash);
		$str = '';
		for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j){
			if ($j >= $keylen){
				$j = 0;
			}
			$temp = ord($data[$i]) - ord($keyhash[$j]);
			if ($temp < 0){
				$temp = $temp + 256;
			}
			$str .= chr($temp);
		}
		return $str;
	}
	/**
	 * Set the Mcrypt Cipher
	 *
	 * @access	public
	 * @param	constant
	 * @return	string
	 */
	function set_cipher($cipher){
		$this->_mcryptCipher = $cipher;
	}
	/**
	 * Set the Mcrypt Mode
	 *
	 * @access	public
	 * @param	constant
	 * @return	string
	 */
	function set_mode($mode){
		$this->_mcryptMode = $mode;
	}
	/**
	 * Get Mcrypt cipher Value
	 *
	 * @access	private
	 * @return	string
	 */
	function _get_cipher(){
		if ($this->_mcryptCipher == ''){
			$this->_mcryptCipher = MCRYPT_RIJNDAEL_256;
		}
		return $this->_mcryptCipher;
	}
	/**
	 * Get Mcrypt Mode Value
	 *
	 * @access	private
	 * @return	string
	 */
	function _get_mode(){
		if ($this->_mcryptMode == ''){
			$this->_mcryptMode = MCRYPT_MODE_ECB;
		}
		return $this->_mcryptMode;
	}
	/**
	 * Set the Hash type
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */
	function set_hash($type = 'sha1'){
		$this->_hashType = ($type != 'sha1' AND $type != 'md5') ? 'sha1' : $type;
	}
	/**
	 * Hash encode a string
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */	
	function hash($str){
		return ($this->_hashType == 'sha1') ? $this->sha1($str) : md5($str);
	}
	/**
	 * Generate an SHA1 Hash
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */
	function sha1($str){
		if ( ! function_exists('sha1')){
			if ( ! function_exists('mhash')){
				require_once(BASEPATH.'libraries/Sha1'.EXT);
				$SH = new CI_SHA;
				return $SH->generate($str);
			} else {
				return bin2hex(mhash(MHASH_SHA1, $str));
			}
		} else {
			return sha1($str);
		}
	}
}
?>