* Date: 2018-06-28
*/
namespace weapp\Systemdoctor\controller;
use think\Backup;
use think\Config;
use think\Db;
use think\Page;
use app\common\controller\Weapp;
use think\Request;
use weapp\Systemdoctor\model\AdminLogModel;
use app\common\model\Weapp as WeappModel;
use app\admin\logic\FilemanagerLogic;
use weapp\Systemdoctor\logic\BomLogic;
use weapp\Systemdoctor\logic\SystemdoctorLogic;
/**
* 插件的控制器
*/
class Systemdoctor extends Weapp
{
// 在线模板管理
public $filemanagerLogic;
public $baseDir = '';
public $maxDir = '';
public $globalTpCache = array();
public $bomLogic;
public $systemdoctorLogic;
/**
* 构造方法
*/
public function __construct()
{
parent::__construct();
/*插件基本信息*/
$this->weappInfo = $this->getWeappInfo();
$this->assign('weappInfo', $this->weappInfo);
/*--end*/
$this->filemanagerLogic = new FilemanagerLogic;
$this->bomLogic = new BomLogic;
$this->systemdoctorLogic = new SystemdoctorLogic;
$this->globalTpCache = $this->filemanagerLogic->globalTpCache;
$this->baseDir = $this->filemanagerLogic->baseDir; // 服务器站点根目录绝对路径
$this->maxDir = $this->filemanagerLogic->maxDir; // 默认文件管理的最大级别目录
}
/**
* 文档链接提取
* @return [type] [description]
*/
public function extract_archives_index()
{
//防止数据过程超时
function_exists('set_time_limit') && set_time_limit(0);
@ini_set('memory_limit','-1');
if (IS_POST) {
$post = input('post.');
if (empty($post['typeid'])) {
$this->error('请选择栏目…!');
}
$post['startid'] = intval($post['startid']);
$post['endid'] = intval($post['endid']);
// 要处理的文档
$where = [];
if (!empty($post['typeid'])) {
$where['a.typeid'] = intval($post['typeid']);
}
if (!empty($post['startid']) && !empty($post['endid'])) {
$where['a.aid'] = array('between', "{$post['startid']}, {$post['endid']}");
}
$where['a.status'] = 1;
$where['a.is_del'] = 0;
$list = Db::name('archives')->field('b.*, a.*')
->alias('a')
->join('arctype b','a.typeid=b.id','LEFT')
->where($where)
->select();
if (empty($list)) {
$this->error('没有符合条件的文档');
}
$arr = [];
$seo_pseudo = tpCache('seo.seo_pseudo');
$seo_pseudo_format = tpCache('seo.seo_pseudo_format');
$channelRow = Db::name('channeltype')->field('id,ctl_name')->where(['is_del'=>0])->getAllWithIndex('id');
foreach ($list as $key => $val) {
// 文档链接
if ($val['is_jump'] == 1) {
$arcurl = $val['jumplinks'];
} else {
$arcurl = arcurl('home/'.$channelRow[$val['channel']]['ctl_name'].'/view', $val, true, true, $seo_pseudo, $seo_pseudo_format);
}
if (1 == $post['output_type']) {
$str = "{$val['title']}={$arcurl}";
} else {
$str = "{$arcurl}";
}
$arr[] = $str;
}
$content = implode(PHP_EOL, $arr);
$this->success("操作成功", null, ['content'=>$content]);
}
/*允许发布文档列表的栏目*/
$arctype_html = allow_release_arctype(0, []);
$assign_data['arctype_html'] = $arctype_html;
/*--end*/
$this->assign($assign_data);
return $this->fetch();
}
/**
* 病毒扫描
*/
public function virus_scan()
{
if (IS_POST) {
//防止超时/内存溢出
function_exists('set_time_limit') && set_time_limit(0);
@ini_set('memory_limit','-1');
tpSetting('weapp', ['weapp_Systemdoctor_1644659535'=>''], 'cn');
$list = [];
$assgin_list = []; //可疑文件
/*----------存放生成静态html的目录 start-----------*/
$html_dir_list = [];
$html_arcdir = tpCache("seo.seo_html_arcdir"); // 检测页面保存目录
if (!empty($html_arcdir)) {
$html_dir_list[] = $html_arcdir;
}
$arctype_list = Db::name('arctype')->field('dirpath,diy_dirpath')->select();
if (!empty($arctype_list)) {
foreach ($arctype_list as $key => $val) {
$dirpath = trim($val['dirpath'], '/');
$dirpathArr = explode('/', $dirpath);
$dirpath_tmp = current($dirpathArr);
if (!empty($dirpath_tmp) && !in_array($dirpath_tmp, $html_dir_list)) {
$html_dir_list[] = $dirpath_tmp;
}
$diy_dirpath = trim($val['diy_dirpath'], '/');
$diy_dirpathArr = explode('/', $diy_dirpath);
$diy_dirpath_tmp = current($diy_dirpathArr);
if (!empty($diy_dirpath_tmp) && !in_array($diy_dirpath_tmp, $html_dir_list)) {
$html_dir_list[] = $diy_dirpath_tmp;
}
}
}
//查看所有静态模板文件,里面不允许存在htm之外的其他类型文件
$allow_files = 'html|jpg|gif|png|bmp|jpeg|ico|php|exe|asp|jsp'; //查看文件类型
foreach ($html_dir_list as $value){
$files = $this->getfiles('.'.$value,$allow_files,'');
foreach ($files as $val){
if (preg_match('/\.html$/i', $val['url'])){
$list[] = $val['url'];
}else if (!preg_match('/\.htaccess/i', $val['url'])){ //不允许其他类型文件存在
$redata = $this->set_assgin_data(-1,$val['url'],'静态文件目录中存在非静态文件,建议直接删掉');
list($arr_key,$assgin_list_data) = $this->set_assgin_list_data($val['url'],$redata);
$assgin_list[$arr_key] = $assgin_list_data;
}
}
}
/*----------存放生成静态html的目录 end-----------*/
$allrootdir = glob(ROOT_PATH.'*'); // 获取根目录里的一级目录
foreach ($allrootdir as $key => $filepath) {
$filepath_tmp = str_replace('\\', '/', $filepath);
$arr_tmp = explode('/', $filepath_tmp);
$dirname = end($arr_tmp); // 目录名或文件名
if (is_dir($filepath)) {
if (in_array($dirname, ['public','static'])) { // 不允许存在php文件的目录
$data = getDirFile($filepath);
foreach ($data as $v) {
if (preg_match('/\.php$/i', $v) && !in_array($v, ['plugins/ckeditor/ckeditor.php','plugins/ckeditor/ckeditor_php4.php','plugins/ckeditor/ckeditor_php5.php'])) {
// $list[] = './' . $dirname . '/' . $v;
$redata = $this->set_assgin_data(-1,'./' . $dirname . '/' . $v,'非法php文件,建议直接删掉');
list($arr_key,$assgin_list_data) = $this->set_assgin_list_data('./' . $dirname . '/' . $v,$redata);
$assgin_list[$arr_key] = $assgin_list_data;
}
}
}
else if ('template' == $dirname) {
$data = getDirFile($filepath);
foreach ($data as $v) {
if (preg_match('/\.php$/i', $v)) { // 不允许存在php文件的目录
// $list[] = './' . $dirname . '/' . $v;
$redata = $this->set_assgin_data(-1,'./' . $dirname . '/' . $v,'模板目录存在php文件,建议直接删掉');
list($arr_key,$assgin_list_data) = $this->set_assgin_list_data('./' . $dirname . '/' . $v,$redata);
$assgin_list[$arr_key] = $assgin_list_data;
} else if (preg_match('/\.htm$/i', $v)) { // htm模板文件也有可能被篡改
$list[] = './' . $dirname . '/' . $v;
}
}
}
else if ('data' == $dirname) {
$data = getDirFile($filepath);
foreach ($data as $v) {
if (preg_match('/^(backup|conf)\//i', $v)) { // 不允许存在php文件的目录
if (preg_match('/\.php$/i', $v)) {
$redata = $this->set_assgin_data(-1,'./' . $dirname . '/' . $v,'非法php文件,建议直接删掉');
list($arr_key,$assgin_list_data) = $this->set_assgin_list_data('./' . $dirname . '/' . $v,$redata);
$assgin_list[$arr_key] = $assgin_list_data;
// $list[] = './' . $dirname . '/' . $v;
}
} else if (preg_match('/\.php$/i', $v)) {
$list[] = './' . $dirname . '/' . $v;
}
}
} else {
$data = getDirFile($filepath);
foreach ($data as $v) {
if (preg_match('/\.php$/i', $v)) {
$list[] = './' . $dirname . '/' . $v;
}
}
}
} else {
if (preg_match('/\.php$/i', $dirname)) {
$list[] = './' . $dirname;
}
}
}
//检测代码特征
foreach ($list as $key => $value){
if (preg_match('/(\\\|\/)FilemanagerModel\.php$/i', $value)) {
@unlink($value);
} else {
$redata = $this->checkCodeFeatures($value);
if (1 != $redata['code']) {
list($arr_key,$assgin_list_data) = $this->set_assgin_list_data($value,$redata);
$assgin_list[$arr_key] = $assgin_list_data;
}
}
}
if (empty($assgin_list)) {
$this->success('没发现疑似木马文件!', null, '', 2);
} else {
tpSetting('weapp', ['weapp_Systemdoctor_1644659535' => json_encode($assgin_list)], 'cn');
}
/*重新生成全部数据表字段缓存文件*/
try {
$this->schemaAllTable();
} catch (\Exception $e) {}
/*--end*/
$this->assign('list', $assgin_list);
}
return $this->fetch();
}
/*
* 生成可疑文件数据
*/
private function set_assgin_data($code,$filepath,$type = '异常文件'){
$filepath_new = @iconv("gb2312//IGNORE", "utf-8", $filepath);
return [
'code' => $code,
'type' => $type, //'异常文件',
'filepath' => !empty($filepath_new) ? $filepath_new : $filepath,
'filename' => preg_replace('/^(.*)\/([^\/]+)$/i', '${2}', $filepath),
'activepath' => preg_replace('/^\.(.*)\/([^\/]+)$/i', '${1}', $filepath),
];
}
/*
* 生成可以数据
*/
private function set_assgin_list_data($value,$redata){
$arr_key = md5($value);
$assgin_list_data = [
'code' => $redata['code'],
'type' => $redata['type'],
'filepath' => $redata['filepath'],
'filename' => $redata['filename'],
'activepath' => $redata['activepath'],
];
return [$arr_key,$assgin_list_data];
}
/**
* 重新生成全部数据表缓存字段文件
*/
private function schemaAllTable()
{
$dbtables = \think\Db::query('SHOW TABLE STATUS');
$tableList = [];
foreach ($dbtables as $k => $v) {
if (preg_match('/^'.PREFIX.'/i', $v['Name'])) {
/*调用命令行的指令*/
\think\Console::call('optimize:schema', ['--table', $v['Name']]);
/*--end*/
}
}
}
//检测文件里面的病毒代码特征
private function checkCodeFeatures($filepath = '')
{
// $filepath_new = @iconv("gb2312//IGNORE", "utf-8", $filepath);
if (preg_match('/\.php$/i', $filepath)) {
$content = @php_strip_whitespace($filepath);
if (!empty($content)) {
$content = preg_replace('/([ \t]*)/i', '', $content);
} else {
$content = @file_get_contents($filepath);
$content = preg_replace('/([ \r\n\t]*)/i', '', $content);
}
if (!empty($content) && preg_match('/((FilemanagerModel\.php)|(\$qaz(\s*)=(\s*)\$qwe)|(include(\s*)\((\s*)([\"\']+)\/tmp\/)|(\$content'.'_mb(\s*)=(\s*))|(file_get_contents(\s*)\((\s*)\$auth_role_admin(\s*)\)))/i', $content)) {
return $this->set_assgin_data(-1,$filepath,'下载官方相同版本包解压对比查看是否存在注入病毒代码');
}else if (2 == count(explode('/', $filepath))) {
static $web_adminbasefile = null;
if ($web_adminbasefile === null) {
$web_adminbasefile = tpCache('web.web_adminbasefile');
$arr = explode('/', $web_adminbasefile);
$web_adminbasefile = end($arr);
}
if (!in_array($filepath, ['./index.php','./'.$web_adminbasefile])) {
return $this->set_assgin_data(-2,$filepath,'根目录下只允许存在index.php和login.php(后台入口文件),其他非自己添加的文件,可以删除掉');
}
}
}
else if (preg_match('/\.htm$/i', $filepath) || preg_match('/\.html$/i', $filepath)) {
$content = @file_get_contents($filepath);
$content = preg_replace('/([ \r\n\t]*)/i', '', $content);
if (!empty($content) && preg_match('/((FilemanagerModel\.php)|(\$qaz(\s*)=(\s*)\$qwe)|(include(\s*)\((\s*)([\"\']+)\/tmp\/)|(\$content'.'_mb(\s*)=(\s*))|(file_get_contents(\s*)\((\s*)\$auth_role_admin(\s*)\)))/i', $content)) {
return $this->set_assgin_data(-1,$filepath,'模板文件可能存在非法代码,请仔细检查修改掉(非自己添加)!');
}
}
return $this->set_assgin_data(1,$filepath,'正常文件');
}
/**
* 清理多余文件
* @return [type] [description]
*/
public function clear_invalidfile()
{
if (IS_POST) {
//防止超时/内存溢出
function_exists('set_time_limit') && set_time_limit(0);
@ini_set('memory_limit','-1');
// 清除缓存
delFile(RUNTIME_PATH, true);
// 清除源码备份文件
$backupArr = glob('./data/backup/*');
foreach ($backupArr as $key => $filepath) {
$filepath_tmp = str_replace('\\', '/', $filepath);
$arr = explode('/', $filepath_tmp);
$filename_tmp = end($arr);
if (!in_array($filename_tmp, ['.htaccess','.','..'])) {
if (is_dir($filepath)) {
delFile($filepath, true);
} else if (is_file($filepath)) {
@unlink($filepath);
}
}
}
// 清除数据表缓存文件
$schemaArr = glob('./data/schema/*');
foreach ($schemaArr as $key => $filepath) {
$filepath_tmp = str_replace('\\', '/', $filepath);
$arr = explode('/', $filepath_tmp);
$filename_tmp = end($arr);
if (!in_array($filename_tmp, ['.htaccess','.','..'])) {
if (is_dir($filepath)) {
delFile($filepath, true);
} else if (is_file($filepath)) {
@unlink($filepath);
}
}
}
// 清除数据库备份目录的多余文件
$sqldataArr = glob('./data/sqldata*/*');
foreach ($sqldataArr as $key => $filepath) {
$filepath_tmp = str_replace('\\', '/', $filepath);
$arr = explode('/', $filepath_tmp);
$filename_tmp = end($arr);
if (!in_array($filename_tmp, ['.htaccess','.','..'])) {
if (is_dir($filepath)) {
delFile($filepath, true);
} else if (is_file($filepath) && !preg_match('/^\d{8,8}-\d{6,6}-\d+-v\d+\.\d+\.\d+(.*)\.sql(?:\.gz)?$/', $filename_tmp)) {
@unlink($filepath);
}
}
}
}
$this->success('清理完成');
}
/*
* 删除uploads疑似病毒文件
*/
public function delete_uploads_file(){
if (IS_AJAX) {
$filename = input('filename/s');
$files_serialize = cache("uploads_files_serialize");
if (!empty($files_serialize)){
$result = unserialize($files_serialize);
}else{
$result = [];
}
$filename = !empty($result[$filename]['filepath']) ? $result[$filename]['filepath'] : '';
if (!empty($filename) && file_exists($filename)) {
//删除文件
if (@unlink($filename)) {
$this->success("删除成功");
}
}
}
$this->error("删除失败,请手动去文件夹手动删除");
}
/**
* 病毒扫描删除文件++++清除缓存
*/
public function delete_file()
{
if (IS_AJAX) {
$filename = input('filename/s');
$result = tpSetting('weapp.weapp_Systemdoctor_1644659535');
$result = json_decode($result, true);
$filename = !empty($result[$filename]['filepath']) ? $result[$filename]['filepath'] : '';
if (!empty($filename) && file_exists($filename)) {
//删除文件
if (@unlink($filename)) {
$this->success("删除成功");
}
}
}
$this->error("删除失败,请手动去文件夹手动删除");
}
/*---------------木马图片扫描 start ----------------*/
/*
* 扫码图片目录木马
*/
public function virus_upload(){
return $this->fetch();
}
/*
* 弹窗检查
*/
public function virus_channel(){
return $this->fetch();
}
/*
* 循环执行
*/
public function buildChannel(){
function_exists('set_time_limit') && set_time_limit(0);
\think\Session::pause(); // 暂停session,防止session阻塞机制
$achievepage = input("param.achieve/d", 0); //已经执行完成的条数
if (empty($achievepage)){
cache("uploads_files_serialize",null);
$this->clear_files_cache();
}
$data = $this->handleBuildChannelList($achievepage);
$result = $data[1];
$this->success($data[0], null,$result);
}
/*
* 处理生成栏目页
* $achievepage 已完成页数
* $limit 单个栏目一次执行最多生成页数
*/
private function handleBuildChannelList($achievepage = 0,$limit = 100){
$result = $this->set_files_cache();
$msg = '';
$pagetotal = $result['pagetotal'];
$files = $result['files'];
$dir_directory = './'; //根目录
$files_serialize = cache("uploads_files_serialize");
if (!empty($files_serialize)){
$upload_files = unserialize($files_serialize);
}else{
$upload_files = [];
}
while ($limit && $pagetotal > $achievepage) {
$url = $files[$achievepage]['url'];
$image_type = $this->get_image_type($dir_directory.$url);
if (!in_array($image_type, [1, 2, 3, 4, 6, 13,17])){
$url = $this->change_encoding($url);
$arr_key = md5($url);
$assgin_list_data = $this->set_assgin_data(-1,$url,'非法图片文件,建议删除');
$upload_files[$arr_key] = $assgin_list_data;
$msg .= '
';
}else if (false === $this->check_illegal($dir_directory.$url)){
$url = $this->change_encoding($url);
$arr_key = md5($url);
$assgin_list_data = $this->set_assgin_data(-1,$url,'非法图片文件,建议删除');
$upload_files[$arr_key] = $assgin_list_data;
$msg .= '';
}
$limit--;
$achievepage++;
}
$data['allpagetotal'] = $pagetotal;
$data['achieve'] = $achievepage;
cache("uploads_files_serialize", serialize($upload_files));
return [$msg, $data];
}
/*
* 转换文件名称格式
*/
private function change_encoding($msg){
if (!empty($msg)){
$out_string = mb_detect_encoding($msg, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"));
if ($out_string !== "UTF-8") {
$msg = mb_convert_encoding($msg,'UTF-8',$out_string);
}
}
return $msg;
}
/*
* 获取所有文件写入缓存
*/
private function set_files_cache(){
$files_serialize = cache("channel_files_serialize");
if (empty($info_serialize)){
$allow_files = 'jpg|gif|png|bmp|jpeg|ico|php|exe|asp|jsp'; //查看文件类型
$dir_name = './uploads/'; //检索文件目录
$files = $this->getfiles($dir_name, $allow_files, '');
$dir_name1 = './public/upload/'; //检索文件目录
$files1 = $this->getfiles($dir_name1, $allow_files, '');
if (!empty($files1)){
$files = array_merge($files,$files1);
}
$pagetotal = count($files);
cache("channel_files_serialize", serialize($files));
cache("channel_files_total_serialize", $pagetotal);
}else {
$files = unserialize($files_serialize);
$pagetotal = cache("channel_files_total_serialize");
}
return ['files' => $files,'pagetotal' => $pagetotal];
}
/*
* 清除缓存
*/
private function clear_files_cache(){
cache("channel_files_serialize", null);
cache("channel_files_total_serialize", null);
}
/**
* 遍历获取目录下的指定类型的文件
* @param $path
* @param array $files
* @return array
*/
private function getfiles($path, $allowFiles, $key, &$files = array()){
if (!is_dir($path)) return null;
if(substr($path, strlen($path) - 1) != '/') $path .= '/';
$handle = opendir($path);
while (false !== ($file = readdir($handle))) {
if ($file != '.' && $file != '..') {
$path2 = $path . $file;
if (is_dir($path2)) {
$this->getfiles($path2, $allowFiles, $key, $files);
} else {
if (preg_match("/\.(".$allowFiles.")$/i", $file) && preg_match("/.*". $key .".*/i", $file)) {
if ($this->is_gb2312($path2)){
$path2 = mb_convert_encoding ($path2,'UTF-8','GBK');
}
$files[] = array(
'url'=> $path2,//ROOT_DIR.'/'.$path2, // 支持子目录
'name'=> $file,
'mtime'=> filemtime($path2)
);
}
}
}
}
return $files;
}
private function is_gb2312($str)
{
for($i=0; $i 127) {
if( ($v >= 228) && ($v <= 233) )
{
if( ($i+2) >= (strlen($str) - 1)) return true; // not enough characters
$v1 = ord( $str[$i+1] );
$v2 = ord( $str[$i+2] );
if( ($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191) ) // utf编码
return false;
else
return true;
}
}
}
return true;
}
//获取图片的类型
private function get_image_type($image)
{
if (function_exists('exif_imagetype')) {
return exif_imagetype($image);
}
try {
$info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
}
//检测文件内部是否存在病毒
private function check_illegal($image){
if (file_exists($image)) {
$resource = fopen($image, 'rb');
$fileSize = filesize($image);
fseek($resource, 0);
$hexCode = fread($resource, $fileSize);
fclose($resource);
if (preg_match('#__HALT_COMPILER()#i', $hexCode) || preg_match('#/script>#i', $hexCode) || preg_match('#<([^?]*)\?php#i', $hexCode) || preg_match('#<\?\=(\s+)#i', $hexCode)) {
return false;
}
}
}
/*---------------木马图片扫描 end ----------------*/
/**
* 检测当前版本的数据库是否与官方一致
*/
public function check_database()
{
if (IS_AJAX_POST) {
/*------------------检测目录读写权限----------------------*/
$tmp_str = 'L2luZGV4LnBocD9tPWFwaSZjPVNlcnZpY2UmYT1nZXRfZGF0YWJhc2VfdHh0';
$service_url = base64_decode(config('service_ey')) . base64_decode($tmp_str);
$url = $service_url . '&version=' . getCmsVersion();
$context = stream_context_set_default(array('http' => array('timeout' => 3, 'method' => 'GET')));
$response = @file_get_contents($url, false, $context);
$params = json_decode($response, true);
if (false == $params) {
$this->error('连接升级服务器超时,请刷新重试,或者联系技术支持!', null, ['code' => 2]);
}
if (is_array($params)) {
if (1 == intval($params['code'])) {
/*------------------组合本地数据库信息----------------------*/
$dbtables = Db::query('SHOW TABLE STATUS');
$local_database = array();
foreach ($dbtables as $k => $v) {
$table = $v['Name'];
if (preg_match('/^' . PREFIX . '/i', $table)) {
$local_database[$table] = [
'name' => $table,
'field' => [],
];
}
}
/*------------------end----------------------*/
/*------------------组合官方远程数据库信息----------------------*/
$info = $params['info'];
$info = preg_replace("#[\r\n]{1,}#", "\n", $info);
$infos = explode("\n", $info);
$infolists = [];
foreach ($infos as $key => $val) {
if (!empty($val)) {
$arr = explode('|', $val);
$infolists[$arr[0]] = $val;
}
}
/*------------------end----------------------*/
/*------------------校验数据库是否合格----------------------*/
foreach ([1] as $testk => $testv) {
$error = '';
// 对比数据表字段数量
foreach ($infolists as $k1 => $v1) {
$arr1 = explode('|', $v1);
if (1 >= count($arr1)) {
continue; // 忽略不对比的数据表
}
$fieldArr = explode(',', $arr1[1]);
$table = preg_replace('/^ey_/i', PREFIX, $arr1[0]);
//判断是否缺少表
if (empty($local_database[$table])) {
$error .= $table . ' 数据表缺失!';
continue;
}
$local_fields = Db::getFields($table); // 本地数据表字段列表
$local_database[$table]['field'] = $local_fields;
if (count($local_fields) < count($fieldArr)) {
//对比缺少的字段
$err_field = '';
foreach ($fieldArr as &$k2) {
if (empty($local_fields[$k2])) {
$err_field .= $k2 . ',';
}
}
$error .= $table . ' 数据表缺失字段 ' . trim($err_field, ',') . '';
}
}
if ($error != '') {
$this->error($error, null, ['code' => 2]);
} else {
$this->success('检测通过!');
}
}
/*------------------end----------------------*/
} else if (2 == intval($params['code'])) {
$this->error('官方缺少版本号' . getCmsVersion() . '的数据库比较文件,请第一时间联系技术支持!', null, ['code' => 2]);
}
}
}
/*------------------end----------------------*/
return $this->fetch('check_database');
}
/**
* SQL命令行
*/
public function sql_command()
{
$data = Db::query("SHOW TABLE STATUS");
foreach ($data as $key => $val) {
$data[$key]['count'] = Db::table($val['Name'])->count();
}
return $this->fetch('sql_command', ['data' => $data]);
}
/**
* SQL命令行-获取详细表结构
*/
public function sql_details()
{
if (IS_AJAX) {
$table = input('table/s');
if (empty($table)) {
$this->error('没有指定数据表');
}
$data = Db::query("show create table " . $table);
$info = $data[0]['Create Table'];
$info = "" . trim($info) . "";
$this->success('成功', '', $info);
}
$this->error('非法访问');
}
/**
* SQL命令行运行
*/
public function run_sql()
{
if (IS_AJAX) {
$command = input('command/s');
if (empty($command)) {
$this->error('没有运行命令');
}
$command = trim($command);
$str_command = str_replace(array("\r\n", "\r", "\n"), " ", $command);
// $str_command = strtoupper($command);
$delete = $this->startsWith($str_command, 'DELETE');
if ($delete) {
$info['msg'] = '删除\'数据表\'或\'数据库\'的语句不允许在这里执行';
$this->error($info['msg']);
}
$select = $this->startsWith($str_command, 'SELECT');
if ($select) {
// 查询
// 启动事务
Db::startTrans();
try {
$data = Db::query($command);
// 提交事务
Db::commit();
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
echo $e;
exit();
}
if (count($data) <= 0) {
$info['msg'] = "运行SQL:" . $command . "成功,无返回记录!";
} else {
if (count($data) > 50) {
$data = array_splice($data, 50);
}
$info['msg'] = "运行SQL:" . $command . "成功,共有" . count($data) . "条记录,最大返回50条!";
foreach ($data as $key => $val) {
$info['msg'] .= "第 " . ($key + 1) . " 条
";
foreach ($val as $k => $v) {
$info['msg'] .= $k . ":" . $v . "";
}
}
}
} else {
//更新/插入
$arr_command = explode(";", $str_command);
$i = 0;
$err_msg = '';
foreach ($arr_command as $val){
if (!empty($val)){
// 启动事务
Db::startTrans();
try {
$arr_data = Db::query($val);
// 提交事务
Db::commit();
$i+=1;
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
$err_msg .= '错误未执行语句:'.$val .'';
continue;
}
}
}
if ($i>0){
$info['msg'] = '成功执行 '. $i .' 条SQL语句'.$err_msg;
}else{
$info['msg'] = $err_msg;
}
}
$this->success('成功', '', $info);
}
$this->error('非法访问');
}
/**
* 插件后台管理 - 列表
*/
public function index()
{
// 上传图片检测木马
$trojan_horse = tpCache('weapp.weapp_check_illegal_open');
$this->assign('trojan_horse', $trojan_horse);
$cms_version = getCmsVersion();
$this->assign('cms_version', $cms_version);
return $this->fetch('index');
}
/**
* 诊断数据表
*/
public function check_table()
{
if (IS_POST) {
$r = Db::name('admin_log')->where("admin_id is NULL OR admin_id = ''")
->update([
'admin_id' => 0,
'log_time' => getTime(),
]);
if ($r) {
$this->success('修复成功');
}
$this->error('修复失败');
}
$this->error('非法访问');
}
/**
* 上传sql文件
*/
public function restoreUpload()
{
if (IS_POST) {
$file = request()->file('sqlfile');
if (empty($file)) {
$this->error('请上传sql文件');
}
// 移动到框架应用根目录/data/sqldata/ 目录下
$path = tpCache('global.web_sqldatapath');
$path = !empty($path) ? $path : config('DATA_BACKUP_PATH');
$path = trim($path, '/');
$image_upload_limit_size = intval(tpCache('basic.file_size') * 1024 * 1024);
$info = $file->validate(['size' => $image_upload_limit_size, 'ext' => 'sql,gz'])->move($path, $_FILES['sqlfile']['name']);
if ($info) {
//上传成功 获取上传文件信息
$file_path_full = $info->getPathName();
if (file_exists($file_path_full)) {
$sqls = Backup::parseSql($file_path_full);
if (Backup::install($sqls)) {
/*清除缓存*/
delFile(RUNTIME_PATH);
/*--end*/
$this->success("执行sql成功");
} else {
$this->error('执行sql失败');
}
} else {
$this->error('sql文件上传失败');
}
} else {
//上传错误提示错误信息
$this->error($file->getError());
}
}
}
/**
* 检测站点根目录权限
*/
public function check_permission()
{
if (IS_AJAX) {
/*------------------检测目录读写权限----------------------*/
$filelist = glob('*', GLOB_ONLYDIR);
$dirs = array();
$i = -1;
foreach ($filelist as $filename) {
$curdir = $filename;
if (!isset($dirs[$curdir])) {
$dirs[$curdir] = $this->TestIsFileDir($curdir);
}
if ($dirs[$curdir]['isdir'] == FALSE) {
continue;
} else {
@tp_mkdir($curdir, 0777);
$dirs[$curdir] = $this->TestIsFileDir($curdir);
}
$i++;
}
if ($i > -1) {
$n = 0;
$dirinfos = '';
foreach ($dirs as $curdir) {
$dirinfos .= $curdir['name'] . " 状态:";
if ($curdir['writeable']) {
$dirinfos .= "[√正常]";
} else {
$n++;
$dirinfos .= "[×不可写]";
}
$dirinfos .= "
";
}
$title = "已检测站点有 {$n} 处没有写入权限:
";
$title .= "问题分析(如有问题,请咨询技术支持):
";
$title .= "1、检查站点目录的用户组与所有者,禁止是 root ;
";
$title .= "2、检查站点目录的读写权限,一般权限值是 0755 ;
";
$title .= "
站点根目录列表如下:
";
$msg = $title . $dirinfos;
$this->error($msg);
}
}
return $this->fetch('check_permission');
}
/**
* 测试目录路径是否有读写权限
* @param string $dirname 文件目录路径
* @return array
*/
private function TestIsFileDir($dirname)
{
$dirs = array('name' => '', 'isdir' => FALSE, 'writeable' => FALSE);
$dirs['name'] = $dirname;
tp_mkdir($dirname);
if (is_dir($dirname)) {
$dirs['isdir'] = TRUE;
$dirs['writeable'] = $this->TestWriteAble($dirname);
}
return $dirs;
}
/**
* 测试目录路径是否有写入权限
* @param string $d 目录路劲
* @return boolean
*/
private function TestWriteAble($d)
{
$tfile = '_eyout.txt';
$fp = @fopen($d . $tfile, 'w');
if (!$fp) {
return false;
} else {
fclose($fp);
$rs = @unlink($d . $tfile);
return true;
}
}
private function startsWith($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
/**
* 检测重复文档
*/
public function repeat_archives_index()
{
$assign_data = array();
$testing = input('param.testing/d');
if (!empty($testing)) {
$condition = array();
// 获取到所有GET参数
$param = input('param.');
// 应用搜索条件
foreach (['keywords','channel'] as $key) {
if (isset($param[$key]) && $param[$key] !== '') {
if ($key == 'keywords') {
$condition['a.title'] = array('LIKE', "%{$param[$key]}%");
} else if ($key == 'channel' && !empty($param[$key])) {
$condition['a.channel'] = $param[$key];
} else {
$condition['a.'.$key] = array('eq', $param[$key]);
}
}
}
// 多语言
$condition['a.lang'] = array('eq', $this->admin_lang);
// 回收站
$condition['a.is_del'] = array('eq', 0);
$pagesize = input('param.size/d', 100);
$row = Db::name('archives')->alias('a')->field('GROUP_CONCAT(aid) as aids, count(aid) as nums,a.title')
->where($condition)
->group('a.title')
->having('count(a.aid) > 1')
->order('aid asc')
->limit($pagesize)
->select();
$assign_data['list'] = $row;
$count = 0;
foreach ($row as $key => $val) {
$count += $val['nums'];
}
$assign_data['count'] = $count;
}
/* 模型 */
$map = [
'id' => ['NOT IN', [6,8]],
'status' => 1,
];
$channeltype_list = model('Channeltype')->getAll('id,title,nid', $map, 'id');
$assign_data['channeltype_list'] = $channeltype_list;
$assign_data['testing'] = $testing;
$deltype = input('param.deltype/s');
$assign_data['deltype'] = $deltype;
$recycle_switch = tpSetting('recycle.recycle_switch');//回收站开关
$this->assign('recycle_switch', $recycle_switch);
$this->assign($assign_data);
return $this->fetch('repeat_archives_index');
}
/**
* 删除文档
*/
public function repeat_archives_del()
{
if (IS_POST) {
$post = input();
$del_id = [];
if (is_array($post['del_id'])){
foreach ($post['del_id'] as $k => $v) {
$arr = explode(",",$v);
sort($arr);
if ('delnew' == $post['deltype']){//保留最旧的一条
unset($arr[0]);
}else{//保留最新的一条
unset($arr[count($arr)-1]);
}
$del_id = array_merge($del_id,$arr);
}
}else{
$arr = explode(",",$post['del_id']);
sort($arr);
if ('delnew' == $post['deltype']){//保留最旧的一条
unset($arr[0]);
}else{//保留最新的一条
unset($arr[count($arr)-1]);
}
$del_id = array_merge($del_id,$arr);
}
$recycle_switch = tpCache('web.recycle_switch');
if (!empty($recycle_switch)) {
$thorough = 1;
} else {
$thorough = 0;
}
$archivesLogic = new \app\admin\logic\ArchivesLogic;
$archivesLogic->del($del_id, $thorough);
}
}
/**
* SQL命令行
*/
public function sql_reset()
{
if (IS_AJAX_POST){
$post = input('post.');
$table = $post['table'];
$table = htmlspecialchars_decode($table);
$table = json_decode($table,true);
foreach ($table as $k) {
Db::execute('alter table '.$k.' AUTO_INCREMENT 1');
}
return true;
}
$data = Db::query("SHOW TABLE STATUS");
foreach ($data as $key => $val) {
$data[$key]['count'] = Db::table($val['Name'])->count();
}
return $this->fetch('sql_reset', ['data' => $data]);
}
/**
* 后台操作日志
* @return mixed
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function admin_log()
{
$list = array();
$keywords = I('keywords/s');
$map = array();
if (!empty($keywords)) {
$map['log_info'] = array('LIKE', "%{$keywords}%");
}
$map['admin_id'] = ['gt', 0];
$count = AdminLogModel::where($map)->count('log_id');// 查询满足要求的总记录数
$pageObj = new Page($count, config('paginate.list_rows'));// 实例化分页类 传入总记录数和每页显示的记录数
$list = AdminLogModel::with('admin')->where($map)->order('log_id desc')->limit($pageObj->firstRow.','.$pageObj->listRows)->select();
$pageStr = $pageObj->show(); // 分页显示输出
$this->assign('list', $list); // 赋值数据集
$this->assign('pageStr', $pageStr); // 赋值分页输出
$this->assign('pageObj', $pageObj); // 赋值分页对象
return $this->fetch('admin_log');
}
/**
* 删除后台操作日志
*/
public function del_admin_log()
{
$id_arr = I('del_id/a');
$id_arr = eyIntval($id_arr);
if(!empty($id_arr)){
$r = AdminLogModel::where("log_id",'IN',$id_arr)->delete();
if($r){
adminLog('日志清除');
$this->success("操作成功!");
}else{
$this->error("操作失败!");
}
}else{
$this->error("参数有误!");
}
}
/**
* 插件后台管理 - 列表
*/
public function data_replace_index()
{
$keywords = I('keywords/s');
$map = array();
if (!empty($keywords)) {
$map['title'] = array('LIKE', "%{$keywords}%");
}
//获取数据表
$dbtables = Db::query('SHOW TABLE STATUS');
$list = array();
foreach ($dbtables as $k => $v) {
if (preg_match('/^'.PREFIX.'/i', $v['Name'])) {
$list[$k] = $v;
}
}
$tables = get_arr_column($list, 'Name');
$this->assign('tables',$tables);
return $this->fetch('data_replace_index');
}
/**
* 根据表名获取字段列表
*/
public function getTableField()
{
$name = Request::instance()->param('table_name');
$fieldArr = Db::getTableFields($name);
if($fieldArr)
{
$this->success("操作成功!",'',['targetTable'=>$name,'fields'=>$fieldArr]);
}
}
/**
* 内容替换主方法
*/
public function th()
{
$data = Request::instance()->param();
//字段安全过滤
$field = $this->filter($data['rpfield']);
switch($data['rptype'])
{
case "replace":
$this->replace($data['tables'],$field,$data['rpstring'],$data['tostring'],$data['condition']);
break;
case "regex":
$this->regex();
break;
}
}
/**
* 普通替换
*/
public function replace($table,$field,$rpstring,$tostring,$condition){
if($condition)
{
$sql = "update {$table} set {$field}=REPLACE({$field},'{$rpstring}','{$tostring}') where $condition;";
}else{
$sql = "update {$table} set {$field}=REPLACE({$field},'{$rpstring}','{$tostring}');";
}
$res = Db::execute($sql);
if ($res)
{
$this->success("普通替换成功,{$res}行受到影响");
}else{
$this->error("替换未成功,没有受到任何影响");
}
}
/**
* 正则替换
*/
public function regex(){
$this->success("正则替换");
}
/**
* 过滤掉重要字段
*/
public function filter($field)
{
$ban = ['id'];
for($i=0; $ierror("存在非法字段,不可替换");
}
}
return $field;
}
/**
* 验证码管理
*/
public function vertify()
{
// 获取插件数据
$row = WeappModel::get(array('code' => $this->weappInfo['code']));
if ($this->request->isPost()) {
// 获取post参数
$inc_type = input('inc_type/s', 'admin_login');
$param = $this->request->only('captcha');
$config = json_decode($row->data, true);
if ('default' == $inc_type) {
if (isset($config[$inc_type])) {
$config['captcha'][$inc_type] = array_merge($config['captcha'][$inc_type], $param['captcha'][$inc_type]);
} else {
$config['captcha'][$inc_type] = $param['captcha'][$inc_type];
}
} else {
$config['captcha'][$inc_type]['is_on'] = $param['captcha'][$inc_type]['is_on'];
if (isset($config['captcha'][$inc_type]['config'])) {
$config['captcha'][$inc_type]['config'] = array_merge($config['captcha'][$inc_type]['config'], $param['captcha'][$inc_type]['config']);
} else {
$config['captcha'][$inc_type]['config'] = $param['captcha'][$inc_type]['config'];
}
}
// 转json赋值
$row->data = json_encode($config);
// 更新数据
$r = $row->save();
if ($r !== false) {
adminLog('编辑验证码:插件配置'); // 写入操作日志
$this->success("操作成功!", weapp_url('Systemdoctor/Systemdoctor/vertify', ['inc_type'=>$inc_type]));
}
$this->error("操作失败!");
}
$inc_type = input('param.inc_type/s', 'admin_login');
$inc_type = preg_replace('/([^\w\-]+)/i', '', $inc_type);
// 获取配置JSON信息转数组
$config = json_decode($row->data, true);
$baseConfig = Config::get("captcha");
if ('default' == $inc_type) {
$row = isset($config['captcha']) ? $config['captcha'] : $baseConfig;
} else {
if (isset($config['captcha'][$inc_type])) {
$row = $config['captcha'][$inc_type];
} else {
$baseConfig[$inc_type]['config'] = !empty($config['captcha']['default']) ? $config['captcha']['default'] : $baseConfig['default'];
$row = $baseConfig[$inc_type];
}
}
$this->assign('row', $row);
$this->assign('inc_type', $inc_type);
return $this->fetch('vertify_'.$inc_type);
}
/**
* 模板管理首页
*/
public function filemanager_index()
{
// 获取到所有GET参数
$param = input('param.', '', null);
$activepath = input('param.activepath', '', null);
$activepath = $this->filemanagerLogic->replace_path($activepath, ':', true);
/*当前目录路径*/
$activepath = !empty($activepath) ? $activepath : $this->maxDir;
$tmp_max_dir = preg_replace("#\/#i", "\/", $this->maxDir);
if (!preg_match("#^".$tmp_max_dir."#i", $activepath)) {
$activepath = $this->maxDir;
}
/*--end*/
$inpath = "";
$activepath = str_replace("..", "", $activepath);
$activepath = preg_replace("#^\/{1,}#", "/", $activepath); // 多个斜杆替换为单个斜杆
if($activepath == "/") $activepath = "";
if(empty($activepath)) {
$inpath = $this->baseDir.$this->maxDir;
} else {
$inpath = $this->baseDir.$activepath;
}
$list = $this->filemanagerLogic->getDirFile($inpath, $activepath);
$assign_data['list'] = $list;
/*文件操作*/
$assign_data['replaceImgOpArr'] = $this->filemanagerLogic->replaceImgOpArr;
$assign_data['editOpArr'] = $this->filemanagerLogic->editOpArr;
$assign_data['renameOpArr'] = $this->filemanagerLogic->renameOpArr;
$assign_data['delOpArr'] = $this->filemanagerLogic->delOpArr;
$assign_data['moveOpArr'] = $this->filemanagerLogic->moveOpArr;
/*--end*/
$assign_data['activepath'] = $activepath;
$this->assign($assign_data);
return $this->fetch();
}
/**
* 替换图片
*/
public function filemanager_replace_img()
{
if (IS_POST) {
$post = input('post.', '', null);
$activepath = !empty($post['activepath']) ? trim($post['activepath']) : '';
if (empty($activepath)) {
$this->error('参数有误');
exit;
}
$file = request()->file('upfile');
if (empty($file)) {
$this->error('请选择上传图片!');
exit;
} else {
$image_type = tpCache('basic.image_type');
$fileExt = !empty($image_type) ? str_replace('|', ',', $image_type) : config('global.image_ext');
$image_upload_limit_size = intval(tpCache('basic.file_size') * 1024 * 1024);
$result = $this->validate(
['file' => $file],
['file'=>'image|fileSize:'.$image_upload_limit_size.'|fileExt:'.$fileExt],
['file.image' => '上传文件必须为图片','file.fileSize' => '上传文件过大','file.fileExt'=>'上传文件后缀名必须为'.$fileExt]
);
if (true !== $result || empty($file)) {
$this->error($result);
exit;
}
}
$res = $this->filemanagerLogic->upload('upfile', $activepath, $post['filename'], 'image');
if ($res['code'] == 1) {
$this->success('操作成功!',weapp_url('Systemdoctor/Systemdoctor/filemanager_index', array('activepath'=>$this->filemanagerLogic->replace_path($activepath, ':', false))));
} else {
$this->error($res['msg'],weapp_url('Systemdoctor/Systemdoctor/filemanager_index', array('activepath'=>$this->filemanagerLogic->replace_path($activepath, ':', false))));
}
}
$filename = input('param.filename/s', '', null);
$activepath = input('param.activepath/s', '', null);
$activepath = $this->filemanagerLogic->replace_path($activepath, ':', true);
if ($activepath == "") $activepathname = "根目录";
else $activepathname = $activepath;
$info = array(
'activepath' => $activepath,
'activepathname' => $activepathname,
'filename' => $filename,
);
$this->assign('info', $info);
return $this->fetch();
}
/**
* 新建文件
*/
public function filemanager_newfile()
{
if (IS_POST) {
$post = input('post.', '', null);
$content = input('post.content', '', null);
$filename = !empty($post['filename']) ? trim($post['filename']) : '';
$content = !empty($content) ? $content : '';
$activepath = !empty($post['activepath']) ? trim($post['activepath']) : '';
if (empty($filename) || empty($activepath)) {
$this->error('参数有误');
exit;
}
$r = $this->filemanagerLogic->editFile($filename, $activepath, $content);
if ($r === true) {
$this->success('操作成功!',weapp_url('Systemdoctor/Systemdoctor/filemanager_index', array('activepath'=>$this->filemanagerLogic->replace_path($activepath, ':', false))));
exit;
} else {
$this->error($r);
exit;
}
}
$activepath = input('param.activepath/s', '', null);
$activepath = $this->filemanagerLogic->replace_path($activepath, ':', true);
$filename = 'newfile.htm';
$content = "";
$info = array(
'filename' => $filename,
'activepath'=> $activepath,
'content' => $content,
'extension' => 'text/html',
);
$this->assign('info', $info);
return $this->fetch();
}
/**
* 模板管理编辑
*/
public function filemanager_edit()
{
if (IS_POST) {
$post = input('post.', '', null);
$content = input('post.content', '', null);
$filename = !empty($post['filename']) ? trim($post['filename']) : '';
$content = !empty($content) ? $content : '';
$activepath = !empty($post['activepath']) ? trim($post['activepath']) : '';
if (empty($filename) || empty($activepath)) {
$this->error('参数有误');
exit;
}
$r = $this->filemanagerLogic->editFile($filename, $activepath, $content);
if ($r === true) {
$this->success('操作成功!',weapp_url('Systemdoctor/Systemdoctor/filemanager_index', array('activepath'=>$this->filemanagerLogic->replace_path($activepath, ':', false))));
exit;
} else {
$this->error($r);
exit;
}
}
$activepath = input('param.activepath/s', '', null);
$activepath = $this->filemanagerLogic->replace_path($activepath, ':', true);
$filename = input('param.filename/s', '', null);
$activepath = str_replace("..", "", $activepath);
$filename = str_replace("..", "", $filename);
$path_parts = pathinfo($filename);
$path_parts['extension'] = strtolower($path_parts['extension']);
/*不允许越过指定最大级目录的文件编辑*/
$tmp_max_dir = preg_replace("#\/#i", "\/", $this->filemanagerLogic->maxDir);
if (!preg_match("#^".$tmp_max_dir."#i", $activepath)) {
$this->error('没有操作权限!');
exit;
}
/*--end*/
/*允许编辑的文件类型*/
if (!in_array($path_parts['extension'], $this->filemanagerLogic->editExt)) {
$this->error('只允许操作文件类型如下:'.implode('|', $this->filemanagerLogic->editExt));
exit;
}
/*--end*/
/*读取文件内容*/
$file = $this->baseDir."$activepath/$filename";
$content = "";
if(is_file($file))
{
$filesize = filesize($file);
if (0 < $filesize) {
$fp = fopen($file, "r");
$content = fread($fp, $filesize);
fclose($fp);
if ('css' != $path_parts['extension']) {
$content = htmlspecialchars($content, ENT_QUOTES);
$content = preg_replace("/(@)?eval(\s*)\(/i", 'intval(', $content);
// $content = preg_replace("/\?\bphp\b/i", "?muma", $content);
}
}
}
/*--end*/
if($path_parts['extension'] == 'js'){
$extension = 'text/javascript';
} else if($path_parts['extension'] == 'css'){
$extension = 'text/css';
} else {
$extension = 'text/html';
}
$info = array(
'filename' => $filename,
'activepath'=> $activepath,
'extension' => $extension,
'content' => $content,
);
$this->assign('info', $info);
return $this->fetch();
}
// 上传图片检测木马
public function trojan_horse()
{
$value = input('post.value/d');
tpCache('weapp', ['weapp_check_illegal_open' => $value]);
$this->success('操作成功!');
}
/**
* 特殊符号/字体
*/
public function special_char_index()
{
$Prefix = config('database.prefix');
if (IS_POST) {
//读取数据库配置文件,替换数据库编码
$databaseConfig = @file_get_contents(APP_PATH . 'database.php');
if (empty($databaseConfig)) {
$this->error("可能存在以下问题:
1.检查 application/database.php 的权限是否为755
2.检查php环境是否支持file_get_contents函数");
}
$databaseConfig = str_ireplace(["'utf8'",'"utf8"'], "'utf8mb4'", $databaseConfig);
@chmod(APP_PATH . 'database.php',0755); //配置文件的地址
$rd = @file_put_contents(APP_PATH . 'database.php', $databaseConfig); //配置文件的地址
$r = true;
// 文档主表
$sql = "ALTER TABLE `{$Prefix}archives` MODIFY COLUMN `title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '标题';";
$r = @Db::execute($sql);
$tableInfo = Db::query("SHOW COLUMNS FROM {$Prefix}archives");
$tableInfo = get_arr_column($tableInfo, 'Field');
if (!empty($tableInfo) && in_array('subtitle', $tableInfo)){
$sql = "ALTER TABLE `{$Prefix}archives` MODIFY COLUMN `subtitle` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '副标题';";
$r = @Db::execute($sql);
}
// 文档附表
$allow_release_channel = config('global.allow_release_channel');
$channeltype_list = Db::name('channeltype')->where(['id'=>['IN',$allow_release_channel]])->order('id asc')->select();
foreach ($channeltype_list as $key => $val) {
if ('ask' == $val['table']) {
continue;
}
$tableInfo = Db::query("SHOW COLUMNS FROM {$Prefix}{$val['table']}_content");
$tableInfo = get_arr_column($tableInfo, 'Field');
if (!empty($tableInfo)){
if (in_array('content', $tableInfo)){
$sql = "ALTER TABLE `{$Prefix}{$val['table']}_content` MODIFY COLUMN `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '内容详情';";
$r = @Db::execute($sql);
}
if (in_array('content_ey_m', $tableInfo)){
$sql = "ALTER TABLE `{$Prefix}{$val['table']}_content` MODIFY COLUMN `content_ey_m` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '手机端内容详情';";
$r = @Db::execute($sql);
}
}
}
// 会员主表
$sql = "ALTER TABLE `{$Prefix}users` MODIFY COLUMN `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名' AFTER `users_id`;";
$r = @Db::execute($sql);
$sql = "ALTER TABLE `{$Prefix}users` MODIFY COLUMN `nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '昵称' AFTER `password`;";
$r = @Db::execute($sql);
if ($r !== false) {
if ($rd === false) {
$this->success('数据表处理完成,数据库文件修改失败
请查看教程修改文件 application/database.php ');
}
$this->success('操作成功');
}
$this->error('操作失败');
}
$tableList = [
'users' => [
'table' => "{$Prefix}users",
'name' => "会员主表",
],
'archives' => [
'table' => "{$Prefix}archives",
'name' => "文档主表",
],
];
// 文档附表
$allow_release_channel = config('global.allow_release_channel');
$channeltype_list = Db::name('channeltype')->where(['id'=>['IN',$allow_release_channel]])->order('id asc')->select();
foreach ($channeltype_list as $key => $val) {
if ('ask' == $val['table']) {
continue;
}
$tableList[$val['table']] = [
'table' => "{$Prefix}{$val['table']}_content",
'name' => "{$val['ntitle']}内容表",
];
}
$this->assign('tableList', $tableList);
return $this->fetch('special_char_index');
}
/*-----------------------检查bom头部信息 start-----------------------*/
/**
* 检查bom头部信息
* @return [type] [description]
*/
public function bom_index()
{
$this->assign('conf_data', $this->bomLogic->getConfData());
$this->assign('tpl_theme', $this->bomLogic->get_tpl_path());
return $this->fetch('bom_index');
}
public function bom_conf()
{
if (IS_POST) {
$post = input('post.');
$row = $this->systemdoctorLogic->getConfData('bom');
$data = empty($row['data']) ? [] : $row['data'];
$data['is_autoclear'] = $post['is_autoclear'];
$data['is_backup'] = $post['is_backup'];
if (empty($row['id'])) {
$r = Db::name('weapp_systemdoctor')->insert([
'code' => 'bom',
'data' => json_encode($data),
'add_time' => getTime(),
]);
} else {
$r = Db::name('weapp_systemdoctor')->where(['id'=>$row['id']])->update([
'data' => json_encode($data),
'update_time' => getTime(),
]);
}
if ($r !== false) {
\think\Cache::clear('hooks');
$this->success("操作成功", weapp_url('Systemdoctor/Systemdoctor/bom_conf'));
}
$this->error("操作失败");
}
$conf_data = $this->bomLogic->getConfData();
$this->assign('conf_data', $conf_data);
return $this->fetch('bom_conf');
}
/**
* 扫描
* @return [type] [description]
*/
public function bom_scan()
{
//防止超时/内存溢出
function_exists('set_time_limit') && set_time_limit(0);
@ini_set('memory_limit','-1');
\think\Session::pause(); // 暂停session,防止session阻塞机制
if (IS_POST) {
Db::name('weapp_systemdoctor_bom_log')->where(['id'=>['gt',0]])->delete();
$conf_data = $this->bomLogic->getConfData();
$is_autoclear = input('post.is_autoclear/d', 0);
$conf_data['is_autoclear'] = $is_autoclear;
$start=getTime();
$list = [];
$html = '';
$dir = $this->bomLogic->get_tpl_path();
if (!is_readable($dir)) {
$dir = str_replace('\\', '/', $dir);
$dir = rtrim($dir, '/');
}
$total = $num_ky = $scanned = 0;
$auth_code = tpCache('system.system_auth_code');
$this->bomLogic->bom_getDirFile($dir, $dir, $list, $total);
foreach ($list as $key => $file_name) {
$md5key = md5($file_name.$auth_code);
$file_name = realpath($file_name);
$fp = fopen($file_name, "r");
$scanned +=1;
$is_suspicious = 0;
$return = $this->bomLogic->bom_checkCode($file_name, $conf_data);
if (empty($return['code'])) {
$num_ky += 1;
$j = $num_ky % 2 + 1;
$is_suspicious = 1;
$str_handle = "";
if (empty($conf_data['is_autoclear'])) {
$str_handle = "立即清理 | ";
} else {
$str_handle = "已清理 | ";
}
$html .= <<
{$num_ky} |
{$file_name} |
{$return['msg']} |
{$str_handle}
EOF;
}
fclose($fp);
Db::name('weapp_systemdoctor_bom_log')->insert([
'md5key' => $md5key,
'file_name' => base64_encode($file_name),
'file_num' => $scanned,
'file_total' => $total,
'file_num_ky' => $num_ky,
'is_suspicious'=>$is_suspicious,
'html' => htmlspecialchars($html),
'add_time' => getTime(),
]);
}
$end = getTime();
$spent = ($end - $start);
$spent_str = '';
$hours = intval($spent/3600);
if (!empty($hours)) {
$spent_str .= $hours."小时";
}
if ($spent >= 60) {
$spent_str .= gmdate('i分', $spent);
}
$spent_str .= gmdate('s秒', $spent);
$msg = "扫描完成,没有发现bom头部信息";
if (empty($num_ky)) {
$html = <<
没有发现bom头部信息
|
EOF;
} else {
if (empty($conf_data['is_autoclear'])) {
$msg = "扫描完成,请手工处理";
} else {
$msg = "扫描完成,已自动处理";
}
}
$data = [
'scanned' => $scanned,
'num_ky' => $num_ky,
'spent' => $spent_str,
'html' => $html,
];
$this->success($msg, null, $data);
}
}
/**
* 扫描进度
* @return [type] [description]
*/
public function bom_progressd()
{
\think\Session::pause(); // 暂停session,防止session阻塞机制
if (IS_AJAX) {
$progress = 0;
$result = [];
$init = input('param.init/d');
if (empty($init)) {
Db::name('weapp_systemdoctor_bom_log')->where(['id'=>['gt',0]])->delete();
} else {
$result = Db::name('weapp_systemdoctor_bom_log')->field('id, file_num, file_total, file_num_ky, html')->order('id desc')->find();
}
if (!empty($result)) {
$progress = $result['file_num'] / $result['file_total'];
$progress = floor($progress*100)/100;
if ($progress >= 1) {
Db::name('weapp_systemdoctor_bom_log')->where(['id'=>['gt',0], 'is_suspicious'=>0])->delete();
}
$progress = strval($progress * 100);
if (empty($result['file_num_ky'])) {
$html = <<
正在扫描中
|
EOF;
} else {
$html = htmlspecialchars_decode($result['html']);
}
$this->success('请求成功', null, ['progress'=>$progress,'file_num'=>$result['file_num'],'file_num_ky'=>$result['file_num_ky'],'html'=>$html]);
} else {
$this->success('请求成功', null, ['progress'=>$progress]);
}
}
}
/**
* 去除bom头部信息
* @return [type] [description]
*/
public function bom_clear()
{
if (IS_AJAX) {
$md5key = input('param.md5key/s');
$result = Db::name('weapp_systemdoctor_bom_log')->where(['md5key'=>$md5key, 'is_suspicious'=>1])->find();
if (empty($result)) {
$this->success('操作成功');
}
$file_name = base64_decode($result['file_name']);
$filename = !empty($file_name) ? trim($file_name, '/') : '';
if (!empty($filename) && is_file($filename)) {
$conf_data = $this->bomLogic->getConfData();
$this->bomLogic->rewrite($filename, $conf_data);
$this->success('操作成功');
}
}
$this->error('操作失败');
}
/*-----------------------检查bom头部信息 end-----------------------*/
}