<?php
/**
 * 炎酷CMS - 内容处理公共库（后台用）
 * yk_content_localize_images()  自动上传内容中的外部图片到 uploads
 * yk_content_strip_urls()       删除内容中的网址
 * yk_article_upload_paths()     提取文章行引用的 uploads 文件
 * yk_delete_upload_files()      删除 uploads 物理文件与 yk_upload 记录
 */
defined('YKCMS') or exit('Access Denied');

require_once SITE_PATH . '/public/api/upload_lib.php';

/** 判断 URL 是否为公网地址（SSRF 防护：拒绝内网/保留段/localhost） */
function yk_url_is_public($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;
    // 本站域名不算外部图片，无需转存
    if (!empty($_SERVER['HTTP_HOST']) && strcasecmp($host, $_SERVER['HTTP_HOST']) === 0) return false;
    $ip = gethostbyname($host); // 解析失败时原样返回
    if (!filter_var($ip, FILTER_VALIDATE_IP)) return false;
    return (bool)filter_var($ip, FILTER_VALIDATE_IP,
        FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
}

/**
 * 下载一张远程图片并按上传安全规则落盘（复用 upload_lib 校验）
 * @return string|false 成功返回相对路径 uploads/YYYYMMDD/xxx.ext，失败 false
 */
function yk_fetch_remote_image($url) {
    if (!yk_url_is_public($url)) return false;
    $maxBytes = yk_up_max_bytes();
    $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);
    $code  = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $ctype = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);
    if ($ok === false || $code !== 200 || $buf === '' || stripos($ctype, 'image/') !== 0) return false;

    $info = @getimagesizefromstring($buf);
    if ($info === false || empty($info['mime'])) return false;
    $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)) return false;

    $tmp = tempnam(sys_get_temp_dir(), 'yk_');
    if ($tmp === false || file_put_contents($tmp, $buf) === false) return false;
    $err = '';
    $ret = yk_up_store($tmp, $ext, basename(parse_url($url, PHP_URL_PATH) ?: 'remote.' . $ext), '编辑器', false, $err);
    if (is_file($tmp)) @unlink($tmp);
    return $ret === false ? false : $ret['url'];
}

/**
 * 自动上传内容中的外部图片：把 <img src="http(s)://外链"> 转存到本站 uploads 并替换 src
 * 本不合法/下载失败的图片保持原样，不影响保存
 */
function yk_content_localize_images($content) {
    if (strpos($content, 'http') === false) return $content;
    return preg_replace_callback(
        '/<img([^>]+?)src=["\'](https?:\/\/[^"\']+)["\']([^>]*)>/i',
        function ($m) {
            $local = yk_fetch_remote_image($m[2]);
            if ($local === false) return $m[0];
            return '<img' . $m[1] . 'src="' . $local . '"' . $m[3] . '>';
        },
        $content
    );
}

/**
 * 删除内容中的网址（在外部图片转存之后调用，本站相对路径不受影响）：
 * 移除 http(s)://... 与 www.... 形式的链接文本与外链属性值
 */
function yk_content_strip_urls($content) {
    $content = preg_replace('#https?://[^\s"\'<>]+#i', '', $content);
    $content = preg_replace('#\bwww\.[^\s"\'<>]+#i', '', $content);
    return $content;
}

/**
 * 按表单选项处理内容（文章/单页保存共用）：
 * 勾选 opt_auto_img 先自动上传外部图片，勾选 opt_strip_url 再删除内容中的网址
 */
function yk_content_prepare($content) {
    if (!empty($_POST['opt_auto_img'])) {
        $content = yk_content_localize_images($content);
    }
    if (!empty($_POST['opt_strip_url'])) {
        $content = yk_content_strip_urls($content);
    }
    return $content;
}

/**
 * 提取文章行引用到的 uploads 相对路径（thumb + photos 图集 + 内容图片）
 * @return array 去重后的相对路径数组（uploads/YYYYMMDD/xxx.ext）
 */
function yk_article_upload_paths($row) {
    $paths = array();
    $pick = function ($p) use (&$paths) {
        $p = trim((string)$p);
        // 兼容相对 uploads/... 与绝对 /uploads/... 两种写法
        if (preg_match('#(?:^|/)(uploads/\d{8}/[^\s"\'<>]+)$#i', $p, $m)) {
            $paths[$m[1]] = true;
        }
    };
    $pick(isset($row['thumb']) ? $row['thumb'] : '');
    $photos = json_decode(isset($row['photos']) ? $row['photos'] : '', true);
    if (is_array($photos)) {
        foreach ($photos as $p) $pick($p);
    }
    if (!empty($row['content']) && preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/i', $row['content'], $mm)) {
        foreach ($mm[1] as $src) $pick($src);
    }
    return array_keys($paths);
}

/**
 * 删除一组 uploads 相对路径对应的物理文件与 yk_upload 记录
 * 仅允许删除 public/uploads/ 目录内文件（realpath 防穿越）
 * @return int 实际删除的文件数
 */
function yk_delete_upload_files($paths) {
    $deleted = 0;
    $uploadRoot = realpath(SITE_PATH . '/public/uploads');
    if (!$uploadRoot) return 0;
    foreach ($paths as $rel) {
        $filePath = realpath(SITE_PATH . '/public/' . ltrim($rel, '/'));
        if ($filePath && strpos($filePath, $uploadRoot) === 0 && is_file($filePath)) {
            if (@unlink($filePath)) $deleted++;
        }
        db_exec('DELETE FROM ' . DB_PREFIX . 'upload WHERE filepath = ?', array($rel));
    }
    return $deleted;
}
