<?php
function get_curl($url, $post=0, $referer=0, $cookie=0, $header=0, $ua=0, $nobaody=0)
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($ch, CURLOPT_TIMEOUT, 30);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
	$httpheader[] = "Accept: */*";
	$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
	$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
	$httpheader[] = "Connection: close";
	curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
	if ($post) {
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
	}
	if ($header) {
		curl_setopt($ch, CURLOPT_HEADER, true);
	}
	if ($cookie) {
		curl_setopt($ch, CURLOPT_COOKIE, $cookie);
	}
	if($referer){
		curl_setopt($ch, CURLOPT_REFERER, $referer);
	}
	if ($ua) {
		curl_setopt($ch, CURLOPT_USERAGENT, $ua);
	}
	else {
		curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0");
	}
	if ($nobaody) {
		curl_setopt($ch, CURLOPT_NOBODY, 1);
	}
	curl_setopt($ch, CURLOPT_ENCODING, "gzip");
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	$ret = curl_exec($ch);
	curl_close($ch);
	return $ret;
}
function real_ip($type=0){
$ip = $_SERVER['REMOTE_ADDR'];
if($type<=0 && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
	foreach ($matches[0] AS $xip) {
		if (filter_var($xip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
			$ip = $xip;
			break;
		}
	}
} elseif ($type<=0 && isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
	$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif ($type<=1 && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
	$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
} elseif ($type<=1 && isset($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
	$ip = $_SERVER['HTTP_X_REAL_IP'];
}
return $ip;
}
function get_ip_city($ip)
{
	$url = 'http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=';
	$city = get_curl($url . $ip);
	$city = mb_convert_encoding($city, "UTF-8", "GB2312");
	$city = json_decode($city, true);
	if ($city['city']) {
		$location = $city['pro'].$city['city'];
	} else {
		$location = $city['pro'];
	}
	if($location){
		return $location;
	}else{
		return false;
	}
}
function daddslashes($string) {
	if(is_array($string)) {
		foreach($string as $key => $val) {
			$string[$key] = daddslashes($val);
		}
	} else {
		$string = addslashes($string);
	}
	return $string;
}

function strexists($string, $find) {
	return !(strpos($string, $find) === FALSE);
}

function dstrpos($string, $arr) {
	if(empty($string)) return false;
	foreach((array)$arr as $v) {
		if(strpos($string, $v) !== false) {
			return true;
		}
	}
	return false;
}

function checkmobile() {
	$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
	$ualist = array('android', 'midp', 'nokia', 'mobile', 'iphone', 'ipod', 'blackberry', 'windows phone');
	if((dstrpos($useragent, $ualist) || strexists($_SERVER['HTTP_ACCEPT'], "VND.WAP") || strexists($_SERVER['HTTP_VIA'],"wap")))
		return true;
	else
		return false;
}
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
	$ckey_length = 4;
	$key = md5($key);
	$keya = md5(substr($key, 0, 16));
	$keyb = md5(substr($key, 16, 16));
	$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
	$cryptkey = $keya.md5($keya.$keyc);
	$key_length = strlen($cryptkey);
	$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
	$string_length = strlen($string);
	$result = '';
	$box = range(0, 255);
	$rndkey = array();
	for($i = 0; $i <= 255; $i++) {
		$rndkey[$i] = ord($cryptkey[$i % $key_length]);
	}
	for($j = $i = 0; $i < 256; $i++) {
		$j = ($j + $box[$i] + $rndkey[$i]) % 256;
		$tmp = $box[$i];
		$box[$i] = $box[$j];
		$box[$j] = $tmp;
	}
	for($a = $j = $i = 0; $i < $string_length; $i++) {
		$a = ($a + 1) % 256;
		$j = ($j + $box[$a]) % 256;
		$tmp = $box[$a];
		$box[$a] = $box[$j];
		$box[$j] = $tmp;
		$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
	}
	if($operation == 'DECODE') {
		if(((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
			return substr($result, 26);
		} else {
			return '';
		}
	} else {
		return $keyc.str_replace('=', '', base64_encode($result));
	}
}

function random($length, $numeric = 0) {
	$seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
	$seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));
	$hash = '';
	$max = strlen($seed) - 1;
	for($i = 0; $i < $length; $i++) {
		$hash .= $seed[mt_rand(0, $max)];
	}
	return $hash;
}
function showmsg($content = '未知的异常',$type = 4,$back = false)
{
switch($type)
{
case 1:
	$panel="success";
break;
case 2:
	$panel="info";
break;
case 3:
	$panel="warning";
break;
case 4:
	$panel="danger";
break;
}

echo '<div class="panel panel-'.$panel.'">
	  <div class="panel-heading">
		<h3 class="panel-title">提示信息</h3>
		</div>
		<div class="panel-body">';
echo $content;

if ($back) {
	echo '<hr/><a href="'.$back.'"><< 返回上一页</a>';
}
else
	echo '<hr/><a href="javascript:history.back(-1)"><< 返回上一页</a>';

echo '</div>
	</div>';
	exit;
}
function sysmsg($msg = '未知的异常',$title = '站点提示信息') {
	?>  
	<!DOCTYPE html>
	<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title><?php echo $title?></title>
		<style type="text/css">
html{background:#eee}body{background:#fff;color:#333;font-family:"微软雅黑","Microsoft YaHei",sans-serif;margin:2em auto;padding:1em 2em;max-width:700px;-webkit-box-shadow:10px 10px 10px rgba(0,0,0,.13);box-shadow:10px 10px 10px rgba(0,0,0,.13);opacity:.8}h1{border-bottom:1px solid #dadada;clear:both;color:#666;font:24px "微软雅黑","Microsoft YaHei",sans-serif;margin:30px 0 0 0;padding:0;padding-bottom:7px}#error-page{margin-top:50px}h3{text-align:center}#error-page p{font-size:9px;line-height:1.5;margin:25px 0 20px}#error-page code{font-family:Consolas,Monaco,monospace}ul li{margin-bottom:10px;font-size:9px}a{color:#21759B;text-decoration:none;margin-top:-10px}a:hover{color:#D54E21}.button{background:#f7f7f7;border:1px solid #ccc;color:#555;display:inline-block;text-decoration:none;font-size:9px;line-height:26px;height:28px;margin:0;padding:0 10px 1px;cursor:pointer;-webkit-border-radius:3px;-webkit-appearance:none;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top}.button.button-large{height:29px;line-height:28px;padding:0 12px}.button:focus,.button:hover{background:#fafafa;border-color:#999;color:#222}.button:focus{-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2)}.button:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}table{table-layout:auto;border:1px solid #333;empty-cells:show;border-collapse:collapse}th{padding:4px;border:1px solid #333;overflow:hidden;color:#333;background:#eee}td{padding:4px;border:1px solid #333;overflow:hidden;color:#333}
		</style>
	</head>
	<body id="error-page">
		<?php echo '<h3>'.$title.'</h3>';
		echo $msg; ?>
	</body>
	</html>
	<?php
	exit;
}

if(!function_exists("is_https")){
	function is_https() {
		if(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443){
			return true;
		}elseif(isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on' || $_SERVER['HTTPS'] == '1')){
			return true;
		}elseif(isset($_SERVER['HTTP_X_CLIENT_SCHEME']) && $_SERVER['HTTP_X_CLIENT_SCHEME'] == 'https'){
			return true;
		}elseif(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'){
			return true;
		}elseif(isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https'){
			return true;
		}elseif(isset($_SERVER['HTTP_EWS_CUSTOME_SCHEME']) && $_SERVER['HTTP_EWS_CUSTOME_SCHEME'] == 'https'){
			return true;
		}
		return false;
	}
}

function checkRefererHost(){
	$http_host = $_SERVER['HTTP_HOST'];
	if(strpos($http_host,':')) $http_host = substr($http_host, 0, strpos($http_host, ':'));
	if(isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN']){
		$origin_arr = parse_url($_SERVER['HTTP_ORIGIN']);
		if(isset($origin_arr['host']) && $origin_arr['host'] !== $http_host) return false;
		return true;
	}
	if(isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']){
		$url_arr = parse_url($_SERVER['HTTP_REFERER']);
		if(isset($url_arr['host']) && $url_arr['host'] !== $http_host) return false;
		return true;
	}
	return true;
}

function checkIfActive($string) {
	$array=explode(',',$string);
	$script = basename($_SERVER['SCRIPT_NAME'], '.php');
	if (in_array($script,$array)){
		return 'active';
	}elseif (isset($_GET['m']) && in_array($_GET['m'],$array)){
		return 'active';
	}else
		return null;
}

function getAllSetting() {
	global $DB;
	static $runtimeCache = null;
	
	// 如果有缓存直接返回
	if (is_array($runtimeCache)) {
		return $runtimeCache;
	}
	
	// 禁用 APCu 缓存，因为某些环境下 APCu 可能有问题
	// 如果需要缓存，可以考虑使用文件缓存或其他方式
	
	$conf = array();
	
	try {
		$result = $DB->getAll("SELECT * FROM pre_config");
		if(!$result) {
			// 查询返回 false 或空，检查错误
			$error = $DB->error();
			if($error) {
				// 尝试使用原生查询
				global $dbconfig;
				try {
					$pdo = new PDO("mysql:host={$dbconfig['host']};port={$dbconfig['port']};dbname={$dbconfig['dbname']}", $dbconfig['user'], $dbconfig['pwd']);
					$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
					$stmt = $pdo->query("SELECT * FROM pre_config");
					$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
				} catch(Exception $e) {
					$conf['_error'] = '数据库查询失败: ' . $e->getMessage();
				}
			}
		}
		
		if($result) {
			foreach($result as $row){
				if(isset($row['k']) && isset($row['v'])) {
					if($row['k']=='cache') continue;
					$conf[ $row['k'] ] = $row['v'];
				}
			}
		}
	} catch(Exception $e) {
		$conf['_error'] = '获取配置失败: ' . $e->getMessage();
	}
	
	$runtimeCache = $conf;
	return $conf;
}
function getSetting($k){
	global $DB;
	return $DB->getColumn("SELECT v FROM pre_config WHERE k=:k LIMIT 1", [':k'=>$k]);
}
function saveSetting($k, $v){
	global $DB;
	$result = $DB->exec("REPLACE INTO pre_config SET v=:v,k=:k", [':v'=>$v, ':k'=>$k]);
	
	// 清除缓存 - 禁用 APCu 因为某些服务器可能不支持
	// 如果需要缓存，可以在配置变更时清除文件缓存
	
	return $result;
}

function size_format($size)
{
	if ($size<1024) {
		$size.=' B';
	} else {
		$size/=1024;
		if ($size<1024) {
			$size=round($size, 2).' KB';
		} else {
			$size/=1024;
			if ($size<1024) {
				$size=round($size, 2).' MB';
			} else {
				$size/=1024;
				if ($size<1024) {
					$size=round($size, 2).' GB';
				}
			}
		}
	}
	return $size;
}

function minetype($type){
	$mime = array (
	//applications
	'ai'  => 'application/postscript',
	'eps'  => 'application/postscript',
	'exe'  => 'application/octet-stream',
	'doc'  => 'application/vnd.ms-word',
	'xls'  => 'application/vnd.ms-excel',
	'ppt'  => 'application/vnd.ms-powerpoint',
	'pps'  => 'application/vnd.ms-powerpoint',
	'pdf'  => 'application/pdf',
	'xml'  => 'application/xml',
	'odt'  => 'application/vnd.oasis.opendocument.text',
	'swf'  => 'application/x-shockwave-flash',
	// archives
	'gz'  => 'application/x-gzip',
	'tgz'  => 'application/x-gzip',
	'bz'  => 'application/x-bzip2',
	'bz2'  => 'application/x-bzip2',
	'tbz'  => 'application/x-bzip2',
	'zip'  => 'application/zip',
	'rar'  => 'application/x-rar',
	'tar'  => 'application/x-tar',
	'7z'  => 'application/x-7z-compressed',
	// texts
	'txt'  => 'text/plain',
	'php'  => 'text/x-php',
	'html' => 'text/html',
	'htm'  => 'text/html',
	'js'  => 'text/javascript',
	'css'  => 'text/css',
	'rtf'  => 'text/rtf',
	'rtfd' => 'text/rtfd',
	'py'  => 'text/x-python',
	'java' => 'text/x-java-source',
	'rb'  => 'text/x-ruby',
	'sh'  => 'text/x-shellscript',
	'pl'  => 'text/x-perl',
	'sql'  => 'text/x-sql',
	// images
	'bmp'  => 'image/x-ms-bmp',
	'jpg'  => 'image/jpeg',
	'jpeg' => 'image/jpeg',
	'gif'  => 'image/gif',
	'png'  => 'image/png',
	'tif'  => 'image/tiff',
	'tiff' => 'image/tiff',
	'tga'  => 'image/x-targa',
	'ico'  => 'image/x-icon',
	'svg'  => 'image/svg+xml',
	'svgz'  => 'image/x-svgz',
	'webp'  => 'image/webp',
	'psd'  => 'image/vnd.adobe.photoshop',
	'heic' => 'image/x-heic',
	'exif' => 'image/jpeg',
	//audio
	'mp3'  => 'audio/mpeg',
	'mid'  => 'audio/midi',
	'ogg'  => 'audio/ogg',
	'mp4a' => 'audio/mp4',
	'm4a' => 'audio/m4a',
	'wav'  => 'audio/wav',
	'wma'  => 'audio/x-ms-wma',
	// video
	'avi'  => 'video/x-msvideo',
	'dv'  => 'video/x-dv',
	'mp4'  => 'video/mp4',
	'f4v'  => 'video/x-flv',
	'mpeg' => 'video/mpeg',
	'mpg'  => 'video/mpeg',
	'mov'  => 'video/quicktime',
	'wmv'  => 'video/x-ms-wmv',
	'flv'  => 'video/x-flv',
	'mkv'  => 'video/x-matroska',
	'ts'  => 'video/mp2t',
	'3gp'  => 'video/3gpp',
	'3gpp'  => 'video/3gpp',
	'webm'  => 'video/webm',
	'm3u8'  => 'application/vnd.apple.mpegurl',
	);
	return isset($mime[$type]) ? $mime[$type] : 'application/octet-stream';
}

function type_to_icon($type){
	global $conf;
	$type_image = explode('|',$conf['type_image']);
	$type_audio = explode('|',$conf['type_audio']);
	$type_video = explode('|',$conf['type_video']);
	$type_image = array_merge($type_image, ['png','jpg','jpeg','gif','bmp','webp','ico','svg','svgz','tif','tiff','heic','psd','exif','pcx','tga','fpx','cdr','pcd','eps','ai','wmf','raw','ufo','jpc','jp2','jpx','xbm','wbmp','avif']);
	$type_audio = array_merge($type_audio, ['mp3','wav','wma','ogg','m4a','flac','ape','aac','ra','cda','midi','mid','aif','au','voc']);
	$type_video = array_merge($type_video, ['mp4','webm','flv','f4v','mov','3gp','3gpp','avi','mpg','mpeg','wmv','mkv','ts','dat','asf','rm','rmvb','ram','divx','vob','qt','fli','flc','mod','m2t','swf','mts','m2ts','mpe','div','lavf','m3u8','m4v','ogm','ogv']);
	$type_text = ['txt','text','log','md','yaml','yml','conf','config','ini'];
	$type_code = ['c','cpp','cxx','rc','php','py','cs','h','htm','html','css','less','js','hdml','dtd','wml','xml','vbs','vb','rtx','xsd','dpr','sql','java','go','jsp','asp','aspx','asa','asax','pl','bat','cmd','rb','reg','sh','json','lua','r','mm','mak','swift','tpl'];
	$type_archive = ['zip','7z','rar','tgz','gz','xz','tar','jar','iso','z','zipx','cab','bz2','arj','lz','lzh'];
	$type_word = ['doc','docx','xps','rtf','wps','odt'];
	$type_excel = ['xls','xlsx','ods'];
	$type_pdf = ['pdf'];
	$type_powerpoint = ['ppt','pptx','pptm'];
	$type_android = ['apk'];
	$type_apple = ['ipa','dmg'];
	$type_windows = ['exe','appx','msi'];
	$type_linux = ['deb','rpm'];
	if(in_array($type, $type_image)){
		return 'fa-file-image-o';
	}elseif(in_array($type, $type_audio)){
		return 'fa-file-audio-o';
	}elseif(in_array($type, $type_video)){
		return 'fa-file-video-o';
	}elseif(in_array($type, $type_text)){
		return 'fa-file-text-o';
	}elseif(in_array($type, $type_code)){
		return 'fa-file-code-o';
	}elseif(in_array($type, $type_archive)){
		return 'fa-file-archive-o';
	}elseif(in_array($type, $type_word)){
		return 'fa-file-word-o';
	}elseif(in_array($type, $type_excel)){
		return 'fa-file-excel-o';
	}elseif(in_array($type, $type_pdf)){
		return 'fa-file-pdf-o';
	}elseif(in_array($type, $type_powerpoint)){
		return 'fa-file-powerpoint-o';
	}elseif(in_array($type, $type_android)){
		return 'fa-android';
	}elseif(in_array($type, $type_apple)){
		return 'fa-apple';
	}elseif(in_array($type, $type_windows)){
		return 'fa-windows';
	}elseif(in_array($type, $type_linux)){
		return 'fa-linux';
	}else{
		return 'fa-file-o';
	}
}

function is_view($type){
	global $conf;
	$type_image = explode('|',$conf['type_image']);
	$type_audio = explode('|',$conf['type_audio']);
	$type_video = explode('|',$conf['type_video']);
	if (in_array($type, $type_image) || in_array($type, $type_audio) || in_array($type, $type_video)) {
		return true;
	}
	return false;
}

function get_view_type($type){
	global $conf;
	$type_image = explode('|',$conf['type_image']);
	$type_audio = explode('|',$conf['type_audio']);
	$type_video = explode('|',$conf['type_video']);
	$type_office = ['doc','docx','xps','rtf','wps','xls','xlsx','ppt','pptx'];
	if (in_array($type, $type_image)) {
		return 'image';
	}elseif (in_array($type, $type_audio)) {
		return 'audio';
	}elseif (in_array($type, $type_video)) {
		return 'video';
	}elseif (in_array($type, $type_office)) {
		return 'office';
	}
	return false;
}


function checkImage($hash, $ext){
	global $conf,$siteurl;
	$apiurl = $conf['apiurl']?$conf['apiurl']:$siteurl;
	$fileurl = $apiurl.'view.php?hash='.$hash.'.'.$ext.'&greencheck=1';
	if($conf['green_check'] == 1){
		return checkImage_aliyun($fileurl);
	}elseif($conf['green_check'] == 2){
		return checkImage_qcloud($fileurl);
	}
	return false;
}
function checkImage_aliyun($fileurl){
	global $conf;
	$scenes = [];
	if ($conf['green_check_porn']==1) {
		$scenes[] = 'porn';
		$label_porn = explode(',', $conf['green_label_porn']);
	}
	if ($conf['green_check_terrorism']==1) {
		$scenes[] = 'terrorism';
		$label_terrorism = explode(',', $conf['green_label_terrorism']);
	}
	if(count($scenes)==0)return false;

	$client = new \lib\AliyunGreen($conf['aliyun_ak'], $conf['aliyun_sk'], $conf['green_check_region']);
	$task1 = array('dataId' => uniqid(), 'url' => $fileurl);
	$request = array("tasks" => array($task1), "scenes" => $scenes);

try {
	$response = $client->doCheck($request);
	//print_r($response);
	if(200 == $response->code){
		$taskResults = $response->data;
		foreach ($taskResults as $taskResult) {
			if(200 == $taskResult->code){
				$sceneResults = $taskResult->results;
				foreach ($sceneResults as $sceneResult) {
					$scene = $sceneResult->scene;
					$label = $sceneResult->label;
					$suggestion = $sceneResult->suggestion;
					if($scene == 'porn' && (in_array($label, $label_porn) || $suggestion == 'block')){
						return true;
					}elseif($scene == 'terrorism' && (in_array($label, $label_terrorism) || $suggestion == 'block')){
						return true;
					}
				}
			}else{
				writeLog("task process fail:" . $taskResult->code . ' ' . $taskResult->msg);
			}
		}
	}else{
		writeLog("detect not success. code:" . $response->code . ' ' . $response->msg);
	}
} catch (Exception $e) {
	print_r($e);
}
return false;
}
function checkImage_qcloud($fileurl){
	global $conf;
	$client = new \lib\QcloudGreen($conf['qcloud_green_id'], $conf['qcloud_green_key'], $conf['green_check_region']);
	$result = $client->ImageModeration($fileurl);
	if(isset($result['Suggestion'])){
		if($result['Suggestion'] == 'Block'){
			return true;
		}
	}else{
		writeLog('detect not success.['.$result['Error']['Code'].']'.$result['Error']['Message']);
	}
	return false;
}
function writeLog($text) {
	file_put_contents ( SYSTEM_ROOT."log.txt", date ( "Y-m-d H:i:s" ) . "  " . $text . "\r\n", FILE_APPEND );
}

function get_file_ext($name){
	$extension=explode('.',$name);
	if (($length = count($extension)) > 1) {
		$ext = strtolower($extension[$length - 1]);
	}
	if(strlen($ext)>6)$ext='';
	return $ext;
}

function file_part_merge($hash, $chunks){
	$tmp_dir = sys_get_temp_dir();
	$savePathTemp = $tmp_dir . '/' . $hash. '.parttmp';
	$tempFilePre = $tmp_dir . '/' . $hash. '.part';
	if(file_exists($savePathTemp)){
		unlink($savePathTemp);
	}
	if(!$out = fopen($savePathTemp, "wb")){
		exit('{"code":-1,"msg":"文件合并失败，临时文件夹无写入权限"}');
	}
	for( $index = 1; $index <= $chunks; $index++ ) {
		$chunk_file = $tempFilePre.$index;
		if (!$fp_in = @fopen($chunk_file,"rb")){
			fclose($out);
			unlink($savePathTemp);
			exit('{"code":-1,"msg":"文件合并失败，第'.$index.'分块读取失败"}');
		}
		while (!feof($fp_in)) {
			fwrite($out, fread($fp_in,1024*200));
		}
		fclose($fp_in);
		unlink($chunk_file);
	}
	fclose($out);
	return $savePathTemp;
}

function get_file_range($size){
	if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE']) && preg_match('/^bytes=(\d+)-(\d*)$/i', $_SERVER['HTTP_RANGE'], $match)){
		$start = intval($match[1]);
		if($match[2] !== ''){
			$end = intval($match[2]);
		}else{
			$end = $size - 1;
		}
		if($start < 0) $start = 0;
		if($end >= $size) $end = $size - 1;
		if($end < $start || $start >= $size) return false;
		return [$start, $end];
	}
	return false;
}

function file_output($hash, $type, $size, $name, $is_view = false, $is_admin = false){
	global $conf, $stor;

	@set_time_limit(0);
	$size = intval($size);
	if($is_admin){
		header("Pragma: no-cache");
    	header("Cache-Control: no-store, no-cache, must-revalidate");
	}else{
		header("Cache-Control: no-cache");
		header("Pragma: no-cache");
	}

	$filename = '"'.$name.'"; filename*=utf-8\'\''.rawurlencode($name);

	if(\lib\StorHelper::is_cloud() && $conf['downfile_type'] == 1){
		$redirect = $stor->getDownUrl($hash, $name, $is_view ? minetype($type) : null);
		if($redirect){
			header("Pragma: no-cache");
			header("Cache-Control: no-store, no-cache, must-revalidate");
			header("Location: ".$redirect);
		}else{
			ob_clean();
			exit('Error:'.$stor->errmsg());
		}
	}else{
		ob_clean();
		if($is_view){
			header("Content-Type: ".minetype($type));
			header("Content-Disposition: inline; filename={$filename}");
		}else{
			header("Content-Description: File Transfer");
        	header("Content-Type: application/force-download");
        	header("Content-Disposition: attachment; filename={$filename}");
		}

		$range = false;
		if(\lib\StorHelper::is_range()){
			header("Accept-Ranges: bytes");
			$range = get_file_range($size);
		}

		if($range){
			header("HTTP/1.1 206 Partial Content");
			header("Content-Length: ".($range[1] - $range[0] + 1));
			header("Content-Range: bytes {$range[0]}-{$range[1]}/{$size}");
			$stor->downfile($hash, $range);
		}else{
			header("Content-Length: {$size}");
			$stor->downfile($hash, $conf['storage']=='local'?[0, $size-1]:false);
		}
	}
}

function get_guest_file_ids(){
	$token = isset($_COOKIE['guest_token']) ? $_COOKIE['guest_token'] : null;
	if(!$token) return [];
	$raw = authcode(daddslashes($token), 'DECODE', SYS_KEY);
	if(!$raw) return [];
	$arr = json_decode($raw, true);
	if(!is_array($arr)) return [];
	$ids = [];
	foreach($arr as $v){
		$ids[] = intval($v);
	}
	return $ids;
}

function set_guest_file_ids($ids){
	if(!is_array($ids)) $ids = [];
	$clean = [];
	foreach($ids as $v){
		$iv = intval($v);
		if($iv>0) $clean[] = $iv;
	}
	// 移除200个ID的限制，保留所有文件ID
	// if(count($clean) > 200){
	//     $clean = array_slice($clean, -200);
	// }
	$raw = json_encode($clean, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
	$token = authcode($raw, 'ENCODE', SYS_KEY, 2592000);
	setcookie('guest_token', $token, [
		'expires' => time() + 2592000,
		'path' => '/',
		'secure' => is_https(),
		'httponly' => true,
		'samesite' => 'Lax',
	]);
}

function add_guest_file_id($id){
	$ids = get_guest_file_ids();
    if(is_array($id)){
        $ids = array_merge($ids, $id);
    }else{
        $ids[] = intval($id);
    }
    // De-duplicate
    $ids = array_values(array_unique($ids));
	set_guest_file_ids($ids);
}

function get_guest_token() {
    $token = isset($_COOKIE['guest_user_token']) ? $_COOKIE['guest_user_token'] : null;
    if ($token && preg_match('/^[a-f0-9]{32}$/', $token)) {
        return $token;
    }
    // Generate new token
    $token = bin2hex(random_bytes(16));
    setcookie('guest_user_token', $token, [
		'expires' => time() + 31536000,
		'path' => '/',
		'secure' => is_https(),
		'httponly' => true,
		'samesite' => 'Lax',
	]);
    return $token;
}

/**
 * 检查用户配额是否超出限制
 * @param array $user 用户信息
 * @param bool $is_accessing_existing 是否为访问/下载现有文件（如果是，则豁免流量和存储限制，仅限本人）
 * @param string $check_type 检查类型：all/traffic/storage
 * @return array ['code'=>0, 'msg'=>''] code: 0-正常, 1-流量超出, 2-空间超出
 */
function check_user_quota(&$user, $is_accessing_existing = false, $check_type = 'all') {
	global $conf, $DB;

	// 检查额外配额有效期
	if (isset($user['uid'])) {
		$updates = [];
		$params = [':uid' => $user['uid']];
		$now = time();
		
		// 存储配额过期检查
		if(!empty($user['u_storage_expire']) && strtotime($user['u_storage_expire']) < $now) {
			if(isset($user['u_storage_quota']) && $user['u_storage_quota'] > 0) {
				$updates[] = "u_storage_quota = 0";
				$user['u_storage_quota'] = 0;
			}
		}
		
		// 流量配额过期检查
		if(!empty($user['u_traffic_expire']) && strtotime($user['u_traffic_expire']) < $now) {
			if(isset($user['u_traffic_quota']) && $user['u_traffic_quota'] > 0) {
				$updates[] = "u_traffic_quota = 0";
				$user['u_traffic_quota'] = 0;
			}
		}
		
		if(!empty($updates)) {
			$DB->exec("UPDATE pre_user SET " . implode(', ', $updates) . " WHERE uid=:uid", $params);
		}
	}

	// Auto-reset traffic quota
	if (isset($user['uid'])) {
		$reset_interval = isset($conf['traffic_quota_reset_interval']) ? intval($conf['traffic_quota_reset_interval']) : 30;
		
		// 增强配置验证 - 确保配置值有效
		if ($reset_interval < 0) $reset_interval = 30; // 防止负数
		
		if ($reset_interval > 0) {
			$last_reset = isset($user['last_traffic_reset_time']) ? $user['last_traffic_reset_time'] : null;
			$should_reset = false;
			
			if (!$last_reset) {
				// 首次设置重置时间，但不立即重置流量
				$DB->exec("UPDATE pre_user SET last_traffic_reset_time = NOW() WHERE uid = :uid", [':uid' => $user['uid']]);
				$user['last_traffic_reset_time'] = date('Y-m-d H:i:s');
			} else {
				$last_reset_time = strtotime($last_reset);
				$current_time = time();
				$seconds_passed = $current_time - $last_reset_time;
				$interval_seconds = $reset_interval * 86400; // 转换为秒
				
				// 确保已经过一个完整的重置周期
				if ($seconds_passed >= $interval_seconds) {
					$should_reset = true;
					$periods = floor($seconds_passed / $interval_seconds);
				}
			}
			
			if ($should_reset) {
				// 获取当前统一流量配额
				$traffic_quota_mb = isset($conf['user_traffic_quota']) ? floatval($conf['user_traffic_quota']) : 0;
				$traffic_quota_bytes = $traffic_quota_mb * 1048576;
				
				// 计算本周期溢出的流量（即消耗的额外配额）
				// 流量重置逻辑：
				// 1. 统一配额部分：每个周期重置（清零已用量）
				// 2. 额外配额部分：一旦使用，永久扣除（从u_traffic_quota中减去）
				// 这样未使用的统一配额不会结转，而购买的额外配额用多少扣多少，未用的保留
				$current_usage = $user['traffic_used'];
				$excess_usage = max(0, $current_usage - $traffic_quota_bytes);
				
				$sql_updates = ["traffic_used = 0", "last_traffic_reset_time = NOW()"];
				$sql_params = [':uid' => $user['uid']];
				
				if ($excess_usage > 0) {
					// 扣除额外配额
					$sql_updates[] = "u_traffic_quota = GREATEST(0, CAST(u_traffic_quota AS SIGNED) - :excess)";
					$sql_params[':excess'] = $excess_usage;
					// 更新内存中的user数组
					$user['u_traffic_quota'] = max(0, ($user['u_traffic_quota'] ?? 0) - $excess_usage);
				}
				
				$DB->exec("UPDATE pre_user SET " . implode(', ', $sql_updates) . " WHERE uid = :uid", $sql_params);
				
				$user['traffic_used'] = 0;
				$user['last_traffic_reset_time'] = date('Y-m-d H:i:s');
			}
		}
	}

	if (!$user || $is_accessing_existing) return ['code' => 0];
	
	// 流量检查（统一配置，不再区分普通用户和VIP用户）
	if($check_type == 'all' || $check_type == 'traffic'){
		$traffic_quota_mb = isset($conf['user_traffic_quota']) ? $conf['user_traffic_quota'] : null;
		$user_extra_traffic = isset($user['u_traffic_quota']) ? $user['u_traffic_quota'] : 0;

		if (($traffic_quota_mb !== null && $traffic_quota_mb !== '') || $user_extra_traffic > 0) {
			$traffic_quota_mb = floatval($traffic_quota_mb);
			$traffic_limit = ($traffic_quota_mb > 0 ? $traffic_quota_mb * 1048576 : 0) + $user_extra_traffic;
			if ($traffic_limit > 0 && $user['traffic_used'] >= $traffic_limit) {
				return ['code' => 1, 'msg' => '当前用户流量配额已用尽'];
			}
		}
	}

	// 空间检查（统一配置，不再区分普通用户和VIP用户）
	if($check_type == 'all' || $check_type == 'storage'){
		$storage_quota_mb = isset($conf['user_storage_quota']) ? $conf['user_storage_quota'] : null;
		$user_extra_storage = isset($user['u_storage_quota']) ? $user['u_storage_quota'] : 0;

		if (($storage_quota_mb !== null && $storage_quota_mb !== '') || $user_extra_storage > 0) {
			$storage_quota_mb = floatval($storage_quota_mb);
			$storage_limit = ($storage_quota_mb > 0 ? $storage_quota_mb * 1048576 : 0) + $user_extra_storage;
			if ($storage_limit > 0 && $user['storage_used'] >= $storage_limit) {
				return ['code' => 2, 'msg' => '当前用户存储空间已满'];
			}
		}
	}

	return ['code' => 0];
}

function log_traffic_usage($file_id, $file_hash, $file_name, $file_size, $file_type, $owner_uid, $visitor_uid, $visitor_ip, $access_type, $traffic_size, $range_start = null, $range_end = null) {
	global $DB;
	
	$sql = "INSERT INTO `pre_traffic_log` (`file_id`, `file_hash`, `file_name`, `file_size`, `file_type`, `owner_uid`, `visitor_uid`, `visitor_ip`, `access_type`, `traffic_size`, `range_start`, `range_end`, `created_at`) 
			VALUES (:file_id, :file_hash, :file_name, :file_size, :file_type, :owner_uid, :visitor_uid, :visitor_ip, :access_type, :traffic_size, :range_start, :range_end, NOW())";
	
	$params = [
		':file_id' => $file_id,
		':file_hash' => $file_hash,
		':file_name' => $file_name,
		':file_size' => $file_size,
		':file_type' => $file_type,
		':owner_uid' => $owner_uid,
		':visitor_uid' => $visitor_uid,
		':visitor_ip' => $visitor_ip,
		':access_type' => $access_type,
		':traffic_size' => $traffic_size,
		':range_start' => $range_start,
		':range_end' => $range_end
	];
	
	return $DB->exec($sql, $params);
}

function get_guest_traffic_used($ip, $quota_type = 'daily') {
	global $DB;
	
	$period_start = get_period_start($quota_type);
	
	$sql = "SELECT SUM(traffic_size) as total FROM pre_traffic_log 
			WHERE visitor_uid = 0 AND visitor_ip = :ip AND created_at >= :period_start";
	
	$result = $DB->getRow($sql, [':ip' => $ip, ':period_start' => $period_start]);
	
	return $result && $result['total'] ? $result['total'] : 0;
}

function get_period_start($quota_type) {
	$now = time();
	
	switch($quota_type) {
		case 'daily':
			return date('Y-m-d 00:00:00', $now);
		case 'monthly':
			return date('Y-m-01 00:00:00', $now);
		case 'yearly':
			return date('Y-01-01 00:00:00', $now);
		default:
			return date('Y-m-d 00:00:00', $now);
	}
}

function get_period_end($quota_type) {
	$now = time();
	
	switch($quota_type) {
		case 'daily':
			return date('Y-m-d 23:59:59', $now);
		case 'monthly':
			return date('Y-m-t 23:59:59', $now);
		case 'yearly':
			return date('Y-12-31 23:59:59', $now);
		default:
			return date('Y-m-d 23:59:59', $now);
	}
}

function check_guest_quota($ip) {
	global $conf, $DB;

	$traffic_quota_mb = isset($conf['user_traffic_quota']) ? $conf['user_traffic_quota'] : null;
	$storage_quota_mb = isset($conf['user_storage_quota']) ? $conf['user_storage_quota'] : null;

	if (($traffic_quota_mb === null || $traffic_quota_mb === '' || $traffic_quota_mb <= 0) &&
		($storage_quota_mb === null || $storage_quota_mb === '' || $storage_quota_mb <= 0)) {
		return ['code' => 0, 'quota_limit' => 0, 'quota_used' => 0];
	}

	$quota_used = get_guest_traffic_used($ip, 'daily');

	if ($traffic_quota_mb !== null && $traffic_quota_mb !== '' && $traffic_quota_mb > 0) {
		$traffic_limit = $traffic_quota_mb * 1048576;
		if ($quota_used >= $traffic_limit) {
			return [
				'code' => 1,
				'msg' => '流量配额已用尽',
				'quota_limit' => $traffic_limit,
				'quota_used' => $quota_used,
				'action' => 'block',
				'quota_type' => 'traffic'
			];
		}
	}

	return [
		'code' => 0,
		'quota_limit' => $traffic_quota_mb > 0 ? $traffic_quota_mb * 1048576 : 0,
		'quota_used' => $quota_used
	];
}

function get_traffic_quota($uid, $ip = null) {
	global $conf, $DB;

	$traffic_quota_mb = isset($conf['user_traffic_quota']) ? $conf['user_traffic_quota'] : null;
	$storage_quota_mb = isset($conf['user_storage_quota']) ? $conf['user_storage_quota'] : null;

	$quota_limit = ($traffic_quota_mb !== null && $traffic_quota_mb > 0) ? $traffic_quota_mb * 1048576 : 0;

	if ($uid > 0) {
		$user = $DB->getRow("SELECT * FROM pre_user WHERE uid=:uid LIMIT 1", [':uid' => $uid]);
		if (!$user) {
			return ['quota_limit' => 0, 'quota_used' => 0, 'quota_type' => 'unified'];
		}

        // 叠加额外配额
        $quota_limit += (isset($user['u_traffic_quota']) ? $user['u_traffic_quota'] : 0);

		$quota_used = $user['traffic_used'];

		return [
			'quota_limit' => $quota_limit,
			'quota_used' => $quota_used,
			'quota_type' => 'unified',
			'quota_percentage' => $quota_limit > 0 ? round(($quota_used / $quota_limit) * 100, 2) : 0
		];
	} else {
		$quota_used = $ip ? get_guest_traffic_used($ip, 'daily') : 0;

		return [
			'quota_limit' => $quota_limit,
			'quota_used' => $quota_used,
			'quota_type' => 'unified',
			'quota_percentage' => $quota_limit > 0 ? round(($quota_used / $quota_limit) * 100, 2) : 0
		];
	}
}

function check_traffic_quota($uid, $ip = null, $is_owner = false, $is_admin = false) {
	global $conf, $DB;

	if ($is_owner || $is_admin) {
		return ['code' => 0];
	}

	if ($uid > 0) {
		$user = $DB->getRow("SELECT * FROM pre_user WHERE uid=:uid LIMIT 1", [':uid' => $uid]);
		if (!$user) {
			return ['code' => 0];
		}

		return check_user_quota($user, false, 'traffic');
	} else {
		if (!$ip) {
			return ['code' => 0];
		}

		return check_guest_quota($ip);
	}
}

function update_traffic_usage($uid, $ip, $traffic_size) {
	global $DB;
	
	if ($uid > 0) {
		$DB->exec("UPDATE pre_user SET traffic_used=traffic_used+:size WHERE uid=:uid", 
			[':size' => $traffic_size, ':uid' => $uid]);
	}
}

/**
 * 检查IP是否被封禁
 * @param string $ip IP地址
 * @return array ['banned'=>true/false, 'reason'=>'封禁原因', 'ban_end'=>'解封时间']
 */
function check_ip_banned($ip) {
	global $DB;
	
	if (empty($ip)) {
		return ['banned' => false];
	}
	
	// 检查IP是否在封禁列表中
	$ban_record = $DB->getRow("SELECT * FROM pre_ip_ban WHERE ip = :ip AND status = 1", [':ip' => $ip]);
	
	if (!$ban_record) {
		return ['banned' => false];
	}
	
	// 检查封禁是否已过期
	if ($ban_record['ban_end'] !== null && strtotime($ban_record['ban_end']) < time()) {
		// 自动解封过期IP
		$DB->exec("UPDATE pre_ip_ban SET status = 0, unbanned_at = NOW(), updated_at = NOW() WHERE id = :id", 
			[':id' => $ban_record['id']]);
		return ['banned' => false];
	}
	
	return [
		'banned' => true,
		'reason' => $ban_record['ban_reason'],
		'ban_type' => $ban_record['ban_type'],
		'ban_start' => $ban_record['ban_start'],
		'ban_end' => $ban_record['ban_end'],
		'traffic_used' => $ban_record['traffic_used'],
		'traffic_limit' => $ban_record['traffic_limit']
	];
}

/**
 * 封禁IP
 * @param string $ip IP地址
 * @param string $ban_type 封禁类型：auto=自动, manual=手动
 * @param string $reason 封禁原因
 * @param int $ban_duration 封禁时长（秒，0表示永久）
 * @param int $operator_uid 操作者UID
 * @param array $extra_data 额外数据
 * @return bool
 */
function ban_ip($ip, $ban_type = 'auto', $reason = '', $ban_duration = 0, $operator_uid = 0, $extra_data = []) {
	global $DB;
	
	if (empty($ip)) {
		return false;
	}
	
	// 检查IP是否已被封禁
	$existing = $DB->getRow("SELECT id FROM pre_ip_ban WHERE ip = :ip AND status = 1", [':ip' => $ip]);
	if ($existing) {
		// 更新现有封禁记录
		return false;
	}
	
	$ban_start = date('Y-m-d H:i:s');
	$ban_end = $ban_duration > 0 ? date('Y-m-d H:i:s', time() + $ban_duration) : null;
	
	// 插入封禁记录
	$sql = "INSERT INTO pre_ip_ban (ip, ip_type, ban_type, ban_reason, ban_start, ban_end, trigger_count, 
			traffic_limit, traffic_used, status, created_by, created_at, updated_at) 
			VALUES (:ip, :ip_type, :ban_type, :ban_reason, :ban_start, :ban_end, :trigger_count, 
			:traffic_limit, :traffic_used, 1, :created_by, NOW(), NOW())";
	
	$params = [
		':ip' => $ip,
		':ip_type' => strpos($ip, ':') !== false ? 1 : 0,
		':ban_type' => $ban_type,
		':ban_reason' => $reason,
		':ban_start' => $ban_start,
		':ban_end' => $ban_end,
		':trigger_count' => isset($extra_data['trigger_count']) ? $extra_data['trigger_count'] : 1,
		':traffic_limit' => isset($extra_data['traffic_limit']) ? $extra_data['traffic_limit'] : 0,
		':traffic_used' => isset($extra_data['traffic_used']) ? $extra_data['traffic_used'] : 0,
		':created_by' => $operator_uid
	];
	
	$result = $DB->exec($sql, $params);
	
	if ($result) {
		// 记录封禁日志
		$operator_name = $operator_uid > 0 ? '管理员' : '系统';
		$DB->exec("INSERT INTO pre_ip_ban_log (ip, action, ban_type, ban_reason, ban_end, operator_uid, operator_name, created_at) 
				VALUES (:ip, 'ban', :ban_type, :ban_reason, :ban_end, :operator_uid, :operator_name, NOW())",
			[':ip' => $ip, ':ban_type' => $ban_type, ':ban_reason' => $reason, ':ban_end' => $ban_end, 
			 ':operator_uid' => $operator_uid, ':operator_name' => $operator_name]);
	}
	
	return $result !== false;
}

/**
 * 解封IP
 * @param string $ip IP地址
 * @param string $reason 解封原因
 * @param int $operator_uid 操作者UID
 * @return bool
 */
function unban_ip($ip, $reason = '', $operator_uid = 0) {
	global $DB;
	
	if (empty($ip)) {
		return false;
	}
	
	// 检查IP是否被封禁
	$ban_record = $DB->getRow("SELECT * FROM pre_ip_ban WHERE ip = :ip AND status = 1", [':ip' => $ip]);
	if (!$ban_record) {
		return false;
	}
	
	// 更新封禁状态
	$result = $DB->exec("UPDATE pre_ip_ban SET status = 0, unbanned_at = NOW(), unban_reason = :reason, updated_at = NOW() 
						WHERE id = :id", 
			[':reason' => $reason, ':id' => $ban_record['id']]);
	
	if ($result) {
		// 记录解封日志
		$operator_name = $operator_uid > 0 ? '管理员' : '系统';
		$DB->exec("INSERT INTO pre_ip_ban_log (ip, action, ban_type, ban_reason, operator_uid, operator_name, created_at) 
				VALUES (:ip, 'unban', :ban_type, :ban_reason, :operator_uid, :operator_name, NOW())",
			[':ip' => $ip, ':ban_type' => $ban_record['ban_type'], ':ban_reason' => $reason, 
			 ':operator_uid' => $operator_uid, ':operator_name' => $operator_name]);
	}
	
	return $result !== false;
}

/**
 * 自动检测并封禁恶意IP
 * 当游客流量超过阈值时自动封禁
 * @param string $ip IP地址
 * @param int $traffic_used 已用流量
 * @param int $traffic_limit 流量限制
 * @return bool 是否被封禁
 */
function auto_ban_guest_ip($ip, $traffic_used, $traffic_limit) {
	global $conf;
	
	// 检查自动封禁是否启用
	$auto_ban_enabled = isset($conf['guest_auto_ban_enabled']) ? $conf['guest_auto_ban_enabled'] : 1;
	if (!$auto_ban_enabled) {
		return false;
	}
	
	// 获取封禁时长（默认24小时）
	$ban_duration = isset($conf['guest_ban_duration']) ? intval($conf['guest_ban_duration']) : 86400;
	
	// 构建封禁原因
	$reason = sprintf('游客流量超限：已用 %s / 限制 %s', 
		size_format($traffic_used), 
		size_format($traffic_limit)
	);
	
	$extra_data = [
		'traffic_used' => $traffic_used,
		'traffic_limit' => $traffic_limit,
		'trigger_count' => 1
	];
	
	return ban_ip($ip, 'auto', $reason, $ban_duration, 0, $extra_data);
}

/**
 * 检查并处理游客流量限制（包含自动封禁逻辑）
 * @param string $ip IP地址
 * @return array ['code'=>0/1, 'msg'=>'', 'banned'=>true/false]
 */
function check_guest_quota_with_ban($ip) {
	// 只检查IP是否被封禁，不再检查流量配额
	// 流量控制已通过IP封禁机制实现
	$ban_check = check_ip_banned($ip);
	if ($ban_check['banned']) {
		$ban_end_text = $ban_check['ban_end'] ? date('Y-m-d H:i:s', strtotime($ban_check['ban_end'])) : '永久';
		return [
			'code' => 1,
			'msg' => '您的IP已被临时封禁，原因：' . $ban_check['reason'] . '，解封时间：' . $ban_end_text,
			'banned' => true,
			'ban_info' => $ban_check
		];
	}
	
	return ['code' => 0, 'banned' => false];
}

function cleanup_old_traffic_logs($retention_days = null) {
	global $conf, $DB;
	
	if ($retention_days === null) {
		$retention_days = isset($conf['traffic_log_retention_days']) ? intval($conf['traffic_log_retention_days']) : 90;
	}
	
	if ($retention_days <= 0) {
		return 0;
	}
	
	$cutoff_date = date('Y-m-d H:i:s', time() - ($retention_days * 86400));
	
	$sql = "DELETE FROM pre_traffic_log WHERE created_at < :cutoff_date";
	$params = [':cutoff_date' => $cutoff_date];
	
	return $DB->exec($sql, $params);
}

function detect_traffic_anomaly($uid = null, $ip = null, $custom_window = null, $custom_count = null, $custom_avg = null) {
	global $conf, $DB;
	
	// 获取配置参数
	$window_minutes = $custom_window !== null ? $custom_window : (isset($conf['traffic_anomaly_window']) ? intval($conf['traffic_anomaly_window']) : 60);
	$count_threshold = $custom_count !== null ? $custom_count : (isset($conf['traffic_anomaly_count']) ? intval($conf['traffic_anomaly_count']) : 50);
	$avg_threshold_mb = $custom_avg !== null ? $custom_avg : (isset($conf['traffic_anomaly_avg_size']) ? intval($conf['traffic_anomaly_avg_size']) : 100);
	
	// 如果都设置为0，则关闭检测
	if ($count_threshold <= 0 && $avg_threshold_mb <= 0) {
		return [
			'is_anomaly' => false,
			'access_count' => 0,
			'total_traffic' => 0,
			'avg_traffic' => 0,
			'window_minutes' => $window_minutes,
			'count_threshold' => $count_threshold,
			'avg_threshold' => $avg_threshold_mb * 1048576
		];
	}
	
	$window_seconds = $window_minutes * 60;
	$time_ago = date('Y-m-d H:i:s', time() - $window_seconds);
	
	$sql = "SELECT COUNT(*) as access_count, SUM(traffic_size) as total_traffic 
			FROM pre_traffic_log 
			WHERE created_at >= :time_ago";
	
	$params = [':time_ago' => $time_ago];
	
	if ($uid > 0) {
		$sql .= " AND visitor_uid = :uid";
		$params[':uid'] = $uid;
	} elseif ($ip) {
		$sql .= " AND visitor_uid = 0 AND visitor_ip = :ip";
		$params[':ip'] = $ip;
	}
	
	$result = $DB->getRow($sql, $params);
	
	if (!$result) {
		return false;
	}
	
	$access_count = intval($result['access_count']);
	$total_traffic = intval($result['total_traffic']);
	
	$avg_traffic = $access_count > 0 ? $total_traffic / $access_count : 0;
	$avg_threshold_bytes = $avg_threshold_mb * 1048576;
	
	// 判断是否异常
	$is_anomaly = false;
	$anomaly_reason = '';
	
	if ($count_threshold > 0 && $access_count > $count_threshold) {
		$is_anomaly = true;
		$anomaly_reason = '访问次数超限';
	}
	
	if ($avg_threshold_mb > 0 && $avg_traffic > $avg_threshold_bytes) {
		$is_anomaly = true;
		$anomaly_reason = ($anomaly_reason ? $anomaly_reason . '、' : '') . '平均流量超限';
	}
	
	return [
		'is_anomaly' => $is_anomaly,
		'access_count' => $access_count,
		'total_traffic' => $total_traffic,
		'avg_traffic' => $avg_traffic,
		'window_minutes' => $window_minutes,
		'count_threshold' => $count_threshold,
		'avg_threshold' => $avg_threshold_bytes,
		'anomaly_reason' => $anomaly_reason
	];
}

function get_traffic_statistics($start_date = null, $end_date = null, $owner_uid = null, $visitor_uid = null) {
	global $DB;
	
	if ($start_date === null) {
		$start_date = date('Y-m-d 00:00:00', time() - 86400 * 7);
	}
	
	if ($end_date === null) {
		$end_date = date('Y-m-d 23:59:59');
	}
	
	$sql = "SELECT 
				COUNT(*) as total_requests,
				SUM(traffic_size) as total_traffic,
				AVG(traffic_size) as avg_traffic,
				COUNT(DISTINCT visitor_uid) as unique_visitors,
				COUNT(DISTINCT visitor_ip) as unique_ips,
				COUNT(DISTINCT owner_uid) as unique_owners
			FROM pre_traffic_log 
			WHERE created_at BETWEEN :start_date AND :end_date";
	
	$params = [
		':start_date' => $start_date,
		':end_date' => $end_date
	];
	
	if ($owner_uid !== null) {
		$sql .= " AND owner_uid = :owner_uid";
		$params[':owner_uid'] = $owner_uid;
	}
	
	if ($visitor_uid !== null) {
		$sql .= " AND visitor_uid = :visitor_uid";
		$params[':visitor_uid'] = $visitor_uid;
	}
	
	$result = $DB->getRow($sql, $params);
	
	return $result;
}

function generate_card_code() {
	return strtoupper(md5(uniqid(mt_rand(), true) . microtime() . random(16)));
}

function create_card($card_type, $storage_quota_mb, $traffic_quota_mb, $expire_days = null, $remark = null, $admin_uid = 0) {
	global $DB;

	$card_code = generate_card_code();
	$storage_quota = $storage_quota_mb > 0 ? $storage_quota_mb * 1048576 : 0;
	$traffic_quota = $traffic_quota_mb > 0 ? $traffic_quota_mb * 1048576 : 0;
	$expire_days = $expire_days > 0 ? intval($expire_days) : 0;

	$sql = "INSERT INTO `pre_card` (`card_code`, `card_type`, `storage_quota`, `traffic_quota`, `status`, `created_by`, `created_at`, `expire_days`, `remark`)
			VALUES (:card_code, :card_type, :storage_quota, :traffic_quota, 0, :created_by, NOW(), :expire_days, :remark)";

	$params = [
		':card_code' => $card_code,
		':card_type' => $card_type,
		':storage_quota' => $storage_quota,
		':traffic_quota' => $traffic_quota,
		':created_by' => $admin_uid,
		':expire_days' => $expire_days,
		':remark' => $remark
	];

	if($DB->exec($sql, $params)) {
		return $DB->lastInsertId();
	}
	return false;
}

function validate_card($card_code, $uid = 0) {
	global $DB;

	$sql = "SELECT * FROM `pre_card` WHERE `card_code`=:card_code AND `status`=0 LIMIT 1";
	$card = $DB->getRow($sql, [':card_code' => $card_code]);

	if(!$card) {
		return ['code' => 1, 'msg' => '卡密不存在或已使用'];
	}

	if($card['status'] == 1) {
		return ['code' => 3, 'msg' => '卡密已被使用'];
	}

	if($card['status'] == 2) {
		return ['code' => 4, 'msg' => '卡密已被禁用'];
	}

	if($card['used_time'] && $card['expire_days'] > 0) {
		$expire_timestamp = strtotime($card['used_time']) + $card['expire_days'] * 86400;
		if($expire_timestamp < time()) {
			return ['code' => 2, 'msg' => '卡密已过期'];
		}
	}

	return ['code' => 0, 'card' => $card];
}

function redeem_card($card_code, $uid) {
	global $DB;
	
	$validation = validate_card($card_code, $uid);
	if($validation['code'] !== 0) {
		return $validation;
	}
	
	$card = $validation['card'];
	
	$DB->beginTransaction();
	
	try {
		// 获取当前用户信息以计算有效期
		$user = $DB->getRow("SELECT * FROM pre_user WHERE uid=:uid FOR UPDATE", [':uid' => $uid]);
		if(!$user) throw new \Exception('用户不存在');

		$updates = [];
		$params = [':uid' => $uid];
		$storage_bytes = 0;
		$traffic_bytes = 0;
		$now = time();

		// VIP会员激活：将会员等级设为1
		if($card['card_type'] == 'vip') {
			$updates[] = "`level` = :level";
			$params[':level'] = 1;
		}
		
		if($card['storage_quota'] > 0) {
			$storage_bytes = intval($card['storage_quota']);
			
			// 处理过期逻辑
			$current_expire = !empty($user['u_storage_expire']) ? strtotime($user['u_storage_expire']) : 0;
			$current_quota = $user['u_storage_quota'];
			
			// 如果已过期，先清空原有配额
			if($current_expire > 0 && $current_expire < $now){
				$current_quota = 0;
				$current_expire = 0; // 视为无有效期重新开始
			}

			// 计算新配额
			$new_quota = $current_quota + $storage_bytes;
			$updates[] = "u_storage_quota = :storage_quota";
			$params[':storage_quota'] = $new_quota;

			// 计算新有效期
			if($card['expire_days'] > 0){
				if($current_expire > $now){
					$new_expire_time = $current_expire + $card['expire_days'] * 86400;
				}else{
					$new_expire_time = $now + $card['expire_days'] * 86400;
				}
				$updates[] = "u_storage_expire = :storage_expire";
				$params[':storage_expire'] = date('Y-m-d H:i:s', $new_expire_time);
			} elseif ($card['expire_days'] === 0) {
				// 0表示永久，设置为NULL? 或者保持原样？
				// 如果卡密是永久的，这里设置为NULL表示永久
				// 但如果之前有有效期，现在变成永久，那之前的有效期限制就取消了
				// 这里假设expire_days=0意为“增加永久配额”或“转换为永久”
				// 商业逻辑上，通常永久卡会覆盖有效期
				$updates[] = "u_storage_expire = NULL";
			}
		}
		
		if($card['traffic_quota'] > 0) {
			$traffic_bytes = intval($card['traffic_quota']);
			
			// 处理过期逻辑
			$current_expire = !empty($user['u_traffic_expire']) ? strtotime($user['u_traffic_expire']) : 0;
			$current_quota = $user['u_traffic_quota'];
			
			// 如果已过期，先清空原有配额
			if($current_expire > 0 && $current_expire < $now){
				$current_quota = 0;
				$current_expire = 0;
			}

			// 计算新配额
			$new_quota = $current_quota + $traffic_bytes;
			$updates[] = "u_traffic_quota = :traffic_quota";
			$params[':traffic_quota'] = $new_quota;

			// 计算新有效期
			if($card['expire_days'] > 0){
				if($current_expire > $now){
					$new_expire_time = $current_expire + $card['expire_days'] * 86400;
				}else{
					$new_expire_time = $now + $card['expire_days'] * 86400;
				}
				$updates[] = "u_traffic_expire = :traffic_expire";
				$params[':traffic_expire'] = date('Y-m-d H:i:s', $new_expire_time);
			} elseif ($card['expire_days'] === 0) {
				$updates[] = "u_traffic_expire = NULL";
			}
		}
		
		if(!empty($updates)) {
			$sql = "UPDATE `pre_user` SET " . implode(', ', $updates) . " WHERE `uid`=:uid";
			$affected = $DB->exec($sql, $params);
			if($affected === false){
				throw new \Exception('用户配额更新失败');
			}
		} else {
			if($card['card_type'] != 'vip') {
				throw new \Exception('卡密未包含可用配额');
			}
		}

		$expire_time = $card['expire_days'] > 0 ? date('Y-m-d H:i:s', time() + $card['expire_days'] * 86400) : null;
		$affected2 = $DB->exec("UPDATE `pre_card` SET `status`=1, `used_uid`=:uid, `used_time`=NOW(), `expire_time`=:expire_time WHERE `card_code`=:card_code",
			[':uid' => $uid, ':card_code' => $card_code, ':expire_time' => $expire_time]);
		if($affected2 === false || $affected2 < 1){
			throw new \Exception('卡密状态更新失败');
		}
		
		$DB->commit();
		
		$result = [
			'code' => 0, 
			'msg' => '卡密兑换成功',
			'storage_added' => $card['storage_quota'],
			'traffic_added' => $card['traffic_quota']
		];
		if($card['card_type'] == 'vip') {
			$result['level_upgraded'] = true;
			$result['msg'] = 'VIP会员激活成功';
		}
		
		return $result;
		
	} catch(Exception $e) {
		$DB->rollBack();
		return ['code' => -1, 'msg' => '兑换失败：' . $e->getMessage()];
	}
}

function get_user_resource_info($uid) {
	global $DB, $conf;
	
	$user = $DB->getRow("SELECT * FROM pre_user WHERE uid=:uid LIMIT 1", [':uid' => $uid]);
	if(!$user) {
		return false;
	}
	
	// 检查并自动重置流量
	check_user_quota($user);
	
	$traffic_quota = get_traffic_quota($uid);
	
	// 获取存储配额（统一配置，不再区分普通用户和VIP用户）
	$storage_quota_mb = isset($conf['user_storage_quota']) ? $conf['user_storage_quota'] : null;
	$storage_quota_total = ($storage_quota_mb !== null && $storage_quota_mb > 0) ? $storage_quota_mb * 1048576 : 0;
	$user_storage_extra = (isset($user['u_storage_quota']) ? $user['u_storage_quota'] : 0);
	$storage_quota_total += $user_storage_extra;
	
	return [
		'uid' => $user['uid'],
		'username' => $user['username'],
		'nickname' => $user['nickname'],
		'level' => $user['level'],
		'storage_used' => $user['storage_used'],
		'traffic_used' => $user['traffic_used'],
		'storage_quota' => $storage_quota_total,
		'storage_quota_user' => $user_storage_extra,
		'traffic_quota' => $traffic_quota['quota_limit'],
		'traffic_quota_user' => isset($user['u_traffic_quota']) ? $user['u_traffic_quota'] : 0,
		'storage_percentage' => $storage_quota_total > 0 ? round(($user['storage_used'] / $storage_quota_total) * 100, 2) : 0,
		'traffic_percentage' => $traffic_quota['quota_percentage'],
		'storage_remaining' => $storage_quota_total > 0 ? max(0, $storage_quota_total - $user['storage_used']) : -1,
		'traffic_remaining' => $traffic_quota['quota_limit'] > 0 ? max(0, $traffic_quota['quota_limit'] - $user['traffic_used']) : -1,
		'storage_unlimited' => $storage_quota_total == 0,
		'traffic_unlimited' => $traffic_quota['quota_limit'] == 0,
		'storage_expire' => isset($user['u_storage_expire']) ? $user['u_storage_expire'] : null,
		'traffic_expire' => isset($user['u_traffic_expire']) ? $user['u_traffic_expire'] : null
	];
}

function get_card_list($status = null, $card_type = null, $page = 1, $page_size = 20) {
	global $DB;
	
	$sql = "SELECT * FROM `pre_card` WHERE 1=1";
	$params = [];
	
	if($status !== null) {
		$sql .= " AND `status`=:status";
		$params[':status'] = $status;
	}
	
	if($card_type !== null) {
		$sql .= " AND `card_type`=:card_type";
		$params[':card_type'] = $card_type;
	}
	
	$sql .= " ORDER BY `created_at` DESC";
	
	$offset = ($page - 1) * $page_size;
	$sql .= " LIMIT " . intval($offset) . ", " . intval($page_size);

	$cards = $DB->getAll($sql, $params);

	$count_sql = str_replace("SELECT *", "SELECT COUNT(*) as total", $sql);
	$count_sql = preg_replace('/LIMIT\s+\d+,\s*\d+/i', '', $count_sql);
	$total = $DB->getColumn($count_sql, $params);
	
	return [
		'cards' => $cards,
		'total' => intval($total),
		'page' => $page,
		'page_size' => $page_size,
		'total_pages' => ceil(intval($total) / $page_size)
	];
}

function disable_card($card_id, $admin_uid = 0) {
	global $DB;
	
	$sql = "UPDATE `pre_card` SET `status`=2 WHERE `id`=:id";
	return $DB->exec($sql, [':id' => $card_id]);
}

function delete_card($card_id) {
	global $DB;
	
	$sql = "DELETE FROM `pre_card` WHERE `id`=:id AND `status`=0";
	return $DB->exec($sql, [':id' => $card_id]);
}

/**
 * 专用的流量配额重置函数
 * 避免在每次配额检查时都触发重置逻辑
 */
function reset_traffic_quota_if_needed($uid) {
	global $DB, $conf;
	
	if (empty($uid)) return false;
	
	$reset_interval = isset($conf['traffic_quota_reset_interval']) ? intval($conf['traffic_quota_reset_interval']) : 30;
	if ($reset_interval <= 0) return false; // 禁用自动重置
	
	// 获取用户信息
	$user = $DB->getRow("SELECT uid, traffic_used, last_traffic_reset_time FROM pre_user WHERE uid = :uid", [':uid' => $uid]);
	if (!$user) return false;
	
	$last_reset = $user['last_traffic_reset_time'];
	if (!$last_reset) {
		// 首次设置重置时间
		$DB->exec("UPDATE pre_user SET last_traffic_reset_time = NOW() WHERE uid = :uid", [':uid' => $uid]);
		return false;
	}
	
	$last_reset_time = strtotime($last_reset);
	$current_time = time();
	$seconds_passed = $current_time - $last_reset_time;
	$interval_seconds = $reset_interval * 86400;
	
	// 只有达到或超过重置周期才重置
	if ($seconds_passed >= $interval_seconds) {
		// 获取流量配额
		$traffic_quota_mb = isset($conf['user_traffic_quota']) ? floatval($conf['user_traffic_quota']) : 0;
		$traffic_quota_bytes = $traffic_quota_mb * 1048576;
		
		// 计算溢出流量
		$current_usage = $user['traffic_used'];
		$excess_usage = max(0, $current_usage - $traffic_quota_bytes);
		
		// 执行重置
		$sql_updates = ["traffic_used = 0", "last_traffic_reset_time = NOW()"];
		$sql_params = [':uid' => $uid];
		
		if ($excess_usage > 0) {
			$sql_updates[] = "u_traffic_quota = GREATEST(0, CAST(u_traffic_quota AS SIGNED) - :excess)";
			$sql_params[':excess'] = $excess_usage;
		}
		
		$result = $DB->exec("UPDATE pre_user SET " . implode(', ', $sql_updates) . " WHERE uid = :uid", $sql_params);
		
		// 记录重置日志（可选）
		if ($result) {
			error_log("Traffic quota reset for user {$uid}: {$current_usage} bytes used, {$excess_usage} excess bytes deducted, interval: {$reset_interval} days");
		}
		
		return $result !== false;
	}
	
	return false;
}

/**
 * 获取流量重置状态信息
 */
function get_traffic_reset_status($uid) {
	global $DB, $conf;
	
	if (empty($uid)) return null;
	
	$reset_interval = isset($conf['traffic_quota_reset_interval']) ? intval($conf['traffic_quota_reset_interval']) : 30;
	
	$user = $DB->getRow("SELECT uid, traffic_used, last_traffic_reset_time FROM pre_user WHERE uid = :uid", [':uid' => $uid]);
	if (!$user) return null;
	
	$last_reset = $user['last_traffic_reset_time'];
	$status = [
		'reset_interval_days' => $reset_interval,
		'last_reset_time' => $last_reset,
		'traffic_used' => $user['traffic_used'],
		'next_reset_time' => null,
		'days_until_reset' => null
	];
	
	if ($last_reset && $reset_interval > 0) {
		$last_reset_time = strtotime($last_reset);
		$next_reset_time = $last_reset_time + ($reset_interval * 86400);
		$status['next_reset_time'] = date('Y-m-d H:i:s', $next_reset_time);
		$status['days_until_reset'] = max(0, ceil(($next_reset_time - time()) / 86400));
	}
	
	return $status;
}
