mirror of
https://gitea.winboll.cc/Studio/WinBoLL.git
synced 2026-08-14 05:27:10 +08:00
feat(libappbase+appbase): 添加SMTP邮件发送工具类及配置界面
libappbase: - 新增SMTPUtils工具类,原生Socket实现QQ SMTP SSL邮件发送(零外部依赖) - 支持sendTextMail/sendHtmlMail,Java7兼容 appbase: - APPExcetionMailSettingsActivity添加5项SMTP参数配置UI (服务器、端口、发件人邮箱、授权码、收件人邮箱) - SharedPreferences持久化存储配置 - 子线程测试发送 + Toast结果反馈 备注:SMTPUtils测试邮件发送功能未调试,待实际环境验证
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue Jul 21 13:23:28 HKT 2026
|
||||
#Tue Jul 21 13:37:58 HKT 2026
|
||||
stageCount=2
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.21
|
||||
publishVersion=15.21.1
|
||||
buildCount=6
|
||||
buildCount=9
|
||||
baseBetaVersion=15.21.2
|
||||
|
||||
+139
@@ -1,23 +1,162 @@
|
||||
package cc.winboll.studio.appbase.develop;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.utils.SMTPUtils;
|
||||
|
||||
/**
|
||||
* @Author 豆包&BigPickle&MiMo&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/07/21 12:44
|
||||
* @Describe 应用异常信息邮件接收账号设置
|
||||
* 配置SMTP服务器参数,用于通过邮件发送异常报告
|
||||
*/
|
||||
public class APPExcetionMailSettingsActivity extends Activity {
|
||||
|
||||
public static final String TAG = "APPExcetionMailSettingsActivity";
|
||||
|
||||
private static final String PREF_NAME = "smtp_config";
|
||||
private static final String KEY_SMTP_SERVER = "smtp_server";
|
||||
private static final String KEY_SMTP_PORT = "smtp_port";
|
||||
private static final String KEY_SENDER_EMAIL = "sender_email";
|
||||
private static final String KEY_AUTH_CODE = "auth_code";
|
||||
private static final String KEY_RECIPIENT_EMAIL = "recipient_email";
|
||||
|
||||
private EditText mEtSmtpServer;
|
||||
private EditText mEtSmtpPort;
|
||||
private EditText mEtSenderEmail;
|
||||
private EditText mEtAuthCode;
|
||||
private EditText mEtRecipientEmail;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_appexcetionmailsettings);
|
||||
|
||||
mEtSmtpServer = (EditText) findViewById(R.id.et_smtp_server);
|
||||
mEtSmtpPort = (EditText) findViewById(R.id.et_smtp_port);
|
||||
mEtSenderEmail = (EditText) findViewById(R.id.et_sender_email);
|
||||
mEtAuthCode = (EditText) findViewById(R.id.et_auth_code);
|
||||
mEtRecipientEmail = (EditText) findViewById(R.id.et_recipient_email);
|
||||
|
||||
loadConfig();
|
||||
|
||||
Button btnSave = (Button) findViewById(R.id.btn_save);
|
||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
saveConfig();
|
||||
}
|
||||
});
|
||||
|
||||
Button btnTest = (Button) findViewById(R.id.btn_test);
|
||||
btnTest.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
testSendMail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
SharedPreferences sp = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
String server = sp.getString(KEY_SMTP_SERVER, "smtp.qq.com");
|
||||
String port = sp.getString(KEY_SMTP_PORT, "465");
|
||||
String sender = sp.getString(KEY_SENDER_EMAIL, "");
|
||||
String auth = sp.getString(KEY_AUTH_CODE, "");
|
||||
String recipient = sp.getString(KEY_RECIPIENT_EMAIL, "");
|
||||
|
||||
mEtSmtpServer.setText(server);
|
||||
mEtSmtpPort.setText(port);
|
||||
mEtSenderEmail.setText(sender);
|
||||
mEtAuthCode.setText(auth);
|
||||
mEtRecipientEmail.setText(recipient);
|
||||
}
|
||||
|
||||
private void saveConfig() {
|
||||
String server = mEtSmtpServer.getText().toString().trim();
|
||||
String port = mEtSmtpPort.getText().toString().trim();
|
||||
String sender = mEtSenderEmail.getText().toString().trim();
|
||||
String auth = mEtAuthCode.getText().toString().trim();
|
||||
String recipient = mEtRecipientEmail.getText().toString().trim();
|
||||
|
||||
SharedPreferences.Editor editor = getSharedPreferences(PREF_NAME, MODE_PRIVATE).edit();
|
||||
editor.putString(KEY_SMTP_SERVER, server);
|
||||
editor.putString(KEY_SMTP_PORT, port);
|
||||
editor.putString(KEY_SENDER_EMAIL, sender);
|
||||
editor.putString(KEY_AUTH_CODE, auth);
|
||||
editor.putString(KEY_RECIPIENT_EMAIL, recipient);
|
||||
editor.apply();
|
||||
|
||||
Toast.makeText(this, "设置已保存", Toast.LENGTH_SHORT).show();
|
||||
LogUtils.d(TAG, "saveConfig success, server=" + server + ", port=" + port + ", sender=" + sender);
|
||||
}
|
||||
|
||||
private void testSendMail() {
|
||||
String server = mEtSmtpServer.getText().toString().trim();
|
||||
String portStr = mEtSmtpPort.getText().toString().trim();
|
||||
String sender = mEtSenderEmail.getText().toString().trim();
|
||||
String auth = mEtAuthCode.getText().toString().trim();
|
||||
String recipient = mEtRecipientEmail.getText().toString().trim();
|
||||
|
||||
if (server.isEmpty()) {
|
||||
Toast.makeText(this, "请输入SMTP服务器地址", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (portStr.isEmpty()) {
|
||||
Toast.makeText(this, "请输入SMTP端口", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (sender.isEmpty()) {
|
||||
Toast.makeText(this, "请输入发件人邮箱", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (auth.isEmpty()) {
|
||||
Toast.makeText(this, "请输入邮箱授权码", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (recipient.isEmpty()) {
|
||||
Toast.makeText(this, "请输入收件人邮箱", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(this, "正在发送测试邮件...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
final String finalRecipient = recipient;
|
||||
final String finalSender = sender;
|
||||
final String finalAuth = auth;
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean success = SMTPUtils.sendTextMail(
|
||||
finalRecipient,
|
||||
"APPBase 测试邮件",
|
||||
"这是一封来自APPBase的测试邮件。\n\n发送时间: " + System.currentTimeMillis(),
|
||||
finalSender,
|
||||
finalAuth);
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (success) {
|
||||
Toast.makeText(APPExcetionMailSettingsActivity.this,
|
||||
"测试邮件发送成功", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(APPExcetionMailSettingsActivity.this,
|
||||
"测试邮件发送失败,请检查设置", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FAFAFA"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="应用异常信息邮件接收账号设置:"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SMTP 邮件发送设置"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SMTP 服务器"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_smtp_server"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textUri"
|
||||
android:hint="smtp.qq.com"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#000000"
|
||||
android:textColorHint="#999999"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SMTP 端口"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_smtp_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number"
|
||||
android:hint="465"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#000000"
|
||||
android:textColorHint="#999999"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="发件人邮箱(QQ邮箱地址)"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_sender_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
android:hint="123456@qq.com"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#000000"
|
||||
android:textColorHint="#999999"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="邮箱授权码(非QQ密码,在QQ邮箱设置中获取)"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_auth_code"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:hint="请输入授权码"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#000000"
|
||||
android:textColorHint="#999999"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="收件人邮箱"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_recipient_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
android:hint="123456@qq.com"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#000000"
|
||||
android:textColorHint="#999999"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="24dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="保存设置"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="#007AFF"
|
||||
android:padding="14dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_test"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="发送测试邮件"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#007AFF"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="14dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue Jul 21 13:23:28 HKT 2026
|
||||
#Tue Jul 21 13:37:58 HKT 2026
|
||||
stageCount=2
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.21
|
||||
publishVersion=15.21.1
|
||||
buildCount=6
|
||||
buildCount=9
|
||||
baseBetaVersion=15.21.2
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
package cc.winboll.studio.libappbase.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* @Author 豆包&BigPickle&MiMo&ZhanGSKen<zhangsken@qq.com>
|
||||
* @CreateDate 2026/07/21
|
||||
* @LastEditDate 2026/07/21
|
||||
* @Describe SMTP邮件发送工具类(原生Socket实现,零外部依赖)
|
||||
* 基于QQ SMTP服务(smtp.qq.com:465 SSL),支持纯文本和HTML邮件
|
||||
* Java7兼容标准,无高版本语法特性
|
||||
*/
|
||||
public final class SMTPUtils {
|
||||
|
||||
private static final String TAG = "SMTPUtils";
|
||||
|
||||
// QQ SMTP 服务器地址
|
||||
private static final String DEFAULT_SMTP_HOST = "smtp.qq.com";
|
||||
// QQ SMTP SSL 端口
|
||||
private static final int DEFAULT_SMTP_PORT = 465;
|
||||
|
||||
// 私有构造,禁止实例化
|
||||
private SMTPUtils() {
|
||||
final String errMsg = "Cannot instantiate static class SMTPUtils";
|
||||
LogUtils.e(TAG, "constructor invoke failed, msg = " + errMsg);
|
||||
throw new AssertionError(errMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送纯文本邮件(QQ SMTP SSL)
|
||||
* @param to收件人邮箱地址
|
||||
* @param subject 邮件主题
|
||||
* @param body 邮件正文(纯文本)
|
||||
* @param from 发件人QQ邮箱地址
|
||||
* @param authCode QQ邮箱授权码(非QQ密码)
|
||||
* @return true=发送成功,false=发送失败
|
||||
*/
|
||||
public static boolean sendTextMail(
|
||||
final String to,
|
||||
final String subject,
|
||||
final String body,
|
||||
final String from,
|
||||
final String authCode) {
|
||||
return sendMail(to, subject, body, from, authCode, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTML邮件(QQ SMTP SSL)
|
||||
* @param to 收件人邮箱地址
|
||||
* @param subject 邮件主题
|
||||
* @param body 邮件正文(HTML格式)
|
||||
* @param from 发件人QQ邮箱地址
|
||||
* @param authCode QQ邮箱授权码(非QQ密码)
|
||||
* @return true=发送成功,false=发送失败
|
||||
*/
|
||||
public static boolean sendHtmlMail(
|
||||
final String to,
|
||||
final String subject,
|
||||
final String body,
|
||||
final String from,
|
||||
final String authCode) {
|
||||
return sendMail(to, subject, body, from, authCode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件核心方法(QQ SMTP SSL)
|
||||
* @param to 收件人邮箱地址
|
||||
* @param subject 邮件主题
|
||||
* @param body 邮件正文
|
||||
* @param from 发件人QQ邮箱地址
|
||||
* @param authCode QQ邮箱授权码
|
||||
* @param isHtml 是否HTML格式
|
||||
* @return true=发送成功,false=发送失败
|
||||
*/
|
||||
private static boolean sendMail(
|
||||
final String to,
|
||||
final String subject,
|
||||
final String body,
|
||||
final String from,
|
||||
final String authCode,
|
||||
final boolean isHtml) {
|
||||
|
||||
LogUtils.d(TAG, "sendMail invoke, to=" + to + ", subject=" + subject + ", isHtml=" + isHtml);
|
||||
|
||||
// 参数校验
|
||||
if (to == null || to.isEmpty()) {
|
||||
LogUtils.e(TAG, "sendMail failed, reason: recipient address is empty");
|
||||
return false;
|
||||
}
|
||||
if (from == null || from.isEmpty()) {
|
||||
LogUtils.e(TAG, "sendMail failed, reason: sender address is empty");
|
||||
return false;
|
||||
}
|
||||
if (authCode == null || authCode.isEmpty()) {
|
||||
LogUtils.e(TAG, "sendMail failed, reason: auth code is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
Socket socket = null;
|
||||
BufferedReader reader = null;
|
||||
OutputStream output = null;
|
||||
|
||||
try {
|
||||
// 1. 创建SSL连接
|
||||
LogUtils.d(TAG, " connecting to " + DEFAULT_SMTP_HOST + ":" + DEFAULT_SMTP_PORT);
|
||||
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||
socket = factory.createSocket(DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT);
|
||||
socket.setSoTimeout(10000);
|
||||
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
output = socket.getOutputStream();
|
||||
|
||||
// 2. 读取服务器欢迎信息
|
||||
String response = readResponse(reader);
|
||||
LogUtils.d(TAG, "S: " + response);
|
||||
if (!response.startsWith("220")) {
|
||||
LogUtils.e(TAG, "sendMail failed, SMTP server greeting error: " + response);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. EHLO 握手
|
||||
sendCommand(output, "EHLO localhost", reader);
|
||||
|
||||
// 4. AUTH LOGIN
|
||||
sendCommand(output, "AUTH LOGIN", reader);
|
||||
|
||||
// 5. 发送Base64编码的用户名(发件人邮箱)
|
||||
sendCommand(output, base64Encode(from), reader);
|
||||
|
||||
// 6. 发送Base64编码的授权码
|
||||
sendCommand(output, base64Encode(authCode), reader);
|
||||
|
||||
// 7. 设置发件人
|
||||
sendCommand(output, "MAIL FROM:<" + from + ">", reader);
|
||||
|
||||
// 8. 设置收件人
|
||||
sendCommand(output, "RCPT TO:<" + to + ">", reader);
|
||||
|
||||
// 9. 开始数据传输
|
||||
sendCommand(output, "DATA", reader);
|
||||
|
||||
// 10. 构建邮件内容
|
||||
String contentType = isHtml ? "text/html; charset=UTF-8" : "text/plain; charset=UTF-8";
|
||||
StringBuilder mailContent = new StringBuilder();
|
||||
mailContent.append("From: ").append(from).append("\r\n");
|
||||
mailContent.append("To: ").append(to).append("\r\n");
|
||||
mailContent.append("Subject: ").append(subject).append("\r\n");
|
||||
mailContent.append("Content-Type: ").append(contentType).append("\r\n");
|
||||
mailContent.append("\r\n");
|
||||
mailContent.append(body).append("\r\n");
|
||||
mailContent.append(".\r\n");
|
||||
|
||||
// 11. 发送邮件内容
|
||||
output.write(mailContent.toString().getBytes("UTF-8"));
|
||||
output.flush();
|
||||
response = readResponse(reader);
|
||||
LogUtils.d(TAG, "S: " + response);
|
||||
|
||||
// 12. QUIT 退出
|
||||
sendCommand(output, "QUIT", reader);
|
||||
|
||||
LogUtils.d(TAG, "sendMail success, to=" + to + ", subject=" + subject);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "sendMail exception: " + e.getMessage(), e);
|
||||
return false;
|
||||
} finally {
|
||||
// 关闭资源
|
||||
try {
|
||||
if (output != null) output.close();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "close output exception: " + e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (reader != null) reader.close();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "close reader exception: " + e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (socket != null) socket.close();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "close socket exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送SMTP命令并读取多行响应
|
||||
* @param output 输出流
|
||||
* @param command SMTP命令
|
||||
* @param reader 输入流
|
||||
* @return 服务器响应字符串
|
||||
*/
|
||||
private static String sendCommand(
|
||||
final OutputStream output,
|
||||
final String command,
|
||||
final BufferedReader reader) throws Exception {
|
||||
|
||||
LogUtils.d(TAG, "C: " + command);
|
||||
output.write((command + "\r\n").getBytes("UTF-8"));
|
||||
output.flush();
|
||||
return readResponse(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取SMTP服务器多行响应
|
||||
* SMTP协议:以"数字 "开头表示最后一行,以"数字-"开头表示后续还有行
|
||||
* @param reader 输入流
|
||||
* @return 完整的服务器响应字符串
|
||||
*/
|
||||
private static String readResponse(final BufferedReader reader) throws Exception {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line).append("\n");
|
||||
// 最后一行:第4位是空格(如 "250 OK")
|
||||
if (line.length() >= 4 && line.charAt(3) == ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准Base64编码(Java7兼容,无第三方依赖)
|
||||
* @param input 待编码字符串
|
||||
* @return Base64编码后的字符串
|
||||
*/
|
||||
static String base64Encode(final String input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
byte[] data = input.getBytes("UTF-8");
|
||||
final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
StringBuilder result = new StringBuilder();
|
||||
int i;
|
||||
// 每3字节一组处理
|
||||
for (i = 0; i + 2 < data.length; i += 3) {
|
||||
int triple = ((data[i] & 0xFF) << 16)
|
||||
| ((data[i + 1] & 0xFF) << 8)
|
||||
| (data[i + 2] & 0xFF);
|
||||
result.append(alphabet.charAt((triple >> 18) & 0x3F));
|
||||
result.append(alphabet.charAt((triple >> 12) & 0x3F));
|
||||
result.append(alphabet.charAt((triple >> 6) & 0x3F));
|
||||
result.append(alphabet.charAt(triple & 0x3F));
|
||||
}
|
||||
// 处理剩余字节
|
||||
if (i < data.length) {
|
||||
int remaining = data[i] & 0xFF;
|
||||
result.append(alphabet.charAt((remaining >> 2) & 0x3F));
|
||||
if (i + 1 < data.length) {
|
||||
remaining = ((remaining << 8) | (data[i + 1] & 0xFF));
|
||||
result.append(alphabet.charAt((remaining >> 4) & 0x3F));
|
||||
result.append(alphabet.charAt((remaining << 2) & 0x3F));
|
||||
} else {
|
||||
result.append(alphabet.charAt((remaining << 4) & 0x3F));
|
||||
result.append('=');
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "base64Encode exception: " + e.getMessage(), e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user