PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

authPlugin.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/authPlugin.class.php'
View Content
<?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 userAuthPlugin extends Controller{
	protected static $authRole;
	function __construct(){
		parent::__construct();
	}

	/**
	 * 插件执行路由检测:
	 * 
	 * plugin/epubReader/index&a=1
	 * plugin/epubReader/&a=1 ==>ignore index;
	 */
	public function autoCheck(){
		$theMod = strtolower(MOD);
		if ($theMod != 'plugin') return;
		if ($this->checkAuth(ST)) return;
		$msg = $this->lastError ? $this->lastError : LNG('explorer.noPermissionAction');
		if($_SERVER['REQUEST_METHOD'] == 'GET'){ // 插件处理;
			show_tips($msg.'; '.ST);
		}
		show_json($msg, false, 2001);
	}

	/**
	 * 插件权限检测
	 * 1. 有该插件,且已开启;
	 * 2. 登录检测;不需要登录的直接返回;
	 * 3. 权限检测
	 */
	public function checkAuth($appName){
		$plugin = Model("Plugin")->loadList($appName);
		if (!$plugin)  return true;//不存在插件,转发接口
		if ($plugin['status'] == 0)  {
			$this->lastError = LNG('admin.plugin.closedError');
			return false;
		}
		
		$config = $plugin['config'];
		if (isset($config['pluginAuthOpen']) && $config['pluginAuthOpen']) return true;
		if (KodUser::isRoot()){
			if($GLOBALS['config']["ADMIN_ALLOW_ALL_ACTION"] || !$GLOBALS['config']["ADMIN_AUTH_LIMIT_PLUGINS"]) return true;
			$disablePlugin = explode(',',strtolower($GLOBALS['config']["ADMIN_AUTH_LIMIT_PLUGINS"]));
			return in_array(strtolower($appName),$disablePlugin) ? false : true;//系统管理员,开启三权分立时,限制插件处理;
		}
		$auth = isset($config['pluginAuth']) ? $config['pluginAuth'] : null;
		if(!$auth) return false;
		return $this->checkAuthValue($auth);
	}

	/**
	 * 检测用户是否在用户选择数据中
	 * @param  [type] $info 组合数据  "{"all":"0","user":"2,4","group":"10,15","role":"4,3"}"
	 * @return [type]       [description]
	 */
	public function checkAuthValue($auth,$user=false){
		if( is_string($auth) ){
			$auth = @json_decode($auth, true);
		}
		if (isset($auth['all']) && $auth['all'] == '1') return true; // 全部则无需登录也可以访问;
		if (!$user){$user = Session::get('kodUser');}
		if (!$auth || !$user || !is_array($auth)) return false;
		
		// all:代表任意登录用户; root:代表系统管理员;
		if ($auth['user'] == 'all')  return true;
		if ($auth['user'] == 'admin' && KodUser::isRoot()) return true;
		if ($auth['role'] === '1' && KodUser::isRoot()) return true;
		
		$groups  = array_to_keyvalue($user['groupInfo'],'','groupID');
		$auth['user']  = $auth['user']  ? explode(',',$auth['user']) :  array();
		$auth['group'] = $auth['group'] ? explode(',',$auth['group']) : array();
		$auth['role']  = $auth['role']  ? explode(',',$auth['role']) :  array();
		
		//所在目标用户、角色、部门进行检测
		if( in_array($user['userID'], $auth['user']) ) return true;
		if( in_array($user['roleID'], $auth['role']) ) return true;
		foreach ($groups as $id) {
			if (in_array($id, $auth['group'])) return true;
		}
		return false;
	}
}
authRole.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/authRole.class.php'
View Content
<?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 userAuthRole extends Controller {
	protected static $authRole;
	function __construct() {
		parent::__construct();
	}
	public function authCanSearch(){return $this->authCan('explorer.search');}
	public function authCanRead(){return $this->authCan('explorer.view');}
	public function authCanEdit(){return $this->authCan('explorer.edit');}
	public function authCanDownload(){return $this->authCan('explorer.download');}
	public function authCan($action){ // action 区分大小写; 与$config['authRoleAction'] key保持一致;
		if(KodUser::isRoot()) return true;
		$userRole = $this->userRoleAuth();
		return $userRole['roleList'][$action] == 1 ? true : false;
	}
	
	// 获取指定用户角色信息;用户被禁用时, 开启了禁用用户时屏蔽该用户分享时则不再可用;
	public function userRoleGet($userID){
		$user = Model('User')->getInfo($userID);
		if(!$user || !$user['roleID']) return false;//用户不存在或角色为空;
		if($user['status'] == '0' && Model('SystemOption')->get('shareLinkUserDisableSkip') == '1'){return false;}
		$roleInfo = $this->userRoleAuth($user['roleID']);
		return $roleInfo ? $roleInfo:false;
	}
	
	// 根据用户角色判断用户,是否启用某个动作(后台配置角色的权限点)
	public function authCanUser($action,$userID){
		$userRole = $this->userRoleGet($userID);
		if(!$userRole) return false;
		if($userRole['info']['administrator'] == 1){return true;}
		return $userRole['roleList'][$action] == 1 ? true : false;
	}

	// 未登录:允许的 控制器方法;
	// 已登录:不允许的 控制器&方法;
	// 其他的可以通过内部Action进行方法调用转发;
	public function autoCheck(){
		$theMod 	= strtolower(MOD);
		$theST 		= strtolower(ST);
		$theAction 	= strtolower(ACTION);
		$authNotNeedLogin = $this->config['authNotNeedLogin'];
		foreach ($authNotNeedLogin as &$val) {
			$val = strtolower($val);
		};unset($val);
		if(in_array($theAction,$authNotNeedLogin)) return;
		foreach ($authNotNeedLogin as $value) {
			$item = explode('.',$value); //MOD,ST,ACT
			if( count($item) == 2 && 
				$item[0] === $theMod && $item[1] === '*'){
				return;
			}
			if( count($item) == 3 && 
				$item[0] === $theMod && $item[1] === $theST  &&$item[2] === '*'){
				return;
			}
		}
		// 排除不需要登录的方法;其他的都需要登录
		$user = Session::get("kodUser");
		if(!is_array($user)){
			if(Session::get('kodUserLogoutTrigger')){
				Session::destory();Cookie::remove('kodToken');
				show_json(LNG('user.logoutTrigger'),ERROR_CODE_USER_INVALID);
			}
			show_json(LNG('user.loginFirst'),ERROR_CODE_LOGOUT);
		}
		$this->authShareLinkUpdate();//分享拆分,老数据迁移;
		//系统管理员不受权限限制
		if(KodUser::isRoot()) return true;
		
		$userRole = $this->userRoleAuth();
		$allowAction = $userRole['allowAction'];
		// pr($allowAction[$theAction],$theAction,$user,$userRole);exit;
		if(!$allowAction[$theAction]){ //不存在该方法或
			show_json(LNG('explorer.noPermissionAction'),false,1004);
		}
	}
	
	// 用户权限解析处理;处理成最终动作
	public function userRoleAuth($roleID=false){
		if(!$roleID){
			$user = Session::get('kodUser');
			if(!$user || !$user['roleID']) return false;
			$roleID = $user['roleID'];
		}
		if(!self::$authRole){self::$authRole = array();}
		if(isset(self::$authRole[$roleID])){
			return self::$authRole[$roleID];
		}

		$roleInfo = Model('SystemRole')->listData($roleID);
		$userRoleAllow  = $this->authCheckAlias($roleInfo['auth']);
		$authRoleList 	= array();$allowAction 	= array();
		foreach ($this->config['authRoleAction'] as $role => $modelActions) {
			$enable = intval(in_array($role,$userRoleAllow));
			$authRoleList[$role] = $enable;
			if(!$modelActions || !is_array($modelActions)) continue;
			$actionArray = array();
			foreach ($modelActions as $controller => $stActions) {
				if(!$stActions) continue;
				$stActions = explode(',',trim($stActions,','));
				foreach ($stActions as $action) {
					$actionArray[] = $controller.'.'.$action;
				}
			}
			foreach ($actionArray as $action) {
				$action = strtolower($action);//统一转为小写
				if(!isset($allowAction[$action])){
					$allowAction[$action] = $enable;
					continue;
				}
				// 重复action点允许true值覆盖的动作,(权限在内部判断)
				if(in_array($role,$this->config['authRoleActionKeepTrue'])){
					if($enable){$allowAction[$action] = $enable;}
					continue;
				}
				
				/**
				 * false可以覆盖true;true不能覆盖false;
				 * 'explorer.download'	=> array('explorer.index'=>'zipDownload...')
				 * 'explorer.zip'		=> array('explorer.index'=>'zipDownload...')
				*/
				if($allowAction[$action]){
					$allowAction[$action] = $enable;
				}
			}
		}
		
		//不需要检测的动作白名单; 优先级最高;
		foreach ($this->config['authAllowAction'] as $action) { 
			$allowAction[strtolower($action)] = 1;
		}
		$result = array(
			'info'			=> $roleInfo,
			'allowAction'	=> $allowAction,
			'roleList'		=> $authRoleList
		);
		self::$authRole[$roleID] = $result;
		return $result;
	}
	
	// 操作时,再根据用户权限角色判断是否允许;
	public function canCheckRole($action){
		$actionMap = array(
			// 'show'		=> true, //不判断查看;
			'view'		=> array('explorer.view'),
			'download'	=> array('explorer.download'),
			'upload'	=> array('explorer.upload'),
			'edit' 		=> array('explorer.edit'),
			'remove'	=> array('explorer.remove'),
			'comment'	=> array('explorer.edit'),
			'event'		=> array('explorer.edit'),
			'root'		=> array('explorer.edit'),
			// 'share'		=> array('explorer.share','explorer.shareLink'), // 不判定; 在explorer.userShare.checkRoleAuth中判断;
		);
		if(!isset($actionMap[$action])){return true;}
		
		//动作可对应多个角色权限点; 任意一个满足则满足;
		foreach ($actionMap[$action] as $key){
			if($this->authCan($key)){return true;}
		}
		return false;
	}
	
	/**
	 * 角色权限升级,老数据处理(1.44)
	 * 分享拆分为=内部协作分享+外链分享 原explorer.share 拆分为 explorer.share+explorer.shareLink
	 * 用户编辑拆分为=用户编辑+用户权限设置; 原admin.member.userEdit 拆分为 admin.member.userEdit+admin.member.userAuth
	 */
	private function authShareLinkUpdate(){
		$model = Model("SystemOption");
		$key   = 'explorerShareUpate';$type = 'system';
		if($model->get($key,$type) == '1.01') return;
		$model->remove($key,$type);$model->set($key,'1.01',$type);
		
		$roleList = Model('SystemRole')->listData();
		foreach ($roleList as $role){
			$auth = $role['auth'];
			if(!strstr($role['auth'],'explorer.shareLink')){
				$auth = str_replace('explorer.share','explorer.share,explorer.shareLink',$auth);
			}
			if(!strstr($role['auth'],'admin.member.userAuth')){
				$auth = str_replace('admin.member.userEdit','admin.member.userEdit,admin.member.userAuth',$auth);
			}
			if($auth == $role['auth']) continue;
			$role['auth'] = $auth;
			Model('SystemRole')->update($role['id'],$role);
		}
	}
	
	// 处理权限前置依赖;
	private function authCheckAlias($auth){
		$authList = explode(',',trim($auth,','));
		$alias = array(
			'explorer.add'			=> 'explorer.view',
			'explorer.download'		=> 'explorer.view',
			'explorer.share'		=> 'explorer.view,explorer.upload,explorer.add,explorer.download',
			'explorer.shareLink'	=> 'explorer.view,explorer.download',//explorer.upload,explorer.add,
			// 'explorer.upload'		=> 'explorer.add',
			'explorer.edit'			=> 'explorer.add,explorer.view,explorer.upload',
			'explorer.remove'		=> 'explorer.edit',
			'explorer.move'			=> 'explorer.edit',
			'explorer.unzip'		=> 'explorer.edit',
			'explorer.zip'			=> 'explorer.edit',
			'explorer.serverDownload'	=> 'explorer.edit',
			
			'admin.role.edit'		=> 'admin.role.list',
			'admin.job.edit'		=> 'admin.job.list',
			'admin.member.userEdit'	=> 'admin.member.list',
			'admin.member.groupEdit'=> 'admin.member.list',
			'admin.auth.edit'		=> 'admin.auth.list',
			'admin.plugin.edit'		=> 'admin.plugin.list',
			'admin.storage.edit'	=> 'admin.storage.list',
			'admin.autoTask.edit'	=> 'admin.autoTask.list',
			
			// 'a'=>'a','c'=>'d','d'=>'c', // 循环依赖处理;
			// 'x1'=>'x2,x3','x3'=>'x1,x2',
		);
		foreach ($alias as $theKey => $aliasAction){
			$alias[$theKey] = explode(',', $aliasAction);
		}
		$aliasAll = array();//key以来的所有上层key;
		foreach ($alias as $theKey => $aliasAction){
			$aliasAll[$theKey] = $this->authCheckAliasParent($theKey,$alias);
		}

		$userRoleAllow = array();
		for ($i=0; $i < count($authList); $i++){
			$authAction = $authList[$i];
			if(!isset($aliasAll[$authAction])){
				$userRoleAllow[] = $authList[$i];continue;
			}
			// 所有依赖都在当前权限中,才算拥有该权限;
			$needAuth = $aliasAll[$authAction];$allHave  = true;
			for ($j=0; $j < count($needAuth); $j++) { 
				if(!in_array($needAuth[$j],$authList)) {$allHave = false;break;}
			}
			if($allHave){$userRoleAllow[] = $authList[$i];}
		}
		return $userRoleAllow;
	}
	private function authCheckAliasParent($theKey,&$alias,&$result=array()){
		$parents = _get($alias, $theKey, '');
		if(!$parents) return false;
		for ($i=0; $i < count($parents); $i++) {
			if(isset($result[$parents[$i]])) continue;
			$result[$parents[$i]] = true;
			$this->authCheckAliasParent($parents[$i],$alias,$result);
		}
		return array_keys($result);
	}
}
bind.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/bind.class.php'
View Content
<?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 userBind extends Controller {
	public function __construct() {
		parent::__construct();
	}
	/**
	 * 发送信息(验证码)-短信、邮件	当前只有个人设置绑定使用,暂时只记为绑定
	 */
	public function sendMsg() {
		$data = Input::getArray(array(
			'type'	 => array('check' => 'in', 'param' => array('email', 'phone')),
			'input'	 => array('check' => 'require'),
		));
		$type	= $data['type'];
		$input	= $data['input'];
		$source = $data['source'] = 'bind';

		// 检查图片验证码
		$checkCode = Input::get('checkCode', 'require', '');
		Action('user.setting')->checkImgCode($checkCode);

		// 1.1 判断邮箱是否已绑定-自己
		$userInfo = Session::get("kodUser");
		if ($userInfo[$data['type']] == $input) {
			show_json(LNG('common.' . $type) . LNG('user.binded'), false);
		}
		// 1.2 判断邮箱是否已绑定-他人
		if ($res = Model('User')->userSearch(array($type => $input), 'name,nickName')) {
			$typeTit = $type . ($type == 'phone' ? 'Number' : '');
			$message = $type == 'phone' ? LNG('ERROR_USER_EXIST_PHONE') : LNG('ERROR_USER_EXIST_EMAIL');
			show_json($message.'.', false);
		}

		// 2 发送邮件/短信
		Action('user.setting')->checkMsgFreq($data);	// 消息发送频率检查
		if($type == 'email'){
			$res = $this->sendEmail($input, $type.'_'.$source);
		}else{
			$res = $this->sendSms($input, $type.'_'.$source);
		}
		if (!$res['code']) {
			show_json(LNG('user.sendFail') . ': ' . $res['data'], false);
		}
		Action('user.setting')->checkMsgFreq($data, true);

		// 3. 存储验证码
		$param = array(
			'type'	=> 'setting',
			'input' => $input
		);
		Action("user.setting")->checkMsgCode($type, $res['data'], $param, true);
		show_json(LNG('user.sendSuccess'), true);
	}

	/**
	 * 发送(验证码)邮件
	 * @param [type] $input
	 * @param [type] $action
	 * @return void
	 */
	public function sendEmail($input, $action,$title = '',$code = false) {
		$systemName = Model('SystemOption')->get('systemName');
		$user = Session::get('kodUser');
		$name = _get($user,'name','');
		$name = _get($user,'nickName',$name);// _get 连续获取,部分安全软件会误报;
		$desc = Model('SystemOption')->get('systemDesc');
		$code = $code ? $code : rand_string(6,1);
		if(!$name && isset($user['name'])){$name = $user['name'];}
		$data = array(
			'type'		=> 'email',
			'input'		=> $input,
			'action'	=> $action,
			'config'	=> array(
				'address'	=> $input,
				'subject'	=> "[".$systemName."]" . LNG('user.emailVerify').$title,
				'content'	=> array(
					'type'	=> 'code', 
					'data'	=> array('user' => $name,'code' => $code)
				),
				'system'	=> array(	// 系统信息
					'icon'	=> STATIC_PATH.'images/icon/fav.png',
					'name'	=> $systemName,
					'desc'	=> $desc
				),
			)
		);
		return Action('user.msg')->send($data);
	}

	/**
	 * 发送(验证码)短信
	 * @param [type] $input
	 * @param [type] $action
	 * @return void
	 */
	public function sendSms($input, $action) {
		$data = array(
			'type'		=> 'sms',
			'input'		=> $input,
			'action'	=> $action
		);
		return Action('user.msg')->send($data);
	}

	/**
	 * 请求Kodapi服务器
	 * @param type $type
	 * @param type $data
	 * @return type
	 */
	public function apiRequest($type, $data = array()) {
		$kodid = md5(BASIC_PATH . Model('SystemOption')->get('systemPassword'));
		if(is_array($data) && defined('INSTALL_CHANNEL')){$data['channel'] = INSTALL_CHANNEL;}
		$post = array(
			'type'		 => $type,
			'kodid'		 => $kodid,
			'timestamp'	 => time(),
			'data'		 => is_array($data) ? json_encode($data) : $data
		);
		$post['sign'] = $this->makeSign($kodid, $post);
		$url = $this->config['settings']['kodApiServer'] . 'plugin/platform/';
		$response = url_request($url,'POST',$post);
		if ($response['status']) {
			$data = json_decode($response['data'], true);
			if (!$data) {	// 平台异常报错(show_tips)
				if ($response['data']) {
					preg_match('/<div id="msgbox">(.*?)<\/div>/s', $response['data'], $matches);
					if ($matches[1]) write_log('API request error: '.$matches[1], 'error');
				}
				return array('code' => false, 'data' => LNG('explorer.systemError'));
			}
			// secret有变更,和平台不一致
			if (!$data['code'] && isset($data['info']) && $data['info'] == '40003') {
				Model('SystemOption')->set('systemSecret', '');
			}
			return $data;
		}
		// Network error. Please check whether the server can access the external network.
		return array('code' => false, 'data' => 'network error.');
	}

	/**
	 * kodapi请求参数签名
	 * @param type $kodid
	 * @param type $post
	 * @return type
	 */
	public function makeSign($kodid, $post) {
		$secret = $this->getApiSecret($kodid, $post['type']);
		ksort($post);
		$tmp = array();
		$post = stripslashes_deep($post);
		foreach ($post as $key => $value) {
			$tmp[] = $key . '=' . $value;
		}
		$md5 = md5(sha1(implode('&', $tmp) . $secret));
		return strtoupper($md5); //生成签名
	}

	//获取api secret
	private function getApiSecret($kodid, $type) {
		$secret = Model('SystemOption')->get('systemSecret');
		if ($secret) return $secret;
		// 本身为获取secret请求时,secret以kodid代替
		if ($type == 'secret') return $kodid;

		// 从平台获取;需要站点认证; kodid变化重新获取(服务端重新生成)
		$initPass  = Model('SystemOption')->get('systemPassword');
		$res  = $this->apiRequest('secret',array('initPath'=>BASIC_PATH,'initPass'=>$initPass));
		if (!$res['code'] || !$res['data']) {
			$msg = !empty($res['data']) ? ': ' . $res['data'] : '';
			show_json('Api secret error. '.$msg, false);
		}
		$secret = addslashes($res['data']);
		Model('SystemOption')->set('systemSecret', $secret);
		return $secret;
	}

}
index.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/index.class.php'
View Content
<?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(){
		@ob_end_clean();
		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');
		}
		if( !file_exists(BASIC_PATH . 'config/setting_user.php') || empty($GLOBALS['config']['database'])){
			show_tips(LNG('explorer.sysSetUserError'));
		}
		$this->initDB();   		//
		Action('filter.index')->bindBefore();
		$this->initSession();   //
		$this->initSetting();   // 
		init_check_update();	// 升级检测处理;
		KodIO::initSystemPath();
		
		Action('filter.index')->bind();
		$this->loginCheck();
		Model('Plugin')->init();
		Action('filter.index')->trigger();
		if(!defined("USER_ID")){
			$userID = Session::get("kodUser.userID");$userID = 0;
			define("USER_ID",$userID ? $userID:0);
		}
	}
	public function shutdownEvent(){
		$GLOBALS['requestShutdownGlobal'] = true;// 请求结束后状态标记;
		http_close();
		TaskQueue::addSubmit();		// 结束后有任务时批量加入
		TaskRun::autoRun();			// 定期执行及延期任务处理;
		CacheLock::unlockRuntime(); // 清空异常时退出,未解锁的加锁;
		Cache::limitRuntimeClear();	// 标记的运行进程结束; 计数-1;
		Hook::trigger('globalRequestAfter');
	}
	private function initDB(){
		think_config($GLOBALS['config']['databaseDefault']);
		think_config($GLOBALS['config']['database']);
	}
	private function initSession(){
		$this->apiSignCheck();
		// 入口不处理cookie,兼容服务器启用了全GET缓存情况(输出前一次用户登录的cookie,导致账号登录异常)
		$action= strtolower(ACTION);
		if( $action == 'user.index.index' || $action == 'user.view.call'){
			Cookie::disable(true);
		}
		
		$systemPass = Model('SystemOption')->get('systemPassword');
		if(isset($_REQUEST['accessToken'])){
			$token = $_REQUEST['accessToken'];
			if(!$token || strlen($token) > 500){show_json('token error!',false,ERROR_CODE_LOGOUT);}

			$pass = substr(md5('kodbox_'.$systemPass),0,15);
			$sessionSign = Mcrypt::decode($token,$pass);
			if(!$sessionSign){show_json(LNG('common.loginTokenError'),ERROR_CODE_LOGOUT);}
			if($action == 'user.index.index'){Cookie::disable(false);} // 带token的url跳转入口页面允许cookie输出;
			Session::sign($sessionSign);
		}
		if(!$GLOBALS['disableSession'] && !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']);
			
			// 老版本升级,没有值情况处理;
			if(isset($sysOption['ignoreName'])){$upload['ignoreName'] = trim($sysOption['ignoreName']);}
			if(isset($sysOption['chunkRetry'])){$upload['chunkRetry'] = intval($sysOption['chunkRetry']);}
			if(isset($sysOption['threads'])){$upload['threads'] = floatval($sysOption['threads']);}
			if($upload['threads'] <= 0){$upload['threads'] = 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']);
			}
		}
		
		// 文件历史版本数量限制; 小于等于1则关闭,大于500则不限制;
		if(isset($sysOption['fileHistoryMax'])){
			$GLOBALS['config']['settings']['fileHistoryMax'] = intval($sysOption['fileHistoryMax']);
		}
		$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( KodUser::isLogin() ){
			return $this->userDataInit();
		}
		if(Session::get('kodUserLogoutTrigger')){return;} //主动设置退出,不自动登录;
		$userID 	= Cookie::get('kodUserID');
		$loginToken = Cookie::get('kodToken');
		if ($userID && $loginToken ) {
			$user = Model('User')->getInfoFull($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);
			}
			
			//优化,避免频繁写入session(file缓存时容易造成并发锁); 变化时更新;或者超过10分钟写入一次;
			$_lastTime = $this->user['_lastTime'];unset($this->user['_lastTime']);
			if($this->user != $findUser || time() - $_lastTime > 600){
			    $findUser['_lastTime'] = time();
			    Session::set('kodUser',$findUser);
			}
			$this->user = $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(){
		$systemPass = Model('SystemOption')->get('systemPassword');
		$pass = substr(md5('kodbox_'.$systemPass),0,15);
		return Mcrypt::encode(Session::sign(),$pass,3600*24*30);
	}
	public function accessTokenGet(){
		if(!KodUser::isLogin()){show_json('user not login!',ERROR_CODE_LOGOUT);}
		show_json($this->accessToken(),true);
	}
	public function accessTokenCheck($token){
		if(!$token || strlen($token) > 500) return false;

		$systemPass = Model('SystemOption')->get('systemPassword');
		$pass = substr(md5('kodbox_'.$systemPass),0,15);
		$sessionSign = Mcrypt::decode($token,$pass);
		if(!$sessionSign || $sessionSign != Session::sign()) return false;
		return true;
	}
		
	// 登录校验并自动跳转 (已登录则直接跳转,未登录则登录成功后跳转)
	public function autoLogin(){
		$link = $this->in['link'];
		if(!$link) return;

		$errorTips = _get($this->in,'msg','');
		$errorTips = $errorTips == '[API LOGIN]' ? '':$errorTips; // 未登录标记,不算做登录错误;
		if(KodUser::isLogin() && !$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,true);
		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');
		Action('user.sso')->logout(); // 单点登录同步跟随退出;清理缓存;
		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'=>500),
			"password"	=> array('check'=>"require",'lengthMax'=>500),
		));
		$data['name'] = trim($data['name']);
		$checkCode = Input::get('checkCode', 'require', '');
		if( need_check_code() && $data['name'] != 'guest'){
			Action('user.setting')->checkImgCode($checkCode);
		}
		$data['password'] = KodUser::parsePass($data['password']);

		$user = $this->userInfo($data['name'],$data['password']);
		if (!is_array($user)){
			show_json($this->loginErrMsg($user),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'];
		$loginToken = trim($this->in['loginToken']);
		$param = explode('|', $loginToken);
		if (strlen($apiToken) < 5 ||
			count($param) != 2 || strlen($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')->getInfoFull($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;
	}
	
	// 刷新用户信息;
	public function refreshUser($userID){
		Model('User')->clearCache($userID);
		$user = Model('User')->getInfoFull($userID);
		Session::set('kodUser', $user);	
	}
	
	//前端(及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($this->loginErrMsg($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);
	}
	// 登录时模糊提示消息
	private function loginErrMsg($code){
		if (in_array($code, array('-1','-2'))) {
			return LNG('user.pwdError');
		}
		$error = UserModel::errorLang($code);
		return $error ? $error : LNG('user.pwdError');
	}
	
	/**
	 * 以当前用户身份临时授权访问接口请求构造
	 * 
	 * 安全性: 不泄露 accessToken; 
	 * 只能访问特定接口特定参数
	 * 一定时间内访问有效
	 * 
	 * 可以按应用授权;维护{appKey:appSecret,...};
	 * 注:完全以当前身份访问, 权限以当前用户为准;
	 */
	public function apiSignMake($action,$args,$timeout=false,$appKey='',$uriIndex=false){
		$appSecret = $this->appKeySecret($appKey);
		if(!$appSecret || !USER_ID){ // 外链分享等情况
			return APP_HOST.'index.php?'.$action.'&'.http_build_query($args);
		}
		
		$userID  = Session::get('kodUser.userID');
		$timeout = $timeout ? $timeout : 3600*24*30; // 咱不使用, 默认一直有效;
		$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,0,md5($appSecret)); // 避免url变化,无法缓存问题;
		$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(){
		if(isset($_REQUEST['accessToken']) && $_REQUEST['accessToken']){return;} // token优先;
		$actionToken = _get($this->in, 'actionToken', '');
		$actionKey	 = _get($this->in, 'actionKey', '');
		$appKey   	 = _get($this->in, 'appKey', '');
		$appSecret	 = $this->appKeySecret($appKey);
		if(!$actionToken || !$actionKey || !$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);
		
		allowCROS();Cookie::disable(true);
		// 当前为自己则默认使用当前session(是否登录保险箱等session共享;)
		if($userID && Session::get('kodUser.userID') == $userID){return;} 
		$userInfo  = $userID ? Model('User')->getInfoFull($userID):false;
		if(!is_array($userInfo)) {show_json(LNG("explorer.systemError").'.[apiSignCheck]',false);};

		// api临时访问接口; 不处理cookie; 不影响已登录用户session; 允许跨域
		Session::$sessionSign = guid();
		Session::set('kodUser', $userInfo);
		Session::set('pathSafe-userIsLogin','1');
		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);
		// 有配置参数则不处理
		if ($GLOBALS['config']['settings']['systemMaintenance'] === 0) return;
		// 管理员or未启动维护,返回
		if(KodUser::isRoot() || !Model('SystemOption')->get('maintenance')) return;
		show_tips(LNG('common.maintenanceTips'), '','',LNG('common.tips'));
	}
}
msg.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/msg.class.php'
View Content
<?php 

