<?php

/*
 * @link http://kodcloud.com/
 * @author warlee | e-mail:kodcloud@qq.com
 * @copyright warlee 2014.(Shanghai)Co.,Ltd
 * @license http://kodcloud.com/tools/license/license.txt
 */

class userIndex extends Controller {
	private $user;  //用户相关信息
	function __construct() {
		parent::__construct();
	}
	public function index(){
		include(TEMPLATE.'user/index.html');
	}
	// 进入初始化; total=10ms左右;
	public function init(){
		Hook::trigger('globalRequestBefore');
		Hook::bind('beforeShutdown','user.index.shutdownEvent');
		if( !file_exists(USER_SYSTEM . 'install.lock') ){
			return ActionCall('install.index.check');
		}
		$this->initDB();   		//
		$this->initSession();   //
		$this->initSetting();   // 
		init_check_update();	// 升级检测处理;
		KodIO::initSystemPath();
		
		Action('filter.index')->bind();
		$this->loginCheck();
		Model('Plugin')->init();
		Action('filter.index')->trigger();
	}
	public function shutdownEvent(){
		TaskQueue::addSubmit();		// 结束后有任务时批量加入
		TaskRun::autoRun();			// 定期执行及延期任务处理;
		CacheLock::unlockRuntime(); // 清空异常时退出,未解锁的加锁;
	}
	private function initDB(){
		think_config($GLOBALS['config']['databaseDefault']);
		think_config($GLOBALS['config']['database']);
	}
	private function initSession(){
		$this->apiSignCheck();
		$systemPassword = Model('SystemOption')->get('systemPassword');
		$accessToken = isset($_REQUEST['accessToken']) ? $_REQUEST['accessToken'] : '';
		if($accessToken && strlen($accessToken) < 500){
			$pass = substr(md5('kodbox_'.$systemPassword),0,15);
			$sessionSign = Mcrypt::decode($accessToken,$pass);
			if(!$sessionSign){show_json(LNG('common.loginTokenError'),ERROR_CODE_LOGOUT);}
			Session::sign($sessionSign);
		}
		
		if(!Session::get('kod')){
			Session::set('kod',1);
			if(!Session::get('kod')){show_tips(LNG('explorer.sessionSaveError'));}
		}
		// 注意: Session设置sessionid的cookie;两个请求时间过于相近,可能导致删除cookie失败的问题;(又有sessionid请求覆盖)
		// 设置csrf防护;
		if(!Cookie::get('CSRF_TOKEN')){Cookie::set('CSRF_TOKEN',rand_string(16));}
	}
	private function initSetting(){
		if(!defined('STATIC_PATH')){
			define('STATIC_PATH',$GLOBALS['config']['settings']['staticPath']);
		}
		$sysOption = Model('SystemOption')->get();
		$upload = &$GLOBALS['config']['settings']['upload'];
		if(isset($sysOption['chunkSize'])){ //没有设置则使用默认;
			$upload['chunkSize']  = floatval($sysOption['chunkSize']);
			$upload['ignoreName'] = trim($sysOption['ignoreName']);
			$upload['chunkRetry'] = intval($sysOption['chunkRetry']);
			$upload['threads'] 	  = floatval($sysOption['threads']);
			// $upload['httpSendFile']  = $sysOption['httpSendFile'] == '1'; //前端默认屏蔽;
			
			// 上传限制扩展名,限制单文件大小;
			$role = Action('user.authRole')->userRoleAuth();
			if($role && $role['info']){
				$roleInfo = $role['info'];
				// if(isset($roleInfo['ignoreExt'])){
				// 	$upload['ignoreExt']  = $roleInfo['ignoreExt'];
				// }
				if(isset($roleInfo['ignoreFileSize'])){
					$upload['ignoreFileSize']  = $roleInfo['ignoreFileSize'];
				}
			}
			if($sysOption['downloadSpeedOpen']){//限速大小;
				$upload['downloadSpeed'] = floatval($sysOption['downloadSpeed']);
			}
		}
		$upload['chunkSize'] = $upload['chunkSize']*1024*1024;
		$upload['chunkSize'] = $upload['chunkSize'] <= 1024*1024*0.1 ? 1024*1024*0.4:$upload['chunkSize'];
		$upload['chunkSize'] = intval($upload['chunkSize']);
	}

