文章详情

返回首页

阅后即焚-index.php

分享文章 作者: Ws01 创建时间: 2025-11-24 📝 字数: 27,402 字 👁️ 阅读: 19 次

// 确保在文件最开头启动会话
if (sessionstatus() === PHPSESSION_NONE) {
session_start();
}

// 设置默认时区
datedefaulttimezone_set('Asia/Shanghai');

// 错误报告设置
errorreporting(EALL);
iniset('displayerrors', 0);
iniset('logerrors', 1);
iniset('errorlog', DIR . '/php_errors.log');

// 配置
$CONFIG = [
'db_path' => DIR . '/messages.db',
'max_size' => 10000,
'max_lifetime' => 86400,
'cleanup_chance' => 20,
'encrypt_key' => 'your-secret-key-here' // 请更改为您的密钥
];

// 初始化数据库
function initDatabase($dbPath) {
try {
$db = new SQLite3($dbPath);
$db->enableExceptions(true);

// 创建消息表
$db->exec("
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
password TEXT,
created_at INTEGER NOT NULL,
expire_at INTEGER NOT NULL,
viewed INTEGER DEFAULT 0
)
");

// 创建索引
$db->exec("CREATE INDEX IF NOT EXISTS idxexpireat ON messages(expire_at)");
$db->exec("CREATE INDEX IF NOT EXISTS idx_viewed ON messages(viewed)");

return $db;
} catch (Exception $e) {
error_log('Database initialization error: ' . $e->getMessage());
throw new Exception('无法初始化数据库');
}
}

// 获取数据库连接
function getDatabase() {
global $CONFIG;
static $db = null;

if ($db === null) {
$db = initDatabase($CONFIG['db_path']);
}

return $db;
}

// 统一JSON响应函数
function sendJsonResponse($success, $message = '', $data = []) {
header('Content-Type: application/json');
$response = [
'success' => $success,
'message' => $message,
'data' => $data
];
echo jsonencode($response, JSONUNESCAPED_UNICODE);
exit;
}

// 加密函数
function encrypt($data, $key) {
$iv = opensslrandompseudobytes(opensslcipherivlength('aes-256-cbc'));
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($iv . $encrypted);
}

// 解密函数
function decrypt($data, $key) {
$data = base64_decode($data);
$iv = substr($data, 0, opensslcipheriv_length('aes-256-cbc'));
$data = substr($data, opensslcipheriv_length('aes-256-cbc'));
return openssl_decrypt($data, 'aes-256-cbc', $key, 0, $iv);
}

// 获取当前URL
function getCurrentUrl() {
$protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
return $protocol . $SERVER['HTTPHOST'] . strtok($SERVER['REQUESTURI'], '?');
}

// 清理过期消息
function cleanupOldMessages() {
global $CONFIG;

if (rand(1, 100) > $CONFIG['cleanup_chance']) {
return;
}

try {
$db = getDatabase();
$stmt = $db->prepare("DELETE FROM messages WHERE expire_at < ?");
$stmt->bindValue(1, time(), SQLITE3_INTEGER);
$stmt->execute();

// 清理已查看的消息(超过1小时)
$stmt = $db->prepare("DELETE FROM messages WHERE viewed = 1 AND created_at < ?");
$stmt->bindValue(1, time() - 3600, SQLITE3_INTEGER);
$stmt->execute();

} catch (Exception $e) {
error_log('Cleanup error: ' . $e->getMessage());
}
}