/**
 * 发送消息:短信、邮件
 */
class userMsg extends Controller {
	public $user;
	public function __construct() {
        parent::__construct();
    }

    /**
     * 发送短信、邮件
     * @param [type] $data => array(
     * type         发送方式:sms、email
     * input        接收地址:手机号码、邮箱地址
     * action       系统发送api请求事件:phone_bind
     * // 非必需参数
     * emailType    邮件发送方式:系统、自定义
     * config       邮箱配置参数
     * config.add   邮箱配置参数追加-自定义服务器
     * ) 
     * @return void
     */
    public function send($data){
        $type = $data['type'];
        $input = $data['input'];
        $check = array('sms' => 'phone', 'email' => 'email');
        if(!isset($check[$type])) {
            return array('code' => false, 'data' => LNG('common.invalidParam'));
        }
        if (!Input::check($input, $check[$type])) {
            return array('code' => false, 'data' => LNG('common.invalidFormat'));
        }
        if ($type == 'sms') {
            return $this->sendSms($data);
        }
        return $this->sendEmail($data);
    }

    // 兼容旧版
    public function sms($data) {
        return $this->sendSms($data);
    }
    public function email($data) {
        return $this->sendEmail($data);
    }
    public function emailByCustom ($data) {
        return $this->sendEmailByOwn($data);
    }

