Improve mail notification plugin
This commit is contained in:
parent
4b11f1b481
commit
9c3758742f
@ -148,9 +148,11 @@ void DevicePluginMailNotification::testLoginFinished(const bool &success)
|
||||
{
|
||||
SmtpClient *smtpClient = static_cast<SmtpClient*>(sender());
|
||||
Device *device = m_clients.value(smtpClient);
|
||||
if(success) {
|
||||
if (success) {
|
||||
qCDebug(dcMailNotification()) << "Email login test successfull";
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
|
||||
} else {
|
||||
qCWarning(dcMailNotification()) << "Email login test failed";
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
|
||||
if(m_clients.contains(smtpClient)) {
|
||||
m_clients.remove(smtpClient);
|
||||
@ -161,9 +163,11 @@ void DevicePluginMailNotification::testLoginFinished(const bool &success)
|
||||
|
||||
void DevicePluginMailNotification::sendMailFinished(const bool &success, const ActionId &actionId)
|
||||
{
|
||||
if(success) {
|
||||
if (success) {
|
||||
qCDebug(dcMailNotification()) << "Email sent successfully";
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
|
||||
} else {
|
||||
qCWarning(dcMailNotification()) << "Email sending failed";
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorDeviceNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,28 +18,28 @@
|
||||
{
|
||||
"id": "af30ce7b-fb6b-42f0-889d-20b32f8b8fa4",
|
||||
"name": "customSender",
|
||||
"displayName": "sender mail",
|
||||
"displayName": "Sender mail",
|
||||
"type": "QString",
|
||||
"inputType": "Mail"
|
||||
},
|
||||
{
|
||||
"id": "b91d0ecc-6903-4991-ae8d-f36757ce40a7",
|
||||
"name": "customUser",
|
||||
"displayName": "user",
|
||||
"displayName": "User",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine"
|
||||
},
|
||||
{
|
||||
"id": "ac29e643-1d18-4612-8d2f-65fb07a67182",
|
||||
"name": "customPassword",
|
||||
"displayName": "password",
|
||||
"displayName": "Password",
|
||||
"type": "QString",
|
||||
"inputType": "Password"
|
||||
},
|
||||
{
|
||||
"id": "d657f002-9741-42e1-9fef-32eae96dacdb",
|
||||
"name": "customRecipient",
|
||||
"displayName": "recipient",
|
||||
"displayName": "Recipient",
|
||||
"type": "QString",
|
||||
"inputType": "Mail"
|
||||
},
|
||||
@ -53,9 +53,9 @@
|
||||
{
|
||||
"id": "56ec204f-2e02-4a17-b9d1-e855e384b689",
|
||||
"name": "port",
|
||||
"displayName": "port",
|
||||
"displayName": "Port",
|
||||
"type": "int",
|
||||
"defaultValue": "465"
|
||||
"defaultValue": 25
|
||||
},
|
||||
{
|
||||
"id": "789b963b-4143-4e21-853c-2612707d726f",
|
||||
|
||||
@ -23,6 +23,10 @@
|
||||
#include "smtpclient.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
Q_LOGGING_CATEGORY(dcSmtpClient, "SmtpClient")
|
||||
|
||||
SmtpClient::SmtpClient(QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
@ -31,6 +35,7 @@ SmtpClient::SmtpClient(QObject *parent):
|
||||
connect(m_socket, &QSslSocket::connected, this, &SmtpClient::connected);
|
||||
connect(m_socket, &QSslSocket::readyRead, this, &SmtpClient::readData);
|
||||
connect(m_socket, &QSslSocket::disconnected, this, &SmtpClient::disconnected);
|
||||
connect(m_socket, &QSslSocket::encrypted, this, &SmtpClient::onEncrypted);
|
||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
}
|
||||
|
||||
@ -44,24 +49,34 @@ void SmtpClient::connectToHost()
|
||||
case EncryptionTypeSSL:
|
||||
m_socket->connectToHostEncrypted(m_host, m_port);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SmtpClient::testLogin()
|
||||
{
|
||||
m_socket->close();
|
||||
m_testLogin = true;
|
||||
setState(StateInitialize);
|
||||
m_socket->close();
|
||||
connectToHost();
|
||||
}
|
||||
|
||||
void SmtpClient::connected()
|
||||
{
|
||||
qCDebug(dcSmtpClient()) << "Connected";
|
||||
}
|
||||
|
||||
void SmtpClient::disconnected()
|
||||
{
|
||||
qCDebug(dcSmtpClient()) << "Disconnected";
|
||||
setState(StateIdle);
|
||||
sendNextMail();
|
||||
}
|
||||
|
||||
void SmtpClient::onEncrypted()
|
||||
{
|
||||
qCDebug(dcSmtpClient()) << "Socket encrypted";
|
||||
send("EHLO localhost");
|
||||
setState(StateAuthentification);
|
||||
}
|
||||
|
||||
void SmtpClient::readData()
|
||||
@ -74,94 +89,84 @@ void SmtpClient::readData()
|
||||
response.append(responseLine);
|
||||
}
|
||||
responseLine.truncate(3);
|
||||
|
||||
qCDebug(dcMailNotification()) << "<--" << response << "|" << responseLine;
|
||||
qCDebug(dcSmtpClient()) << "<--" << response;
|
||||
|
||||
switch (m_state) {
|
||||
case InitState:
|
||||
case StateIdle:
|
||||
sendNextMail();
|
||||
break;
|
||||
case StateInitialize:
|
||||
if (responseLine == "220") {
|
||||
send("EHLO localhost");
|
||||
|
||||
if (m_encryptionType == EncryptionTypeNone) {
|
||||
m_state = AuthentificationState;
|
||||
setState(StateAuthentification);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_encryptionType == EncryptionTypeSSL) {
|
||||
m_state = HandShakeState;
|
||||
setState(StateHandShake);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_encryptionType == EncryptionTypeTLS) {
|
||||
m_state = StartTlsState;
|
||||
setState(StateStartTls);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HandShakeState:
|
||||
if (responseLine == "250") {
|
||||
case StateHandShake:
|
||||
if (responseLine == "250" || responseLine == "220") {
|
||||
if (!m_socket->isEncrypted() && m_encryptionType != EncryptionTypeNone) {
|
||||
qCDebug(dcSmtpClient()) << "Start client encryption...";
|
||||
m_socket->startClientEncryption();
|
||||
}
|
||||
send("EHLO localhost");
|
||||
m_state = AuthentificationState;
|
||||
}
|
||||
|
||||
if (responseLine == "220") {
|
||||
if (!m_socket->isEncrypted() && m_encryptionType != EncryptionTypeNone) {
|
||||
m_socket->startClientEncryption();
|
||||
}
|
||||
|
||||
send("EHLO localhost");
|
||||
m_state = AuthentificationState;
|
||||
}
|
||||
|
||||
break;
|
||||
case StartTlsState:
|
||||
case StateStartTls:
|
||||
if (responseLine == "250") {
|
||||
send("STARTTLS");
|
||||
m_state = HandShakeState;
|
||||
setState(StateHandShake);
|
||||
}
|
||||
|
||||
break;
|
||||
case AuthentificationState:
|
||||
case StateAuthentification:
|
||||
if (responseLine == "250") {
|
||||
if (m_authenticationMethod == AuthenticationMethodLogin) {
|
||||
send("AUTH LOGIN");
|
||||
m_state = UserState;
|
||||
setState(StateUser);
|
||||
break;
|
||||
}
|
||||
if (m_authenticationMethod == AuthenticationMethodPlain) {
|
||||
send("AUTH PLAIN " + QByteArray().append((char) 0).append(m_user).append((char) 0).append(m_password).toBase64());
|
||||
// if we just want to test the Login, we are almost done here
|
||||
if (!m_testLogin) {
|
||||
m_state = MailState;
|
||||
setState(StateMail);
|
||||
} else {
|
||||
m_state = TestLoginFinishedState;
|
||||
setState(StateTestLoginFinished);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UserState:
|
||||
case StateUser:
|
||||
if (responseLine == "334") {
|
||||
send(QByteArray().append(m_user).toBase64());
|
||||
m_state = PasswordState;
|
||||
setState(StatePassword);
|
||||
}
|
||||
break;
|
||||
case PasswordState:
|
||||
case StatePassword:
|
||||
if (responseLine == "334") {
|
||||
send(QByteArray().append(m_password).toBase64());
|
||||
// if we just want to test the Login, we are almost done here
|
||||
if (!m_testLogin) {
|
||||
m_state = MailState;
|
||||
setState(StateMail);
|
||||
} else {
|
||||
m_state = TestLoginFinishedState;
|
||||
setState(StateTestLoginFinished);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TestLoginFinishedState:
|
||||
case StateTestLoginFinished:
|
||||
if (responseLine == "235") {
|
||||
emit testLoginFinished(true);
|
||||
} else {
|
||||
@ -170,77 +175,56 @@ void SmtpClient::readData()
|
||||
m_socket->close();
|
||||
m_testLogin = false;
|
||||
break;
|
||||
case MailState:
|
||||
case StateMail:
|
||||
if (responseLine == "235") {
|
||||
send("MAIL FROM:<" + m_sender + ">");
|
||||
m_state = RcptState;
|
||||
setState(StateRcpt);
|
||||
}
|
||||
break;
|
||||
case RcptState:
|
||||
case StateRcpt:
|
||||
if (responseLine == "250") {
|
||||
send("RCPT TO:<" + m_rcpt + ">");
|
||||
m_state = DataState;
|
||||
setState(StateData);
|
||||
}
|
||||
break;
|
||||
case DataState:
|
||||
case StateData:
|
||||
if (responseLine == "250") {
|
||||
send("DATA");
|
||||
m_state = BodyState;
|
||||
setState(StateBody);
|
||||
}
|
||||
break;
|
||||
case BodyState:
|
||||
case StateBody:
|
||||
if (responseLine == "354") {
|
||||
send(m_message + "\r\n.\r\n");
|
||||
m_state = QuitState;
|
||||
send(m_messageData + "\r\n.\r\n");
|
||||
setState(StateQuit);
|
||||
}
|
||||
break;
|
||||
case QuitState:
|
||||
case StateQuit:
|
||||
if (responseLine == "250") {
|
||||
emit sendMailFinished(true, m_actionId);
|
||||
emit sendMailFinished(true, m_message.actionId);
|
||||
send("QUIT");
|
||||
m_state = CloseState;
|
||||
setState(StateClose);
|
||||
}
|
||||
break;
|
||||
case CloseState:
|
||||
case StateClose:
|
||||
if (responseLine == "221") {
|
||||
m_socket->close();
|
||||
}
|
||||
// some mail server does not recognize the QUIT command...so close the connection either way
|
||||
m_socket->close();
|
||||
break;
|
||||
default:
|
||||
// unexpecterd response code received...
|
||||
if (m_testLogin) {
|
||||
emit testLoginFinished(false);
|
||||
m_testLogin = false;
|
||||
m_socket->close();
|
||||
break;
|
||||
}
|
||||
emit sendMailFinished(false, m_actionId);
|
||||
m_socket->close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SmtpClient::sendMail(const QString &subject, const QString &body, const ActionId &actionId)
|
||||
void SmtpClient::sendMail(const QString &subject, const QString &body, const ActionId &actionId)
|
||||
{
|
||||
m_actionId = actionId;
|
||||
Message message;
|
||||
message.subject = subject;
|
||||
message.body = body;
|
||||
message.actionId = actionId;
|
||||
|
||||
// create mail content
|
||||
m_state = InitState;
|
||||
m_message.clear();
|
||||
|
||||
m_message = "To: " + m_rcpt + "\n";
|
||||
m_message.append("From: " + m_sender + "\n");
|
||||
m_message.append("Subject: [nymea notification] | " + subject + "\n");
|
||||
m_message.append(body);
|
||||
m_message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) );
|
||||
m_message.replace( QString::fromLatin1( "\r\n.\r\n" ), QString::fromLatin1( "\r\n..\r\n" ) );
|
||||
m_message.append("\r\n.\r\n");
|
||||
m_socket->close();
|
||||
connectToHost();
|
||||
|
||||
return true;
|
||||
m_messageQueue.enqueue(message);
|
||||
sendNextMail();
|
||||
}
|
||||
|
||||
void SmtpClient::setHost(const QString &host)
|
||||
@ -248,7 +232,7 @@ void SmtpClient::setHost(const QString &host)
|
||||
m_host = host;
|
||||
}
|
||||
|
||||
void SmtpClient::setPort(const int &port)
|
||||
void SmtpClient::setPort(const quint16 &port)
|
||||
{
|
||||
m_port = port;
|
||||
}
|
||||
@ -283,14 +267,88 @@ void SmtpClient::setRecipient(const QString &rcpt)
|
||||
m_rcpt = rcpt;
|
||||
}
|
||||
|
||||
QString SmtpClient::createDateString()
|
||||
{
|
||||
return QDateTime::currentDateTime().toString(Qt::RFC2822Date);
|
||||
}
|
||||
|
||||
void SmtpClient::setState(SmtpClient::State state)
|
||||
{
|
||||
if (m_state == state)
|
||||
return;
|
||||
|
||||
qCDebug(dcSmtpClient()) << state;
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
bool SmtpClient::verifyResponseCode(int responseCode)
|
||||
{
|
||||
if (responseCode >= 500) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmtpClient::sendNextMail()
|
||||
{
|
||||
// Check if there is a mail left to send
|
||||
if (m_messageQueue.isEmpty())
|
||||
return;
|
||||
|
||||
// Check if busy
|
||||
if (m_state != StateIdle) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendEmailInternally(m_messageQueue.dequeue());
|
||||
}
|
||||
|
||||
void SmtpClient::sendEmailInternally(const Message &message)
|
||||
{
|
||||
qCDebug(dcMailNotification()) << "Start sending message" << message.subject << message.body;
|
||||
|
||||
// Initialize data for sending
|
||||
m_message = message;
|
||||
m_messageData.clear();
|
||||
|
||||
// Create plain message content
|
||||
m_messageData = "To: " + m_rcpt + "\n";
|
||||
m_messageData.append("From: " + m_sender + "\n");
|
||||
m_messageData.append("Subject: " + message.subject + "\n");
|
||||
m_messageData.append("Date: " + createDateString() + "\n");
|
||||
m_messageData.append("Content-Type: text/plain; charset=\"UTF-8\"\n");
|
||||
m_messageData.append("Content-Transfer-Encoding: quoted-printable\n");
|
||||
m_messageData.append("MIME-Version: 1.0\n");
|
||||
m_messageData.append("X-Mailer: nymea;\n");
|
||||
m_messageData.append(message.body);
|
||||
m_messageData.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) );
|
||||
m_messageData.replace( QString::fromLatin1( "\r\n.\r\n" ), QString::fromLatin1( "\r\n..\r\n" ) );
|
||||
m_messageData.append("\r\n.\r\n");
|
||||
|
||||
setState(StateInitialize);
|
||||
|
||||
// Make sure the connection starts from the beginning
|
||||
m_socket->close();
|
||||
connectToHost();
|
||||
}
|
||||
|
||||
void SmtpClient::onSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qCWarning(dcMailNotification) << "Mail socket error" << error << m_socket->errorString();
|
||||
qCWarning(dcMailNotification()) << "Mail socket error" << error << m_socket->errorString();
|
||||
if (m_state != StateIdle) {
|
||||
if (m_testLogin) {
|
||||
emit testLoginFinished(false);
|
||||
} else {
|
||||
emit sendMailFinished(false, m_message.actionId);
|
||||
}
|
||||
m_socket->close();
|
||||
setState(StateIdle);
|
||||
}
|
||||
}
|
||||
|
||||
void SmtpClient::send(const QString &data)
|
||||
{
|
||||
qCDebug(dcMailNotification()) << "-->" << qUtf8Printable(data.toUtf8());
|
||||
qCDebug(dcSmtpClient()) << "-->" << data;
|
||||
m_socket->write(data.toUtf8() + "\r\n");
|
||||
m_socket->flush();
|
||||
}
|
||||
|
||||
@ -27,9 +27,20 @@
|
||||
#include <QDebug>
|
||||
#include <QTcpSocket>
|
||||
#include <QSslSocket>
|
||||
#include <QLoggingCategory>
|
||||
#include <QQueue>
|
||||
|
||||
#include "plugin/deviceplugin.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcSmtpClient)
|
||||
|
||||
struct Message {
|
||||
QString subject;
|
||||
QString body;
|
||||
ActionId actionId;
|
||||
};
|
||||
|
||||
|
||||
class SmtpClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -41,22 +52,23 @@ public:
|
||||
};
|
||||
Q_ENUM(AuthenticationMethod)
|
||||
|
||||
enum SendState{
|
||||
InitState,
|
||||
HandShakeState,
|
||||
AuthentificationState,
|
||||
StartTlsState,
|
||||
UserState,
|
||||
PasswordState,
|
||||
TestLoginFinishedState,
|
||||
MailState,
|
||||
RcptState,
|
||||
DataState,
|
||||
BodyState,
|
||||
QuitState,
|
||||
CloseState
|
||||
enum State{
|
||||
StateIdle,
|
||||
StateInitialize,
|
||||
StateHandShake,
|
||||
StateAuthentification,
|
||||
StateStartTls,
|
||||
StateUser,
|
||||
StatePassword,
|
||||
StateTestLoginFinished,
|
||||
StateMail,
|
||||
StateRcpt,
|
||||
StateData,
|
||||
StateBody,
|
||||
StateQuit,
|
||||
StateClose
|
||||
};
|
||||
Q_ENUM(SendState)
|
||||
Q_ENUM(State)
|
||||
|
||||
enum EncryptionType{
|
||||
EncryptionTypeNone,
|
||||
@ -65,14 +77,14 @@ public:
|
||||
};
|
||||
Q_ENUM(EncryptionType)
|
||||
|
||||
explicit SmtpClient(QObject *parent = 0);
|
||||
explicit SmtpClient(QObject *parent = nullptr);
|
||||
|
||||
void connectToHost();
|
||||
void testLogin();
|
||||
bool sendMail(const QString &subject, const QString &body, const ActionId &actionId);
|
||||
void sendMail(const QString &subject, const QString &body, const ActionId &actionId);
|
||||
|
||||
void setHost(const QString &host);
|
||||
void setPort(const int &port);
|
||||
void setPort(const quint16 &port);
|
||||
void setEncryptionType(const EncryptionType &encryptionType);
|
||||
void setAuthenticationMethod(const AuthenticationMethod &authenticationMethod);
|
||||
void setUser(const QString &user);
|
||||
@ -82,9 +94,9 @@ public:
|
||||
|
||||
private:
|
||||
QSslSocket *m_socket = nullptr;
|
||||
SendState m_state = InitState;
|
||||
State m_state = StateIdle;
|
||||
QString m_host = "127.0.0.1";
|
||||
int m_port = 25;
|
||||
quint16 m_port = 25;
|
||||
|
||||
QString m_user;
|
||||
QString m_password;
|
||||
@ -92,13 +104,22 @@ private:
|
||||
AuthenticationMethod m_authenticationMethod;
|
||||
EncryptionType m_encryptionType;
|
||||
QString m_rcpt;
|
||||
QString m_subject;
|
||||
QString m_boy;
|
||||
QString m_message;
|
||||
ActionId m_actionId;
|
||||
|
||||
// Created for each message
|
||||
Message m_message;
|
||||
QString m_messageData;
|
||||
|
||||
QQueue<Message> m_messageQueue;
|
||||
|
||||
bool m_testLogin = false;
|
||||
|
||||
QString createDateString();
|
||||
void setState(State state);
|
||||
bool verifyResponseCode(int responseCode);
|
||||
|
||||
void sendNextMail();
|
||||
void sendEmailInternally(const Message &message);
|
||||
|
||||
signals:
|
||||
void sendMailFinished(const bool &success, const ActionId &actionId);
|
||||
void testLoginFinished(const bool &success);
|
||||
@ -107,6 +128,7 @@ private slots:
|
||||
void onSocketError(QAbstractSocket::SocketError error);
|
||||
void connected();
|
||||
void disconnected();
|
||||
void onEncrypted();
|
||||
void readData();
|
||||
void send(const QString &data);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user