package Sdf1_login; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.Base64; import java.util.logging.Logger; public class EmailManager { private final ConfigManager config; private final Logger logger; /** * 发送SMTP验证码 */ public boolean sendVerifyCode(String to, String code) { if (!isConfigured()) { plugin.getLogger().severe( "[Sdf1_login] SMTP未配置完整," + "请检查SMTP设置.txt"); return false; } String subject = "[Sdf1_login] SMTP配置验证码"; String body = "您的SMTP配置验证码是: " + code + "\n" + "验证码5分钟内有效。\n" + "如非本人操作请忽略此邮件。"; try { return sendEmail(to, subject, body); } catch (Exception e) { plugin.getLogger().severe( "[Sdf1_login] 邮件发送异常: " + e.getClass().getSimpleName() + ": " + e.getMessage()); if (e.getCause() != null) { plugin.getLogger().severe( "[Sdf1_login] 根因: " + e.getCause().getMessage()); } return false; } } public EmailManager(ConfigManager config) { this.config = config; this.logger = Logger.getLogger("Sdf1_login"); } /** * 检查SMTP是否已配置 */ public boolean isConfigured() { String host = config.getSmtp("smtp地址"); String user = config.getSmtp("发件邮箱"); String pass = config.getSmtp("授权码"); return host != null && !host.isEmpty() && user != null && !user.isEmpty() && pass != null && !pass.isEmpty(); } /** * 发送验证码,返回是否成功发起 */ public boolean sendVerifyCode(String to, String player, String code) { if (!isConfigured()) { logger.warning("[Sdf1_login] SMTP未配置,无法发送验证码到 " + to); return false; } logger.info("[Sdf1_login] 发送验证码到 " + to + " (玩家: " + player + ")"); return sendEmailSync(to, "Sdf1_login 验证码", "玩家 " + player + " 的验证码: " + code); } /** * 发送临时密码,返回是否成功发起 */ public boolean sendTempPassword(String to, String player, String tempPwd) { if (!isConfigured()) { logger.warning("[Sdf1_login] SMTP未配置,无法发送临时密码到 " + to); return false; } logger.info("[Sdf1_login] 发送临时密码到 " + to + " (玩家: " + player + ")"); return sendEmailSync(to, "Sdf1_login 临时密码", "玩家 " + player + " 的临时密码: " + tempPwd + "\n请尽快登录并修改密码!"); } private boolean sendEmailSync(String to, String subject, String body) { final boolean[] result = {false}; Thread t = new Thread(() -> { try { result[0] = sendEmail(to, subject, body); } catch (Exception e) { logger.severe("[Sdf1_login] 邮件线程异常: " + e.getMessage()); } }, "Sdf1_login-SMTP"); t.setDaemon(true); t.start(); try { t.join(15000); } catch (InterruptedException ignored) {} if (t.isAlive()) { t.interrupt(); logger.severe("[Sdf1_login] SMTP超时(15秒)"); return false; } return result[0]; } private boolean sendEmail(String to, String subject, String body) { String host = config.getSmtp("smtp地址"); String portStr = config.getSmtp("smtp端口"); String user = config.getSmtp("发件邮箱"); String pass = config.getSmtp("授权码"); String fromName = config.getSmtp("发件人名"); int port; try { port = Integer.parseInt(portStr); } catch (Exception e) { port = 465; } try { String encSubject = "=?UTF-8?B?" + Base64.getEncoder().encodeToString(subject.getBytes("UTF-8")) + "?="; String encBody = Base64.getEncoder().encodeToString(body.getBytes("UTF-8")); if (port == 465) { SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket socket = (SSLSocket) factory.createSocket(host, port); socket.setSoTimeout(10000); doSmtp(socket, host, user, pass, to, encSubject, encBody, fromName); socket.close(); } else { Socket plainSocket = new Socket(host, port); plainSocket.setSoTimeout(10000); BufferedReader preReader = new BufferedReader(new InputStreamReader(plainSocket.getInputStream(), "UTF-8")); PrintWriter preWriter = new PrintWriter(plainSocket.getOutputStream(), true); checkResponse(preReader, "220"); sendLine(preWriter, "EHLO " + host); checkResponse(preReader, "250"); sendLine(preWriter, "STARTTLS"); checkResponse(preReader, "220"); SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(plainSocket, host, port, true); sslSocket.startHandshake(); doSmtp(sslSocket, host, user, pass, to, encSubject, encBody, fromName); sslSocket.close(); } logger.info("[Sdf1_login] 邮件发送成功 -> " + to); return true; } catch (Exception e) { logger.severe("[Sdf1_login] 邮件异常: " + e.getMessage()); return false; } } private void doSmtp(SSLSocket socket, String host, String user, String pass, String to, String subject, String body, String fromName) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); OutputStream rawOut = socket.getOutputStream(); PrintWriter writer = new PrintWriter(new java.io.BufferedOutputStream(rawOut), true); checkResponse(reader, "220"); sendLine(writer, "EHLO " + host); checkResponse(reader, "250"); sendLine(writer, "AUTH LOGIN"); checkResponse(reader, "334"); sendLine(writer, Base64.getEncoder().encodeToString(user.getBytes("UTF-8"))); checkResponse(reader, "334"); sendLine(writer, Base64.getEncoder().encodeToString(pass.getBytes("UTF-8"))); checkResponse(reader, "235"); sendLine(writer, "MAIL FROM:<" + user + ">"); checkResponse(reader, "250"); sendLine(writer, "RCPT TO:<" + to + ">"); checkResponse(reader, "250"); sendLine(writer, "DATA"); checkResponse(reader, "354"); sendLine(writer, "From: " + fromName + " <" + user + ">"); sendLine(writer, "To: <" + to + ">"); sendLine(writer, "Reply-To: <" + user + ">"); sendLine(writer, "Subject: " + subject); sendLine(writer, "MIME-Version: 1.0"); sendLine(writer, "Content-Type: text/plain; charset=UTF-8"); sendLine(writer, "Content-Transfer-Encoding: base64"); sendLine(writer, ""); sendLine(writer, body); sendLine(writer, "."); checkResponse(reader, "250"); sendLine(writer, "QUIT"); } private void sendLine(PrintWriter writer, String line) { writer.print(line + "\r\n"); writer.flush(); } private void checkResponse(BufferedReader reader, String expected) throws Exception { StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); if (line.length() >= 4 && Character.isDigit(line.charAt(0)) && line.charAt(3) == ' ') break; } String resp = sb.toString().trim(); if (!resp.startsWith(expected)) { if (resp.startsWith("535")) logger.severe("[Sdf1_login] SMTP 535 认证失败"); throw new Exception("SMTP响应异常: 期望 " + expected + ",实际: " + resp); } } }