    /**
     * 发送短信
     * @param [type] $data
     * @return void
     */
    public function sendSms($data){
        $data = array(
			'type'		 => $data['action'],
			'input'		 => $data['input'], // 邮箱or手机
            'language'	 => i18n::getType(),
            'config'     => isset($data['config']) ? $data['config'] : array()
		);
        $res = Hook::trigger('send.sms.before', $data);
        if ($res) return $res;  // [data,code] || false
		return Action('user.bind')->apiRequest('sms', $data);
    }

    /**
     * 发送邮件
     * @param [type] $data
     * @return void
     */
    public function sendEmail($data){
        if (isset($data['emailType'])) {
			$type = $data['emailType'];
		} else {
			$type = Model('SystemOption')->get('emailType');
        }
        // 自定义发送
		if ((int) $type) {
			return $this->sendEmailByOwn($data);
		}
		// 系统默认发送
        $data = array(
			'type'      => $data['action'],
			'input'     => $data['input'], // 邮箱or手机
            'language'  => i18n::getType(),
            'config'    => isset($data['config']) ? $data['config'] : ''
        );
        $this->setEmailInfo($data);
        if(!$data['config']) unset($data['config']);
		return Action('user.bind')->apiRequest('email', $data);
    }

    /**
     * 发送邮件-自定义(服务器)
     * @param [type] $data
     * @return void
     */
    public function sendEmailByOwn($data){
        $init = array(
            'address'   => '',  // 收件人
            'cc'        => '',  // 抄送 a;b;c
            'subject'   => '',  // 主题
            'content'   => '',  // 内容
            'signature' => '',  // 发送者名称——和邮件内容里的签名不同
            'html'      => 1,   // 是否为html
        );
        foreach($init as $key => &$value) {
            if(isset($data['config'][$key])) $value = $data['config'][$key];
        };
		if(!$init['cc']){unset($init['cc']);}
		
        // 发件服务器信息
        if(isset($data['config']['server'])) {
            $init = array_merge($init, $data['config']['server']);
        }
        // 发送者名称
        $signature = _get($data, 'config.system.name');
        if (!$signature) $signature = Model('SystemOption')->get('systemName');
        $init['signature'] = $signature;
        // 邮件内容,自定义内容为字符串;根据模板获取为数组:array('type'=>'code','data'=>array('code' => 123))
        if(is_array($init['content'])) {
            $init['content'] = $this->getEmailContent($data);
        }

        // 邮件发送
		$mail = new Mailer();
        $res = $mail->send($init);
        $type = _get($data, 'config.content.type');
        if($res['code'] && $type == 'code') {
            $res['data'] = _get($data, 'config.content.data.code');
        }
        return $res;
    }

