Files
Leantime/dev/DWGViewer/api/model.php
wangruiguo d647428529
Some checks failed
Security Audit / Composer Security Audit (push) Has been cancelled
Security Audit / NPM Security Audit (push) Has been cancelled
Security Audit / CodeQL Analysis (push) Has been cancelled
OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
2026-09-03 18:49:20 +08:00

81 lines
3.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* DWG/DXF 查看器 - 图纸读取接口
* 校验令牌 → .enc 解密 / 原文件原样 → 流式返回。
* SECRET 必须与 token.php、upload.php 一致。
*/
$SECRET = 'be323d1b90bd2fda624083d4d1d716b7';
// 与 upload.php 保持一致的私有目录配置:图纸放 Web 目录外,web 里根本没有文件可下载
$PRIVATE_DIR = '/volume1/dwgviewer_private';
function json_out($code, $obj) {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($obj, JSON_UNESCAPED_UNICODE);
exit;
}
$f = $_GET['f'] ?? '';
$t = $_GET['t'] ?? '';
$e = (int)($_GET['e'] ?? 0);
if (!$f || !$t || !preg_match('/\.(dwg|dxf|enc)$/i', $f)) json_out(400, ['error' => 'bad params']);
if (strpos($f, '..') !== false) json_out(400, ['error' => 'bad path']);
$sig = substr(hash_hmac('sha256', $f . '|' . $e, $SECRET), 0, 16);
if (!hash_equals($sig, $t) || time() > $e) json_out(403, ['error' => 'invalid or expired token']);
$root = realpath(__DIR__ . '/..');
if ($PRIVATE_DIR !== '') {
// 私有目录优先(嵌入链接写纯文件名即可);目录不存在时退回站内路径,
// 让留在 web 目录里的样例图纸按原路径继续可用(这些文件本来就允许直链,
// 退回不新增暴露面)。要保护的图纸必须移出 web 目录放进私有目录。
$full = realpath($PRIVATE_DIR . '/' . basename($f));
$ok = $full && strpos($full, realpath($PRIVATE_DIR)) === 0 && is_file($full);
if (!$ok) {
$full = realpath($root . '/' . $f);
$ok = $full && strpos($full, $root) === 0 && is_file($full);
}
} else {
$full = realpath($root . '/' . $f);
$ok = $full && strpos($full, $root) === 0 && is_file($full);
}
if (!$ok) json_out(404, ['error' => 'not found']);
$data = file_get_contents($full);
$origName = basename($f);
if (preg_match('/\.enc$/i', $f)) {
$nonce = substr($data, 0, 16);
$key = hash('sha256', $SECRET . $nonce, true);
$body = substr($data, 16);
$out = '';
for ($i = 0; $i < strlen($body); $i++) {
$out .= $body[$i] ^ $key[$i % strlen($key)];
}
// 加密体头部藏着原文件名(落盘名是随机的,链接里看不出是哪张图)
if (strlen($out) > 2) {
$nl = unpack('n', substr($out, 0, 2))[1];
if ($nl > 0 && $nl <= 255 && strlen($out) > 2 + $nl) {
$origName = substr($out, 2, $nl);
$out = substr($out, 2 + $nl);
}
}
$data = $out;
}
header('X-Filename: ' . rawurlencode($origName));
header('Access-Control-Expose-Headers: X-Filename');
// 上传时已经 gzip 过,解密后就是 gzip 流,直接标 Content-Encoding 让浏览器解;
// 明文文件(如 dxf)在线压一次,DXF 文本能压到 1/8 左右
$send = $data;
if (substr($send, 0, 2) === "\x1f\x8b") {
header('Content-Encoding: gzip');
} elseif (!ini_get('zlib.output_compression') && strlen($data) > 1024) {
$gz = gzencode($data, 6);
if ($gz !== false) { $send = $gz; header('Content-Encoding: gzip'); }
}
header('Content-Type: application/octet-stream');
header('Content-Length: ' . strlen($send));
header('Cache-Control: no-store');
header('Content-Disposition: inline');
echo $send;