time = time(); $this->ac_appcore = new appcore(); //应用入口 $this->ac_get_url = $this->ac_appcore->get_app_url(); } /** * 检测入口 */ public function doindex() { global $_M; if(!file_exists(INS_LOCK_FILE) || $_M['form']['check']){ //应用信息 $this->input['app'] = $this->ac_appcore->met_app(); $this->input['app']['icon'] = $_M['url']['app'].M_NAME.'/icon.png'; $this->input['app']['appname'] = get_word($this->input['app']['appname']); $this->input['app']['addtime'] = date('Y-m-d H:i:s',$this->input['app']['addtime']); //整理检查项 if(!file_exists(INS_LOCK_FILE) || file_exists(UPD_LOCK_FILE) ){ define("CHECK_ITEM", "server|system|accredit|appfile|weqrcode|appcheck"); }else{ define("CHECK_ITEM", "server|system|accredit|appfile|weqrcode|update|appcheck"); } //页面检查项 $this->input['ckitem'] = CHECK_ITEM; require $this->show('app/index',$this->input); }else{ turnover($this->ac_get_url,'No prompt'); } } /* + Ajax 各种数据校验  +-------------------------------------------------------------------------------------------  + doappcheck : 用来进行应用的各项检测入口  +------------------------------------------------------------------------------------------- */ public function doappcheck() { global $_M; $this->ac_session = new session(); $this->method = $_M['form']['type']; //格式掉之前的旧数据 if($this->method == 'server') self::del_cache(); // 大于 PHP5.4 self::{$this->method}(); self::result(); echo $this->ac_appcore->resmsg(false); } /* + Ajax 下载更新应用文件  +-------------------------------------------------------------------------------------------  + download : 应用文件的下载入口  +------------------------------------------------------------------------------------------- */ public function download() { global $_M; (new download($this->ac_appcore))->download($_M['form']['data']); echo $this->ac_appcore->resmsg(false); } /* + Ajax modal body区域  +-------------------------------------------------------------------------------------------  + domodal : 应用模态框数据处理入口  +------------------------------------------------------------------------------------------- */ public function domodal() { global $_M; $this->ac_session = new session(); switch ($_M['form']['type']) { case 'appfile': $modal = self::appfile_modal(); break; case 'weqrcode': $modal = self::weqrcode_modal(); break; case 'wechat': $modal = self::wechat_modal(); break; case 'update': $modal = self::update_modal(); break; default: break; } echo $this->ac_appcore->jsoncallback($modal); } /* + Ajax  +-------------------------------------------------------------------------------------------  + doajax : 应用最终验证结果核对,以及应用程序使用过程中的应用校验数据延迟访问入口  +------------------------------------------------------------------------------------------- */ public function doajax() { global $_M; /* * 查询API访问时间 * 若当天没有访问则访问API * 若已访问则跳过执行下面 * */ //sign $bool = false; //wechat、update 值若为1 则是通过,0 为需要执行的,2为忽略的 if($_M['form']['sign'] == 'appcheck'){ $time = date('Y-m-d H:i:s',$this->time); $ac_session = new session(); $cache = [ //记录时间 'time' => strtotime("{$time} +1 day"), //授权结果 'check' => $ac_session->get('check'), //公众号结果 'wechat' => (int) $ac_session->get('wechat'), //是否有最新版本 'update' => (int) $ac_session->get('update') ]; $listkey = $ac_session->get('ckitem'); $ckitem = stringto_array(CHECK_ITEM,'|'); // 后期根据情况再进行具体处理 if(!in_array(0, $listkey)){ $bool = true; if(file_exists(INS_LOCK_FILE)){ $msgtext = '进入应用'; }else{ $msgtext = ''; } } $ac_session->del('ckitem'); }else{ //清除缓存 $this->ac_session = new session(); self::del_cache(); //进行检测 $ac_cache = $this->ac_appcore->mysql_config(array('ac_cache')); //解密后的数组 $cache = (new curls($this->ac_appcore))->postr($ac_cache,'',false)->resdata(); $post = $this->ac_appcore->met_all(); //判断是否超时 if(empty($ac_cache) || $cache['time'] < strtotime("+1 hour") ){ //API获取 $this->method = 'check'; $ajax = self::curljson($post,false,25); $cache = $ajax['code']?$ajax['cache']:[]; } //查看本地是否存在禁止更新锁 if(file_exists(UPD_LOCK_FILE)) $cache['update'] = 1; //需要对比数据 if(!empty($cache)){ $check = (new curls($this->ac_appcore)) ->apikey_decode([$cache['check'],$post['web']['met_weburl'],$post['app']['m_name'],$post['app']['addtime'] ]); $state = $check['state'] && $check['app']['v_m_name'] == M_NAME?true:false; if($state){ if($check['vatime']['minute'] > 0){ //对有效时间计算 $time = date('Y-m-d H:i:s',$check['vatime']['stime']); $endtime = strtotime("{$time} +{$check['vatime']['minute']} minute"); if($endtime <= $this->time ) $state = false; } //最终结果判断 $bool = $state && !empty($cache['wechat']) && !empty($cache['update'])?true:false; } } //版本号 if($bool){ $vertype = ['Y','S','D']; $msgtext = "版本号:{$vertype[$check['app']['v_vertype']]}.{$check['app']['v_ver']}"; if($check['vatime']['minute'] > 0){ //体验版本结束日期 $endtime = date('Y-m-d H:i:s',$endtime); $msgtext .= " ({$endtime})"; } } } $ac_cache['exetime'] = $this->time; //缓存记录 $ac_cache = (new curls($this->ac_appcore))->apipost($cache)->resdata(true); $this->ac_appcore->mysql_config(['ac_cache',$ac_cache],false); //返回 $bool?$this->ac_appcore->msgkey(1)->msgmeet($msgtext,true):$this->ac_appcore->locmsg(4012); echo $this->ac_appcore->resmsg(false); } // +---------------------------------------------------------------------- // | 应用主方法 限制内部调用 // +---------------------------------------------------------------------- /* + 内部调用  +-------------------------------------------------------------------------------------------  + server : 进行进行服务通信的验证,提供站点信息,站点应用信息给服务端, + 从服务端获取当前应用版本的详细信息,以及系统环境的检测结果并缓存到session  +-------------------------------------------------------------------------------------------  + system : 直接获取缓存的session 直接将环境结果处理返回  +-------------------------------------------------------------------------------------------  + accredit : 提供APP信息给服务端,从服务端获取验证结果  +-------------------------------------------------------------------------------------------  + appfile : 根据APP版本信息,从服务端获取对应的对比指纹 + 核心文件出现问题,会直接从服务端自动更新, + 非核心文件会提供手动处理方法,必须修复 + 暂时不提供非应用文件进行删除处理。  +-------------------------------------------------------------------------------------------  + weqrcode : 根据站点信息和用户信息,从服务端查询是否符合微信公众号关注要求  +-------------------------------------------------------------------------------------------  + update : 应用在线更新功能  +------------------------------------------------------------------------------------------- */ # 服务端通信检测 # 通过加密进行处理可防止数据丢失,防止某些敏感参数被过滤掉 protected function server() { global $_M; //数据准备 $server = self::curljson($this->ac_appcore->met_web_app()); //进行结果处理 if($server['code']){ $this->ac_appcore->sucmsg($server['text']); }else{ $this->ac_appcore->errmsg($server['errcode'],$server['errmsg']); } //缓存app版本以及环境检测结果 $this->ac_session->set('applist',$server['applist']); $this->ac_session->set('system',$server['system']); } # 网站环境检测 # 环境不存在中间值,要么支持,要么不支持 protected function system() { global $_M; //读取缓存 $system = $this->ac_session->get('system'); //进行结果处理 $metver = $system['metver']['code']; $phpver = $system['phpver']['code']; $text = [ [$phpver,$system['phpver']['text']], [$metver,$system['metver']['text']] ]; $this->ac_appcore->vagmsg($metver && $phpver?1:0,$text); //清空缓存 $this->ac_session->del('system'); } # 应用授权检测 protected function accredit() { global $_M; //数据准备 $post = $this->ac_appcore->met_web_app(); $accredit = self::curljson($post,false,20); //进行结果处理 if($accredit['code']){ //解密 $apikey = (new curls($this->ac_appcore))->apikey_decode([$accredit['codekey'],$post['web']['met_weburl'],$post['app']['m_name'],$post['app']['addtime']]); //校验 if(!empty($apikey['state']) && $apikey['vatime']['minute'] > 0){ //对有效时间计算 $time = date('Y-m-d H:i:s',$apikey['vatime']['stime']); $text = '体验结束时间:'.date('Y-m-d H:i:s',strtotime("{$time} +{$apikey['vatime']['minute']} minute")); } $apikey['state']?$this->ac_appcore->sucmsg($accredit['text'].$text):$this->ac_appcore->locmsg(4024); }else{ $this->ac_appcore->errmsg($accredit['errcode'],$accredit['errmsg']); } //记录授权结果 $this->ac_session->set('check',$accredit['codekey']); } #文件指纹对比 protected function appfile() { global $_M; //清空需要删的参数 $this->ac_session->del('repair'); //获取缓存 $appfile = $this->ac_session->get('appfile'); if(!$appfile){ $appfile = self::curljson($this->ac_appcore->met_web_app()); //进行结果处理 if($appfile['code']) { $this->ac_session->set('appfile',$appfile); }else{ return $this->ac_appcore->errmsg($appfile['errcode'],$appfile['errmsg']); } } //文件修复 $dirfinger = new dirfinger(); $corelist = $dirfinger->fingers($appfile['corelist']); //先自动修复核心框架,再强制让用户修复有问题的文件。 if(count($corelist['dllist']) > 0){ //核心文件异常则启动自动修复,若修复失败再进行提示 $this->ac_session->set('dllist',$corelist['dllist']); $modal = $this->ac_appcore->msgkey()->msgtext(4004)->resmsg(); $modal['suc'] = false; $modal['data'] = 'core|check'; return $this->ac_appcore->newres()->modalmsg($modal, 4001); } $fingers = $dirfinger->fingers($appfile['fingers']); //删除应用多余的文件 //$dirfinger->del_appfile($appfile['fingers']); //判断是否需要文件修复 $inslock = (int) file_exists(INS_LOCK_FILE); //安装锁不存在说明新安装,不需要执行文件修复 $dllock = count($fingers['dllist']) == 0?1:0; //需要修复的文件为0 说明不需要修复 if(!$inslock || $dllock) { //清理掉不再需要的缓存 $this->ac_session->del('repair'); $this->ac_session->del('appfile'); //删除下载的缓存文件 $this->ac_appcore->del_update_dir(); return $this->ac_appcore->sucmsg()->msgtext(4003); } //非强制更新和非安装状态 且$fingers 大于0时才执行文件修复 if(count($fingers['dllist']) > 0){ $this->ac_session->set('dllist',$fingers['dllist']); $this->ac_session->set('repair',$fingers); $modal = [ 'suc' => true, 'title' => '应用文件修复', 'body' => '
', 'url' => $_M['url']['own_form'].'a=domodal&type=appfile' ]; $text = '修复异常文件'; return $this->ac_appcore->modalmsg($modal, [4002,$text]); } } # 微信公众号 protected function weqrcode() { global $_M; //缓存 $wechat = $this->ac_session->get('wechat'); if($wechat !== '') sleep(1); //验证 $weqrcode = self::curljson($this->ac_appcore->met_all()); if($weqrcode['code']){ if($weqrcode['wx']['key'] == 1 ){ $this->ac_appcore->sucmsg($weqrcode['text']); }else{ $modal = [ 'suc' => true, 'title' => '关注微信公众号', 'body' => '
', 'url' => $_M['url']['own_form'].'a=domodal&type=weqrcode', ]; $text = '关注公众号'; $this->ac_appcore->modalmsg($modal) ->sucmsg($weqrcode['wx']['ret']['info'].$text,$weqrcode['wx']['key']); } }else{ $this->ac_appcore->errmsg($weqrcode['errcode'],$weqrcode['errmsg']); } $this->ac_session->set('wechat',$weqrcode['wx']['key']); } # 在线更新检测 protected function update() { global $_M; $this->ac_session->del('dllist'); $this->ac_session->del('newapp'); $applist = $this->ac_session->get('applist'); //当前版本禁止检测升级 直接报已是最新 //下个版本禁止升级,直接报已是最新 // UPD_LOCK_FILE 存在直接报最新 if(file_exists(UPD_LOCK_FILE) || empty($applist['v_ckupdate'])){ // 1 为无须无法升级 // 0、2 有升级 $this->ac_session->set('update',1); return $this->ac_appcore->sucmsg('已是最新版。'); } //直接检测 $update = self::curljson($this->ac_appcore->met_web_app()); if($update['code']){ //判断是否有指纹需要处理 if($update['code'] == 8002){ $this->ac_session->set('update',1); return $this->ac_appcore->sucmsg($update['text']); } //升级文件处理 $this->ac_session->set('newapp',$update['newapp']); $modal = [ 'suc' => true, 'title' => $update['newapp']['v_ver'].' 更新内容', 'body' => '
', 'url' => $_M['url']['own_form'].'a=domodal&type=update' ]; $text = '在线更新'; $upkey = $update['newapp']['v_update'] == 1?0:2; $this->ac_appcore->modalmsg($modal) ->sucmsg($update['text'].$text,$upkey); }else{ $upkey = 1; $this->ac_appcore->errmsg($update['errcode'],$update['errmsg']); if(empty($update['system'])){ $phpver = arrayto_string($update['newapp']['v_phpver'],' -- '); $metver = arrayto_string($update['newapp']['v_metver'],' -- '); $phpli = count($update['newapp']['v_phpver']) == 1?'最低':''; $metli = count($update['newapp']['v_metver']) == 1?'最低':''; $text = "
".$update['newapp']['v_ver']." 版本环境要求:
  1. 应用".$phpli."支持PHP ".$phpver." 版本;
  2. 应用".$metli."支持MetInfo ".$metver." 版本;
