First working version of the state machine implementation.

This commit is contained in:
Attila Tőkés 2012-09-08 00:07:45 +03:00 committed by Attila
parent 618d583551
commit 297f2f8178
2 changed files with 127 additions and 83 deletions

View File

@ -178,73 +178,15 @@ bool SmtpClient::login(const QString &user, const QString &password, AuthMethod
bool SmtpClient::sendMail(MimeMessage& email) bool SmtpClient::sendMail(MimeMessage& email)
{ {
try this->email = &email;
{ changeState(MailSendingState);
// Send the MAIL command with the sender
sendMessage("MAIL FROM: <" + email.getSender().getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
// Send RCPT command for each recipient
QList<EmailAddress*>::const_iterator it, itEnd;
// To (primary recipients)
for (it = email.getRecipients().begin(), itEnd = email.getRecipients().end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Cc (carbon copy)
for (it = email.getRecipients(MimeMessage::Cc).begin(), itEnd = email.getRecipients(MimeMessage::Cc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Bcc (blind carbon copy)
for (it = email.getRecipients(MimeMessage::Bcc).begin(), itEnd = email.getRecipients(MimeMessage::Bcc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Send DATA command
sendMessage("DATA");
waitForResponse();
if (responseCode != 354) return false;
sendMessage(email.toString());
// Send \r\n.\r\n to end the mail data
sendMessage(".");
waitForResponse();
if (responseCode != 250) return false;
}
catch (ResponseTimeoutException)
{
return false;
}
return true; return true;
} }
void SmtpClient::quit() void SmtpClient::quit()
{ {
sendMessage("QUIT"); changeState(DisconnectingState);
} }
/* [3] --- */ /* [3] --- */
@ -291,6 +233,19 @@ void SmtpClient::changeState(ClientState state) {
} }
break; break;
case AuthenticatingState:
changeState(authMethod == AuthPlain ? _AUTH_PLAIN_0 : _AUTH_LOGIN_0);
break;
case MailSendingState:
changeState(_MAIL_0_FROM);
break;
case DisconnectingState:
sendMessage("QUIT");
socket->disconnectFromHost();
break;
case _EHLO_State: case _EHLO_State:
// Service ready. Send EHLO message and chage the state // Service ready. Send EHLO message and chage the state
sendMessage("EHLO " + name); sendMessage("EHLO " + name);
@ -325,10 +280,6 @@ void SmtpClient::changeState(ClientState state) {
break; break;
/* --- AUTH --- */ /* --- AUTH --- */
case AuthenticatingState:
changeState(authMethod == AuthPlain ? _AUTH_PLAIN_0 : _AUTH_LOGIN_0);
break;
case _AUTH_PLAIN_0: case _AUTH_PLAIN_0:
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password) // Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user) sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user)
@ -354,11 +305,58 @@ void SmtpClient::changeState(ClientState state) {
emit authenticated(); emit authenticated();
break; break;
/* --- MAIL --- */
case _MAIL_0_FROM:
sendMessage("MAIL FROM: <" + email->getSender().getAddress() + ">");
break;
case _MAIL_1_RCPT_INIT:
rcptType++;
switch (rcptType)
{
case _TO:
addressList = &email->getRecipients(MimeMessage::To);
break;
case _CC:
addressList = &email->getRecipients(MimeMessage::Cc);
break;
case _BCC:
addressList = &email->getRecipients(MimeMessage::Bcc);
break;
default:
changeState(_MAIL_3_DATA);
return;
}
addressIt = addressList->constBegin();
changeState(_MAIL_2_RCPT);
break;
case _MAIL_2_RCPT:
if (addressIt != addressList->end()) {
sendMessage("RCPT TO: <" + (*addressIt)->getAddress() + ">");
addressIt++;
} else {
changeState(_MAIL_1_RCPT_INIT);
}
break;
case _MAIL_3_DATA:
sendMessage("DATA");
break;
case _MAIL_4_SEND_DATA:
sendMessage(email->toString());
sendMessage(".");
break;
case _READY_MailSended:
changeState(ReadyState);
emit mailSended();
break;
default: default:
; ;
} }
} }
void SmtpClient::processResponse() { void SmtpClient::processResponse() {
@ -405,7 +403,7 @@ void SmtpClient::processResponse() {
case _AUTH_PLAIN_0: case _AUTH_PLAIN_0:
// If the response is not 235 then the authentication was failed // If the response is not 235 then the authentication was failed
if (responseCode != 235) { if (responseCode != 235) {
emit smtpError(AuthenticationFailedError); emit smtpError(AuthenticationError);
return; return;
} }
changeState(_READY_Authenticated); changeState(_READY_Authenticated);
@ -413,7 +411,7 @@ void SmtpClient::processResponse() {
case _AUTH_LOGIN_0: case _AUTH_LOGIN_0:
if (responseCode != 334) { if (responseCode != 334) {
emit smtpError(AuthenticationFailedError); emit smtpError(AuthenticationError);
return; return;
} }
changeState(_AUTH_LOGIN_1_USER); changeState(_AUTH_LOGIN_1_USER);
@ -421,7 +419,7 @@ void SmtpClient::processResponse() {
case _AUTH_LOGIN_1_USER: case _AUTH_LOGIN_1_USER:
if (responseCode != 334) { if (responseCode != 334) {
emit smtpError(AuthenticationFailedError); emit smtpError(AuthenticationError);
return; return;
} }
changeState(_AUTH_LOGIN_2_PASS); changeState(_AUTH_LOGIN_2_PASS);
@ -429,12 +427,46 @@ void SmtpClient::processResponse() {
case _AUTH_LOGIN_2_PASS: case _AUTH_LOGIN_2_PASS:
if (responseCode != 235) { if (responseCode != 235) {
emit smtpError(AuthenticationFailedError); emit smtpError(AuthenticationError);
return; return;
} }
changeState(_READY_Authenticated); changeState(_READY_Authenticated);
break; break;
/* --- MAIL --- */
case _MAIL_0_FROM:
if (responseCode != 250) {
emit smtpError(MailSendingError);
return;
}
changeState(_MAIL_1_RCPT_INIT);
break;
case _MAIL_2_RCPT:
if (responseCode != 250) {
emit smtpError(MailSendingError);
return;
}
changeState(_MAIL_2_RCPT);
break;
case _MAIL_3_DATA:
if (responseCode != 354) {
emit smtpError(MailSendingError);
return;
}
changeState(_MAIL_4_SEND_DATA);
break;
case _MAIL_4_SEND_DATA:
if (responseCode != 250) {
emit smtpError(MailSendingError);
return;
}
changeState(_READY_MailSended);
break;
default: default:
; ;
} }

View File

@ -40,12 +40,13 @@ public:
enum SmtpError enum SmtpError
{ {
ConnectionTimeoutError, ConnectionTimeoutError = 0,
ResponseTimeoutError, ResponseTimeoutError = 1,
AuthenticationFailedError, AuthenticationError = 2,
ServerError, // 4xx smtp error MailSendingError = 3,
ClientError, // 5xx smtp error ServerError = 4, // 4xx smtp error
SocketError ClientError = 5, // 5xx smtp error
SocketError = 6
}; };
enum ConnectionType enum ConnectionType
@ -81,12 +82,17 @@ public:
_TLS_2_EHLO = 62, _TLS_2_EHLO = 62,
// AUTH // AUTH
_AUTH_PLAIN_0 = 63, _AUTH_PLAIN_0 = 70,
_AUTH_LOGIN_0 = 64, _AUTH_LOGIN_0 = 71,
_AUTH_LOGIN_1_USER = 65, _AUTH_LOGIN_1_USER = 72,
_AUTH_LOGIN_2_PASS = 66 _AUTH_LOGIN_2_PASS = 73,
// MAIL
_MAIL_0_FROM = 81,
_MAIL_1_RCPT_INIT = 82,
_MAIL_2_RCPT = 83,
_MAIL_3_DATA = 84,
_MAIL_4_SEND_DATA = 85
}; };
/* [0] --- */ /* [0] --- */
@ -172,6 +178,12 @@ protected:
QString tempResponse; QString tempResponse;
int responseCode; int responseCode;
MimeMessage *email;
QList<EmailAddress*>::const_iterator addressIt;
const QList<EmailAddress*> *addressList;
int rcptType;
enum _RcptType { _TO = 1, _CC = 2, _BCC = 3};
class ResponseTimeoutException {}; class ResponseTimeoutException {};
@ -205,7 +217,7 @@ signals:
/* [7] Signals */ /* [7] Signals */
void smtpError(SmtpError e); void smtpError(SmtpClient::SmtpError e);
void stateChanged(SmtpClient::ClientState s); void stateChanged(SmtpClient::ClientState s);
void connected(); void connected();
void readyConnected(); void readyConnected();