	/**
	 * 登录检测;并初始化数据状态
	 * 通过session或kodToken检测登录
	 */
	public function loginCheck() {
		if( is_array(Session::get('kodUser')) ){
			return $this->userDataInit();
		}
		if(Session::get('kodUserLogoutTrigger')){return;} //主动设置退出,不自动登录;
		$userID 	= Cookie::get('kodUserID');
		$loginToken = Cookie::get('kodToken');
		if ($userID && $loginToken ) {
			$user = Model('User')->getInfo($userID);
			if ($user && $user['status'] != '0' && $this->makeLoginToken($user['userID']) == $loginToken ) {
				return $this->loginSuccess($user);
			}
		}
	}
	
	private function logoutError($msg,$code){
		Session::destory();
		Cookie::remove('kodToken');
		show_json($msg,$code);
	}
	private function userDataInit() {
		$this->user = Session::get('kodUser');
		if($this->user){
			$findUser = Model('User')->getInfoFull($this->user['userID']);
			// 用户账号hash对比; 账号密码修改自动退出处理;
			if($findUser['userHash'] != $this->user['userHash']){
				$this->logoutError(LNG('common.loginTokenError').'(hash)',ERROR_CODE_LOGOUT);
			}
			$this->user = $findUser;
			Session::set('kodUser',$findUser);
		}
		if(!$this->user) {
			$this->logoutError('user data error!',ERROR_CODE_LOGOUT);
		}else if($this->user['status'] == 0) {
			$this->logoutError(LNG('user.userEnabled'),ERROR_CODE_USER_INVALID);
		}else if($this->user['roleID'] == '') {
			$this->logoutError(LNG('user.roleError'),ERROR_CODE_LOGOUT);
		}
		
		$GLOBALS['isRoot'] = 0;
		$role = Model('SystemRole')->listData($this->user['roleID']);
		if($role['administrator'] == '1'){
			$GLOBALS['isRoot'] = 1;
		}
		
		// 计划任务处理; 目录读写所有者为系统;
		if( strtolower(ACTION) == 'user.view.call'){
			define('USER_ID','0');
			define('MY_HOME','');
			define('MY_DESKTOP','');
			return;
		}
				
		define('USER_ID',$this->user['userID']);
		define('MY_HOME',KodIO::make($this->user['sourceInfo']['sourceID']));
		define('MY_DESKTOP',KodIO::make($this->user['sourceInfo']['desktop']));
	}

	public function accessToken(){
		$pass = Model('SystemOption')->get('systemPassword');
		$pass = substr(md5('kodbox_'.$pass),0,15);
		$token = Mcrypt::encode(Session::sign(),$pass,3600*24*30);
		return $token;
	}
	public function accessTokenGet(){
		if(!Session::get('kodUser')){
			show_json('user not login!',ERROR_CODE_LOGOUT);
		}
		show_json($this->accessToken(),true);
	}
	
	// 登录校验并自动跳转 (已登录则直接跳转,未登录则登录成功后跳转)
	public function autoLogin(){
		$link = $this->in['link'];
		if(!$link) return;

		$errorTips = _get($this->in,'msg','');
		$errorTips = $errorTips == '[API LOGIN]' ? '':$errorTips; // 未登录标记,不算做登录错误;
		if(Session::get('kodUser') && !$errorTips){
			$param = 'kodTokenApi='.$this->accessToken();
			if($this->in['callbackToken'] == '1'){
				$link .= strstr($link,'?') ? '&'.$param:'?'.$param;
			}
			header('Location:'.$link);exit;
		}
		
		$param  = '#user/login&link='.rawurlencode($link);
		$param .= isset($this->in['msg']) ? "&msg=".$this->in['msg']:'';
		$param .= isset($this->in['callbackToken']) ? '&callbackToken=1':'';
		header('Location:'.APP_HOST.$param);exit;
	}

	/**
	 * 根据用户名密码获取用户信息
	 * @param [type] $name
	 * @param [type] $password
	 */
	public function userInfo($name, $password){
		$user = Model("User")->userLoginCheck($name,$password);
		if(!is_array($user)) {
			$userHook = Hook::trigger("user.index.userInfo",$name, $password);
			if(is_array($userHook)) return $userHook;// 第三方登陆不做检测处理;
		}
		return Hook::trigger('user.index.loginSubmitBefore',$name,$user);
	}
	
	/**
	 * 退出处理
	 */
	public function logout() {
		$user = Session::get('kodUser');
		if(!is_array($user) || !$user['userID']){show_json('ok');}
		Hook::trigger('user.index.logoutBefore',$user);
		
		$lastLogin = time() - $GLOBALS['config']['cache']['sessionTime'] - 10;
		Model('User')->userEdit($user['userID'],array("lastLogin"=>$lastLogin));
		Session::destory();
		Cookie::remove(SESSION_ID,true);
		Cookie::remove('kodToken');
		show_json('ok');
	}

