state machine implementation, connectToHost and login working
This commit is contained in:
parent
b0e7a31b64
commit
618d583551
@ -21,6 +21,8 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
/* [1] Constructors and destructors */
|
/* [1] Constructors and destructors */
|
||||||
|
|
||||||
@ -88,6 +90,10 @@ void SmtpClient::setConnectionType(ConnectionType ct)
|
|||||||
case SslConnection:
|
case SslConnection:
|
||||||
case TlsConnection:
|
case TlsConnection:
|
||||||
socket = new QSslSocket(this);
|
socket = new QSslSocket(this);
|
||||||
|
//QSslSocket &s = *((QSslSocket*) socket);
|
||||||
|
connect(socket, SIGNAL(encrypted()),
|
||||||
|
this, SLOT(socketEncrypted()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,155 +158,22 @@ QTcpSocket* SmtpClient::getSocket() {
|
|||||||
|
|
||||||
bool SmtpClient::connectToHost()
|
bool SmtpClient::connectToHost()
|
||||||
{
|
{
|
||||||
switch (connectionType)
|
changeState(ConnectingState);
|
||||||
{
|
|
||||||
case TlsConnection:
|
|
||||||
case TcpConnection:
|
|
||||||
socket->connectToHost(host, port);
|
|
||||||
break;
|
|
||||||
case SslConnection:
|
|
||||||
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tries to connect to server
|
|
||||||
if (!socket->waitForConnected(connectionTimeout))
|
|
||||||
{
|
|
||||||
emit smtpError(ConnectionTimeoutError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Wait for the server's response
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// If the response code is not 220 (Service ready)
|
|
||||||
// means that is something wrong with the server
|
|
||||||
if (responseCode != 220)
|
|
||||||
{
|
|
||||||
emit smtpError(ServerError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a EHLO/HELO message to the server
|
|
||||||
// The client's first command must be EHLO/HELO
|
|
||||||
sendMessage("EHLO " + name);
|
|
||||||
|
|
||||||
// Wait for the server's response
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// The response code needs to be 250.
|
|
||||||
if (responseCode != 250) {
|
|
||||||
emit smtpError(ServerError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionType == TlsConnection) {
|
|
||||||
// send a request to start TLS handshake
|
|
||||||
sendMessage("STARTTLS");
|
|
||||||
|
|
||||||
// Wait for the server's response
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// The response code needs to be 220.
|
|
||||||
if (responseCode != 220) {
|
|
||||||
emit smtpError(ServerError);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
((QSslSocket*) socket)->startClientEncryption();
|
|
||||||
|
|
||||||
if (!((QSslSocket*) socket)->waitForEncrypted(connectionTimeout)) {
|
|
||||||
qDebug() << ((QSslSocket*) socket)->errorString();
|
|
||||||
emit SmtpError(ConnectionTimeoutError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send ELHO one more time
|
|
||||||
sendMessage("EHLO " + name);
|
|
||||||
|
|
||||||
// Wait for the server's response
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// The response code needs to be 250.
|
|
||||||
if (responseCode != 250) {
|
|
||||||
emit smtpError(ServerError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ResponseTimeoutException)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no errors occured the function returns true.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SmtpClient::login()
|
bool SmtpClient::login()
|
||||||
{
|
{
|
||||||
return login(user, password, authMethod);
|
changeState(AuthenticatingState);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method)
|
bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method)
|
||||||
{
|
{
|
||||||
try {
|
this->user = user;
|
||||||
if (method == AuthPlain)
|
this->password = password;
|
||||||
{
|
this->authMethod = method;
|
||||||
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
|
return login();
|
||||||
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user).append((char) 0).append(password).toBase64());
|
|
||||||
|
|
||||||
// Wait for the server's response
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// If the response is not 235 then the authentication was faild
|
|
||||||
if (responseCode != 235)
|
|
||||||
{
|
|
||||||
emit smtpError(AuthenticationFailedError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (method == AuthLogin)
|
|
||||||
{
|
|
||||||
// Sending command: AUTH LOGIN
|
|
||||||
sendMessage("AUTH LOGIN");
|
|
||||||
|
|
||||||
// Wait for 334 response code
|
|
||||||
waitForResponse();
|
|
||||||
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
|
|
||||||
|
|
||||||
// Send the username in base64
|
|
||||||
sendMessage(QByteArray().append(user).toBase64());
|
|
||||||
|
|
||||||
// Wait for 334
|
|
||||||
waitForResponse();
|
|
||||||
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
|
|
||||||
|
|
||||||
// Send the password in base64
|
|
||||||
sendMessage(QByteArray().append(password).toBase64());
|
|
||||||
|
|
||||||
// Wait for the server's responce
|
|
||||||
waitForResponse();
|
|
||||||
|
|
||||||
// If the response is not 235 then the authentication was faild
|
|
||||||
if (responseCode != 235)
|
|
||||||
{
|
|
||||||
emit smtpError(AuthenticationFailedError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ResponseTimeoutException e)
|
|
||||||
{
|
|
||||||
// Responce Timeout exceeded
|
|
||||||
emit smtpError(AuthenticationFailedError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SmtpClient::sendMail(MimeMessage& email)
|
bool SmtpClient::sendMail(MimeMessage& email)
|
||||||
@ -392,41 +265,103 @@ void SmtpClient::waitForResponse() throw (ResponseTimeoutException)
|
|||||||
void SmtpClient::changeState(ClientState state) {
|
void SmtpClient::changeState(ClientState state) {
|
||||||
this->state = state;
|
this->state = state;
|
||||||
|
|
||||||
|
#ifdef QT_NO_DEBUG
|
||||||
|
// Emit stateChanged signal only for non-internal states
|
||||||
|
if (state <= DisconnectingState) {
|
||||||
|
emit stateChanged(state);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// emit all in debug mode
|
||||||
|
emit stateChanged(state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case _ELHO_State:
|
case ConnectingState:
|
||||||
// Service ready. Send ELHO message and chage the state
|
switch (connectionType)
|
||||||
sendMessage("ELHO " + name);
|
{
|
||||||
|
case TlsConnection:
|
||||||
|
case TcpConnection:
|
||||||
|
socket->connectToHost(host, port);
|
||||||
|
break;
|
||||||
|
case SslConnection:
|
||||||
|
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case _EHLO_State:
|
||||||
|
// Service ready. Send EHLO message and chage the state
|
||||||
|
sendMessage("EHLO " + name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _READY_Connected:
|
||||||
|
changeState(ReadyState);
|
||||||
|
emit readyConnected();
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* --- TLS --- */
|
||||||
case _TLS_State:
|
case _TLS_State:
|
||||||
|
changeState(_TLS_0_STARTTLS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _TLS_0_STARTTLS:
|
||||||
// send a request to start TLS handshake
|
// send a request to start TLS handshake
|
||||||
sendMessage("STARTTLS");
|
sendMessage("STARTTLS");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case _TLS_1_ENCRYPT:
|
||||||
|
((QSslSocket*) socket)->startClientEncryption();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _TLS_2_EHLO:
|
||||||
|
// Send EHLO one more time
|
||||||
|
sendMessage("EHLO " + name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _READY_Encrypted:
|
||||||
|
changeState(_READY_Connected);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* --- AUTH --- */
|
||||||
|
case AuthenticatingState:
|
||||||
|
changeState(authMethod == AuthPlain ? _AUTH_PLAIN_0 : _AUTH_LOGIN_0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_PLAIN_0:
|
||||||
|
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
|
||||||
|
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user)
|
||||||
|
.append((char) 0).append(password).toBase64());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_0:
|
||||||
|
sendMessage("AUTH LOGIN");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_1_USER:
|
||||||
|
// Send the username in base64
|
||||||
|
sendMessage(QByteArray().append(user).toBase64());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_2_PASS:
|
||||||
|
// Send the password in base64
|
||||||
|
sendMessage(QByteArray().append(password).toBase64());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _READY_Authenticated:
|
||||||
|
changeState(ReadyState);
|
||||||
|
emit authenticated();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state <= DisconnectingState) { // don't emit for internal signals
|
|
||||||
emit stateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmtpClient::processResponse() {
|
void SmtpClient::processResponse() {
|
||||||
// Save the server's response
|
|
||||||
responseText = socket->readAll();
|
|
||||||
|
|
||||||
// Extract the respose code from the server's responce (first 3 digits)
|
|
||||||
responseCode = responseText.left(3).toInt();
|
|
||||||
|
|
||||||
if (responseCode / 100 == 4) {
|
|
||||||
emit smtpError(ServerError); return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseCode / 100 == 5) {
|
|
||||||
emit smtpError(ClientError); return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
@ -435,16 +370,69 @@ void SmtpClient::processResponse() {
|
|||||||
if (responseCode != 220) {
|
if (responseCode != 220) {
|
||||||
emit smtpError(ServerError); return;
|
emit smtpError(ServerError); return;
|
||||||
}
|
}
|
||||||
changeState(_ELHO_State);
|
changeState(_EHLO_State);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _ELHO_State:
|
case _EHLO_State:
|
||||||
// The response code needs to be 250.
|
// The response code needs to be 250.
|
||||||
if (responseCode != 250) {
|
if (responseCode != 250) {
|
||||||
emit smtpError(ServerError); return;
|
emit smtpError(ServerError); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
changeState((connectionType != TlsConnection) ? ReadyState : _TLS_State);
|
changeState((connectionType != TlsConnection) ? _READY_Connected : _TLS_State);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* --- TLS --- */
|
||||||
|
case _TLS_0_STARTTLS:
|
||||||
|
// The response code needs to be 220.
|
||||||
|
if (responseCode != 220) {
|
||||||
|
emit smtpError(ServerError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_TLS_1_ENCRYPT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _TLS_2_EHLO:
|
||||||
|
// The response code needs to be 250.
|
||||||
|
if (responseCode != 250) {
|
||||||
|
emit smtpError(ServerError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_READY_Encrypted);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* --- AUTH --- */
|
||||||
|
case _AUTH_PLAIN_0:
|
||||||
|
// If the response is not 235 then the authentication was failed
|
||||||
|
if (responseCode != 235) {
|
||||||
|
emit smtpError(AuthenticationFailedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_READY_Authenticated);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_0:
|
||||||
|
if (responseCode != 334) {
|
||||||
|
emit smtpError(AuthenticationFailedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_AUTH_LOGIN_1_USER);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_1_USER:
|
||||||
|
if (responseCode != 334) {
|
||||||
|
emit smtpError(AuthenticationFailedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_AUTH_LOGIN_2_PASS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _AUTH_LOGIN_2_PASS:
|
||||||
|
if (responseCode != 235) {
|
||||||
|
emit smtpError(AuthenticationFailedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeState(_READY_Authenticated);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -454,6 +442,7 @@ void SmtpClient::processResponse() {
|
|||||||
|
|
||||||
void SmtpClient::sendMessage(const QString &text)
|
void SmtpClient::sendMessage(const QString &text)
|
||||||
{
|
{
|
||||||
|
socket->flush();
|
||||||
socket->write(text.toUtf8() + "\r\n");
|
socket->write(text.toUtf8() + "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,12 +467,47 @@ void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SmtpClient::socketError(QAbstractSocket::SocketError socketError) {
|
void SmtpClient::socketError(QAbstractSocket::SocketError socketError) {
|
||||||
|
qDebug() << "this is sparta" << endl;
|
||||||
emit smtpError(SocketError);
|
emit smtpError(SocketError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmtpClient::socketReadyRead()
|
void SmtpClient::socketReadyRead()
|
||||||
{
|
{
|
||||||
|
QString responseLine;
|
||||||
|
|
||||||
|
while (socket->canReadLine()) {
|
||||||
|
// Save the server's response
|
||||||
|
responseLine = socket->readLine();
|
||||||
|
tempResponse += responseLine;
|
||||||
|
|
||||||
|
// Extract the respose code from the server's responce (first 3 digits)
|
||||||
|
responseCode = responseLine.left(3).toInt();
|
||||||
|
|
||||||
|
// Check for server error
|
||||||
|
if (responseCode / 100 == 4) {
|
||||||
|
emit smtpError(ServerError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for client error
|
||||||
|
if (responseCode / 100 == 5) {
|
||||||
|
emit smtpError(ClientError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this the last line of the response
|
||||||
|
if (responseLine[3] == ' ') {
|
||||||
|
responseText = tempResponse;
|
||||||
|
processResponse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmtpClient::socketEncrypted() {
|
||||||
|
if (state == _TLS_1_ENCRYPT) {
|
||||||
|
changeState(_TLS_2_EHLO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [5] --- */
|
/* [5] --- */
|
||||||
|
@ -65,13 +65,27 @@ public:
|
|||||||
DisconnectingState = 6,
|
DisconnectingState = 6,
|
||||||
|
|
||||||
/* Internal States */
|
/* Internal States */
|
||||||
_ELHO_State = 50,
|
_EHLO_State = 50,
|
||||||
_TLS_State = 51,
|
_TLS_State = 51,
|
||||||
|
|
||||||
|
_READY_Connected = 52,
|
||||||
|
_READY_Authenticated = 53,
|
||||||
|
_READY_MailSended = 54,
|
||||||
|
_READY_Encrypted = 55,
|
||||||
|
|
||||||
/* Internal Substates */
|
/* Internal Substates */
|
||||||
_TLS_0_START = 60,
|
|
||||||
|
// TLS
|
||||||
|
_TLS_0_STARTTLS = 60,
|
||||||
_TLS_1_ENCRYPT = 61,
|
_TLS_1_ENCRYPT = 61,
|
||||||
_TLS_2_ELHO = 62
|
_TLS_2_EHLO = 62,
|
||||||
|
|
||||||
|
// AUTH
|
||||||
|
_AUTH_PLAIN_0 = 63,
|
||||||
|
_AUTH_LOGIN_0 = 64,
|
||||||
|
_AUTH_LOGIN_1_USER = 65,
|
||||||
|
_AUTH_LOGIN_2_PASS = 66
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,7 +138,8 @@ public:
|
|||||||
bool connectToHost();
|
bool connectToHost();
|
||||||
|
|
||||||
bool login();
|
bool login();
|
||||||
bool login(const QString &user, const QString &password, AuthMethod method = AuthLogin);
|
bool login(const QString &user, const QString &password,
|
||||||
|
AuthMethod method = AuthLogin);
|
||||||
|
|
||||||
bool sendMail(MimeMessage& email);
|
bool sendMail(MimeMessage& email);
|
||||||
|
|
||||||
@ -154,6 +169,7 @@ protected:
|
|||||||
int responseTimeout;
|
int responseTimeout;
|
||||||
|
|
||||||
QString responseText;
|
QString responseText;
|
||||||
|
QString tempResponse;
|
||||||
int responseCode;
|
int responseCode;
|
||||||
|
|
||||||
|
|
||||||
@ -180,6 +196,7 @@ protected slots:
|
|||||||
void socketStateChanged(QAbstractSocket::SocketState state);
|
void socketStateChanged(QAbstractSocket::SocketState state);
|
||||||
void socketError(QAbstractSocket::SocketError error);
|
void socketError(QAbstractSocket::SocketError error);
|
||||||
void socketReadyRead();
|
void socketReadyRead();
|
||||||
|
void socketEncrypted();
|
||||||
|
|
||||||
/* [6] --- */
|
/* [6] --- */
|
||||||
|
|
||||||
@ -189,7 +206,12 @@ signals:
|
|||||||
/* [7] Signals */
|
/* [7] Signals */
|
||||||
|
|
||||||
void smtpError(SmtpError e);
|
void smtpError(SmtpError e);
|
||||||
void stateChanged(ClientState s);
|
void stateChanged(SmtpClient::ClientState s);
|
||||||
|
void connected();
|
||||||
|
void readyConnected();
|
||||||
|
void authenticated();
|
||||||
|
void mailSended();
|
||||||
|
void disconnected();
|
||||||
|
|
||||||
/* [7] --- */
|
/* [7] --- */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user