".$update['newapp']['v_ver']." 版本环境检测结果:
    ".$update['system']['text']."
"; $this->ac_appcore->msgmeet($text); } } //需要缓存的结果 $this->ac_session->set('update',$upkey); } // +---------------------------------------------------------------------- // | 应用主方法 限制内部调用,主要处理模态框数据的交互 // +---------------------------------------------------------------------- // 文件修复列表 protected function appfile_modal() { global $_M; $download = new download($this->ac_appcore); $download->download('filelist|check'); $dllist = $download->app_get_file(); if(count($dllist['dllist']) == 0){ return '
用文件不存在异常。
'; } $resmsg = $this->ac_appcore->resmsg(); if($resmsg > 0){ $fingers = $this->ac_session->get('repair'); foreach ($dllist['dllist'] as $key => $val) { $text = ''; if($fingers['updlist'][$val]) $text = '文件被修改'; if($fingers['dowlist'][$val]) $text = '文件不存在'; $html .=<< {$text} {$val} 点击下载

EOT; } $html = [ 'body' => "
{$html}
", 'foot' => '' ]; }else{ $html = ['body' => $resmsg['text'] ]; } return $html; } //微信公众号二维码 protected function weqrcode_modal() { global $_M; $this->method = 'qrcode'; $weqrcode = self::curljson($this->ac_appcore->met_all(),false,25,'wx'); if($weqrcode['code']){ $wehtml = [ 'body' => '
', 'foot' => $weqrcode['qrcode']['text']['info'], 'callback' => 'wechat_qrcode' ]; }else{ $wehtml = [ 'body' => "

({$weqrcode['errcode']}){$weqrcode['errmsg']}

" ]; } return $wehtml; } //微信公众号轮询 protected function wechat_modal() { global $_M; $sign = random(6); sleep(1); $this->method = 'check'; $weqrcode = self::curljson($this->ac_appcore->met_all(),false,20,'wx'); //结果返回 if($weqrcode['code']){ $this->ac_appcore->sucmsg($weqrcode['wx']['ret']['info'],$weqrcode['wx']['key']); }else{ $this->ac_appcore->errmsg($weqrcode['errcode'],$weqrcode['errmsg']); } //计算2分钟超时 if(empty($_M['form']['endtime'])){ $time = date('Y-m-d H:i:s', $this->time); $endtime = strtotime("{$time} +2 minute"); }else{ if($_M['form']['endtime'] < $this->time){ $text = '刷新二维码'; $this->ac_appcore->locmsg([4023,$text]); }else{ $endtime = $_M['form']['endtime']; } } $resmsg = $this->ac_appcore->resmsg(); $resmsg['endtime'] = $endtime; $resmsg['sign'] = $sign; return $resmsg; } //在线更新 protected function update_modal() { global $_M; $newapp = $this->ac_session->get('newapp'); return [ 'body' => strlen($newapp['v_daily']) > 0?"

{$newapp['v_daily']}
":'

没有更新说明。

', 'foot' => '' ]; } // +---------------------------------------------------------------------- // | 辅助方法 // +---------------------------------------------------------------------- # 需要格式化掉的数据防止二次使用时错误 private function del_cache() { global $_M; $this->ac_session->del('applist'); $this->ac_session->del('system'); $this->ac_session->del('check'); $this->ac_session->del('appfile'); $this->ac_session->del('repair'); $this->ac_session->del('dllist'); $this->ac_session->del('wechat'); $this->ac_session->del('update'); $this->ac_session->del('ckitem'); } # 记录各项检查结果 private function result() { global $_M; $code = false; $ckitem = $this->ac_session->get('ckitem'); if($this->method != 'appcheck'){ $key = $this->ac_appcore->resmsg(); $ckitem[$this->method] = $key['key']; $this->ac_session->set('ckitem',$ckitem); } } # 将返回信息转为JSON数据 private function curljson($post = [],$type = false,$timeout = 15,$sign = 'ck' ) { global $_M; return (new curls($this->ac_appcore))->apiurl($this->method,$sign) ->apipost($post) ->curls($timeout) ->resdata($type); } # 应用内容呈现 protected function show($file, $data){ global $_M; $view =load::sys_class('engine','new'); require_once $view->dodisplay($file, $data); } } // +---------------------------------------------------------------------- // | class appcore 应用客户端核心处理方法 // +---------------------------------------------------------------------- // | Copyright: 1.0 // +---------------------------------------------------------------------- // | Author site: www.metinfo.wang,www.metinfo.cc // +---------------------------------------------------------------------- // | Author QQ: 415420792 // +---------------------------------------------------------------------- class appcore{ # 应用文件名 private $m_name; # 站点信息 private $web = []; # 应用信息 private $app = []; # 管理员权限 private $aop = []; # 错误信息的提示 private $tipmsg = []; # 返回的信息结果 private $resmsg = []; # 初始化 public function __construct() { global $_M; $this->m_name = M_NAME; } # 获取应用入口 public function get_app_url() { global $_M; $settings = file_exists(INS_LOCK_FILE)?json_decode(file_get_contents(INS_LOCK_FILE),true):[]; if(empty($settings['url'])){ unset($settings['url'],$settings['time']); $get_url = $_M['url']['own_name'].http_build_query($settings); }else{ $get_url = $settings['url']; } return $get_url; } # 站点信息和应用信息 public function met_web_app() { global $_M; return [ 'web' => self::met_web(), 'app' => self::met_app() ]; } # 获取站点信息以及权限查询 public function met_all() { global $_M; $met = self::met_web_app(); $met['aop'] = self::met_aop(); return $met; } # 站点信息查询 public function met_web() { global $_M; // 站点信息 $name = ['met_weburl','met_webname','metcms_v','met_keywords','met_description','met_skin_user']; foreach ($name as $val) { $lang = $val == 'metcms_v'?'metinfo':$_M['lang']; $met = DB::get_one("SELECT value FROM {$_M['table']['config']} WHERE name = '{$val}' AND lang = '{$lang}' "); $this->web[$val] = $met['value']; } $this->web['webip'] = $_SERVER['SERVER_ADDR']; $this->web['sysver'] = php_uname('s').' '.php_uname('r'); $this->web['phpver'] = PHP_VERSION; $this->web['sqlver'] = DB::version(); $this->web['webver'] = str_replace("PHP/{$this->web['phpver']}","",$_SERVER['SERVER_SOFTWARE']); $this->web['lang'] = $_M['lang']; return $this->web; } # 应用信息查询 public function met_app() { global $_M; //应用信息 if(!empty($this->m_name)) { $this->app = DB::get_one("SELECT no,ver,addtime,m_name,appname FROM {$_M['table']['applist']} WHERE m_name='{$this->m_name}' "); } $this->app['client_ip'] = self::get_client_ip(); return $this->app; } # 下载权限处理需求 public function met_adl() { global $_M; //应用信息 if(!empty($this->m_name)) { $this->adl = DB::get_one("SELECT no,ver,addtime,m_name FROM {$_M['table']['applist']} WHERE m_name='{$this->m_name}' "); } //域名 $met = DB::get_one("SELECT value FROM {$_M['table']['config']} WHERE name = 'met_weburl' AND lang = '{$_M['lang']}' "); $this->adl['met_weburl'] = $met['value']; return $this->adl; } # 公众号关注管理员权限 public function met_aop() { global $_M; $admin = admin_information(); //管理员权限 $this->aop = [ 'admin_id' => $admin['admin_id'], 'admin_op' => $admin['admin_op'], 'admin_ip' => $admin['admin_modify_ip'], 'admin_time' => $admin['admin_modify_date'], ]; return $this->aop; } # 对resmsg 初始化 public function newres() { global $_M; $this->resmsg = []; return $this; } # 提示信息返回 # $type true 为数组,false 为json # $unset true 清空避免重复使用出现问题 false 暂时不清空 public function resmsg($type = true,$unset = false) { global $_M; //检测值是否存在 if(strlen($this->resmsg['key']) == 0) self::locmsg(4012); //检测项的值 $this->resmsg['eqkey'] = $_M['form']['eqkey']; //返回值 $resmsg = $type?$this->resmsg: self::jsoncallback($this->resmsg); //注销 if($unset) unset($this->resmsg); return $resmsg; } # 正确结果返回 public function sucmsg($msg,$code = 1) { global $_M; $this->resmsg['key'] = $code; $this->resmsg['text'] = self::fontcolor($code,$msg); return $this; } # 多种结果返回 public function vagmsg($code,$msg = []) { global $_M; $text = ''; foreach ($msg as $val) { list($k,$v) = $val; $text .= self::fontcolor($k,$k?$v['info']:"({$v['coding']}){$v['info']}"); } $this->resmsg['key'] = $code; $this->resmsg['text'] = $text; return $this; } # 错误结果返回 # $code 这个是错误的编码 # $msg 错误的信息提示 public function errmsg($coding,$msg,$code = 0) { global $_M; $this->resmsg['key'] = $code; $this->resmsg['text'] = self::fontcolor(0,"({$coding}){$msg}"); return $this; } # 返回本地编码信息 # $code 状态 # $msg 错误的信息提示 public function locmsg($msg = [],$code = 0) { global $_M; if(!empty($msg)) self::codemsg($msg); $this->resmsg['key'] = $code; $this->resmsg['text'] = self::fontcolor($code,"({$this->tipmsg['errcode']}){$this->tipmsg['errmsg']}"); return $this; } # 错误结果返回 # $modal 模态框的内容 # $code 校验结果状态 # $error 错误信息提示 提示码以及需要增加的文字 public function modalmsg($modal = [], $error = [],$code = 0) { global $_M; self::locmsg($error,$code); $this->resmsg['modal'] = $modal; return $this; } # 替换key public function msgkey($key = 4) { global $_M; $this->resmsg['key'] = $key; return $this; } # 使用本地错误代码替换返回信息内的text public function msgtext($error = []) { global $_M; self::codemsg($error); $this->resmsg['text'] = self::fontcolor($this->resmsg['key'],$this->tipmsg['errmsg']); return $this; } # text # $type true 覆盖,false 为拼接 public function msgmeet($text,$type = false) { global $_M; $this->resmsg['text'] = $type?$text:$this->resmsg['text'].$text; return $this; } # 对提示文字的HTML处理 private function fontcolor($code,$text) { global $_M; // 红、绿、黄 $color = ['#E9595B','#36AB7A','#EC9940','#76838f','#2a333c']; return ''.$text.''; } # 错误提示语返回字符串格式 # $type true 字符串,false 为JSON public function coderes($type = true) { global $_M; return $type?$this->tipmsg['errmsg']:json_encode($this->tipmsg,JSON_UNESCAPED_UNICODE); } # 错误代码信息 public function codemsg($coding,$text = '') { global $_M; if(is_array($coding)) list($coding,$text) = $coding; $msg = [ '4000' => "当前环境缺少CURL支持!", '4001' => "核心文件存在异常,准备自动修复核心文件...", //修复文件 '4002' => "应用文件存在异常请点击 {$text} !", //修复文件 '4003' => "应用文件正常!", '4004' => "文件权限检测中...", '4005' => " {$text} 不存在,请手动创建!", '4006' => " {$text} 权限不足,请修改为[777/775]增加写入权限!", '4007' => " {$text} 数据写入错误,请自行检测权限!", '4008' => "下载权限不足(请检查【网站网址】是否为购买应用的域名)!", '4009' => "应用文件下载中...", '4010' => "应用文件下载中...{$text} (正在进行下载,请不要操作页面!)", '4011' => "服务端异常[{$text}],通信失败!", '4012' => "系统异常!", '4013' => "应用文件,开始修复...", '4014' => "[核心文件自动修复] 修复完成。", '4015' => "应用文件修复完成。", '4016' => "[{$text}] 下载失败!", '4017' => "修复失败!", '4018' => "安装失败!", '4019' => "安装成功!", '4020' => "应用文件,开始安装...", '4021' => "[{$text}] 写入失败!", '4022' => "未检测到文件!", '4023' => "微信公众号二维码扫描超时!{$text}", '4024' => "域名未获得授权!", '4025' => "安装锁权限不足!", ]; $this->tipmsg = ['errcode' => $coding,'errmsg' => $msg[$coding]]; return $this; } # 密钥获取和保存 # $mode true是读取 false是保存 public function mysql_config($arr = [],$mode = true) { global $_M; list($key,$val) = $arr; $table = $_M['table']['cloud_config']; $lang = 'cloud'; if($mode){ $config = DB::get_one("select value from {$table} where name = '{$key}' AND m_name = '{$this->m_name}' AND lang = '{$lang}' "); $val = $config['value']; }else{ DB::query("INSERT INTO {$table} (name,lang,m_name,value) VALUES ('{$key}','{$lang}','{$this->m_name}','{$val}') ON DUPLICATE KEY UPDATE value='{$val}' "); } return $val; } // 获取客户端IP public function get_client_ip(){ $arr_ip_header = [ // 非常见 'HTTP_CDN_SRC_IP', 'HTTP_PROXY_CLIENT_IP', 'HTTP_WL_PROXY_CLIENT_IP', // 一般用下面三个 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' ]; $client_ip = 'unknown'; foreach ($arr_ip_header as $key){ if (!empty($_SERVER[$key]) && strtolower($_SERVER[$key]) != 'unknown' && filter_var($_SERVER[$key], FILTER_VALIDATE_IP)){ $client_ip = $_SERVER[$key]; break; } } return $client_ip; } /* * 把数组转成JSON,用于ajax返回,可以用于普通json请求返回,也可以用于跨域的ajax的jsonp格式的数据请求返回。 * @param array $back 输出字符串或数组 * @param string $callback ajax的回调函数的名称 */ public function jsoncallback($back, $callback = 'callback') { global $_M; header('Content-type: application/x-javascript'); $callback = $_M['form'][$callback]; $json = json_encode($back,JSON_UNESCAPED_UNICODE); echo $callback?$callback . '(' . $json . ')':$json; } //删除更新文件夹 public function del_update_dir() { global $_M; @clearstatcache(); //检测文件是否存在,删除缓存的file if(is_dir(APP_UPDATE_DIR) && file_exists(APP_UPDATE_DIR)) deldir(APP_UPDATE_DIR); } } // +---------------------------------------------------------------------- // | class curls 应用CURL // +---------------------------------------------------------------------- // | Copyright: 1.0 // +---------------------------------------------------------------------- // | Author site: www.metinfo.wang,www.metinfo.cc // +---------------------------------------------------------------------- // | Author QQ: 415420792 // +---------------------------------------------------------------------- class curls{ # 应用主方法 private $appcore; # 应用APIURL private $urlarr = []; # 应用API private $apiurl = []; # 网址协议 private $scheme; # 提交数据和返回数据的缓存 private $data; # 默认密钥 private $apikey; # 信息 private $headers = []; # 初始化 public function __construct($appcore) { global $_M; $this->appcore = $appcore; // 选择执行URL,提交的数据,对数据的处理,提交服务端,数据的处理,返回结果 $this->urlarr = [ // 应用信息验证URL 'ck' => 'https://app.muban.net.cn/api/appcheck.php?a=do', // 应用下载更新URL 'dl' => 'https://app.muban.net.cn/api/download.php?a=do', //公众号 'wx' => 'https://app.muban.net.cn/api/wechat.php?a=do' ]; $this->apikey = md5($_M['config']['met_weburl']); $this->headers = [ 'METWEBURL:' . $_M['config']['met_weburl'], 'AUTHORIZATION:' . $this->apikey ]; } # URL选择 public function apiurl($method,$type = 'ck') { global $_M; $this->apiurl = $this->urlarr[$type].$method; $this->scheme = self::purl($this->apiurl,'scheme'); return $this; } # 数据处理 # $ende true加密 false 不执行加密 public function apipost($post,$ende = true) { global $_M; $this->data = $post; // 若要采用非默认密钥这里设置false,单独调用加密方法 if($ende) self::auth_encode(); return $this; } # 对数据加密 # $str 加密数据 $key 加密密钥 public function auth_encode($key = null) { global $_M; if(!empty($key)) $this->apikey = $key; if(is_array($this->data)) self::arrjson(false); $this->data = authcode($this->data,'ENCODE', $this->apikey); return $this; } # 授权查询 授权代码结束 public function curls($timeout = 15) { global $_M; if (get_extension_funcs('curl') && function_exists('curl_init') && function_exists('curl_setopt') && function_exists('curl_exec') && function_exists('curl_close')) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $this->apiurl); if($this->scheme === 'https') { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //不做服务器认证 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); //不做客户端认证 } curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_setopt($curl, CURLOPT_REFERER, $_SERVER["HTTP_HOST"]); curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); curl_setopt($curl, CURLOPT_FAILONERROR, 1); //返回http >= 400的错误代码 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, ['dense' => $this->data]); $data = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); //http响应码 $errno = curl_errno($curl); //错误码 curl_close($curl); $this->data = empty($errno)?$data:$this->appcore->codemsg(4011,"{$code}-{$errno}")->coderes(false); }else{ $this->data = $this->appcore->codemsg(4000)->coderes(false); } return $this; } # 对数据解密 # $str 加密数据 $key 加密密钥 public function auth_decode($key = null) { global $_M; if(!empty($key)) $this->apikey = $key; $this->data = authcode($this->data,'DECODE', $this->apikey); return $this; } # 数据格式转换 # $type true 转为数组 false 转为json public function arrjson($type = true) { global $_M; $this->data = $type?json_decode($this->data,true):json_encode($this->data,JSON_UNESCAPED_UNICODE); return $this; } # 返回的数据处理 # $type true 返回源数据,false 返回数组 public function resdata($type = false) { global $_M; if($type == false) self::arrjson(); return $this->data; } # 单独处理数据的加密和解密 # $type true 加密,false 解密 # $post 有数据则执行,无数据则直接返回postr public function postr($post = null,$key = null,$type = true) { global $_M; self::apipost($post,false); if($type){ self::auth_encode($key); }else{ self::auth_decode($key); } return $this; } # 授权密钥解密 public function apikey_decode($appkey = []) { global $_M; list($apikey,$domian,$m_name,$addtime) = $appkey; $domian = self::purl($domian); return self::postr($apikey, md5($domian.md5($m_name.$addtime)),false)->resdata(); } # 分析网址 public function purl($url,$type = 'host') { global $_M; $url = url_standard($url); $array = parse_url($url); return $type == 'arr'?$array:$array[$type]; } } // +---------------------------------------------------------------------- // | class session 应用检测会话缓存 // +---------------------------------------------------------------------- // | Copyright: 1.0 // +---------------------------------------------------------------------- // | Author site: www.metinfo.wang,www.metinfo.cc // +---------------------------------------------------------------------- // | Author QQ: 415420792 // +---------------------------------------------------------------------- class session{ # 应用文件名 private $se_m_name = M_NAME; public function __construct() { global $_M; self::start(); } public function start(){ $ip = self::getip(); session_id(md5($_SERVER['HTTP_USER_AGENT'].$this->se_m_name.$ip)); session_start(); } public function set($name, $value){ self::start(); $_SESSION[$name] = $value; } public function get($name){ self::start(); return $_SESSION[$name]; } public function del($name){ self::start(); unset($_SESSION[$name]); } public function getip() { $unknown = 'unknown'; if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], $unknown)){ $pro = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = $pro[0]; }elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], $unknown)) { $ip = $_SERVER['REMOTE_ADDR']; } return $ip; } } // +---------------------------------------------------------------------- // | class dirfinger 应用检测指纹校对 // +---------------------------------------------------------------------- // | Copyright: 1.0 // +---------------------------------------------------------------------- // | Author site: www.metinfo.wang,www.metinfo.cc // +---------------------------------------------------------------------- // | Author QQ: 415420792 // +---------------------------------------------------------------------- class dirfinger { # 应用所在目录 private $appfile; # 需要跳过的文件,不分目录 private $skipfile = []; # 需要跳过的目录以及按照目录跳过文件 private $skipdir = []; # 跟服务端指纹不同的文件路径 private $updlist = []; # 需要从服务器下载的文件 private $dllist = []; # 初始化 public function __construct() { global $_M; $this->appfile = PATH_APP_FILE; $this->skipfile = []; $this->skipdir = [ 'config/install.lock', 'config/update.lock', 'config/uninstall.lock', 'config/table' ]; @clearstatcache(); } # 对比指纹入口 public function fingers($fingers = []) { global $_M; //指纹对比 self::fingerprint_check($fingers); //记录已经进行过指纹对比 return [ 'updlist' => $this->updlist, //需要更新的文件,跟服务端不同的文件 'dowlist' => $this->dowlist, //需要下载的文件,客户端不存在,而服务端存在的 'dllist' => $this->dllist //需要从服务器下载的文件,包含更新和下载 ]; } //删除应用文件以外的文件 public function del_appfile($fingers = [], $dir = '') { global $_M; $files = scandir($this->appfile.$dir); foreach ($files as $file) { $dirfile = $dir.$file; if(stristr(PHP_OS, "WIN")){ $dirfiles = iconv("GBK","UTF-8", $dirfile); $file = iconv("GBK","UTF-8", $file); }else{ $dirfiles = $dirfile; } if(in_array($dirfiles,$this->skipdir) || in_array($file,$this->skipfile)) continue; if(is_dir($this->appfile.$dirfile)){ self::del_appfile($fingers,$dirfile.'/'); }else{ //判断文件是否存在 if(!array_key_exists($dirfiles,$fingers) ) delfile($dirfile); } } } # 应用指纹校验 private function fingerprint_check($flist = ''){ global $_M; foreach ($flist as $key => $val) { //文件路径 $dirfile = $this->appfile.$key; if(stristr(PHP_OS, "WIN")) $dirfile = iconv("UTF-8","GBK", $dirfile); //检查文件是否存在 if(file_exists($dirfile)){ //比对MD5 if(md5_file($dirfile) != $val){ $this->updlist[$key] = $val; $this->dllist[$key] = $val; } }else{ //需要下载的 $this->dowlist[$key] = $val; $this->dllist[$key] = $val; } } } } // +---------------------------------------------------------------------- // | class download 应用文件下载更新 // +---------------------------------------------------------------------- // | Copyright: 1.0 // +---------------------------------------------------------------------- // | Author site: www.metinfo.wang,www.metinfo.cc // +---------------------------------------------------------------------- // | Author QQ: 415420792 // +---------------------------------------------------------------------- class download{ #传送的数据 private $modal; #远端接口 private $method; # 应用主方法 public $ac_appcore; private $ac_session; # 应用文件 private $appfile; # 应用下载缓存文件夹 private $appupdir; # 下载的文件存放目录,方便迁移 private $appupfile; # 应用下载缓存文件 private $appdllist; # 下载需要的网站应用数据 private $met_adl; # 当前执行的状态 private $cksign; # 初始化 public function __construct($appcore) { global $_M; $this->appfile = PATH_APP_FILE; $this->appupdir = APP_UPDATE_DIR; $this->appupfile = APP_UPDATE_DIR.'/file/'; $this->appdllist = APP_UPDATE_DIR.'/dllist.json'; $this->ac_appcore = $appcore; $this->ac_session = new session(); $this->met_adl = $this->ac_appcore->met_adl(); } # 下载更新入口 public function download($data) { global $_M; //执行方法,进度 list($cksign,$method,$step,$rate,$total) = explode('|', $data); //正确时的回调值 $this->cksign = $cksign; $this->method = $method; //回调传送的数据 $this->modal = [ 'suc' => false, 'data' => ['cksign' => $this->cksign, 'method' => $this->method, 'step' => $step] ]; //若是更新则获取最新版本的版本号 if($this->cksign == 'update') { $this->met_adl['oldver'] = $this->met_adl['ver']; $this->met_adl['ver'] = self::app_update_ver(); } switch ($method) { case 'check': self::app_file_check(); break; case 'dl': self::app_file_dl($step); break; case 'core': self::app_file_core(); break; case 'repair': self::app_file_repair(); break; case 'dlfile': $this->method = 'dl'; //单个文件下载并更新,只要返回的结果 self::app_file_dl($step); self::app_file_repair(); $resmsg = $this->ac_appcore->resmsg(); if($resmsg['key'] == 4) $this->ac_appcore->msgkey(1); break; case 'update': case 'install': //全新安装 self::app_file_install(); break; default: break; } } # 检测应用版本是否存在 public function app_file_check() { global $_M; //检测文件是否存在,删除缓存的file if(file_exists($this->appupdir)){ deldir($this->appupdir,1); modifydirpower($this->appupdir,0777); }else{ modifydirpower($this->appfile,0777); makedir($this->appupdir); } //在这里查看文件是否存在 $dir = str_replace(PATH_WEB,'',$this->appupdir); if(!file_exists($this->appupdir)) return $this->ac_appcore->locmsg([4005,$dir]); //验证文件是否可写 if(!getdirpower($this->appupdir)) return $this->ac_appcore->locmsg([4006,$dir]); //检查授权 $statekey = self::statekey(); if(empty($statekey)) return $this->ac_appcore->locmsg(4008); //获取下载指纹列表 if(in_array($this->cksign,['install','update'])){ $check = self::curljson(['adl' => $this->met_adl]); if($check['code']){ $dllist = $check['dllist']; }else{ return $this->ac_appcore->errmsg($check['errcode'],$check['errmsg']); } }else{ $dllist = $this->ac_session->get('dllist'); $this->ac_session->del('dllist'); } if(count($dllist) > 0){ //写入指纹文件 $phpcode = json_encode(['dllist' => array_keys($dllist),'fingers' => $dllist ],JSON_UNESCAPED_UNICODE); if(file_put_contents($this->appdllist,$phpcode) === false){ $file = str_replace(PATH_WEB,'',$this->appdllist); return $this->ac_appcore->locmsg([4007,$file]); } //返回开始下载文件 $this->ac_appcore->modalmsg(self::modal(0,'dl'))->msgkey()->msgtext(4009); }else{ $this->ac_appcore->locmsg(4022); } } # APP文件下载 public function app_file_dl($step) { global $_M; //获取缓存信息 $filelist = self::app_get_file(); if(count($filelist['dllist']) == $step){ $this->ac_appcore->modalmsg(self::modal($step,$this->cksign)) ->msgkey() ->msgtext(in_array($this->cksign,['install','update'])?4020:4013); }else{ set_time_limit(60); //数据整理 $this->met_adl['dirfile'] = $filelist['dllist'][$step]; $this->met_adl['filehash'] = $filelist['fingers'][$this->met_adl['dirfile']]; $file = self::curljson(['adl' => $this->met_adl]); //处理结果 if($file['code']){ $dirfile = $this->appupfile.$this->met_adl['dirfile']; if (stristr(PHP_OS, "WIN")) $dirfile = iconv("UTF-8","GBK", $dirfile); if (!file_exists($dirfile)) makefile($dirfile); //写入文件 $defile = base64_decode($file['file']); if(file_put_contents($dirfile, $defile) == false && strlen($defile) > 0){ $this->ac_appcore->locmsg([4021,$this->met_adl['dirfile']]); }else{ //检查指纹 if($this->met_adl['filehash'] != md5_file($dirfile)){ $this->ac_appcore->locmsg([4016,$this->met_adl['dirfile']]); }else{ $step++; $text = floor((($step)/count($filelist['dllist']))*100)."%"; $this->ac_appcore->modalmsg(self::modal($step))->msgkey()->msgtext([4010,$text]); } } } else { $this->ac_appcore->errmsg($file['errcode'],$file['errmsg']); } } } # 自动更新处理 public function app_file_core() { global $_M; //进行文件移动覆盖,成功则返回相关信息 if(movedir($this->appupfile, $this->appfile)){ @clearstatcache(); unset($this->modal['data']); $this->ac_appcore->modalmsg(self::modal()) ->msgkey(1) ->msgtext(4014); }else{ $this->ac_appcore->locmsg(4017); } } # 手动文件更新 public function app_file_repair() { global $_M; //进行文件复制,成功则返回相关信息 if(copydir($this->appupfile, $this->appfile)){ @clearstatcache(); unset($this->modal['data']); $this->ac_appcore->modalmsg(self::modal()) ->msgkey(1) ->msgtext(4015); }else{ $this->ac_appcore->locmsg(4017); } } # 文件全新安装 public function app_file_install() { global $_M; $bool = false; //文件移动 if(file_exists($this->appupfile)){ $bool = movedir($this->appupfile, $this->appfile); @clearstatcache(); } if($bool){ //对升级文件处理 self::_require(); //检查安装锁 !file_exists(INS_LOCK_FILE)?$this->ac_appcore->locmsg(4025):$this->ac_appcore->msgkey(1)->msgtext(4019); }else{ $this->ac_appcore->locmsg(4018); } } # 读取本地文件 public function app_get_file() { global $_M; return file_exists($this->appdllist)?json_decode(file_get_contents($this->appdllist),true):[]; } # 读取本地文件 public function app_update_ver() { global $_M; $newapp = $this->ac_session->get('newapp'); return $newapp['v_ver']; } # 获取下载权限 private function statekey() { global $_M; $state = false; //解密 $apikey = $this->ac_session->get('check'); $retkey = (new curls($this->ac_appcore))->apikey_decode([$apikey,$this->met_adl['met_weburl'],$this->met_adl['m_name'],$this->met_adl['addtime']]); //校验 if(!empty($retkey['state'])){ $state = true; if($retkey['vatime']['minute'] > 0){ //对有效时间计算 $time = date('Y-m-d H:i:s',$retkey['vatime']['stime']); if(strtotime("{$time} +{$retkey['vatime']['minute']} minute") <= time()) $state = false; } } return $state; } # 模态框数据 modal private function modal($step = '',$method) { global $_M; if(strlen($step) > 0) $this->modal['data']['step'] = $step; if(strlen($method) > 0) $this->modal['data']['method'] = $method; $modal = $this->modal; if(count($this->modal['data']) > 0) $modal['data'] = arrayto_string($modal['data'],'|'); return $modal; } # CURL private function curljson($post = [],$type = false,$timeout = 60,$sign = 'dl') { global $_M; return (new curls($this->ac_appcore))->apiurl($this->method,$sign) ->apipost($post) ->curls($timeout) ->resdata($type); } # 文件导入 private function _require() { global $_M; $dir = $this->appupdir.'/update.class.php'; $para = []; if(file_exists($dir)){ $update = load::own_class('update/update','new'); $update->set($this->met_adl)->start(); } //检测文件是否存在,删除缓存的file $this->ac_appcore->del_update_dir(); } } ?>