    /**
     * 获取邮件内容
     * @param [type] $type
     * @param [type] $data
     * @return void
     */
    public function getEmailContent($data){
        $system = $this->setEmailInfo($data);
        $addr = _get($data, 'config.address');
        $type = _get($data, 'config.content.type');
        $data = _get($data, 'config.content.data');
        $user = _get($data, 'user');
        if (!$user) $user = $addr;

        switch($type) {
            case 'code':
                $data = array(
                    'type'  => 'code',
                    'dear'  => sprintf(LNG('admin.emailDear'), $user),
                    'text'  => LNG('admin.emailCodeText'),
                    'code'  => $data['code'],
                    'date'  => date('Y-m-d'),
                    'system'=> $system,
                );
                break;
            case 'notice':
                $data = array(
                    'type'  => 'notice',
                    'dear'  => sprintf(LNG('admin.emailDear'), $user),
                    'text'  => is_array($data['text']) ? $data['text'] : array($data['text']),  // 正文
                    'date'  => date('Y-m-d'),
                    'system'=> $system,
                );
                break;
            default:
                return '';
                break;
        }
        ob_end_clean();
		ob_start();
		extract(array('data' => $data));
		require(TEMPLATE . '/user/email.html');
		$html = ob_get_contents();
		ob_end_clean();
		return $html;
    }
    private function setEmailInfo(&$data){
        $system = _get($data, 'config.system', array());
        $icon = 'https://api.kodcloud.com/static/images/icon/fav.png';
        if(Model('SystemOption')->get('versionType') == 'A'){
            $system['icon'] = $icon;
            $system['name'] = LNG('common.copyright.name');
            $system['desc'] = LNG('common.copyright.nameDesc');
        } else {
            $icon = _get($GLOBALS, 'config.settingSystemDefault.systemIcon', '');
            if ($icon) $system['icon'] = $icon;
        }
        if (!$system['icon']) $system['icon'] = $icon;
        if (!$system['name']) $system['name'] = Model('SystemOption')->get('systemName');
        if (!$system['desc']) $system['desc'] = Model('SystemOption')->get('systemDesc');
        $data['config']['system'] = $system;
        return $system;
    }
}
regist.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/regist.class.php'
View Content
<?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 userRegist extends Controller {
	public function __construct() {
		parent::__construct();
	}
	
	public function checkAllow($regist=true){
		if (!isset($this->regOpen)) {
			$regist = Model("SystemOption")->get("regist");
			$this->regOpen = $regist['openRegist'] == '1';
		}
		if ($this->regOpen) return true;
		$msg = $regist ? LNG('user.registNotAllow') : LNG('user.deregistNotAllow');
		show_json($msg,false);
	}
	
	/**
	 * 发送验证码——注册、找回密码
	 */
	public function sendMsgCode() {
		$data = Input::getArray(array(
			'type'		=> array('check' => 'in', 'param' => array('email', 'phone')),
			'input'		=> array('check' => 'require'),
			'source'	=> array('check' => 'require'),
			// 'checkCode'	=> array('check' => 'require'),
		));
		// 非后端调用(前端请求)需要图形验证码
		if (!$GLOBALS['BACKEND_CALL_SENDMSGCODE']) {
			$data['checkCode'] = Input::get('checkCode', 'require');
		}
		$type	= $data['type'];
		$input	= $data['input'];
		$source	= $data['source'];

		// 个人设置、注册、找回密码、注销
		if(!in_array($source, array('setting', 'regist', 'findpwd', 'deregist'))){
			show_json(LNG('common.invalidRequest'), false);
		}
		if (!Input::check($input, $type)) {
			$text = $type . ($type == 'phone' ? 'Number' : '');
			show_json(LNG('common.invalid') . LNG('common.' . $text), false);
		}
		// 图形验证码
		if (!$GLOBALS['BACKEND_CALL_SENDMSGCODE']) {
			Action('user.setting')->checkImgCode($data['checkCode']);
		} else {
			unset($GLOBALS['BACKEND_CALL_SENDMSGCODE']);
		}

		// 1.1前端注册检测
		if ($source == 'regist') {
			$this->checkAllow();
			$this->userRegistCheck($data);
		}
		// 1.2找回密码(前端找回、后端重置)检测
		if ($source == 'findpwd') {
			$this->userFindPwdCheck($data);
		}

		// 2.发送邮件/短信
		Action('user.setting')->checkMsgFreq($data);	// 消息发送频率检查
		if ($type == 'email') {
			$res = Action('user.bind')->sendEmail($input, $type.'_'.$source);
		} else {
			$res = Action('user.bind')->sendSms($input, $type.'_'.$source);
		}
		if (!$res['code']) {
			show_json(LNG('user.sendFail') . ': ' . $res['data'], false);
		}
		Action('user.setting')->checkMsgFreq($data, true);

		// 3.存储验证码
		$param = array(
			'type'	=> $source,
			'input' => $input
		);
		Action('user.setting')->checkMsgCode($type, $res['data'], $param, true);
		show_json(LNG('user.sendSuccess'), true);
	}

	/**
	 * 判断号码、邮箱是否已注册
	 * @param type $data
	 */
	private function userRegistCheck($data) {
		$where = array($data['type'] => $data['input']);
		if (Model('User')->userSearch($where)) {
			show_json(LNG('common.' . $data['type']) . LNG('user.registed'), false);
		}
	}

	/**
	 * 判断账号(及图片验证码-前端)是否有效-找回密码
	 * @param type $data
	 * @return type
	 */
	private function userFindPwdCheck($data) {
		$userID = Input::get('userID', 'require', '0');
		$text = $data['type'] . ($data['type'] == 'phone' ? 'Number' : '');
		// 前端找回密码
		if ($userID == '0') {
			$where = array($data['type'] => $data['input']);
			if (!Model('User')->userSearch($where)) {
				show_json(LNG('common.' . $text) . LNG('common.error'), false);
				// show_json(LNG('common.' . $data['type']) . LNG('user.notRegist'), false);
			}
			return;
		}
		// 后端重置密码
		$userInfo = Model('User')->getInfoSimple($userID);
		if (empty($userInfo)) {
			show_json(LNG('common.illegalRequest'), false);
		}
		if(!$userInfo[$data['type']]) {
			show_json(LNG('common.' . $text) . LNG('common.error'), false);
			// show_json(LNG('common.' . $text) . LNG('user.notBind'), false);
		}
		// 提交的邮箱、手机和用户信息中的不匹配
		if ($userInfo[$data['type']] != $data['input']) {
			show_json(sprintf(LNG('user.inputNotMatch'), LNG('common.' . $text)), false);
		}
	}

	/**
	 * 注册
	 */
	public function regist() {
		$this->checkAllow();
		$data = Input::getArray(array(
			'type'		 => array('check' => 'in', 'param' => array('email', 'phone')),
			'input'		 => array('check' => 'require'),
			'name'	 	 => array('default' => null),
			'nickName'	 => array('default' => null),
			'password'	 => array('check' => 'require'),
			'msgCode' 	 => array('check' => 'require'),	// 消息验证码
		));
		foreach ($data as $k => $val) {
			$data[$k] = rawurldecode($val);
		}
		if(empty($data['name'])) $data['name'] = $data['input'];	// 兼容app注册

		// 邮箱/手机号校验
		if (!Input::check($data['input'], $data['type'])) {
			$text = $data['type'] . ($data['type'] == 'phone' ? 'Number' : '');
			show_json(LNG('common.invalid') . LNG('common.' . $text), false);
		}
		// 消息验证码校验
		if(!$msgCode = Input::get('msgCode')){
			show_json(LNG('user.inputVerifyCode'), false);
		}
		$param = array(
			'type' => 'regist',
			'input' => $data['input']
		);
		Action('user.setting')->checkMsgCode($data['type'], $msgCode, $param);

		// 密码校验
		$data['password'] = KodUser::parsePass($data['password']);
		if( !ActionCall('filter.userCheck.password',$data['password']) ){
			return ActionCall('filter.userCheck.passwordTips');
		}
		$this->addUser($data);
	}
	
	

	/**
	 * 新增/注册用户
	 * @param type $data
	 * @return type
	 */
	public function addUser($data) {
		$this->checkAllow();
		$name = $data['name'];
		$nickName = trim($data['nickName']);
		$nickName = $nickName ? $nickName : '';

		$bindRegist = true;	// 绑定注册
		if (isset($data['type']) && isset($data['input'])) {
			$bindRegist = false;
			if (Model('User')->userSearch(array($data['type'] => $data['input'])) ) {
				$text = $data['type'] . ($data['type'] == 'phone' ? 'Number' : '');
				return show_json(LNG('common.' . $text) . LNG('common.error'), false);
				// return show_json(LNG('common.' . $data['type']) . LNG('user.registed'), false);
			}
		}
		// 3.1用户基础信息保存
		$regist = Model("SystemOption")->get("regist");
		$this->in = array(
			'name'		 => $name,
			'nickName'	 => $nickName,
			'password'	 => $data['password'],
			'roleID'	 => $regist['roleID'],
			'email'		 => isset($data['email']) ? $data['email'] : '',
			'phone'		 => isset($data['phone']) ? $data['phone'] : '',
			'avatar'	 => isset($data['avatar']) ? $data['avatar'] : '',
			'sex'	 	 => isset($data['sex']) ? $data['sex'] : 1,
			'sizeMax'	 => floatval($regist['sizeMax']), //M
			'status'	 => $regist['checkRegist'] == 1 ? 0 : 1, //0禁用;1启用 等待审核可以改为-1
			'groupInfo'  => $regist['groupInfo']
		);
		!$bindRegist && $this->in[$data['type']] = $data['input'];

		$res = ActionCallHook('admin.member.add');
		// 绑定注册,直接返回新增结果
		if ($bindRegist) return $res;	// show_json(true, true, userID)
		if(!$res['code']) {
			$msg = $res['data'] ? $res['data'] : LNG('explorer.error');
			show_json($msg, false);
		}

		$code = true;
		$msg = LNG('user.registSuccess');
		if(!$this->in['status']){
			$code = ERROR_CODE_USER_INVALID;
			$msg .= LNG('user.waitCheck');
		}
		show_json($msg, $code);
	}

	/**
	 * 注销
	 * 1.确认注销——发送验证码
	 * 2.输入验证码,提交注销
	 * @return void
	 */
	public function deregist(){
		KodUser::checkLogin();
		$this->checkAllow(false);	// 未开启注册

		// 1.验证用户资格
		$userInfo = Session::get("kodUser");
		if (!$userInfo) {show_json(LNG('oauth.main.notLogin'), false);}
		if ($userInfo['userID'] == '1') {
			show_json('系统管理员不支持此操作!', false);
		}
		if (!$userInfo['email']) {
			show_json('请先绑定邮箱,用于验证码获取!', false);
		}

		// 2.注销用户
		// 2.1 发送验证码
		if (!isset($this->in['code'])) {
			$this->in = array(
				'type'		=> 'email',
				'input'		=> $userInfo['email'],
				'source'	=> 'deregist',
			);
			$GLOBALS['BACKEND_CALL_SENDMSGCODE'] = true;
			ActionCallResult("user.regist.sendMsgCode",function(&$res){
				if ($res['code']) {
					$res['data'] = LNG('explorer.safe.sendMailTips') . ', ' . LNG('explorer.safe.sendMailGet');
				}
				return $res;
			});
		}

		// 2.2 验证验证码,注销用户
		$data = array(
			'type'	=> 'email',
			'input'	=> $userInfo['email'],
		);
		// 消息验证码校验
		if(!$code = Input::get('code')){
			show_json(LNG('user.inputVerifyCode'), false);
		}
		$param = array(
			'type'	=> 'deregist',
			'input' => $data['input']
		);
		Action('user.setting')->checkMsgCode($data['type'], $code, $param);

		// 删除用户
		$this->in['userID'] = $userInfo['userID'];
		Action("admin.member")->remove();
	}

}
setting.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/setting.class.php'
View Content
<?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 userSetting extends Controller {
	public $user;
	public function __construct() {
		parent::__construct();
		$this->user = Session::get('kodUser');
		$this->model = Model('User');
	}

	/**
	 * 参数设置
	 * 可以同时修改多个:key=a,b,c&value=1,2,3
	 * 防xss 做过滤
	 */
	public function setConfig() {
		$optionKey = array_keys($this->config['settingDefault']);
		$data = Input::getArray(array(
			"key"	=> array("check"=>"in","param"=>$optionKey),
			"value"	=> array("check"=>"require"),
		));
		Model('UserOption')->set($data['key'],$data['value']);
		
		// listType,listSort 显示模式,排序方式跟随文件夹配置记录;
		if(isset($this->in['listViewPath']) && $this->in['listViewPath']){
			Action('explorer.listView')->dataSave($this->in);
		}
		show_json(LNG('explorer.settingSuccess'));
	}
	public function getConfig(){
	}

	/**
	 * 个人中心-账号设置保存
	 */
	public function setUserInfo() {
		$limit = array('nickName', 'email', 'phone', 'password');
		$data = Input::getArray(array(
			"type"		 => array("check" => "in", "param" => $limit),
			"msgCode"	 => array("default" => null),
		));
		if($data['type'] != 'password') {
			$input = Input::get('input','require');
			$input = trim(rawurldecode($input));
		}
		$input = html2txt($input);
		$userID = $this->user['userID'];
		if(in_array($data['type'], array('email', 'phone'))){
			if(!isset($data['msgCode'])){$data['msgCode'] = '000';}
			$this->userMsgCheck($input, $data); // 修改邮箱/手机号,需要验证码校验;
			if($input == $this->user[$data['type']]){
				show_json(LNG('common.' . $data['type']) . LNG('user.binded'), false);
			}
		}

		// 昵称校验——更新时校验
		// 密码校验
		if ($data['type'] == 'password') {
			$input = $this->userPwdCheck($data);
		}

		// 更新用户信息
		$res = $this->model->userEdit($userID, array($data['type'] => $input));
		if ($res <= 0) {
			$msg = $this->model->errorLang($res);
			show_json(($msg ? $msg : LNG('explorer.error')), false);
		}
		Action('user.index')->refreshUser($userID);
		$userInfo = Model('User')->getInfo($userID);
		show_json(LNG('explorer.success'), true, $userInfo);
	}

	/**
	 * (图片)验证码校验
	 * @param type $code
	 */
	public function checkImgCode($code){
		$checkCode = Session::get('checkCode');
		Session::remove('checkCode');
		if (!$checkCode || strtolower($checkCode) !== strtolower(trim($code))) {
			show_json(LNG('user.codeError'), false, ERROR_IMG_CODE);
		}
	}

	/**
	 * 手机、邮箱验证码存储、验证
	 * @param type $type	email、phone
	 * @param type $code
	 * @param type $data	{type: [source], input: ''}
	 * @param type $set	首次存储验证码(检测错误次数)
	 * @return type
	 */
	public function checkMsgCode($type, $code, $data = array(), $set = false) {
		$typeList = array('setting', 'regist', 'findpwd', 'deregist');	// 个人设置、注册、找回密码、注销
		if(!in_array($data['type'], $typeList)){
			show_json(LNG('common.invalid') . LNG('explorer.file.action'), false);
		}
		$name = md5("{$data['type']}_{$type}_{$data['input']}_msgcode");
		// 1. 存储
		if ($set) {
			$sess = array(
				'code'	 => $code,
				'cnt'	 => 0,
				'time'	 => time()
			);
			return Session::set($name, $sess);
		}
		// 2. 验证
		$type = $type == 'phone' ? 'sms' : $type;
		if (!$sess = Session::get($name)) {
			$msg = LNG('common.invalid') . LNG('common.' . $type) . LNG('user.code');
			show_json($msg, false);
		}
		// 超过20分钟
		if (($sess['time'] + 60 * 20) < time()) {
			Session::remove($name);
			show_json(LNG('common.' . $type) . LNG('user.codeExpired'), false);
		}
		// 错误次数过多,锁定一段时间——没有锁定,重新获取
		if ($sess['cnt'] >= 10) {
			Session::remove($name);
			show_json(LNG('common.' . $type) . LNG('user.codeErrorTooMany'), false);
		}
		if (strtolower($sess['code']) != strtolower($code)) {
			$sess['cnt'] ++;
			Session::set($name, $sess);
			show_json(LNG('common.' . $type) . LNG('user.codeError'), false);
		}
		Session::remove($name);
	}

	/**
	 * 消息发送频率检查
	 * [type/input/source]
	 * @param [type] $data
	 * @return void
	 */
	public function checkMsgFreq($data, $set=false){
		$cckey = md5("{$data['type']}_{$data['input']}_{$data['source']}_msgtime");
		$cache = Cache::get($cckey);
		// 保存
		if ($set) {
			$cnt   = intval(_get($cache,'cnt',0));
			$cache = array('time' => time(), 'cnt' => $cnt++);
			return Cache::set($cckey, $cache);
		}
		// 获取
		if (!$cache) return;
		$time = $data['type'] == 'email' ? 60 : 90;
		if(($cache['time'] + $time) > time()) {
			show_json(LNG('user.codeErrorFreq'), false);
		}
		// if($cache['cnt'] >= 10) {
		// 	show_json(sprintf(LNG('user.codeErrorCnt'), $hours), false);
		// }
	}

	/**
	 * (短信、邮箱)验证码校验
	 * @param type $input
	 * @param type $data
	 */
	private function userMsgCheck($input, $data) {
		$type = $data['type'];
		// 判断邮箱、手机号是否已被绑定
		if($this->user[$type] == $input) return;

		$where = array($type=> $input);
		if ($res = Model('User')->userSearch($where, 'name,nickName')) {
			$typeTit = $type . ($type == 'phone' ? 'Number' : '');
			show_json(LNG('common.' . $typeTit) . LNG('common.error'), false);
		}
		// 判断邮箱、短信验证码
		$param = array(
			'type' => 'setting',
			'input' => $input
		);
		$this->checkMsgCode($type, $data['msgCode'], $param);
	}

	/**
	 * 修改密码检测
	 * @param type $data
	 * @return type
	 */
	private function userPwdCheck($data) {
		$newpwd = Input::get('newpwd','require');
		$newpwd = KodUser::parsePass($newpwd);
		// 密码为空则不检查原密码
		$info = Model('User')->getInfoSimple($this->user['userID']);
		if(empty($info['password'])) return $newpwd;

		$oldpwd = Input::get('oldpwd','require');
		$oldpwd = KodUser::parsePass($oldpwd);
		if (!$this->model->userPasswordCheck($this->user['userID'], $oldpwd)) {
			show_json(LNG('user.oldPwdError'), false);
		}
		if( !ActionCall('filter.userCheck.password',$newpwd) ){
			return ActionCall('filter.userCheck.passwordTips');
		}
		return $newpwd;
	}

	/**
	 * 用户头像(上传)
	 */
	public function uploadHeadImage(){
		$ext = get_path_ext(Uploader::fileName());
		if(!in_array($ext,array('png','jpg','jpeg','gif','webp','bmp','ico'))){
			show_json("only support image",false);
		}

		$path = KodIO::systemFolder('avataImage');
		$image = 'avata-'.USER_ID.'.'.$ext;	// jpg
		$pathInfo 	= IO::infoFullSimple($path.'/'.$image);
		if($pathInfo){
			IO::remove($pathInfo['path'], false);
		}
		
		// pr($imagePath,$path,IO::infoFull($imagePath));exit;
		$this->in['fullPath'] = '';
		$this->in['name'] = $image;
		$this->in['path'] = $path;
		Action('explorer.upload')->fileUpload();
	}
	/**
	 * 用户头像(设置)
	 */
	public function setHeadImage() {
		$link = Input::get('link', 'require');
		if(strpos($link, APP_HOST) !== 0) {
			show_json(LNG('common.illegalRequest'), false);
		}
		$userID = USER_ID;
		$link = str_replace(APP_HOST, './', $link);
		if(!$this->model->userEdit($userID, array("avatar" => $link))) {
			show_json(LNG('explorer.upload.error'), false);
		}
		Action('user.index')->refreshUser($userID);
		$userInfo = Model('User')->getInfo($userID);
		show_json($link, true, $userInfo);
	}

	/**
	 * 重置密码
	 */
	public function changePassword() {
		if (empty($this->user['email']) && empty($this->user['phone'])) {
			show_json('请先绑定邮箱或手机号!', false);
		}
		show_json('', true);
	}

	/**
	 * 找回密码
	 */
	public function findPassword() {
		$token = Input::get('token', null, null);
		if(!$token){
			$res = $this->findPwdCheck();
		}else{
			$res = $this->findPwdReset();
		}
		show_json($res, true);
	}

	/**
	 * 找回密码 step1:根据账号检测并获取用户信息
	 * @return type
	 */
	private function findPwdCheck() {
		$data = Input::getArray(array(
			'type'			=> array('check' => 'in','default'=>'','param'=>array('phone','email')),
			'input'			=> array('check' => 'require'),
			'msgCode'		=> array('check' => 'require')
		));
		// 是否绑定
		$res = Model('User')->userSearch(array($data['type'] => $data['input']), 'userID');
		if (empty($res)) {
			show_json(LNG('user.notBind'), false);
		}
		$param = array(
			'type' => 'findpwd',
			'input' => $data['input']
		);
		$this->checkMsgCode($data['type'], $data['msgCode'], $param);

		$data = array(
			'type' => $data['type'],
			'input' => $data['input'],
			'userID' => $res['userID'],
			'time' => time()
		);
		$pass = md5('findpwd_' . implode('_', $data));
		Cache::set($pass, $data, 60 * 20);	// 有效期20分钟
		return $pass;
	}

	/**
	 * 找回密码 step1:更新密码
	 * @return type
	 */
	private function findPwdReset() {
		$token = Input::get('token', 'require');
		$password = Input::get('password', 'require');
		$password = KodUser::parsePass($password);
		// 检测token是否有效
		$cache = Cache::get($token);
		if(!$cache) show_json(LNG('common.errorExpiredRequest'), false);
		if(!isset($cache['type']) || !isset($cache['input']) || !isset($cache['userID']) || !isset($cache['time'])){
			show_json(LNG('common.illegalRequest'), false);
		}
		if($cache['time'] < time() - 60 * 10){
			show_json(LNG('common.expiredRequest'), false);
		}
		$res = Model('User')->userSearch(array($cache['type'] => $cache['input']), 'userID');
		if(empty($res) || $res['userID'] != $cache['userID']){
			show_json(LNG('common.illegalRequest'), false);
		}
		if (!Action('user.authRole')->authCanUser('user.edit',$res['userID'])) {
			show_json(LNG('explorer.noPermissionAction'),false,1004);
		}
		if( !ActionCall('filter.userCheck.password',$password) ){
			return ActionCall('filter.userCheck.passwordTips');
		}
		Cache::remove($token);
		if (!$this->model->userEdit($res['userID'], array('password' => $password))) {
			show_json(LNG('explorer.error'), false);
		}
		return LNG('explorer.success');
	}

	// 个人空间使用统计
	public function userChart(){
		ActionCall('admin.analysis.chart');
	}
	// 个人操作日志
	public function userLog(){
		$type = Input::get('type', null, null);
		$this->in['userID'] = KodUser::id();
		if(!$this->in['userID']){return;}
		if(!$type){
			return ActionCall('admin.log.userLog');
		}
		if($type == 'user.index.loginSubmit'){
			return ActionCall('admin.log.userLogLogin');
		}
	}
	// 个人登录设备
	public function userDevice(){
		$fromTime = time() - 3600 * 24 * 30 * 3;//最近3个月;
		$res = Model('SystemLog')->deviceList(USER_ID,$fromTime);
		show_json($res);
	}
	
	// 当前账号在线设备列表;
	public function userLoginList(){
		$sign = Session::sign();
		$arr  = Action("filter.userLoginState")->userListLoad(USER_ID);
		$arr[$sign]['isSelf'] = true;
		foreach ($arr as $key => $item) {
			$arr[$key]['address'] = IpLocation::get($item['ip']);
		}
		show_json(array_values($arr));
	}
	// 踢下线某个登录设备;
	public function userLogoutSet(){
		$sign = Input::get('sign', null, null);
		Action("filter.userLoginState")->userLogoutTrigger(USER_ID,$sign);
		show_json(LNG('explorer.success'));
	}

	public function taskList(){ActionCall('admin.task.taskList',USER_ID);}
	public function taskKillAll(){ActionCall('admin.task.taskKillAll',USER_ID);}
	public function taskAction(){
		$result = ActionCall('admin.task.taskActionRun',false);
		if( !is_array($result['taskInfo'])){show_json(LNG('common.notExists'),false,'taskEmpty');}
		if( $result['taskInfo']['userID'] != USER_ID){show_json('User error',false);}
		show_json($result['result'],true);
	}
	public function notice(){
		$data	= Input::getArray(array(
			'id'		=> array('default' => false),
			'action'	=> array('check' => 'in','param' => array('get','edit','remove')),
		));
		$action = 'admin.notice.notice' . ucfirst($data['action']);
		ActionCall($action, $data['id']);
	}
}
sso.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/sso.class.php'
View Content
<?php

