64 lines
2.7 KiB
JavaScript
64 lines
2.7 KiB
JavaScript
|
|
"use strict";
|
||
|
|
|
||
|
|
const tls = require("node:tls");
|
||
|
|
|
||
|
|
function sendSmtpMail({ to, subject, approvalUrl, order, text, smtpHost, smtpPort, smtpUser, smtpPass }) {
|
||
|
|
if (!to || !smtpUser || !smtpPass || (!text && !approvalUrl)) return Promise.resolve(false);
|
||
|
|
const plain = text || [
|
||
|
|
"光湖小湖灯安全协议系统 · 授权请求",
|
||
|
|
"",
|
||
|
|
`人格体: ${order.persona.name} (${order.persona.pid})`,
|
||
|
|
`目标节点: ${order.target}`,
|
||
|
|
`授权范围: ${order.scope}`,
|
||
|
|
`登记动作: ${order.action}`,
|
||
|
|
"有效期: 批准后 1 小时,仅限这台服务器",
|
||
|
|
"",
|
||
|
|
"打开以下链接查看工单并确认授权:",
|
||
|
|
approvalUrl,
|
||
|
|
"",
|
||
|
|
"如果不是你发起的操作,请不要点击。",
|
||
|
|
].join("\n");
|
||
|
|
const message = [
|
||
|
|
`From: =?UTF-8?B?${Buffer.from("光湖小湖灯").toString("base64")}?= <${smtpUser}>`,
|
||
|
|
`To: <${to}>`,
|
||
|
|
`Subject: =?UTF-8?B?${Buffer.from(subject).toString("base64")}?=`,
|
||
|
|
"MIME-Version: 1.0",
|
||
|
|
'Content-Type: text/plain; charset="utf-8"',
|
||
|
|
"Content-Transfer-Encoding: base64",
|
||
|
|
"",
|
||
|
|
Buffer.from(plain).toString("base64").replace(/(.{76})/g, "$1\r\n"),
|
||
|
|
".",
|
||
|
|
"",
|
||
|
|
].join("\r\n");
|
||
|
|
|
||
|
|
return new Promise(resolve => {
|
||
|
|
let settled = false;
|
||
|
|
const done = value => { if (!settled) { settled = true; resolve(value); } };
|
||
|
|
const socket = tls.connect({ host: smtpHost, port: smtpPort, servername: smtpHost, rejectUnauthorized: true });
|
||
|
|
let stage = 0;
|
||
|
|
let buffer = "";
|
||
|
|
socket.setTimeout(15000, () => { socket.destroy(); done(false); });
|
||
|
|
socket.on("error", () => done(false));
|
||
|
|
socket.on("data", data => {
|
||
|
|
buffer += data.toString();
|
||
|
|
const lines = buffer.split("\r\n");
|
||
|
|
buffer = lines.pop();
|
||
|
|
for (const line of lines) {
|
||
|
|
if (!/^\d{3}[ -]/.test(line) || line[3] === "-") continue;
|
||
|
|
const code = Number(line.slice(0, 3));
|
||
|
|
if (code >= 400) { socket.end(); done(false); return; }
|
||
|
|
if (stage === 0 && code === 220) { stage = 1; socket.write("EHLO guanghu-lake-lamp\r\n"); }
|
||
|
|
else if (stage === 1 && code === 250) { stage = 2; socket.write(`AUTH PLAIN ${Buffer.from(`\0${smtpUser}\0${smtpPass}`).toString("base64")}\r\n`); }
|
||
|
|
else if (stage === 2 && code === 235) { stage = 3; socket.write(`MAIL FROM:<${smtpUser}>\r\n`); }
|
||
|
|
else if (stage === 3 && code === 250) { stage = 4; socket.write(`RCPT TO:<${to}>\r\n`); }
|
||
|
|
else if (stage === 4 && code === 250) { stage = 5; socket.write("DATA\r\n"); }
|
||
|
|
else if (stage === 5 && code === 354) { stage = 6; socket.write(message); }
|
||
|
|
else if (stage === 6 && code === 250) { socket.write("QUIT\r\n"); socket.end(); done(true); }
|
||
|
|
}
|
||
|
|
});
|
||
|
|
socket.on("close", () => done(false));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { sendSmtpMail };
|