// 处理AJAX请求
if ($SERVER['REQUESTMETHOD'] === 'POST' &&
isset($SERVER['HTTPXREQUESTEDWITH']) &&
strtolower($SERVER['HTTPXREQUESTEDWITH']) === 'xmlhttprequest') {

try {
// 获取并清理输入
$content = trim($_POST['content'] ?? '');
$password = trim($POST['msgpassword'] ?? '');
$expirehours = max(1, min(720, intval($POST['expire_hours'] ?? 24)));

// 验证输入
if (empty($content)) {
sendJsonResponse(false, '消息内容不能为空');
}

if (strlen($content) > $CONFIG['max_size']) {
sendJsonResponse(false, '消息过长,最多允许' . $CONFIG['max_size'] . '个字符');
}

// 处理特殊字符
$content = htmlspecialchars_decode($content);

// 生成唯一ID
$id = md5(uniqid(mt_rand(), true));

// 准备存储数据
$encryptedContent = encrypt($content, $CONFIG['encrypt_key']);
$createdAt = time();
$expireAt = $createdAt + ($expire_hours * 3600);
$hashedPassword = !empty($password) ? passwordhash($password, PASSWORDDEFAULT) : null;

// 存储消息到数据库
$db = getDatabase();
$stmt = $db->prepare("
INSERT INTO messages (id, content, password, createdat, expireat, viewed)
VALUES (?, ?, ?, ?, ?, 0)
");

$stmt->bindValue(1, $id, SQLITE3_TEXT);
$stmt->bindValue(2, $encryptedContent, SQLITE3_TEXT);
$stmt->bindValue(3, $hashedPassword, SQLITE3_TEXT);
$stmt->bindValue(4, $createdAt, SQLITE3_INTEGER);
$stmt->bindValue(5, $expireAt, SQLITE3_INTEGER);

if (!$stmt->execute()) {
throw new Exception('无法保存消息到数据库');
}

// 生成访问链接
$url = getCurrentUrl() . '?id=' . $id;

// 返回成功响应
sendJsonResponse(true, '安全链接已生成', [
'url' => $url,
'expire_time' => date('Y-m-d H:i:s', $expireAt),
'generation_time' => date('Y-m-d H:i:s', $createdAt)
]);
} catch (Exception $e) {
error_log('Error: ' . $e->getMessage());
sendJsonResponse(false, '服务器内部错误: ' . $e->getMessage());
}
}

// 处理消息查看请求
if (isset($_GET['id'])) {
handleViewRequest();
exit;
}

// 处理表单提交
if ($SERVER['REQUESTMETHOD'] === 'POST') {
handlePostRequest();
}

// 显示主页面
displayHomePage();

// 处理消息查看请求
function handleViewRequest() {
global $CONFIG;

// 安全过滤ID
$id = pregreplace('/[^a-f0-9]/', '', $GET['id']);
if (strlen($id) !== 32) {
displayError('无效的消息ID');
return;
}

try {
$db = getDatabase();
$stmt = $db->prepare("SELECT * FROM messages WHERE id = ? AND viewed = 0");
$stmt->bindValue(1, $id, SQLITE3_TEXT);
$result = $stmt->execute();
$message = $result->fetchArray(SQLITE3_ASSOC);

if (!$message) {
displayError('消息不存在或已被查看');
return;
}

// 检查是否过期
if ($message['expire_at'] < time()) {
// 删除过期消息
$stmt = $db->prepare("DELETE FROM messages WHERE id = ?");
$stmt->bindValue(1, $id, SQLITE3_TEXT);
$stmt->execute();

displayError('消息已过期');
return;
}

// 检查密码
if (!empty($message['password']) && !isset($_POST['password'])) {
displayPasswordForm($id);
return;
}

if (!empty($message['password']) && !passwordverify($POST['password'], $message['password'])) {
displayError('密码错误');
displayPasswordForm($id);
return;
}

// 解密内容
$content = decrypt($message['content'], $CONFIG['encrypt_key']);

// 标记消息为已查看
$stmt = $db->prepare("UPDATE messages SET viewed = 1 WHERE id = ?");
$stmt->bindValue(1, $id, SQLITE3_TEXT);
$stmt->execute();

// 显示消息
displayMessage($content);

// 随机清理旧消息
cleanupOldMessages();

} catch (Exception $e) {
error_log('View message error: ' . $e->getMessage());
displayError('服务器内部错误');
}
}

// 显示主页面
function displayHomePage() {
?>

阅后即焚

阅后即焚

最多允许10000个字符

type="number"
name="expire_hours"
id="expire_hours"
class="w-full px-3 py-2 border rounded-md focus:ring-1 focus:ring-green-500 focus:border-green-500"
value="24"
min="1"
max="720"
required
>

范围:1-720小时(30天)

type="password"
name="msg_password"
id="msg_password"
class="w-full px-3 py-2 border rounded-md focus:ring-1 focus:ring-green-500 focus:border-green-500"
placeholder="设置查看密码"
>

阅后即焚 ©

SERVER['HTTPHOST']; ?>

}

function displayMessage($content) {
?>

秘密消息 - 阅后即焚

秘密消息

此消息已被销毁,刷新页面将无法再次查看

}

// 显示错误页面
function displayError($message) {
?>

错误 - 阅后即焚

}

// 显示密码表单
function displayPasswordForm($id) {
?>

需要密码 - 阅后即焚

需要密码

此消息受密码保护,请输入密码查看

type="password"
name="password"
class="w-full px-3 py-2 border rounded-md focus:ring-1 focus:ring-green-500 focus:border-green-500"
required
>

阅后即焚 ©

SERVER['HTTPHOST']; ?>

}

// 处理表单提交(非AJAX)
function handlePostRequest() {
global $CONFIG;

$content = trim($_POST['content'] ?? '');
$password = trim($POST['msgpassword'] ?? '');
$expirehours = max(1, min(720, intval($POST['expire_hours'] ?? 24)));

if (empty($content)) {
displayError('消息内容不能为空');
return;
}

if (strlen($content) > $CONFIG['max_size']) {
displayError('消息过长,最多允许' . $CONFIG['max_size'] . '个字符');
return;
}

try {
// 生成唯一ID
$id = md5(uniqid(mt_rand(), true));

// 准备存储数据
$encryptedContent = encrypt($content, $CONFIG['encrypt_key']);
$createdAt = time();
$expireAt = $createdAt + ($expire_hours * 3600);
$hashedPassword = !empty($password) ? passwordhash($password, PASSWORDDEFAULT) : null;

// 存储消息到数据库
$db = getDatabase();
$stmt = $db->prepare("
INSERT INTO messages (id, content, password, createdat, expireat, viewed)
VALUES (?, ?, ?, ?, ?, 0)
");

$stmt->bindValue(1, $id, SQLITE3_TEXT);
$stmt->bindValue(2, $encryptedContent, SQLITE3_TEXT);
$stmt->bindValue(3, $hashedPassword, SQLITE3_TEXT);
$stmt->bindValue(4, $createdAt, SQLITE3_INTEGER);
$stmt->bindValue(5, $expireAt, SQLITE3_INTEGER);

if (!$stmt->execute()) {
throw new Exception('无法保存消息到数据库');
}

// 生成访问链接
$url = getCurrentUrl() . '?id=' . $id;

// 显示成功页面
displaySuccess($url, $expire_hours);

} catch (Exception $e) {
error_log('Post request error: ' . $e->getMessage());
displayError('服务器内部错误');
}
}

// 显示成功页面
function displaySuccess($url, $expire_hours) {
$_SESSION['result'] = [
'status' => 'success',
'message' => '安全链接已生成',
'url' => $url,
'expiretime' => date('Y-m-d H:i:s', time() + ($expirehours * 3600)),
'generation_time' => date('Y-m-d H:i:s')
];
header('Location: ' . getCurrentUrl());
exit;
}