/**
 * 共享账号登录;支持限定账户,部门,权限组;
 */
class userSso extends Controller{
	public function __construct(){
		parent::__construct();
	}

	// sdk模式; 引入代码调用;
	public function check($appName){
		$urlInfo = parse_url(this_url());
		$GLOBALS['API_SSO_KEY']  = 'kodTokenApi-'.substr(md5($urlInfo['path']),0,5);
		$GLOBALS['API_SSO_PATH'] = $this->thisPathUrl();
		if(isset($this->in['kodTokenApi'])){
			$_REQUEST['accessToken'] = $this->in['kodTokenApi'];
		}
		
		$app = new Application();
		$app->setDefault('user.index.index');
		$result = $this->checkAuth($appName);
		$theUrl = $this->urlRemoveKey(this_url(),'kodTokenApi');
		if($result === true){
			if(isset($this->in['kodTokenApi'])){// 登录成功处理;	
				header('Location:'.$theUrl);exit;
			}
			return $this->userInfo();
		}
		$login = 'index.php?user/index/autoLogin&link='.rawurlencode($theUrl).'&callbackToken=1&msg='.$result;
		header('Location:'.APP_HOST.$login);exit;
	}
	private function userInfo(){
		$userInfo = Session::get('kodUser');
		if(!$userInfo) return false;
		
		$keys = explode(',','userID,name,email,phone,nickName,avatar,sex,avatar');
		$user = array_field_key($userInfo,$keys);
		$user['accessToken'] = Action('user.index')->accessToken();
		return $user;
	}
	private function thisPathUrl(){
		$uriInfo = parse_url(this_url());
		$uriPath = dirname($uriInfo['path']);
		if(substr($uriPath,-1) == '/'){$uriPath = $uriInfo['path'];}
		return '/'.trim($uriPath,'/');
	}
	
