<?php
/**
 * 炎酷CMS - UEditor 1.4.x 服务端（自研替代官方 php/ 后端）
 * GET  ?action=config        返回 UEditor 配置（公开）
 * POST ?action=uploadimage   图片上传（字段 file，需登录）
 * POST ?action=scrawlimage   涂鸦 base64 上传（需登录）
 * POST ?action=catchimage    抓取远程图片转存（source[]，需登录，SSRF 防护）
 * GET  ?action=listimage     图片管理分页（start/size，需登录）
 */
define('YKCMS', 1);
session_start();
require_once dirname(dirname(__DIR__)) . '/inc/functions.php';
require_once __DIR__ . '/upload_lib.php';

header('Content-Type: application/json; charset=utf-8');

function ue_json($arr) {
    echo json_encode($arr, JSON_UNESCAPED_UNICODE);
    exit;
}
function ue_fail($msg) {
    ue_json(array('state' => $msg));
}

$action = isset($_REQUEST['action']) ? trim($_REQUEST['action']) : '';
$maxBytes = yk_up_max_bytes();

// 白名单 => [".jpg", ...]
$allowFiles = array();
foreach (yk_up_allow_exts() as $e) {
    $allowFiles[] = '.' . $e;
}

/* ---------------- 配置（公开，UEditor 初始化需要） ---------------- */
if ($action === 'config') {
    ue_json(array(
        /* 上传图片 */
        'imageActionName'    => 'uploadimage',
        'imageFieldName'     => 'file',
        'imageMaxSize'       => $maxBytes,
        'imageAllowFiles'    => $allowFiles,
        'imageCompressEnable'=> true,
        'imageCompressBorder'=> 1600,
        'imageInsertAlign'   => 'none',
        'imageUrlPrefix'     => '',
        'imagePathFormat'    => 'uploads/{yyyy}{mm}{dd}/{time}{rand:6}',
        /* 涂鸦 */
        'scrawlActionName'   => 'scrawlimage',
        'scrawlFieldName'    => 'file',
        'scrawlMaxSize'      => $maxBytes,
        'scrawlUrlPrefix'    => '',
        /* 远程抓图 */
        'catcherActionName'  => 'catchimage',
        'catcherFieldName'   => 'source',
        'catcherMaxSize'     => $maxBytes,
        'catcherAllowFiles'  => $allowFiles,
        'catcherUrlPrefix'   => '',
        'catcherPathFormat'  => 'uploads/{yyyy}{mm}{dd}/{time}{rand:6}',
        /* 图片管理 */
        'imageManagerActionName'  => 'listimage',
        'imageManagerListSize'    => 24,
        'imageManagerUrlPrefix'   => '',
        'imageManagerInsertAlign' => 'none',
        'imageManagerAllowFiles'  => $allowFiles,
        /* 其他功能未启用 */
        'videoActionName'   => 'none',
        'fileActionName'    => 'none',
        'scrawlInsertAlign' => 'none',
    ));
}

/* 除 config 外均需登录 */
if (empty($_SESSION['yk_admin_id'])) {
    ue_fail('未登录');
}

/* ---------------- 图片上传 ---------------- */
if ($action === 'uploadimage') {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_FILES['file'])) {
        ue_fail('无效的上传请求');
    }
    $file = $_FILES['file'];
    if (!isset($file['error']) || $file['error'] !== UPLOAD_ERR_OK || !is_uploaded_file($file['tmp_name'])) {
        ue_fail('文件上传失败');
    }
    if ($file['size'] <= 0 || $file['size'] > $maxBytes) {
        ue_fail('文件大小超出限制');
    }
    $err = '';
    $ext = yk_up_check_filename(isset($file['name']) ? $file['name'] : '', $err);
    if ($ext === false) {
        ue_fail($err);
    }
    if (!yk_up_check_image_content($file['tmp_name'], $ext, $err)) {
        ue_fail($err);
    }
    $ret = yk_up_store($file['tmp_name'], $ext, $file['name'], '编辑器', true, $err);
    if ($ret === false) {
        ue_fail($err);
    }
    ue_json(array(
        'state'    => 'SUCCESS',
        'url'      => '/' . $ret['url'], // 根绝对路径，避免编辑器按对话框相对路径解析
        'title'    => $ret['name'],
        'original' => $ret['name'],
        'size'     => $ret['size'],
    ));
}

/* ---------------- 涂鸦（base64） ---------------- */
if ($action === 'scrawlimage') {
    $b64 = isset($_POST['file']) ? $_POST['file'] : (isset($_POST['upfile']) ? $_POST['upfile'] : '');
    $b64 = preg_replace('/^data:image\/[a-z0-9.+-]+;base64,/i', '', trim((string)$b64));
    $bin = base64_decode($b64, true);
    if ($bin === false || strlen($bin) === 0) {
        ue_fail('涂鸦数据无效');
    }
    if (strlen($bin) > $maxBytes) {
        ue_fail('文件大小超出限制');
    }
    $info = @getimagesizefromstring($bin);
    if ($info === false || empty($info['mime'])) {
        ue_fail('文件内容不是有效图片');
    }
    $ext = '';
    foreach (yk_up_mime_map() as $m => $exts) {
        if ($m === $info['mime']) { $ext = $exts[0]; break; }
    }
    if ($ext === '' || !in_array($ext, yk_up_allow_exts(), true)) {
        ue_fail('不允许的图片类型');
    }
    $tmp = tempnam(sys_get_temp_dir(), 'ue_');
    if ($tmp === false || file_put_contents($tmp, $bin) === false) {
        ue_fail('临时文件写入失败');
    }
    $err = '';
    $ret = yk_up_store($tmp, $ext, 'scrawl.' . $ext, '编辑器', false, $err);
    if (is_file($tmp)) @unlink($tmp);
    if ($ret === false) {
        ue_fail($err);
    }
    ue_json(array(
        'state'    => 'SUCCESS',
        'url'      => '/' . $ret['url'],
        'title'    => $ret['name'],
        'original' => $ret['name'],
        'size'     => $ret['size'],
    ));
}

