為抑制垃圾郵件,多數郵件伺服器採用 ESMTP 身份驗證。BCB 6 的 NMSMTP 元件雖方便,但不支援驗證功能。本文分析 ESMTP AUTH LOGIN 流程,並實作 Base64 編碼,擴充元件實現帶驗證的郵件發送。
以網易郵箱為例(C=客戶端,S=伺服器):
(1) C: AUTH LOGIN
(2) S: 334 dXNlcm5hbWU6 // Base64("username:") -> 要求用戶名
(3) C: d3lxX2puX3NkX2Nu // Base64 編碼用戶名
(4) S: 334 UGFzc3dvcmQ6 // Base64("password:") -> 要求密碼
(5) C: [Base64 編碼密碼]
(6) S: 235 Authentication successful
關鍵點:334 表示繼續,235 表示成功。所有認證資料均為 Base64 編碼傳輸。
A-Z a-z 0-9 + /。= 填充(1 個 = 代表缺 1 位元組,2 個 = 缺 2 位元組)。NMSMTP.Connect() 連線伺服器。OnConnect 事件中,利用元件繼承自 PowerSocket 的 SendBuffer、Read、DataAvailable 等底層網路函數,手動實作 AUTH LOGIN 交互。// 1. 登入按鈕觸發連線
void __fastcall TForm1::Logon1Click(TObject *Sender) {
AddLog("正在登入 " + Edit1->Text + "...");
NMSMTP1->Host = Edit1->Text;
NMSMTP1->Port = 25;
NMSMTP1->UserID = Edit4->Text; // 用戶名
NMSMTP1->Connect();
}
// 2. OnConnect 事件處理認證流程
void __fastcall TForm1::NMSMTP1Connect(TObject *Sender) {
AddLog("連線伺服器成功。");
AnsiString Data, rData; bool b_ok;
if (CheckBox1->Checked) { // 需要驗證
// 步驟 1: 發送 AUTH LOGIN
Data = "AUTH LOGIN\r\n";
NMSMTP1->SendBuffer(Data.c_str(), Data.Length());
rData = WaitForReply(5);
if (!(rData.Length()>=3 && rData.TrimLeft().SubString(1,3)=="334")) { Fail(); return; }
AddLog("正在驗證身份...");
// 步驟 2: 發送 Base64 用戶名
Data = encode(Edit4->Text) + "\r\n";
NMSMTP1->SendBuffer(Data.c_str(), Data.Length());
rData = WaitForReply(5);
if (!(rData.Length()>=3 && rData.TrimLeft().SubString(1,3)=="334")) { Fail(); return; }
AddLog("正在驗證密碼...");
// 步驟 3: 發送 Base64 密碼
Data = encode(Edit5->Text) + "\r\n";
NMSMTP1->SendBuffer(Data.c_str(), Data.Length());
rData = WaitForReply(5);
if (!(rData.Length()>=3 && rData.TrimLeft().SubString(1,3)=="235")) { Fail(); return; }
AddLog("登入成功!");
}
SendMail->Enabled = true;
disconnect->Enabled = true;
Logon1->Enabled = false;
}
// 3. Base64 編碼函數
AnsiString TForm1::encode(AnsiString s) {
int m_len = s.Length(), i, m_tmp;
AnsiString m_64code;
char *m_s = s.c_str();
char m_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (i=0; i<m_len-m_len%3; i+=3) { // 每 3 位元組處理
m_tmp = m_s[i] / 4; m_64code += m_64[m_tmp];
m_tmp = m_s[i]%4*16 + m_s[i+1]/16; m_64code += m_64[m_tmp];
m_tmp = m_s[i+1]%16*4 + m_s[i+2]/64; m_64code += m_64[m_tmp];
m_tmp = m_s[i+2]%64; m_64code += m_64[m_tmp];
}
// 餘 2 位元組
if (m_len%3 == 2) {
m_tmp = m_s[m_len-2]/4; m_64code += m_64[m_tmp];
m_tmp = m_s[m_len-2]%4*16 + m_s[m_len-1]/16; m_64code += m_64[m_tmp];
m_tmp = m_s[m_len-1]%16*4; m_64code += m_64[m_tmp];
m_64code += '=';
}
// 餘 1 位元組
if (m_len%3 == 1) {
m_tmp = m_s[m_len-1]/4; m_64code += m_64[m_tmp];
m_tmp = m_s[m_len-1]%4*16; m_64code += m_64[m_tmp];
m_64code += "==";
}
return m_64code;
}
在 Windows 2000 + BCB 6.0 環境下,分別測試網易與新浪郵箱,均能順利完成身份驗證與郵件發送。