211 lines
8.6 KiB
Plaintext
211 lines
8.6 KiB
Plaintext
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);
|
|
}
|
|
}
|
|
}
|