diff options
Diffstat (limited to 'src/smtp/smtpclient.cpp')
-rw-r--r-- | src/smtp/smtpclient.cpp | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/smtp/smtpclient.cpp b/src/smtp/smtpclient.cpp new file mode 100644 index 00000000..f1d9fe0d --- /dev/null +++ b/src/smtp/smtpclient.cpp @@ -0,0 +1,405 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtp/smtpclient.h" + +#include <QByteArray> +#include <QFileInfo> + +/* [1] Constructors and destructors */ + +SmtpClient::SmtpClient(const QString &host, int port, + ConnectionType connectionType) + : socket(NULL), name("localhost"), authMethod(AuthPlain), + connectionTimeout(5000), responseTimeout(5000), + sendMessageTimeout(60000) { + setConnectionType(connectionType); + + this->host = host; + this->port = port; + + connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, + SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, + SLOT(socketError(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); +} + +SmtpClient::~SmtpClient() { + if (socket) + delete socket; +} + +/* [1] --- */ + +/* [2] Getters and Setters */ + +void SmtpClient::setUser(const QString &user) { this->user = user; } + +void SmtpClient::setPassword(const QString &password) { + this->password = password; +} + +void SmtpClient::setAuthMethod(AuthMethod method) { this->authMethod = method; } + +void SmtpClient::setHost(const QString &host) { this->host = host; } + +void SmtpClient::setPort(int port) { this->port = port; } + +void SmtpClient::setConnectionType(ConnectionType ct) { + this->connectionType = ct; + + if (socket) + delete socket; + + switch (connectionType) { + case TcpConnection: + socket = new QTcpSocket(this); + break; + case SslConnection: + case TlsConnection: + socket = new QSslSocket(this); + } +} + +const QString &SmtpClient::getHost() const { return this->host; } + +const QString &SmtpClient::getUser() const { return this->user; } + +const QString &SmtpClient::getPassword() const { return this->password; } + +SmtpClient::AuthMethod SmtpClient::getAuthMethod() const { + return this->authMethod; +} + +int SmtpClient::getPort() const { return this->port; } + +SmtpClient::ConnectionType SmtpClient::getConnectionType() const { + return connectionType; +} + +const QString &SmtpClient::getName() const { return this->name; } + +void SmtpClient::setName(const QString &name) { this->name = name; } + +const QString &SmtpClient::getResponseText() const { return responseText; } + +int SmtpClient::getResponseCode() const { return responseCode; } + +QTcpSocket *SmtpClient::getSocket() { return socket; } + +int SmtpClient::getConnectionTimeout() const { return connectionTimeout; } + +void SmtpClient::setConnectionTimeout(int msec) { connectionTimeout = msec; } + +int SmtpClient::getResponseTimeout() const { return responseTimeout; } + +void SmtpClient::setResponseTimeout(int msec) { responseTimeout = msec; } +int SmtpClient::getSendMessageTimeout() const { return sendMessageTimeout; } +void SmtpClient::setSendMessageTimeout(int msec) { sendMessageTimeout = msec; } + +/* [2] --- */ + +/* [3] Public methods */ + +bool SmtpClient::connectToHost() { + switch (connectionType) { + 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; + } catch (SendMessageTimeoutException) { + return false; + } + + // If no errors occured the function returns true. + return true; +} + +bool SmtpClient::login() { return login(user, password, authMethod); } + +bool SmtpClient::login(const QString &user, const QString &password, + AuthMethod method) { + try { + if (method == AuthPlain) { + // Sending command: AUTH PLAIN base64('\0' + username + '\0' + password) + sendMessage("AUTH PLAIN " + QByteArray() + .append((char)0) + .append(user.toUtf8()) + .append((char)0) + .append(password.toUtf8()) + .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.toUtf8()).toBase64()); + + // Wait for 334 + waitForResponse(); + if (responseCode != 334) { + emit smtpError(AuthenticationFailedError); + return false; + } + + // Send the password in base64 + sendMessage(QByteArray().append(password.toUtf8()).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) { + // Responce Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } catch (SendMessageTimeoutException) { + // Send Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } + + return true; +} + +bool SmtpClient::sendMail(MimeMessage &email) { + try { + // 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; + } catch (SendMessageTimeoutException) { + return false; + } + + return true; +} + +void SmtpClient::quit() { + try { + sendMessage("QUIT"); + } catch (SmtpClient::SendMessageTimeoutException) { + // Manually close the connection to the smtp server if message "QUIT" wasn't + // received by the smtp server + if (socket->state() == QAbstractSocket::ConnectedState || + socket->state() == QAbstractSocket::ConnectingState || + socket->state() == QAbstractSocket::HostLookupState) + socket->disconnectFromHost(); + } +} + +/* [3] --- */ + +/* [4] Protected methods */ + +void SmtpClient::waitForResponse() { + do { + if (!socket->waitForReadyRead(responseTimeout)) { + emit smtpError(ResponseTimeoutError); + throw ResponseTimeoutException(); + } + + while (socket->canReadLine()) { + // Save the server's response + responseText = socket->readLine(); + + // Extract the respose code from the server's responce (first 3 digits) + responseCode = responseText.left(3).toInt(); + + if (responseCode / 100 == 4) + emit smtpError(ServerError); + + if (responseCode / 100 == 5) + emit smtpError(ClientError); + + if (responseText[3] == ' ') { + return; + } + } + } while (true); +} + +void SmtpClient::sendMessage(const QString &text) { + socket->write(text.toUtf8() + "\r\n"); + if (!socket->waitForBytesWritten(sendMessageTimeout)) { + emit smtpError(SendDataTimeoutError); + throw SendMessageTimeoutException(); + } +} + +/* [4] --- */ + +/* [5] Slots for the socket's signals */ + +void SmtpClient::socketStateChanged(QAbstractSocket::SocketState /*state*/) {} + +void SmtpClient::socketError(QAbstractSocket::SocketError /*socketError*/) {} + +void SmtpClient::socketReadyRead() {} + +/* [5] --- */ |