2010-06-28 140 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;  
}