	/**
	 * 登录数据提交处理；登录跳转：
	 */
	public function loginSubmit() {
		$res = $this->loginWithToken();
		if($res || $res !== false) return $res;
		$res = $this->loginWithThird();	// app第三方账号登录
		if($res || $res !== false) return $res;
		$data = Input::getArray(array(
			"name"		=> array("check"=>"require",'lengthMax'=>100),
			"password"	=> array('check'=>"require",'lengthMax'=>100),
			"salt"		=> array("default"=>false),
		));
		$checkCode = Input::get('checkCode', 'require', '');
		if( need_check_code() && $data['name'] != 'guest'){
			Action('user.setting')->checkImgCode($checkCode);
		}
		if ($data['salt']) {
			$key = substr($data['password'], 0, 5) . "2&$%@(*@(djfhj1923";
			$data['password'] = Mcrypt::decode(substr($data['password'], 5), $key);
		}
		$user = $this->userInfo($data['name'],$data['password']);
		if (!is_array($user)){
			$error = UserModel::errorLang($user);
			$error = $error ? $error:LNG('user.pwdError');
			show_json($error,false);
		}
		if(!$user['status']){
			show_json(LNG('user.userEnabled'), ERROR_CODE_USER_INVALID);
		}
		$this->loginSuccessUpdate($user);
		//自动登录跳转; http://xxx.com/?user/index/loginSubmit&name=guest&password=guest&auto=1
		$this->loginAuto();
		show_json('ok',true,$this->accessToken());
	}
	private function loginWithToken(){
		if (!isset($this->in['loginToken'])) return false;
		$apiToken = $this->config['settings']['apiLoginToken'];
		$param = explode('|', $this->in['loginToken']);
		if (strlen($apiToken) < 5 ||
			count($param) != 2 || strlen($this->in['loginToken']) > 500 ||
			md5(base64_decode($param[0]) . $apiToken) != $param[1]
		) {
			return show_json('API 接口参数错误!', false);
		}
		$name = base64_decode($param[0]);
		$res = Model('User')->where(array('name' => $name))->field('userID')->find();
		if(empty($res['userID'])) {
			return show_json(LNG('user.pwdError'),false);
		}
		$user = Model('User')->getInfo($res['userID']);
		$this->loginSuccessUpdate($user);
		$this->loginAuto();
		return show_json('ok',true,$this->accessToken());
	}
	
	// 更新登录时间
	public function loginSuccessUpdate($user){
		$this->loginSuccess($user);
		Model('User')->userEdit($user['userID'],array("lastLogin"=>time()));
		ActionCall('admin.log.loginLog');	// 登录日志
	}

	/**
	 * （app）第三方登录
	 */
	private function loginWithThird(){
		if (!isset($this->in['third'])) return false;
		$third = Input::get('third');
		if(empty($third)) return false;
		$third = is_array($third) ? $third : json_decode($third, true);

		// 判断执行结果
		if(isset($third['avatar'])) $third['avatar'] = rawurldecode($third['avatar']);
		Hook::trigger('user.bind.withApp', $third);
		$this->loginAuto();
		return show_json('ok',true,$this->accessToken());
	}

	// 登录后自动跳转
	private function loginAuto() {
		if($this->in['auto'] != '1') return;
		header('Location: '.APP_HOST);exit;
	}
	
	/**
	 * 前端（及app）找回密码
	 */
	public function findPassword(){
		return Action('user.setting')->findPassword();
	}
	
	/**
	 * app端请求
	 */
	private function findPwdWidthApp(){
		// api，直接填写手机/邮箱验证码、密码进行修改
		$data = Input::getArray(array(
			'type'		 => array('check' => 'in','default'=>'','param'=>array('phone','email')),
			'input'		 => array('check' => 'require'),
			'code'		 => array('check' => 'require'),
			'password'	 => array('check' => 'require'),
		));
		$param = array(
			'type' => 'regist',
			'input' => $data['input']
		);
		Action('user.setting')->checkMsgCode($data['type'], $data['code'], $param);
		$user = Model('User')->where(array($data['type'] => $data['input']))->find();
		if (empty($user)) {
			show_json(LNG('user.notBind'), false);
		}
		if (!Model('User')->userEdit($user['userID'], array('password' => $data['password']))) {
			show_json(LNG('explorer.error'), false);
		}
		show_json(LNG('explorer.success'));
	}
	