	private function checkAuth($appName){
		Action('user.index')->init();
		if(!Session::get('kodUser.userID')) return '[API LOGIN]';

		// user:所有登录用户, root:系统管理员用户; 其他指定用户json:指定用户处理;
		if(!$appName || $appName == 'user:all'){$appName = '{"user":"all"}';}
		if($appName == 'user:admin'){$appName = '{"user":"admin"}';}
		if(substr($appName,0,1) == '{'){
			//支持直接传入权限设定对象;{"user":"1,3","group":"1","role":"1,2"}
			$allow = Action('user.AuthPlugin')->checkAuthValue($appName);
		}else{
			$allow = Action('user.AuthPlugin')->checkAuth($appName);
		}
		if(!$allow){return LNG('user.loginNoPermission');}
		return true;
	}

	// 第三方通过url调用请求;
	public function apiCheckToken(){
		$result  = $this->checkAuth($_GET['appName']);
		$content = "[error]:".$result;
		if($result === true){
			ob_get_clean();
			$content = json_encode($this->userInfo());
		}
		echo $content;
	}
	// -> login&apiLogin => 第三方app&token=accessToken;
	public function apiLogin(){
		$result = $this->checkAuth($_GET['appName']);
		$callbackUrl = $_GET['callbackUrl'];
		if($result === true){
			$token = Action('user.index')->accessToken();
			$callbackUrl = $this->urlRemoveKey($callbackUrl,'kodTokenApi');
			if(strstr($callbackUrl,'?')){
				$callbackUrl = $callbackUrl.'&kodTokenApi='.$token;
			}else{
				$callbackUrl = $callbackUrl.'?kodTokenApi='.$token;
			}
			// pr($callbackUrl,$token);exit;
			header('Location:'.$callbackUrl);exit;
		}
		
		$link = APP_HOST.'#user/login&link='.rawurlencode($callbackUrl).'&callbackToken=1&msg='.$result;
		header('Location:'.$link);exit;
	}
	
