2010-06-28 222 views
2

我想創建一個簡單的發送郵件函數的自定義應用程序創建的Qt框架在C + +。我非常接近SMTP正試圖進行身份驗證的地步,並且無法讓我的生活得以實現。這是我正在與SMTP身份驗證QT/C++

do { 
    responseLine = socket->readLine(); 
    response += responseLine; 
} 
while (socket->canReadLine() && responseLine[3] != ' '); 
    responseLine.truncate(3); 
if (state == Init && responseLine[0] == '2') { 
    // banner was okay, let's go on 
    *t << "HELO there\r\n"; 
    t->flush(); 
    state = Auth; 
} else if (state == Auth && responseLine[0] == '2') { 
    *t << "STARTTLS\r\n"; 
    *t << "AUTH PLAIN\r\n"; 
    t->flush(); 
    state = User; 
} else if (state == User && responseLine[0] == '2') { 
    *t << "username\r\n"; 
    t->flush(); 
    state = Pass; 
} else if (state == Pass && responseLine[0] == '2') { 
    *t << "pass\r\n"; 
    t->flush(); 
    state = Mail; 
} else if (state == Mail && responseLine[0] == '2') { 
    // HELO response was okay (well, it has to be) 
    *t << "MAIL FROM: " << from << "\r\n"; 
      t->flush(); 
    state = Rcpt; 
} else if (state == Rcpt && responseLine[0] == '2') { 
    *t << "RCPT TO: " << rcpt << "\r\n"; //r 
      t->flush(); 
    state = Data; 
} else if (state == Data && responseLine[0] == '2') { 
    *t << "DATA\r\n"; 
      t->flush(); 
    state = Body; 
} else if (state == Body && responseLine[0] == '3') { 
    *t << message << "\r\n.\r\n"; 
      t->flush(); 
    state = Quit; 
} else if (state == Quit && responseLine[0] == '2') { 
    *t << "QUIT\r\n"; 
      t->flush(); 
    // here, we just close. 
    state = Close; 
    emit status(tr("Message sent")); 
} else if (state == Close) { 
    deleteLater(); 
    return; 
} else { 
    // something broke. 
+0

這是一個加密問題嗎?我可以看到你正在使用STARTTLS,但是我看不到你如何處理後續行的實際解密/解密...... – orithena 2010-06-28 18:56:39

回答

1

這是一個加密問題?我可以看到你正在使用STARTTLS,但我看不到你如何處理後續行的實際解密/解密......

根據RFC 2595,加密必須在對STARTTLS命令的OK響應之後立即開始。您的代碼表明您甚至不會等待那麼好,而是立即發送AUTH PLAIN,從而在服務器期望加密時從您的客戶端延續純文本。

編輯:如果不需要加密,只留下了STARTTLS和堅持(E)SMTP標準如RFC中定義......對於ESMTP驗證您可能需要使用EHLO而不是HELO,否則服務器可能不理解AUTH。

您可以在corresponding Wikipedia page上找到所有與SMTP相關的RFC。最值得注意的是,您需要RFC5321(SMTP的最新摘要)和RFC4954(ESMTP AUTH的最新描述)。但是......它可能比你想象的更復雜。當連接未加密時,可能有一些SMTP服務器不接受AUTH PLAIN。您需要評估服務器對EHLO命令的響應(請參閱RFC4954中的示例部分)。

我認爲只要使用一個庫會比較容易。我不知道一個用於C++的,你需要自己找到它。

+0

我不需要加密,我將如何使用純文本用戶名/密碼進行身份驗證 – rizzo0917 2010-06-28 19:19:23

+0

我只是添加了一些關於SMTP RFC的信息...... – orithena 2010-06-30 11:38:46

1

您正在使用QTextStream?這似乎是錯誤的。 QTextStream用於編寫經過編碼的文本,並且會混淆編碼和行結尾,這不是實現協議時所需的。更好地使用QByteArray(而不是QString)和QDataStream而不是QTextStream,或者爲了更好的錯誤處理,直接寫入套接字。

0

檢查響應行[0]不是最好的事情...當您請求'AUTH PLAIN'或'AUTH LOGIN'時,服務器將響應一個用戶名請求(後跟一個密碼請求,寫完用戶名)的形式'334 VXNlcm5hbWU6'(密碼請求是相同的形式)...所以當你檢查,看看你是否說有一個錯誤,當在現實中沒有錯誤responseLine[0] == '2'。所以我會更改下面的代碼來檢查不僅僅是響應行的第一個字符。

else if (state == User && responseLine[0] == '2') { 
    *t << "username\r\n"; 
    t->flush(); 
    state = Pass; 
} else if (state == Pass && responseLine[0] == '2') { 
    *t << "pass\r\n"; 
    t->flush(); 
    state = Mail; 

你可以做類似else if (state == Pass && responseLine.contains("334")

UPDATE

'334 VXNlcm5hbWU6' 

只有當你使用AUTH LOGIN,如果你使用AUTH PLAIN你可以簡單的從服務器獲取一個334。 此外,您可能需要編碼您的用戶名/密碼爲Base64

0

嘗試簡單的C++ SSL SMTP電子郵件客戶端都可以完美 https://github.com/breakermind/SslSMTPClient 您可以從應用程序發送電子郵件而無需SMTP服務器(獲取MX主機的DNS併發送至服務器):

#include <stdio.h> 
#include <unistd.h> 
#include <malloc.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <resolv.h> 
#include <netdb.h> 
// openssl 
#include <openssl/ssl.h> 
#include <openssl/err.h> 

// apt-get install libssl-dev openssl 
// g++ -o start main.cpp sslsmtpex.cpp sslsmtpex.h -lssl -lresolv -lcrypto -sd=c++11 -std=c++14 

// Include client 
#include "sslsmtpex.h" 

using namespace std; 

// main - create SSL context and connect 
int main(int count, char *strings[]) 
{ 
    cout << "C++ ssl smtp send email with STARTTLS\r\n";  

    // Add attachments to message if you want 
    vector<string> files; 
    // files.push_back("file9.jpg"); 
    // files.push_back("filek.pdf"); 

    // Initialize 
    sslsmtpEx sm; 
    sm.sslsmtpExSet("localhost", 25); 

    // EHLO hostname 
    sm.heloHostname("qflash.pl"); 

    // Display logs 
    // sm.showLogs(); 

    // get MX records from dns for recipient 
    vector<string> mx = sm.getMX("[email protected]",0,0); 

    // Send email to each mx host from recipient domain DNS (You need send only to one server !!!) 
    for (int i = 0; i < mx.size(); i++){ 

     // Set hostname from mx dns 
     sm.sslsmtpExSet(mx.at(i), 25); 
     cout << "Mx host: " << mx.at(i) << endl;  

     // send email 
     int ok = sm.Send("[email protected]", "[email protected]", "[email protected]", "Smtp client test", "<h1>Smtp test</h1>", "<h1>Smtp test</h1>", files); 

     cout << "Email has been sent : " << ok << endl; 

     if(ok){ 
      // if email has been sent, end loop with next mxhost 
      break; 
     }    
    } 
    sleep(10); 

return 0;  
}