	public function loginSuccess($user) {
		// 登录管控
		$result = Hook::trigger('user.index.loginBefore',$user);
		if($result !== true) {
			show_tips(userModel::errorLang($result), APP_HOST);exit;
		}
		Hook::trigger('user.index.loginAfter',$user);

		Session::set('kodUser', $user);
		Cookie::set('kodUserID', $user['userID']);
		Cookie::set('kodTokenUpdate','1');//更新token通知;//自动登录处理;
		$kodToken = Cookie::get('kodToken');
		if($kodToken){//已存在则延期
			Cookie::setSafe('kodToken',$kodToken);
		}
		if (!empty($this->in['rememberPassword'])) {
			$kodToken = $this->makeLoginToken($user['userID']);
			Cookie::setSafe('kodToken',$kodToken);
		}
		$this->userDataInit($user);
		Hook::trigger("user.loginSuccess",$user);
	}

	
	/**
	 * 以当前用户身份临时授权访问接口请求构造
	 * 
	 * 安全性: 不泄露 accessToken; 
	 * 只能访问特定接口特定参数
	 * 一定时间内访问有效
	 * 
	 * 可以按应用授权;维护{appKey:appSecret,...};
	 * 注:完全以当前身份访问, 权限以当前用户为准;
	 */
	public function apiSignMake($action,$args,$timeout=false,$appKey='',$uriIndex=false){
		if(!defined('USER_ID') || !USER_ID) return false;
		$appSecret = $this->appKeySecret($appKey);
		if(!$appSecret) return false;
		
		$timeout = $timeout ? $timeout : 3600*24*3;
		$param   = '';
		$keyList = array(strtolower($action));
		$signArr = array(strtolower($action),$appSecret);
		foreach($args as $key=>$val){
			$keyList[] = strtolower($key);
			$signArr[] = strtolower($key).'='.base64_encode($val);
			$param = $key.'='.rawurlencode($val).'&';
		}
		$signToken = md5(implode(';',$signArr));//解密时获取
		$acionKey  = hash_encode(implode(';',$keyList));
		$actionToken = Mcrypt::encode(USER_ID,$signToken,$timeout);
		
		$param .= 'actionToken='.$actionToken.'&actionKey='.$acionKey;
		// 包含index.php; (跨域时浏览器请求options, 避免被nginx拦截提前处理)
		if($uriIndex){return APP_HOST.'index.php?'.$action.'&'.$param;}
		return urlApi($action,$param);
	}

	// 解析处理;
	public function apiSignCheck(){
		$actionToken = $this->in['actionToken'];
		$actionKey   = $this->in['actionKey'];
		$appSecret	 = $this->appKeySecret($this->in['appKey']);
		if(!$actionToken || !$actionToken || !$appSecret) return;
		if(strlen($actionToken) > 500) return;
		
		$action  = str_replace('.','/',ACTION);
		$keyList = explode(';',hash_decode($actionKey));
		$signArr = array(strtolower($action),$appSecret);
		if(strtolower($action) != $signArr[0]) return;
		for ($i = 1; $i < count($keyList); $i++) {
			$key = $keyList[$i];
			$signArr[] = strtolower($key).'='.base64_encode($this->in[$key]);
		}
		$signToken = md5(implode(';',$signArr));//同上加密计算
		$userID    = Mcrypt::decode($actionToken,$signToken);
		$userInfo  = $userID ? Model('User')->getInfo($userID):false;
		
		allowCROS();Cookie::disable(true);
		if(!is_array($userInfo)) {show_json(LNG("explorer.systemError").'.[apiSignCheck]',false);};

		// api临时访问接口; 不处理cookie; 不影响已登录用户session; 允许跨域
		Session::$sessionSign = guid();
		Session::set('kodUser', $userInfo);
		unset($_REQUEST['accessToken']);
	}
	// 维护多个应用
	public function appKeySecret($appKey=''){
		if(!$appKey) return md5(Model('SystemOption')->get('systemPassword'));
		$appList = Model('SystemOption')->get('appKeySecret');
		if(!$appList || !is_array($appList[$appKey])) return '';
		return $appList[$appKey]['appSecret'];
	}
	

	//登录token
	private function makeLoginToken($userID) {
		$pass = Model('SystemOption')->get('systemPassword');
		$user = Model('User')->getInfo($userID);
		if(!$user) return false;
		return md5($user['password'] . $pass . $userID);
	}

	// 系统维护中
	public function maintenance($update=false,$value=0){
		// Model('SystemOption')->set('maintenance',0);exit;
		if($update) return Model('SystemOption')->set('maintenance', $value);
		// 管理员or未启动维护，返回
		if(_get($GLOBALS,'isRoot') || !Model('SystemOption')->get('maintenance')) return;
		show_tips(LNG('common.maintenanceTips'), '','',LNG('common.tips'));
	}
}