/* ---------------- 远程抓图（SSRF 防护） ---------------- */
if ($action === 'catchimage') {
    /** 解析 host 并判断是否公网地址 */
    $isPublicUrl = function ($url) {
        if (!preg_match('#^https?://#i', $url)) return false;
        $host = parse_url($url, PHP_URL_HOST);
        if (!is_string($host) || $host === '') return false;
        $host = strtolower(trim($host));
        if ($host === 'localhost' || substr($host, -6) === '.local') return false;
        $ip = gethostbyname($host); // 解析失败时原样返回
        if (!filter_var($ip, FILTER_VALIDATE_IP)) return false;
        // 同时排除私有段(10/8,172.16/12,192.168/16)与保留段(127/8,169.254/16,0.0.0.0 等)
        return (bool)filter_var($ip, FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
    };

    $sources = isset($_POST['source']) ? $_POST['source'] : (isset($_GET['source']) ? $_GET['source'] : array());
    if (!is_array($sources)) $sources = array($sources);
    if (!$sources) {
        ue_fail('参数为空');
    }

    $mimeToExt = array();
    foreach (yk_up_mime_map() as $m => $exts) {
        $mimeToExt[$m] = $exts[0];
    }

    $list = array();
    foreach ($sources as $url) {
        $url = trim((string)$url);
        if ($url === '' || !$isPublicUrl($url)) {
            $list[] = array('state' => '非法的图片来源', 'source' => $url);
            continue;
        }
        // curl 下载：不跟随重定向，限制 8 秒，超量即中断
        $buf = '';
        $ch = curl_init($url);
        curl_setopt_array($ch, array(
            CURLOPT_HEADER         => false,
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_PROTOCOLS      => CURLPROTO_HTTP | CURLPROTO_HTTPS,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_TIMEOUT        => 8,
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; YkCms/1.0)',
            CURLOPT_WRITEFUNCTION  => function ($ch, $data) use (&$buf, $maxBytes) {
                if (strlen($buf) + strlen($data) > $maxBytes) return 0; // 超限中断
                $buf .= $data;
                return strlen($data);
            },
        ));
        $ok        = curl_exec($ch);
        $httpCode  = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $ctype     = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        curl_close($ch);
        if ($ok === false || $httpCode !== 200 || $buf === '') {
            $list[] = array('state' => '下载远程图片失败', 'source' => $url);
            continue;
        }
        if (stripos($ctype, 'image/') !== 0) {
            $list[] = array('state' => '远程内容不是图片', 'source' => $url);
            continue;
        }
        $info = @getimagesizefromstring($buf);
        if ($info === false || empty($info['mime']) || !isset($mimeToExt[$info['mime']])) {
            $list[] = array('state' => '图片内容校验失败', 'source' => $url);
            continue;
        }
        $ext = $mimeToExt[$info['mime']];
        if (!in_array($ext, yk_up_allow_exts(), true)) {
            $list[] = array('state' => '不允许的图片类型', 'source' => $url);
            continue;
        }
        $tmp = tempnam(sys_get_temp_dir(), 'ue_');
        if ($tmp === false || file_put_contents($tmp, $buf) === false) {
            $list[] = array('state' => '临时文件写入失败', 'source' => $url);
            continue;
        }
        $err = '';
        $ret = yk_up_store($tmp, $ext, basename(parse_url($url, PHP_URL_PATH) ?: 'remote.' . $ext), '编辑器', false, $err);
        if (is_file($tmp)) @unlink($tmp);
        if ($ret === false) {
            $list[] = array('state' => $err, 'source' => $url);
            continue;
        }
        $list[] = array(
            'state'  => 'SUCCESS',
            'url'    => '/' . $ret['url'],
            'source' => $url,
            'title'  => $ret['name'],
            'size'   => $ret['size'],
        );
    }
    ue_json(array('state' => 'SUCCESS', 'list' => $list));
}

/* ---------------- 图片管理 ---------------- */
if ($action === 'listimage') {
    $start = max(0, (int)(isset($_GET['start']) ? $_GET['start'] : 0));
    $size  = (int)(isset($_GET['size']) ? $_GET['size'] : 24);
    $size  = $size > 0 ? min($size, 100) : 24;
    $total = (int)db_val('SELECT COUNT(*) FROM ' . DB_PREFIX . 'upload');
    $rows  = db_all('SELECT filepath, add_time FROM ' . DB_PREFIX . "upload
                     ORDER BY id DESC LIMIT $start, $size");
    $list = array();
    foreach ($rows as $r) {
        $list[] = array('url' => '/' . ltrim($r['filepath'], '/'), 'mtime' => (int)$r['add_time']);
    }
    ue_json(array('state' => 'SUCCESS', 'list' => $list, 'start' => $start, 'total' => $total));
}

ue_fail('未知的 action');
