Skip to content

Commit

Permalink
impliments STARTTLS extension for SMTP.
Browse files Browse the repository at this point in the history
  • Loading branch information
treefrogframework committed Jun 19, 2016
1 parent c132b3a commit 8d90b5e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 23 deletions.
3 changes: 3 additions & 0 deletions defaults/application.ini
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ ActionMailer.smtp.HostName=
# Specify the connection's port number.
ActionMailer.smtp.Port=

# Enables STARTTLS extension if true.
ActionMailer.smtp.EnableSTARTTLS=false

# Enables SMTP authentication if true; disables SMTP
# authentication if false.
ActionMailer.smtp.Authentication=false
Expand Down
1 change: 1 addition & 0 deletions src/tactionmailer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ bool TActionMailer::deliver(const QString &templateName)
TSmtpMailer *mailer = new TSmtpMailer;
mailer->setHostName(Tf::appSettings()->value(Tf::ActionMailerSmtpHostName).toByteArray());
mailer->setPort(Tf::appSettings()->value(Tf::ActionMailerSmtpPort).toUInt());
mailer->setStartTlsEnabled(Tf::appSettings()->value(Tf::ActionMailerSmtpEnableSTARTTLS).toBool());
mailer->setAuthenticationEnabled(Tf::appSettings()->value(Tf::ActionMailerSmtpAuthentication).toBool());
mailer->setUserName(Tf::appSettings()->value(Tf::ActionMailerSmtpUserName).toByteArray());
mailer->setPassword(Tf::appSettings()->value(Tf::ActionMailerSmtpPassword).toByteArray());
Expand Down
1 change: 1 addition & 0 deletions src/tappsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class AttributeMap : public QMap<int, QString>
insert(Tf::ActionMailerDelayedDelivery, "ActionMailer.DelayedDelivery");
insert(Tf::ActionMailerSmtpHostName, "ActionMailer.smtp.HostName");
insert(Tf::ActionMailerSmtpPort, "ActionMailer.smtp.Port");
insert(Tf::ActionMailerSmtpEnableSTARTTLS, "ActionMailer.smtp.EnableSTARTTLS");
insert(Tf::ActionMailerSmtpAuthentication, "ActionMailer.smtp.Authentication");
insert(Tf::ActionMailerSmtpUserName, "ActionMailer.smtp.UserName");
insert(Tf::ActionMailerSmtpPassword, "ActionMailer.smtp.Password");
Expand Down
1 change: 1 addition & 0 deletions src/tfnamespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ namespace Tf
RedisSettingsFile,
LDPreload,
JavaScriptPath,
ActionMailerSmtpEnableSTARTTLS,
};

// Reason codes why a web socket has been closed
Expand Down
79 changes: 58 additions & 21 deletions src/tsmtpmailer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* the New BSD License, which is incorporated herein by reference.
*/

#include <QTcpSocket>
#include <QSslSocket>
#include <QHostAddress>
#include <QStringListIterator>
#include <QDateTime>
Expand All @@ -23,6 +23,9 @@
# define CRLF "\r\n"
#endif

//#define tSystemError(fmt, ...) printf(fmt "\n", ## __VA_ARGS__)
//#define tSystemDebug(fmt, ...) printf(fmt "\n", ## __VA_ARGS__)

static QMutex sendMutex;

/*!
Expand All @@ -32,13 +35,14 @@ static QMutex sendMutex;
*/

TSmtpMailer::TSmtpMailer(QObject *parent)
: QObject(parent), socket(new QTcpSocket), smtpPort(0), authEnable(false), pop(0)
: QObject(parent), socket(new QSslSocket), smtpPort(0), authEnable(false),
tlsEnable(false), tlsAvailable(false), pop(nullptr)
{ }


TSmtpMailer::TSmtpMailer(const QString &hostName, quint16 port, QObject *parent)
: QObject(parent), socket(new QTcpSocket), smtpHostName(hostName), smtpPort(port),
authEnable(false), pop(0)
: QObject(parent), socket(new QSslSocket), smtpHostName(hostName), smtpPort(port),
authEnable(false), tlsEnable(false), tlsAvailable(false), pop(nullptr)
{ }


Expand All @@ -49,9 +53,9 @@ TSmtpMailer::~TSmtpMailer()
// tSystemWarn("Mail not sent. Deleted it.");
}

if (pop)
if (pop) {
delete pop;

}
delete socket;
}

Expand Down Expand Up @@ -162,6 +166,13 @@ bool TSmtpMailer::send()
return false;
}

if (tlsEnable && tlsAvailable) {
if (!cmdStartTls()) {
cmdQuit();
return false;
}
}

