SMTP-аутентификация QT / C ++ - PullRequest
2 голосов
/ 28 июня 2010

Я пытаюсь создать простую функцию отправки почты для пользовательского приложения, созданного с помощью инфраструктуры Qt в C ++. Я очень близок как раз к тому моменту, когда SMTP пытается аутентифицироваться, и не могу всю жизнь заставить это работать. Вот с чем я работаю

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.

Ответы [ 4 ]

1 голос
/ 29 июня 2010

Вы используете QTextStream?Это кажется неправильным.QTextStream предназначен для написания закодированного текста и будет связываться как с кодировкой, так и с окончаниями строк, а это не то, что вам нужно при реализации протокола.Лучше использовать QByteArray (вместо QString) и QDataStream вместо QTextStream, или для лучшей обработки ошибок напишите непосредственно в сокет.

1 голос
/ 28 июня 2010

Это проблема шифрования?Я вижу, что вы используете STARTTLS, но я не вижу, как вы справляетесь с фактическим шифрованием / расшифровкой последующих строк ...

Согласно RFC 2595 , шифрование должно начинаться немедленнопосле ответа OK на команду STARTTLS.Ваш код предполагает, что вы даже не ждете этого OK, вместо этого вы немедленно отправляете AUTH PLAIN, в результате чего ваш клиент продолжает текстовое сообщение, пока сервер ожидает шифрование.шифрование не требуется, просто оставьте STARTTLS и придерживайтесь стандарта (E) SMTP, как определено в RFC ... Для аутентификации ESMTP вам может понадобиться использовать EHLO вместо HELO, иначе сервер может не понимать AUTH.

Все RFC, относящиеся к SMTP, вы найдете на соответствующей странице Википедии .В частности, вам понадобятся RFC5321 (последнее резюме SMTP) и RFC4954 (самое последнее описание ESMTP AUTH).Но ... это может быть сложнее, чем вы думаете.Могут быть некоторые SMTP-серверы, которые не принимают AUTH PLAIN, когда соединение не зашифровано.Вам необходимо оценить реакцию сервера на команду EHLO (см. Пример раздела в RFC4954).

Я думаю, вам будет проще использовать библиотеку.Я не знаю один для C ++, вам нужно найти его самостоятельно.

0 голосов
/ 28 ноября 2017

Попробуйте простой почтовый клиент smtp на C ++ ssl отлично работает https://github.com/breakermind/SslSMTPClient Вы можете отправлять электронную почту без сервера smtp из своего приложения (получить mx hosts из 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("nanomoow@gmail.com",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@qflash.pl", "nanomoow@gmail.com", "email@qflash.pl", "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;    
}
0 голосов
/ 05 июня 2012

Проверка строки ответа [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")

ОБНОВЛЕНИЕ

'334 VXNlcm5hbWU6' 

только при использовании AUTH LOGIN, если вы используетеAUTH PLAIN вы просто получите 334 с сервера.Также вам может понадобиться закодировать ваше имя пользователя / перейти к base64

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...