	// 清除sso 登录cache缓存;
	public function logout(){
		$ssoKey 	= 'KOD_SSO_CACHE_KEY';
		$cachePath 	= BASIC_PATH.'data/temp/_cache/';
		$keys 		= isset($_COOKIE[$ssoKey]) ? $_COOKIE[$ssoKey] : '';
		if(!$keys){return;}
		
		$keyArr = explode(',',rawurldecode($keys));
		foreach ($keyArr as $key){
			$key = str_replace(array("/",'\\','?'),"_",$key);
        	$cacheFile = $cachePath."cache_api_".$key.'.php';
			if($key && @file_exists($cacheFile)){
				@unlink($cacheFile);
			}
		}
		Cookie::remove($ssoKey,true);
	}
	
	private function urlRemoveKey($url,$key){
		$parse = parse_url($url);
		parse_str($parse['query'],$get);
		unset($get[$key]);
		$query = http_build_query($get);
		$query = $query ? '?'.$query : '';
		$port  = (isset($parse['port']) && $parse['port'] != '80' ) ? ':'.$parse['port']:'';
		return $parse['scheme'].'://'.$parse['host'].$port.$parse['path'].$query;
	}
}
view.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/view.class.php'
View Content
<?php

class userView extends Controller{
	public function __construct(){
		parent::__construct();
	}
	public function options(){
		$user = Session::get("kodUser");
		if( isset($user['metaInfo'])) unset($user['metaInfo']);
		if( isset($this->config['settings']['language']) ){
			$this->config['settingAll']['language'] = array();
		}
		$options = array(
			"kod"	=> array(
				'systemOS'		=> '-',
				'phpVersion'	=> '-',
				'appApi'		=> appHostGet(),
				'APP_HOST'		=> APP_HOST,
				'APP_HOST_LINK' => $this->config['APP_HOST_LINK'],
				'ENV_DEV'		=> GLOBAL_DEBUG,
				'staticPath'	=> STATIC_PATH,
				'version'		=> KOD_VERSION,
				'build'			=> KOD_VERSION_BUILD,
				'channel'		=> INSTALL_CHANNEL,
			),
			"user"	=> array(
				'userID'		=> USER_ID ? USER_ID:'',
				'myhome'    	=> defined('MY_HOME') ? MY_HOME : '',
				'desktop'   	=> defined('MY_DESKTOP') ? MY_DESKTOP : '',
				'isRoot'		=> KodUser::isRoot() ? 1:0,//固定数值,true/false Android会异常;
				'info'			=> $user,
				'role'			=> Action('user.authRole')->userRoleAuth(),
				'config'		=> $this->config['settingDefault'],
				'editorConfig'	=> $this->config['editorDefault'],
				'isRootAllowIO'	=> $this->config["ADMIN_ALLOW_IO"], //后端处理
				'isRootAllowAll'	=> KodUser::isRoot() ? $this->config["ADMIN_ALLOW_ALL_ACTION"] : 1,
			),
			"system" => array(
				'settings'		=> $this->config['settings'],
				'all'			=> $this->config['settingAll'],
				'options'		=> array(),
			),
			"io"	=> KodIO::typeList(),
			"lang"	=> I18n::getType(),
		);
		if($user){//空间大小信息;
			$userInfoFull = Model('User')->getInfoFull(USER_ID);
			$options['user']['targetSpace'] = Action('explorer.auth')->space('user',USER_ID);
			$options['user']['role'] = $options['user']['role']['roleList'];
			$options['user']['config'] = array_merge($this->config['settingDefault'],Model('UserOption')->get());
			$options['user']['editorConfig'] = array_merge($this->config['editorDefault'],Model('UserOption')->get(false,'editor'));
			$options['user']['isOpenSafeSpace'] = _get($userInfoFull,'metaInfo.pathSafeFolder',0) ? true:false;
		}
		
		// 外链分享,界面部分配置采用分享者的配置;
		if(!$user && isset($this->in['shareID']) && $this->in['shareID']){
			$share = Model('Share')->getInfoByHash($this->in['shareID']);
			$share = $share ? $share : array('userID'=>'_');
			$userOptions = Model('user_option')->where(array('userID'=>$share['userID'],'type'=>''))->select();
			$userOptions = array_to_keyvalue($userOptions,'key','value');
			$userOptions = array_field_key($userOptions,array('theme','themeStyle'));
			$options['user']['config'] = array_merge($options['user']['config'],$userOptions);
		}
		
		if(KodUser::isRoot()){
			$options['kod']['WEB_ROOT']   = WEB_ROOT;
			$options['kod']['BASIC_PATH'] = BASIC_PATH;
			$options['kod']['systemOS']   = $this->config['systemOS'];
			$options['kod']['phpVersion'] = PHP_VERSION;
		}
		unset($options['system']['settings']['sysTempPath']);
		unset($options['system']['settings']['sysTempFiles']);
		
		//为https时自适应为https; 兼容https无法请求http的情况;
		if(strstr(APP_HOST,'https://')){
			$api = $options['system']['settings']['kodApiServer'];
			$options['system']['settings']['kodApiServer'] = str_replace('http://','https://',$api);
		}
		$options = Hook::filter('user.view.options.before',$options);
		$options = Hook::filter('user.view.options.after',$options); 
		$options = $this->parseMenu($options,!!$user);
		$this->companyInfo($options);
		
		if($this->in['full'] == '1'){
			$options['_lang'] = array(
				"list"	=> I18n::getAll(),
				"lang"	=> I18n::getType(),
			);
		}
		show_json($options);
	}
	
	private function companyInfo(&$options){
		$op = &$options['system']['options'];$_op = $this->config['settingSystemDefault']; 
		// 兼容解决部分用户误操作设置logo包含身份token问题;
		if(stristr($op['systemLogo'],'accessToken')){$op['systemLogo'] = $_op['systemLogo'];}
		if(stristr($op['systemLogoMenu'],'accessToken')){$op['systemLogoMenu'] = $_op['systemLogoMenu'];}

		$groupSelf  = array_to_keyvalue(Session::get("kodUser.groupInfo"),'','groupID');
		$groupCompany = $GLOBALS['config']['settings']['groupCompany'];
		if(!$groupCompany || !$groupSelf || KodUser::isRoot()) return false;

		$groupAllowShow = Model('Group')->groupShowRoot($groupSelf[0],$groupCompany);
		$groupInfo = Model('Group')->getInfo($groupAllowShow[0]);
		$options['kod']['companyInfo'] = array('name'=>$groupInfo['name'],'logoType'=>'text','logoText'=>$groupInfo['name']);
	}
	
	// 检测是否支持二进制上传;(部分apache服务器,上传会被拦截报403错误;自动处理为表单上传;)
	public function uploadBindaryCheck(){
		$input = file_get_contents("php://input");
		$result= trim($input) == '[uploadCheck]' ? '[ok]':'[error]';
		echo $result;
	}
	
	/**
	 * 根据权限设置筛选菜单;
	 */
	private function parseMenu($options,$user=false){
		$menus  = &$options['system']['options']['menu'];
		$result = array();
		if ($user) {	// 未登录时不显示菜单信息
			foreach ($menus as $item) {
				if(!isset($item['pluginAuth'])){
					$allow = true;
				}else{
					$allow = ActionCall("user.authPlugin.checkAuthValue",$item['pluginAuth']);
				}
				if($allow){$result[] = $item;}
			}
		}
		$menus = $result;
		return $options;
	}
	
	public function lang(){
		if($this->in['_t']) return;
		$result = array(
			"list"	=> I18n::getAll(),
			"lang"	=> I18n::getType(),
		);
		show_json($result);
	}
	
	public function plugins(){
		$GLOBALS['SHOW_OUT_EXCEPTION'] = true;// 拦截show_out/show_tips; 转为抛出异常;
		hook::bind('eventRun.error',array($this,'pluginsError'));
		
		ob_get_clean();
		header("Content-Type: application/javascript; charset=utf-8");
		echo 'var kodReady=[];';
		Hook::trigger('user.commonJs.insert');
		$useTime = sprintf('%.4f',mtime() - TIME_FLOAT);
		echo "\n/* time={$useTime} */\n";
	}
	//  输出插件js过程中,报错处理;
	public function pluginsError($error=''){
		// $error = $error."\n;call:".get_caller_msg();
		echo ';{console.error(decodeURIComponent("'.rawurlencode($error).'"));};';
		return true;
	}
	
	
	// 计划任务触发;
	public function call(){
		header('Content-Type: application/javascript');
		http_close();
		Action('explorer.index')->clearCache();
		Action('explorer.attachment')->clearCache();
		Action("admin.repair")->sourceNameInit();
		
		AutoTask::start();		// 后台计划任务自动启动;已启动则不再处理;
		TaskQueue::runThread(); // 后台任务队列,允许多个进程处理后台任务队列(最多允许3个,队列没有任务则退出);
		Cache::clearTimeout();
		$this->autoSetUploadMax();
	}
	
	// 自动优化配置上传文件分片大小(如果获取到php限制及nginx限制;取限制的最小值,如果大于20M则分片设置为20M)
	private function autoSetUploadMax(){
		$chunkSize   = $GLOBALS['config']['settings']['upload']['chunkSize'];
		if($chunkSize != 0.5*1024*1024){return;}
		if(Model('SystemOption')->get('autoSetUploadMax_set')){return;}

		Model('SystemOption')->set('autoSetUploadMax_set','1');
		$postMaxPhp   = get_post_max();
		$postMaxNginx = get_nginx_post_max();
		if($postMaxPhp && $postMaxNginx){
			$sizeMin = min($postMaxPhp,$postMaxNginx,20*1024*1024) / (1024*1024);// MB;
			if($sizeMin > 0.3){Model('SystemOption')->set('chunkSize',$sizeMin);}
		}
	}
	