if (authEnable) {
if (!cmdAuth()) {
tSystemError("SMTP: User Authentication Failed: username:%s", userName.data());
Expand Down Expand Up @@ -232,13 +243,38 @@ bool TSmtpMailer::cmdEhlo()
}

// Gets AUTH methods
for (QListIterator<QByteArray> i(reply); i.hasNext(); ) {
QString str(i.next());
for (auto &s : reply) {
QString str(s);
if (str.startsWith("AUTH ", Qt::CaseInsensitive)) {
svrAuthMethods = str.mid(5).split(' ', QString::SkipEmptyParts);
tSystemDebug("AUTH: %s", qPrintable(svrAuthMethods.join(",")));
break;
}
if (str.startsWith("STARTTLS", Qt::CaseInsensitive)) {
tlsAvailable = true;
}
}
return true;
}


bool TSmtpMailer::cmdStartTls()
{
int code = cmd("STARTTLS");
if (code != 220) {
tSystemError("SMTP: STARTTLS failed [reply:%d]", code);
return false;
}

socket->startClientEncryption();
if (!socket->waitForEncrypted(5000)) {
tSystemError("SMTP STARTTLS negotiation timeout: %s", qPrintable(socket->errorString()));
return false;
}

if (!cmdEhlo()) {
tSystemError("SMTP: EHLO Command Failed");
cmdQuit();
return false;
}
return true;
}
Expand Down Expand Up @@ -398,39 +434,40 @@ int TSmtpMailer::read(QList<QByteArray> *reply)
211 System status, or system help reply
214 Help message
(Information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user)
(Information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user)
220 <domain> Service ready
221 <domain> Service closing transmission channel
235 Authentication successful
250 Requested mail action okay, completed
251 User not local; will forward to <forward-path>
252 Cannot VRFY user, but will accept message and attempt
delivery
delivery
334 Continuation
354 Start mail input; end with <CRLF>.<CRLF>
421 <domain> Service not available, closing transmission channel
(This may be a reply to any command if the service knows it
must shut down)
(This may be a reply to any command if the service knows it
must shut down)
450 Requested mail action not taken: mailbox unavailable
(e.g., mailbox busy)
(e.g., mailbox busy)
451 Requested action aborted: local error in processing
452 Requested action not taken: insufficient system storage
454 TLS not available due to temporary reason
500 Syntax error, command unrecognized
(This may include errors such as command line too long)
(This may include errors such as command line too long)
501 Syntax error in parameters or arguments
502 Command not implemented (see section 4.2.4)
503 Bad sequence of commands
504 Command parameter not implemented
550 Requested action not taken: mailbox unavailable
(e.g., mailbox not found, no access, or command rejected
for policy reasons)
(e.g., mailbox not found, no access, or command rejected
for policy reasons)
551 User not local; please try <forward-path>
(See section 3.4)
(See section 3.4)
552 Requested mail action aborted: exceeded storage allocation
553 Requested action not taken: mailbox name not allowed
(e.g., mailbox syntax incorrect)
(e.g., mailbox syntax incorrect)
554 Transaction failed (Or, in the case of a connection-opening
response, "No SMTP service here")
*/
14 changes: 12 additions & 2 deletions src/tsmtpmailer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <QByteArray>
#include <TMailMessage>

class QTcpSocket;
class QSslSocket;
class TPopMailer;

class T_CORE_EXPORT TSmtpMailer : public QObject
Expand All @@ -30,6 +30,7 @@ class T_CORE_EXPORT TSmtpMailer : public QObject
quint16 port() const { return smtpPort; }
void setPort(quint16 port);
void setAuthenticationEnabled(bool enable);
void setStartTlsEnabled(bool enable);
void setPopBeforeSmtpAuthEnabled(const QString &popServer, quint16 port, bool apop, bool enable);
void setUserName(const QByteArray &username);
void setPassword(const QByteArray &password);
Expand All @@ -46,6 +47,7 @@ protected slots:
bool send();
bool connectToHost(const QString &hostName, quint16 port);
bool cmdEhlo();
bool cmdStartTls();
bool cmdAuth();
bool cmdRset();
bool cmdMail(const QByteArray &from);
Expand All @@ -60,12 +62,14 @@ protected slots:
private:
Q_DISABLE_COPY(TSmtpMailer)

QTcpSocket *socket;
QSslSocket *socket;
QString smtpHostName;
quint16 smtpPort;
TMailMessage mailMessage;
QStringList svrAuthMethods;
bool authEnable;
bool tlsEnable;
bool tlsAvailable;
QByteArray userName;
QByteArray password;
TPopMailer *pop;
Expand All @@ -78,6 +82,12 @@ inline void TSmtpMailer::setAuthenticationEnabled(bool enable)
}


inline void TSmtpMailer::setStartTlsEnabled(bool enable)
{
tlsEnable = enable;
}


inline void TSmtpMailer::setUserName(const QByteArray &username)
{
userName = username;
Expand Down

0 comments on commit 8d90b5e

Please sign in to comment.