-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathapi.php
More file actions
379 lines (319 loc) · 12.8 KB
/
Copy pathapi.php
File metadata and controls
379 lines (319 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
<?php
require 'config.php';
check_auth();
global $db;
$action = $_GET['action'] ?? '';
// 账号列表
if ($action === 'list') {
echo json_encode($db->query("SELECT * FROM accounts ORDER BY id DESC")->fetchAll(PDO::FETCH_ASSOC));
exit;
}
// 添加账号
if ($action === 'add') {
$raw = trim($_POST['raw_data'] ?? '');
$p = explode('----', $raw);
if (count($p) >= 4) {
$email = trim($p[0]);
$pass = trim($p[1]);
$cid = trim($p[2]);
$token = trim($p[3]);
$stmt = $db->prepare("INSERT INTO accounts (email,password,client_id,refresh_token,remark) VALUES (?,?,?,?,?)");
$stmt->execute([$email, $pass, $cid, $token, $_POST['remark']]);
echo json_encode(['status'=>'success']);
exit;
}
exit;
}
// 更新账号信息 (核心修复:解决编辑备注无效)
if ($action === 'update_account') {
$id = (int)($_POST['id'] ?? 0);
$email = trim($_POST['email'] ?? '');
$pass = trim($_POST['password'] ?? '');
$cid = trim($_POST['client_id'] ?? '');
$token = trim($_POST['refresh_token'] ?? '');
$remark = $_POST['remark'] ?? '';
if ($id > 0) {
$stmt = $db->prepare("UPDATE accounts SET email=?, password=?, client_id=?, refresh_token=?, remark=? WHERE id=?");
$stmt->execute([$email, $pass, $cid, $token, $remark, $id]);
echo json_encode(['status' => 'success']);
} else {
echo json_encode(['status' => 'error', 'message' => 'ID无效']);
}
exit;
}
/**
* 核心辅助函数:提取并解码邮件正文
* 处理 Base64, Quoted-Printable 以及 GBK 编码
*/
function extract_message_body($part) {
// 分割头部和正文(邮件协议中头部和正文由第一个空行分隔)
$parts = preg_split('/\r?\n\r?\n/', $part, 2);
if (count($parts) < 2) return '';
$header = $parts[0];
$content = trim($parts[1]);
// 1. 处理传输编码 (Transfer-Encoding)
if (preg_match('/Content-Transfer-Encoding:\s*base64/i', $header)) {
$content = base64_decode(str_replace(["\r", "\n"], '', $content));
} elseif (preg_match('/Content-Transfer-Encoding:\s*quoted-printable/i', $header)) {
$content = quoted_printable_decode($content);
}
// 2. 处理字符集编码 (Charset)
if (preg_match('/charset="?([^"\r\n;]+)"?/i', $header, $charMat)) {
$charset = strtoupper($charMat[1]);
if ($charset !== 'UTF-8' && $charset !== 'US-ASCII') {
$content = mb_convert_encoding($content, 'UTF-8', $charset);
}
}
return $content;
}
// =============================================================================
// 获取邮件逻辑 - 已修复 SEARCH 报错与乱码
// =============================================================================
if ($action === 'get_mails') {
$id = (int)$_GET['id'];
$acc = $db->query("SELECT * FROM accounts WHERE id=$id")->fetch(PDO::FETCH_ASSOC);
if (!$acc) {
echo json_encode(['value' => []]);
exit;
}
$email = trim($acc['email']);
$client_id = trim($acc['client_id']);
$refresh_token = trim($acc['refresh_token']);
// 刷新 AccessToken
$data = [
'client_id' => $client_id,
'grant_type' => 'refresh_token',
'refresh_token' => $refresh_token
];
$ch = curl_init('https://login.microsoftonline.com/consumers/oauth2/v2.0/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data)
]);
$res = curl_exec($ch);
curl_close($ch);
$res = json_decode($res, true);
if (empty($res['access_token'])) {
echo json_encode(['error' => 'Token失效,请重新获取账号']);
exit;
}
// 更新 Token
if (!empty($res['refresh_token']) && $res['refresh_token'] != $refresh_token) {
$db->prepare("UPDATE accounts SET refresh_token=? WHERE id=?")
->execute([$res['refresh_token'], $id]);
}
$access_token = $res['access_token'];
$messages = [];
$hosts = ['outlook.live.com', 'outlook.office365.com'];
foreach ($hosts as $host) {
$sock = fsockopen('ssl://' . $host, 993, $errno, $errstr, 10);
if (!$sock) continue;
fgets($sock, 1024);
$auth_str = base64_encode("user=$email\x01auth=Bearer $access_token\x01\x01");
fputs($sock, "A01 AUTHENTICATE XOAUTH2 $auth_str\r\n");
$login = fgets($sock, 1024);
if (!str_contains($login, 'A01 OK')) { fclose($sock); continue; }
fputs($sock, "A02 SELECT INBOX\r\n");
while ($line = fgets($sock, 1024)) { if (str_contains($line, 'A02 OK')) break; }
// --- 修复 SEARCH 部分,防止 Undefined variable $ids ---
fputs($sock, "A03 SEARCH ALL\r\n");
$ids = [];
while ($line = fgets($sock, 4096)) {
$line = trim($line);
if (preg_match('/^\* SEARCH (.*)/i', $line, $match)) {
$ids = array_filter(explode(' ', trim($match[1])));
}
if (str_contains($line, 'A03 OK')) break;
}
if (empty($ids)) { fclose($sock); continue; }
rsort($ids, SORT_NUMERIC);
$ids = array_slice($ids, 0, 15); // 获取最近15封
foreach ($ids as $mid) {
fputs($sock, "A04 FETCH $mid (RFC822)\r\n");
$rawMail = '';
while ($line = fgets($sock, 16384)) {
$rawMail .= $line;
// IMAP 响应结束标识
if (preg_match('/^A04 OK/m', $line)) break;
}
// 提取主题和发件人
preg_match('/^Subject:\s*(.*?)$/im', $rawMail, $subjMat);
$subject = isset($subjMat[1]) ? iconv_mime_decode(trim($subjMat[1]), 0, 'UTF-8') : '无主题';
preg_match('/^From:\s*(.*?)$/im', $rawMail, $fromMat);
$from = isset($fromMat[1]) ? iconv_mime_decode(trim($fromMat[1]), 0, 'UTF-8') : '未知发件人';
// 处理 Boundary
preg_match('/boundary="?([^"\r\n;]+)"?/is', $rawMail, $bndMat);
$boundary = $bndMat[1] ?? '';
$body = '';
if ($boundary) {
$parts = explode("--$boundary", $rawMail);
// 优先取 HTML
foreach ($parts as $part) {
if (stripos($part, 'Content-Type: text/html') !== false) {
$body = extract_message_body($part);
break;
}
}
// 没 HTML 取纯文本
if (empty($body)) {
foreach ($parts as $part) {
if (stripos($part, 'Content-Type: text/plain') !== false) {
$body = extract_message_body($part);
break;
}
}
}
} else {
// 非多段格式
$body = extract_message_body($rawMail);
}
$messages[] = [
'subject' => $subject,
'from' => ['emailAddress' => ['name' => $from]],
'body' => ['content' => $body]
];
}
fclose($sock);
if (!empty($messages)) break;
}
echo json_encode(['value' => $messages]);
exit;
}
// -------------------------- 保持不变的辅助功能 --------------------------
if ($action === 'delete') {
$id = $_POST['id'];
$db->prepare("DELETE FROM accounts WHERE id=?")->execute([$id]);
echo json_encode(['status'=>'success']);
exit;
}
if ($action === 'save_settings') {
// 1. 处理 TG 设置 (更新 settings 表)
foreach(['tg_token', 'tg_chatid'] as $k){
if(isset($_POST[$k])){
$db->prepare("UPDATE settings SET value=? WHERE key=?")->execute([$_POST[$k], $k]);
}
}
// 2. 处理管理员用户名 (更新 admin 表)
if (!empty($_POST['admin_user'])) {
// 假设你要修改 id 为 1 的那个管理员(即默认管理员)
$db->prepare("UPDATE admin SET username=? WHERE id=1")->execute([$_POST['admin_user']]);
}
// 3. 处理管理员密码 (更新 admin 表)
if (!empty($_POST['admin_pass'])) {
$new_pass = password_hash($_POST['admin_pass'], PASSWORD_DEFAULT);
$db->prepare("UPDATE admin SET password=? WHERE id=1")->execute([$new_pass]);
}
echo json_encode(['status'=>'success']);
exit;
}
if ($action === 'import') {
if(isset($_FILES['file'])){
$data = json_decode(file_get_contents($_FILES['file']['tmp_name']),true);
if($data){
foreach($data as $i){
$db->prepare("INSERT INTO accounts (email,password,client_id,refresh_token,remark) VALUES (?,?,?,?,?)")
->execute([$i['email'],$i['password'],$i['client_id'],$i['refresh_token'],$i['remark']]);
}
}
}
echo json_encode(['status'=>'success']);
exit;
}
if ($action === 'export') {
$data = $db->query("SELECT * FROM accounts")->fetchAll(PDO::FETCH_ASSOC);
// 生成文件名,带上当前日期
$filename = "outlook_accounts_" . date('Ymd_His') . ".json";
// 核心:设置响应头强制浏览器弹出下载框
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Pragma: no-cache');
header('Expires: 0');
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
if ($action === 'get_settings') {
// 获取 TG 设置
$q = $db->query("SELECT * FROM settings");
$data = [];
while ($r = $q->fetch()) $data[$r['key']] = $r['value'];
// 获取当前管理员用户名 (从 admin 表)
$admin = $db->query("SELECT username FROM admin WHERE id=1")->fetch();
$data['admin_user'] = $admin['username'] ?? 'admin';
echo json_encode($data);
exit;
}
// =============================================================================
// 退出登录
// =============================================================================
if ($action === 'logout') {
// 清空当前 Session 数据
$_SESSION = [];
// 删除 Session Cookie
// 防止浏览器继续保存登录状态
if (isset($_COOKIE[session_name()])) {
setcookie(
session_name(), // Session 名称
'', // 清空内容
time() - 42000, // 设置为过去时间立即失效
'/' // 全站生效
);
}
// 销毁 Session
session_destroy();
// 跳转回网站首页(登录页)
header('Location: /');
// 终止程序执行
exit;
}
// TG 备份功能
if ($action === 'tg_backup') {
// 1. 获取所有账号数据
$stmt = $db->query("SELECT * FROM accounts ORDER BY id DESC");
$accounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
$json_data = json_encode($accounts, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 2. 获取 TG 配置
$q = $db->query("SELECT * FROM settings");
$settings = [];
while ($r = $q->fetch()) $settings[$r['key']] = $r['value'];
$token = trim($settings['tg_token'] ?? '');
$chat_id = trim($settings['tg_chatid'] ?? '');
if (empty($token) || empty($chat_id)) {
echo json_encode(['status' => 'error', 'message' => '请先在设置中配置 TG Token 和 ChatID']);
exit;
}
// 3. 发送 JSON 文件到 Telegram (推荐发送文件,因为账号多了会触发消息长度限制)
// 创建临时文件
$tmpFile = tempnam(sys_get_temp_dir(), 'backup_');
file_put_contents($tmpFile, $json_data);
$url = "https://api.telegram.org/bot{$token}/sendDocument";
$post_data = [
'chat_id' => $chat_id,
'document' => new CURLFile($tmpFile, 'application/json', 'outlook_backup_' . date('Ymd_His') . '.json'),
'caption' => "📅 Outlook Panel 自动备份\n⏰ 时间: " . date('Y-m-d H:i:s') . "\n👤 账号总数: " . count($accounts)
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post_data,
CURLOPT_SSL_VERIFYPEER => false, // 如果服务器证书有问题,可跳过
CURLOPT_TIMEOUT => 30
]);
$res = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
// 删除临时文件
@unlink($tmpFile);
if ($err) {
echo json_encode(['status' => 'error', 'message' => 'CURL 错误: ' . $err]);
} else {
$res_arr = json_decode($res, true);
if ($res_arr && $res_arr['ok']) {
echo json_encode(['status' => 'success', 'message' => '备份已发送至 Telegram']);
} else {
echo json_encode(['status' => 'error', 'message' => 'TG 返回错误: ' . ($res_arr['description'] ?? '未知')]);
}
}
exit;
}