	public function parseUrl($link){
		if(!$link || !trim($link)) return '';
		if(substr($link,0,1) ==  '/'){return HOST.substr($link,1);}
		if(substr($link,0,2) == './'){return APP_HOST.substr($link,2);}
		if(substr($link,0,strlen(HOST)) == HOST){return $link;}
		
		// 域名切换情况处理(用户头像等信息url缓存情况处理)
		if(strstr($link,'explorer/share/file&hash')){
			$hostInfo = parse_url(APP_HOST);
			$linkInfo = parse_url($link);
			$linkQuery= parse_url_query($link);
			if($hostInfo['path'] != $linkInfo['path']){return $link;} // 站点不同则不再继续自适应;
			if(!isset($linkQuery['hash']) || !$linkQuery['hash']){return $link;}
			
			$pathTrue = Mcrypt::decode($linkQuery['hash'],Model('SystemOption')->get('systemPassword'));
			if(!$pathTrue){return $link;}
			
			$linkPort = isset($linkInfo['port']) && $linkInfo['port'] != '80' ? ':'.$linkInfo['port'] : '';
			$linkHost = _get($linkInfo,'scheme','http').'://'.$linkInfo['host'].$linkPort.'/';
			return str_replace($linkHost,HOST,$link);
		}
		return $link;
	}
	public function imageRequest(){
		Action('user.viewImage')->request('start');
	}
	
	
	// 验证码-登录、注册、找回密码、个人设置
	public function checkCode() {
		$captcha = new MyCaptcha(4);
		Session::set('checkCode', $captcha->getString());
	}

	// 发送(消息)验证码-注册、找回密码
	public function sendCode(){
		Action('user.regist')->sendMsgCode();
	}
	public function qrcode() {
		$url = $this->in['url'];
		if (function_exists('imagecolorallocate')) {
			ob_get_clean();
			QRcode::png($url,false,QR_ECLEVEL_L,5,2);
		} else {
			// https://api.pwmqr.com/qrcode/create/?url=
			// https://demo.kodcloud.com/?user/view/qrcode&url=
			// https://api.qrserver.com/v1/create-qr-code/?data=
			header('location: https://api.pwmqr.com/qrcode/create/?url='.rawurlencode($url));
		}
	}
	public function taskAction(){
		$result = ActionCall('admin.task.taskActionRun',0);
		show_json($result['result'],true);
	}

	// 插件说明
	public function pluginDesc(){
		$path = PLUGIN_DIR.KodIO::clear($this->in['app']).'/';
		$lang = I18n::getType();
		$file = $path.'readme.md';
		if(file_exists($path.'readme_'.$lang.'.md')){
			$file = $path.'readme_'.$lang.'.md';
		}
		$content = '';
		if(file_exists($file)){
			$content = file_get_contents($file);
		}
		if(!preg_match("/^[0-9a-zA-Z_.]+$/",$_GET['callback'])){
			die("calllback error!");
		}
		echo $_GET['callback'].'("'.base64_encode($content).'")';
	}
	
	//chrome安装: 必须https;serviceWorker引入处理;manifest配置; [manifest.json配置目录同sw.js引入];
	public function manifest(){
		$json   = file_get_contents(LIB_DIR.'template/user/manifest.json');
		$name   = stristr(I18n::getType(),'zh') ? '可道云':'kodcloud';
		$opt    = $GLOBALS['config']['settingSystemDefault'];
		$name   = isset($opt['systemNameApp']) ? $opt['systemNameApp']: $name;
		$static = STATIC_PATH == './static/' ? APP_HOST.'static/':STATIC_PATH;
		$assign = array(
			"{{name}}"		=> $name,
			"{{appDesc}}"	=> LNG('common.copyright.name'),
			"{{static}}"	=> $static,
		);
		$json = str_replace(array_keys($assign),array_values($assign),$json);
		if(isset($opt['systemIcon'])){
			$jsonArr = json_decode($json,true);
			$jsonArr['icons'] = array(array("src"=>$opt['systemIcon'],"sizes"=>"512x512","type"=>"image/png"));
			if(isset($opt['systemThemeColor'])){$jsonArr['theme_color'] = $opt['systemThemeColor'];}
			$json = json_encode_force($jsonArr);
			$json = str_replace('\\','',$json);
		}
		
		header("Content-Type: application/javascript; charset=utf-8");
		echo $json;
	}
	public function manifestJS(){
		header("Content-Type: application/javascript; charset=utf-8");
		echo file_get_contents(BASIC_PATH.'static/app/vender/sw.js');
	}
}
viewImage.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/user/viewImage.class.php'
View Content
<?php 

/**
 * 图片请求转发;
 * https://api.kodcloud.com/#explorer&pathFile=%2Fwww%2Fwwwroot%2Fapi.kodcloud.com%2Fplugins%2FplatformKod%2Fcontroller%2Ftools%2FwallpageApi.class.php
 */
class userViewImage extends Controller{
	public function __construct(){
		parent::__construct();
	}
	
	// type=show;[page=xx]; type=search;[words=xx,page=xx]
	public static $allowCache = 1;
	public function request($load){
		$apiArr = $this->loadApi();
		$type   = $this->in['type'] == 'search' ? 'search':'show';
		if(!is_array($apiArr) || !is_array($apiArr[$type]) ){
			show_json(LNG('common.version.networkError'),false);
		}
		$api = $apiArr[$type];
		$search = _get($this->in,'search','');
		$page   = intval(_get($this->in,'page','1'));
		$pageMax= intval(_get($api['parse'],'pageTotalSet',0));
		$page   = $page <= 0 ? 1: (($pageMax && $page >= $pageMax) ? $pageMax : $page);
		$pageValue = $api['parse']['pageOffset'] ? intval($api['parse']['pageOffset'] * $page) : $page;
		$replaceTo = array($pageValue,rawurlencode($search));
		$url = str_replace(array('{{page}}','{{search}}'),$replaceTo,$api['url']);
		
		$cacheKey = "wallpageImageApi-".md5($url);
		$result = Cache::get($cacheKey);
		if(is_array($result) && self::$allowCache){show_json($result,true);}
		
		$header = is_array($api['header']) ? $api['header']: false;
		$res  = url_request($url,'GET',false,$header,false,false,30);
		$result = $this->imageParse($res['data'],$api);
		if(!$result){show_json("Request data error!",false);}
		
		$result['pageInfo']['page'] = $page;
		Cache::set($cacheKey,$result,600);
		show_json($result,true);
	}
	private function loadApi(){
		$url = $GLOBALS['config']['settings']['kodApiServer'];
		if(!$url) return false;
		$result = Cache::get("wallpageImageApi");
		if(is_array($result) && self::$allowCache) return $result;

		$res  = url_request($url.'wallpage/api','GET',false,false,false,false,30);
		$json = json_decode(_get($res,'data',''),true);
		if(!$json || !$json['code'] || !is_array($json['data'])) return false;
		Cache::set("wallpageImageApi",$json['data'],3600);
		return $json['data'];
	}
	private function imageParse(&$body,$api){
		if( strstr($body,'charset=gb2312') || 
			strstr($body,'charset=gbk')){
			$body = iconv_to($body,'gbk','utf-8');
		}
		$parse = $api['parse'];
		$list  = array();
		$pageInfo = array(
			'pageTotal'	=> _get($parse,'pageTotalSet',''),
			'totalNum'	=> _get($parse,'totalNumSet',''),
		);
		$urlAdd = $parse['urlAdd'] ? $parse['urlAdd']: '';
		if($parse['type'] == 'json'){
			$json = json_decode($body,true);
			$listData = $parse['arr'] ? _get($json,$parse['arr'],array()) : $json;
			if(!$listData) return false;
			
			foreach ($listData as $item) {
				$list[] = array(
					'link'	=> $urlAdd._get($item,$parse['link']),
					'thumb'	=> _get($item,$parse['thumb']),
					'title'	=> _get($item,$parse['title'])
				);
			}
			$pageInfo['pageTotal'] = _get($json,$parse['pageTotal'],$pageInfo['pageTotal']);
			$pageInfo['totalNum']  = _get($json,$parse['totalNum'], $pageInfo['totalNum']);
			return array('list'=>$list,'pageInfo'=>$pageInfo);
		}
		
		if(!$parse['link'] || !$body) return false;
		$this->matchSet('link',$parse,$body,$list,true);	
		$this->matchSet('thumb',$parse,$body,$list,true);
		$this->matchSet('title',$parse,$body,$list,true);

		$this->matchSet('pageTotal',$parse,$body,$pageInfo);
		$this->matchSet('totalNum',$parse,$body,$pageInfo);		
		return array('list'=>$list,'pageInfo'=>$pageInfo);
	}
	
	private function matchSet($key,$parse,$body,&$data,$isList=false){
		$isArr  = in_array($key,array('thumb','title'));
		$reg    = $parse[$key] ? $parse[$key] : ($isArr ? $parse['link']:'');
		$regAt  = $parse[$key.'Reg'];
		$regReplace = $parse[$key.'Replace'];
		if(!$reg && !$regAt) return;

		$match = preg_match_all('/'.$reg.'/',$body,$matchRes);//pr($matchRes);exit;
		if(!$match || !is_array($matchRes[0])) return;
		if(!$isList){ // 单个值处理;
			$regAt = str_replace('{{last}}',count($matchRes[0]) - 1,$regAt);
			$data[$key] = intval(_get($matchRes,$regAt));
			return;
		}
		
		// 多个值处理;
		$listMatch = _get($matchRes,$regAt);
		foreach ($listMatch as $i => $val){
			$value = $val;
			if(!$data[$i]){$data[$i] = array();}
			if(is_array($regReplace) && !$regReplace[0]){
				$value = str_replace($regReplace[1],$regReplace[2],$value);
			}
			if(is_array($regReplace) && $regReplace[0]){
				$value = preg_replace('/'.$regReplace[1].'/',$regReplace[2],$value);
			}
			$data[$i][$key] = $value;
		}
	}
}