Fix coding style (Issue #2192).

This commit is contained in:
Vladimir Golovnev (Glassez) 2015-05-13 18:39:48 +03:00
parent 191cdc2849
commit 5f288d228d
12 changed files with 820 additions and 784 deletions

View file

@ -42,30 +42,28 @@ QT_END_NAMESPACE
namespace Http namespace Http
{ {
class IRequestHandler;
class IRequestHandler; class Connection : public QObject
{
class Connection : public QObject
{
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(Connection) Q_DISABLE_COPY(Connection)
public: public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
~Connection(); ~Connection();
private slots: private slots:
void read(); void read();
private: private:
static bool acceptsGzipEncoding(const QString &encoding); static bool acceptsGzipEncoding(const QString &encoding);
void sendResponse(const Response &response); void sendResponse(const Response &response);
QTcpSocket *m_socket; QTcpSocket *m_socket;
IRequestHandler *m_requestHandler; IRequestHandler *m_requestHandler;
QByteArray m_receivedData; QByteArray m_receivedData;
}; };
} }
#endif // HTTP_CONNECTION_H #endif // HTTP_CONNECTION_H

View file

@ -33,14 +33,12 @@
namespace Http namespace Http
{ {
class IRequestHandler
class IRequestHandler {
{ public:
public:
virtual ~IRequestHandler() {} virtual ~IRequestHandler() {}
virtual Response processRequest(const Request &request, const Environment &env) = 0; virtual Response processRequest(const Request &request, const Environment &env) = 0;
}; };
} }
#endif // HTTP_IREQUESTHANDLER_H #endif // HTTP_IREQUESTHANDLER_H

View file

@ -36,10 +36,9 @@
namespace Http namespace Http
{ {
class RequestParser
class RequestParser {
{ public:
public:
enum ErrorCode enum ErrorCode
{ {
NoError = 0, NoError = 0,
@ -51,7 +50,7 @@ public:
// Warning! Header names are converted to lower-case. // Warning! Header names are converted to lower-case.
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */); static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
private: private:
RequestParser(uint maxContentLength); RequestParser(uint maxContentLength);
ErrorCode parseHttpRequest(const QByteArray &data, Request &request); ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
@ -67,8 +66,7 @@ private:
const uint m_maxContentLength; const uint m_maxContentLength;
Request m_request; Request m_request;
}; };
} }
#endif // HTTP_REQUESTPARSER_H #endif // HTTP_REQUESTPARSER_H

View file

@ -34,13 +34,12 @@
namespace Http namespace Http
{ {
class ResponseBuilder : public QObject
class ResponseBuilder : public QObject {
{ public:
public:
explicit ResponseBuilder(QObject *parent = 0); explicit ResponseBuilder(QObject *parent = 0);
protected: protected:
void status(uint code = 200, const QString &text = QLatin1String("OK")); void status(uint code = 200, const QString &text = QLatin1String("OK"));
void header(const QString &name, const QString &value); void header(const QString &name, const QString &value);
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML); void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
@ -49,12 +48,11 @@ protected:
Response response() const; Response response() const;
private: private:
void print_impl(const QByteArray &data, const QString &type); void print_impl(const QByteArray &data, const QString &type);
Response m_response; Response m_response;
}; };
} }
#endif // HTTP_RESPONSEBUILDER_H #endif // HTTP_RESPONSEBUILDER_H

View file

@ -37,13 +37,11 @@
namespace Http namespace Http
{ {
class ResponseGenerator
class ResponseGenerator {
{ public:
public:
static QByteArray generate(Response response); static QByteArray generate(Response response);
}; };
} }
#endif // HTTP_RESPONSEGENERATOR_H #endif // HTTP_RESPONSEGENERATOR_H

View file

@ -38,7 +38,7 @@
using namespace Http; using namespace Http;
Server::Server(IRequestHandler *requestHandler, QObject* parent) Server::Server(IRequestHandler *requestHandler, QObject *parent)
: QTcpServer(parent) : QTcpServer(parent)
, m_requestHandler(requestHandler) , m_requestHandler(requestHandler)
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL

View file

@ -41,40 +41,38 @@
namespace Http namespace Http
{ {
class IRequestHandler;
class Connection;
class IRequestHandler; class Server : public QTcpServer
class Connection; {
class Server : public QTcpServer
{
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(Server) Q_DISABLE_COPY(Server)
public: public:
Server(IRequestHandler *requestHandler, QObject *parent = 0); Server(IRequestHandler *requestHandler, QObject *parent = 0);
~Server(); ~Server();
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
void enableHttps(const QSslCertificate &certificate, const QSslKey &key); void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
void disableHttps(); void disableHttps();
#endif #endif
private: private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
void incomingConnection(qintptr socketDescriptor); void incomingConnection(qintptr socketDescriptor);
#else #else
void incomingConnection(int socketDescriptor); void incomingConnection(int socketDescriptor);
#endif #endif
private: private:
IRequestHandler *m_requestHandler; IRequestHandler *m_requestHandler;
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
bool m_https; bool m_https;
QSslCertificate m_certificate; QSslCertificate m_certificate;
QSslKey m_key; QSslKey m_key;
#endif #endif
}; };
} }
#endif // HTTP_SERVER_H #endif // HTTP_SERVER_H

View file

@ -37,59 +37,57 @@ typedef QMap<QString, QString> QStringMap;
namespace Http namespace Http
{ {
const QString HEADER_SET_COOKIE = "Set-Cookie";
const QString HEADER_CONTENT_TYPE = "Content-Type";
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
const QString HEADER_CONTENT_LENGTH = "Content-Length";
const QString HEADER_CACHE_CONTROL = "Cache-Control";
const QString HEADER_SET_COOKIE = "Set-Cookie"; const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
const QString HEADER_CONTENT_TYPE = "Content-Type"; const QString CONTENT_TYPE_GIF = "image/gif";
const QString HEADER_CONTENT_ENCODING = "Content-Encoding"; const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
const QString HEADER_CONTENT_LENGTH = "Content-Length"; const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8";
const QString HEADER_CACHE_CONTROL = "Cache-Control"; const QString CONTENT_TYPE_PNG = "image/png";
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8"; struct Environment
const QString CONTENT_TYPE_GIF = "image/gif"; {
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8";
const QString CONTENT_TYPE_PNG = "image/png";
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
struct Environment
{
QHostAddress clientAddress; QHostAddress clientAddress;
}; };
struct UploadedFile struct UploadedFile
{ {
QString filename; // original filename QString filename; // original filename
QString type; // MIME type QString type; // MIME type
QByteArray data; // File data QByteArray data; // File data
}; };
struct Request struct Request
{ {
QString method; QString method;
QString path; QString path;
QStringMap headers; QStringMap headers;
QStringMap gets; QStringMap gets;
QStringMap posts; QStringMap posts;
QMap<QString, UploadedFile> files; QMap<QString, UploadedFile> files;
}; };
struct ResponseStatus struct ResponseStatus
{ {
uint code; uint code;
QString text; QString text;
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {} ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
}; };
struct Response struct Response
{ {
ResponseStatus status; ResponseStatus status;
QStringMap headers; QStringMap headers;
QByteArray content; QByteArray content;
Response(uint code = 200, const QString& text = "OK"): status(code, text) {} Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
}; };
} }
#endif // HTTP_TYPES_H #endif // HTTP_TYPES_H

View file

@ -35,18 +35,21 @@
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QUrlQuery> #include <QUrlQuery>
#endif #endif
#include "dnsupdater.h"
#include "core/logger.h" #include "core/logger.h"
#include "dnsupdater.h"
using namespace Net; using namespace Net;
DNSUpdater::DNSUpdater(QObject *parent) : DNSUpdater::DNSUpdater(QObject *parent)
QObject(parent), m_state(OK), m_service(DNS::NONE) : QObject(parent)
, m_state(OK)
, m_service(DNS::NONE)
{ {
updateCredentials(); updateCredentials();
// Load saved settings from previous session // Load saved settings from previous session
const Preferences* const pref = Preferences::instance(); const Preferences *const pref = Preferences::instance();
m_lastIPCheckTime = pref->getDNSLastUpd(); m_lastIPCheckTime = pref->getDNSLastUpd();
m_lastIP = QHostAddress(pref->getDNSLastIP()); m_lastIP = QHostAddress(pref->getDNSLastIP());
@ -56,15 +59,16 @@ DNSUpdater::DNSUpdater(QObject *parent) :
m_ipCheckTimer.start(); m_ipCheckTimer.start();
// Check lastUpdate to avoid flooding // Check lastUpdate to avoid flooding
if (!m_lastIPCheckTime.isValid() || if (!m_lastIPCheckTime.isValid()
m_lastIPCheckTime.secsTo(QDateTime::currentDateTime())*1000 > IP_CHECK_INTERVAL_MS) { || (m_lastIPCheckTime.secsTo(QDateTime::currentDateTime()) * 1000 > IP_CHECK_INTERVAL_MS)) {
checkPublicIP(); checkPublicIP();
} }
} }
DNSUpdater::~DNSUpdater() { DNSUpdater::~DNSUpdater()
{
// Save lastupdate time and last ip // Save lastupdate time and last ip
Preferences* const pref = Preferences::instance(); Preferences *const pref = Preferences::instance();
pref->setDNSLastUpd(m_lastIPCheckTime); pref->setDNSLastUpd(m_lastIPCheckTime);
pref->setDNSLastIP(m_lastIP.toString()); pref->setDNSLastIP(m_lastIP.toString());
} }
@ -73,8 +77,7 @@ void DNSUpdater::checkPublicIP()
{ {
Q_ASSERT(m_state == OK); Q_ASSERT(m_state == OK);
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipRequestFinished(QNetworkReply *)));
SLOT(ipRequestFinished(QNetworkReply*)));
m_lastIPCheckTime = QDateTime::currentDateTime(); m_lastIPCheckTime = QDateTime::currentDateTime();
QNetworkRequest request; QNetworkRequest request;
request.setUrl(QUrl("http://checkip.dyndns.org")); request.setUrl(QUrl("http://checkip.dyndns.org"));
@ -88,7 +91,8 @@ void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
if (reply->error()) { if (reply->error()) {
// Error // Error
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
} else { }
else {
// Parse response // Parse response
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>"); QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
QString ret = reply->readAll(); QString ret = reply->readAll();
@ -103,10 +107,12 @@ void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
m_lastIP = new_ip; m_lastIP = new_ip;
updateDNSService(); updateDNSService();
} }
} else { }
else {
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string"; qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
} }
} else { }
else {
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address"; qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
} }
} }
@ -120,8 +126,7 @@ void DNSUpdater::updateDNSService()
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
// Prepare request // Prepare request
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipUpdateFinished(QNetworkReply *)));
SLOT(ipUpdateFinished(QNetworkReply*)));
m_lastIPCheckTime = QDateTime::currentDateTime(); m_lastIPCheckTime = QDateTime::currentDateTime();
QNetworkRequest request; QNetworkRequest request;
request.setUrl(getUpdateUrl()); request.setUrl(getUpdateUrl());
@ -175,7 +180,8 @@ void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
if (reply->error()) { if (reply->error()) {
// Error // Error
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
} else { }
else {
// Pase reply // Pase reply
processIPUpdateReply(reply->readAll()); processIPUpdateReply(reply->readAll());
} }
@ -186,7 +192,7 @@ void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
void DNSUpdater::processIPUpdateReply(const QString &reply) void DNSUpdater::processIPUpdateReply(const QString &reply)
{ {
Logger* const logger = Logger::instance(); Logger *const logger = Logger::instance();
qDebug() << Q_FUNC_INFO << reply; qDebug() << Q_FUNC_INFO << reply;
QString code = reply.split(" ").first(); QString code = reply.split(" ").first();
qDebug() << Q_FUNC_INFO << "Code:" << code; qDebug() << Q_FUNC_INFO << "Code:" << code;
@ -194,7 +200,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO); logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
return; return;
} }
if (code == "911" || code == "dnserr") { if ((code == "911") || (code == "dnserr")) {
logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL); logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
m_lastIP.clear(); m_lastIP.clear();
// It will retry in 30 minutes because the timer was not stopped // It will retry in 30 minutes because the timer was not stopped
@ -235,8 +241,8 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
void DNSUpdater::updateCredentials() void DNSUpdater::updateCredentials()
{ {
if (m_state == FATAL) return; if (m_state == FATAL) return;
Preferences* const pref = Preferences::instance(); Preferences *const pref = Preferences::instance();
Logger* const logger = Logger::instance(); Logger *const logger = Logger::instance();
bool change = false; bool change = false;
// Get DNS service information // Get DNS service information
if (m_service != pref->getDynDNSService()) { if (m_service != pref->getDynDNSService()) {
@ -278,7 +284,7 @@ void DNSUpdater::updateCredentials()
change = true; change = true;
} }
if (m_state == INVALID_CREDS && change) { if ((m_state == INVALID_CREDS) && change) {
m_state = OK; // Try again m_state = OK; // Try again
m_ipCheckTimer.start(); m_ipCheckTimer.start();
checkPublicIP(); checkPublicIP();

View file

@ -40,32 +40,31 @@
namespace Net namespace Net
{ {
// Based on http://www.dyndns.com/developers/specs/
/*! class DNSUpdater : public QObject
* Based on http://www.dyndns.com/developers/specs/ {
*/
class DNSUpdater : public QObject
{
Q_OBJECT Q_OBJECT
public:
public:
explicit DNSUpdater(QObject *parent = 0); explicit DNSUpdater(QObject *parent = 0);
~DNSUpdater(); ~DNSUpdater();
static QUrl getRegistrationUrl(int service); static QUrl getRegistrationUrl(int service);
public slots: public slots:
void updateCredentials(); void updateCredentials();
private slots: private slots:
void checkPublicIP(); void checkPublicIP();
void ipRequestFinished(QNetworkReply* reply); void ipRequestFinished(QNetworkReply *reply);
void updateDNSService(); void updateDNSService();
void ipUpdateFinished(QNetworkReply* reply); void ipUpdateFinished(QNetworkReply *reply);
private: private:
QUrl getUpdateUrl() const; QUrl getUpdateUrl() const;
void processIPUpdateReply(const QString &reply); void processIPUpdateReply(const QString &reply);
private: private:
QHostAddress m_lastIP; QHostAddress m_lastIP;
QDateTime m_lastIPCheckTime; QDateTime m_lastIPCheckTime;
QTimer m_ipCheckTimer; QTimer m_ipCheckTimer;
@ -76,11 +75,16 @@ private:
QString m_username; QString m_username;
QString m_password; QString m_password;
private: private:
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
enum State { OK, INVALID_CREDS, FATAL };
};
enum State
{
OK,
INVALID_CREDS,
FATAL
};
};
} }
#endif // DNSUPDATER_H #endif // DNSUPDATER_H

View file

@ -50,12 +50,13 @@
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QStringList> #include <QStringList>
namespace { namespace
const short DEFAULT_PORT = 25;
const short DEFAULT_PORT_SSL = 465;
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
{ {
const short DEFAULT_PORT = 25;
const short DEFAULT_PORT_SSL = 465;
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
{
const int blockSize = 64; // HMAC-MD5 block size const int blockSize = 64; // HMAC-MD5 block size
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
key = QCryptographicHash::hash(key, QCryptographicHash::Md5); key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
@ -77,80 +78,83 @@ QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
part.append(msg); part.append(msg);
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5)); total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
return QCryptographicHash::hash(total, QCryptographicHash::Md5); return QCryptographicHash::hash(total, QCryptographicHash::Md5);
} }
QByteArray determineFQDN() QByteArray determineFQDN()
{ {
QString hostname = QHostInfo::localHostName(); QString hostname = QHostInfo::localHostName();
if (hostname.isEmpty()) if (hostname.isEmpty())
hostname = "localhost"; hostname = "localhost";
return hostname.toLocal8Bit(); return hostname.toLocal8Bit();
} }
} // namespace } // namespace
using namespace Net; using namespace Net;
Smtp::Smtp(QObject *parent): QObject(parent), Smtp::Smtp(QObject *parent)
state(Init), use_ssl(false) { : QObject(parent)
, m_state(Init)
, m_useSsl(false)
{
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
socket = new QSslSocket(this); m_socket = new QSslSocket(this);
#else #else
socket = new QTcpSocket(this); m_socket = new QTcpSocket(this);
#endif #endif
connect(socket, SIGNAL(readyRead()), SLOT(readyRead())); connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), SLOT(deleteLater())); connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html) // Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex() Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
== "750c783e6ab0b503eaa86e310a5db738"); == "750c783e6ab0b503eaa86e310a5db738");
Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), "Hi There").toHex()
"Hi There").toHex()
== "9294727a3638bb1c13f48ef8158bfc9d"); == "9294727a3638bb1c13f48ef8158bfc9d");
} }
Smtp::~Smtp() { Smtp::~Smtp()
{
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
} }
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body) { void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
{
const Preferences* const pref = Preferences::instance(); const Preferences* const pref = Preferences::instance();
QTextCodec* latin1 = QTextCodec::codecForName("latin1"); QTextCodec* latin1 = QTextCodec::codecForName("latin1");
message = ""; m_message = "";
message += encode_mime_header("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1); m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
message += encode_mime_header("From", from, latin1); m_message += encodeMimeHeader("From", from, latin1);
message += encode_mime_header("Subject", subject, latin1); m_message += encodeMimeHeader("Subject", subject, latin1);
message += encode_mime_header("To", to, latin1); m_message += encodeMimeHeader("To", to, latin1);
message += "MIME-Version: 1.0\r\n"; m_message += "MIME-Version: 1.0\r\n";
message += "Content-Type: text/plain; charset=UTF-8\r\n"; m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
message += "Content-Transfer-Encoding: base64\r\n"; m_message += "Content-Transfer-Encoding: base64\r\n";
message += "\r\n"; m_message += "\r\n";
// Encode the body in base64 // Encode the body in base64
QString crlf_body = body; QString crlf_body = body;
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64(); QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
int ct = b.length(); int ct = b.length();
for (int i = 0; i < ct; i += 78) for (int i = 0; i < ct; i += 78)
{ m_message += b.mid(i, 78);
message += b.mid(i, 78); m_from = from;
} m_rcpt = to;
this->from = from;
rcpt = to;
// Authentication // Authentication
if (pref->getMailNotificationSMTPAuth()) { if (pref->getMailNotificationSMTPAuth()) {
username = pref->getMailNotificationSMTPUsername(); m_username = pref->getMailNotificationSMTPUsername();
password = pref->getMailNotificationSMTPPassword(); m_password = pref->getMailNotificationSMTPPassword();
} }
// Connect to SMTP server // Connect to SMTP server
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
if (pref->getMailNotificationSMTPSSL()) { if (pref->getMailNotificationSMTPSSL()) {
socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL); m_socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL);
use_ssl = true; m_useSsl = true;
} else { }
else {
#endif #endif
socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT); m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
use_ssl = false; m_useSsl = false;
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
} }
#endif #endif
@ -160,18 +164,17 @@ void Smtp::readyRead()
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
// SMTP is line-oriented // SMTP is line-oriented
buffer += socket->readAll(); m_buffer += m_socket->readAll();
while (true) while (true) {
{ int pos = m_buffer.indexOf("\r\n");
int pos = buffer.indexOf("\r\n");
if (pos < 0) return; // Loop exit condition if (pos < 0) return; // Loop exit condition
QByteArray line = buffer.left(pos); QByteArray line = m_buffer.left(pos);
buffer = buffer.mid(pos + 2); m_buffer = m_buffer.mid(pos + 2);
qDebug() << "Response line:" << line; qDebug() << "Response line:" << line;
// Extract reponse code // Extract reponse code
QByteArray code = line.left(3); QByteArray code = line.left(3);
switch(state) { switch (m_state) {
case Init: { case Init: {
if (code[0] == '2') { if (code[0] == '2') {
// The server may send a multiline greeting/INIT/220 response. // The server may send a multiline greeting/INIT/220 response.
@ -180,9 +183,10 @@ void Smtp::readyRead()
break; break;
// Connection was successful // Connection was successful
ehlo(); ehlo();
} else { }
else {
logError("Connection failed, unrecognized reply: "+line); logError("Connection failed, unrecognized reply: "+line);
state = Close; m_state = Close;
} }
break; break;
} }
@ -194,82 +198,88 @@ void Smtp::readyRead()
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
case StartTLSSent: case StartTLSSent:
if (code == "220") { if (code == "220") {
socket->startClientEncryption(); m_socket->startClientEncryption();
ehlo(); ehlo();
} else { }
else {
authenticate(); authenticate();
} }
break; break;
#endif #endif
case AuthRequestSent: case AuthRequestSent:
case AuthUsernameSent: case AuthUsernameSent:
if (authType == AuthPlain) authPlain(); if (m_authType == AuthPlain) authPlain();
else if (authType == AuthLogin) authLogin(); else if (m_authType == AuthLogin) authLogin();
else authCramMD5(line.mid(4)); else authCramMD5(line.mid(4));
break; break;
case AuthSent: case AuthSent:
case Authenticated: case Authenticated:
if (code[0] == '2') { if (code[0] == '2') {
qDebug() << "Sending <mail from>..."; qDebug() << "Sending <mail from>...";
socket->write("mail from:<" + from.toLatin1() + ">\r\n"); m_socket->write("mail from:<" + m_from.toLatin1() + ">\r\n");
socket->flush(); m_socket->flush();
state = Rcpt; m_state = Rcpt;
} else { }
else {
// Authentication failed! // Authentication failed!
logError("Authentication failed, msg: "+line); logError("Authentication failed, msg: "+line);
state = Close; m_state = Close;
} }
break; break;
case Rcpt: case Rcpt:
if (code[0] == '2') { if (code[0] == '2') {
socket->write("rcpt to:<" + rcpt.toLatin1() + ">\r\n"); m_socket->write("rcpt to:<" + m_rcpt.toLatin1() + ">\r\n");
socket->flush(); m_socket->flush();
state = Data; m_state = Data;
} else { }
else {
logError("<mail from> was rejected by server, msg: "+line); logError("<mail from> was rejected by server, msg: "+line);
state = Close; m_state = Close;
} }
break; break;
case Data: case Data:
if (code[0] == '2') { if (code[0] == '2') {
socket->write("data\r\n"); m_socket->write("data\r\n");
socket->flush(); m_socket->flush();
state = Body; m_state = Body;
} else { }
else {
logError("<Rcpt to> was rejected by server, msg: "+line); logError("<Rcpt to> was rejected by server, msg: "+line);
state = Close; m_state = Close;
} }
break; break;
case Body: case Body:
if (code[0] == '3') { if (code[0] == '3') {
socket->write(message + "\r\n.\r\n"); m_socket->write(m_message + "\r\n.\r\n");
socket->flush(); m_socket->flush();
state = Quit; m_state = Quit;
} else { }
else {
logError("<data> was rejected by server, msg: "+line); logError("<data> was rejected by server, msg: "+line);
state = Close; m_state = Close;
} }
break; break;
case Quit: case Quit:
if (code[0] == '2') { if (code[0] == '2') {
socket->write("QUIT\r\n"); m_socket->write("QUIT\r\n");
socket->flush(); m_socket->flush();
// here, we just close. // here, we just close.
state = Close; m_state = Close;
} else { }
else {
logError("Message was rejected by the server, error: "+line); logError("Message was rejected by the server, error: "+line);
state = Close; m_state = Close;
} }
break; break;
default: default:
qDebug() << "Disconnecting from host"; qDebug() << "Disconnecting from host";
socket->disconnectFromHost(); m_socket->disconnectFromHost();
return; return;
} }
} }
} }
QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix) QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix)
{ {
QByteArray rv = ""; QByteArray rv = "";
QByteArray line = key.toLatin1() + ": "; QByteArray line = key.toLatin1() + ": ";
@ -287,7 +297,8 @@ QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QT
line += " " + word; line += " " + word;
firstWord = false; firstWord = false;
} }
} else { }
else {
// The text cannot be losslessly encoded as Latin-1. Therefore, we // The text cannot be losslessly encoded as Latin-1. Therefore, we
// must use base64 encoding. // must use base64 encoding.
QByteArray utf8 = value.toUtf8(); QByteArray utf8 = value.toUtf8();
@ -310,58 +321,65 @@ QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QT
void Smtp::ehlo() void Smtp::ehlo()
{ {
QByteArray address = determineFQDN(); QByteArray address = determineFQDN();
socket->write("ehlo " + address + "\r\n"); m_socket->write("ehlo " + address + "\r\n");
socket->flush(); m_socket->flush();
state = EhloSent; m_state = EhloSent;
} }
void Smtp::helo() void Smtp::helo()
{ {
QByteArray address = determineFQDN(); QByteArray address = determineFQDN();
socket->write("helo " + address + "\r\n"); m_socket->write("helo " + address + "\r\n");
socket->flush(); m_socket->flush();
state = HeloSent; m_state = HeloSent;
} }
void Smtp::parseEhloResponse(const QByteArray& code, bool continued, const QString& line) void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QString &line)
{ {
if (code != "250") { if (code != "250") {
// Error // Error
if (state == EhloSent) { if (m_state == EhloSent) {
// try to send HELO instead of EHLO // try to send HELO instead of EHLO
qDebug() << "EHLO failed, trying HELO instead..."; qDebug() << "EHLO failed, trying HELO instead...";
helo(); helo();
} else { }
else {
// Both EHLO and HELO failed, chances are this is NOT // Both EHLO and HELO failed, chances are this is NOT
// a SMTP server // a SMTP server
logError("Both EHLO and HELO failed, msg: "+line); logError("Both EHLO and HELO failed, msg: "+line);
state = Close; m_state = Close;
} }
return; return;
} }
if (state != EhloGreetReceived) {
if (m_state != EhloGreetReceived) {
if (!continued) { if (!continued) {
// greeting only, no extensions // greeting only, no extensions
qDebug() << "No extension"; qDebug() << "No extension";
state = EhloDone; m_state = EhloDone;
} else { }
else {
// greeting followed by extensions // greeting followed by extensions
state = EhloGreetReceived; m_state = EhloGreetReceived;
qDebug () << "EHLO greet received"; qDebug () << "EHLO greet received";
return; return;
} }
} else { }
else {
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper() qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
<< line.section(' ', 1); << line.section(' ', 1);
extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); m_extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
if (!continued) if (!continued)
state = EhloDone; m_state = EhloDone;
} }
if (state != EhloDone) return;
if (extensions.contains("STARTTLS") && use_ssl) { if (m_state != EhloDone) return;
if (m_extensions.contains("STARTTLS") && m_useSsl) {
qDebug() << "STARTTLS"; qDebug() << "STARTTLS";
startTLS(); startTLS();
} else { }
else {
authenticate(); authenticate();
} }
} }
@ -369,21 +387,21 @@ void Smtp::parseEhloResponse(const QByteArray& code, bool continued, const QStri
void Smtp::authenticate() void Smtp::authenticate()
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
if (!extensions.contains("AUTH") || if (!m_extensions.contains("AUTH") ||
username.isEmpty() || password.isEmpty()) { m_username.isEmpty() || m_password.isEmpty()) {
// Skip authentication // Skip authentication
qDebug() << "Skipping authentication..."; qDebug() << "Skipping authentication...";
state = Authenticated; m_state = Authenticated;
// At this point the server will not send any response // At this point the server will not send any response
// So fill the buffer with a fake one to pass the tests // So fill the buffer with a fake one to pass the tests
// in readyRead() // in readyRead()
buffer.push_front("250 QBT FAKE RESPONSE\r\n"); m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
return; return;
} }
// AUTH extension is supported, check which // AUTH extension is supported, check which
// authentication modes are supported by // authentication modes are supported by
// the server // the server
QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); QStringList auth = m_extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
if (auth.contains("CRAM-MD5")) { if (auth.contains("CRAM-MD5")) {
qDebug() << "Using CRAM-MD5 authentication..."; qDebug() << "Using CRAM-MD5 authentication...";
authCramMD5(); authCramMD5();
@ -395,16 +413,17 @@ void Smtp::authenticate()
else if (auth.contains("LOGIN")) { else if (auth.contains("LOGIN")) {
qDebug() << "Using LOGIN authentication..."; qDebug() << "Using LOGIN authentication...";
authLogin(); authLogin();
} else { }
else {
// Skip authentication // Skip authentication
logError("The SMTP server does not seem to support any of the authentications modes " logError("The SMTP server does not seem to support any of the authentications modes "
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, " "we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|")); "knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
state = Authenticated; m_state = Authenticated;
// At this point the server will not send any response // At this point the server will not send any response
// So fill the buffer with a fake one to pass the tests // So fill the buffer with a fake one to pass the tests
// in readyRead() // in readyRead()
buffer.push_front("250 QBT FAKE RESPONSE\r\n"); m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
} }
} }
@ -412,9 +431,9 @@ void Smtp::startTLS()
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
socket->write("starttls\r\n"); m_socket->write("starttls\r\n");
socket->flush(); m_socket->flush();
state = StartTLSSent; m_state = StartTLSSent;
#else #else
authenticate(); authenticate();
#endif #endif
@ -422,56 +441,57 @@ void Smtp::startTLS()
void Smtp::authCramMD5(const QByteArray& challenge) void Smtp::authCramMD5(const QByteArray& challenge)
{ {
if (state != AuthRequestSent) { if (m_state != AuthRequestSent) {
socket->write("auth cram-md5\r\n"); m_socket->write("auth cram-md5\r\n");
socket->flush(); m_socket->flush();
authType = AuthCramMD5; m_authType = AuthCramMD5;
state = AuthRequestSent; m_state = AuthRequestSent;
} else { }
QByteArray response = username.toLatin1() + ' ' else {
+ hmacMD5(password.toLatin1(), QByteArray::fromBase64(challenge)).toHex(); QByteArray response = m_username.toLatin1() + ' '
socket->write(response.toBase64() + "\r\n"); + hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
socket->flush(); m_socket->write(response.toBase64() + "\r\n");
state = AuthSent; m_socket->flush();
m_state = AuthSent;
} }
} }
void Smtp::authPlain() void Smtp::authPlain()
{ {
if (state != AuthRequestSent) { if (m_state != AuthRequestSent) {
authType = AuthPlain; m_authType = AuthPlain;
// Prepare Auth string // Prepare Auth string
QByteArray auth; QByteArray auth;
auth += '\0'; auth += '\0';
auth += username.toLatin1(); auth += m_username.toLatin1();
qDebug() << "username: " << username.toLatin1(); qDebug() << "username: " << m_username.toLatin1();
auth += '\0'; auth += '\0';
auth += password.toLatin1(); auth += m_password.toLatin1();
qDebug() << "password: " << password.toLatin1(); qDebug() << "password: " << m_password.toLatin1();
// Send it // Send it
socket->write("auth plain "+ auth.toBase64() + "\r\n"); m_socket->write("auth plain "+ auth.toBase64() + "\r\n");
socket->flush(); m_socket->flush();
state = AuthSent; m_state = AuthSent;
} }
} }
void Smtp::authLogin() void Smtp::authLogin()
{ {
if (state != AuthRequestSent && state != AuthUsernameSent) { if ((m_state != AuthRequestSent) && (m_state != AuthUsernameSent)) {
socket->write("auth login\r\n"); m_socket->write("auth login\r\n");
socket->flush(); m_socket->flush();
authType = AuthLogin; m_authType = AuthLogin;
state = AuthRequestSent; m_state = AuthRequestSent;
} }
else if (state == AuthRequestSent) { else if (m_state == AuthRequestSent) {
socket->write(username.toLatin1().toBase64() + "\r\n"); m_socket->write(m_username.toLatin1().toBase64() + "\r\n");
socket->flush(); m_socket->flush();
state = AuthUsernameSent; m_state = AuthUsernameSent;
} }
else { else {
socket->write(password.toLatin1().toBase64() + "\r\n"); m_socket->write(m_password.toLatin1().toBase64() + "\r\n");
socket->flush(); m_socket->flush();
state = AuthSent; m_state = AuthSent;
} }
} }

View file

@ -52,54 +52,74 @@ QT_END_NAMESPACE
namespace Net namespace Net
{ {
class Smtp : public QObject
class Smtp : public QObject { {
Q_OBJECT Q_OBJECT
public: public:
Smtp(QObject *parent = 0); Smtp(QObject *parent = 0);
~Smtp(); ~Smtp();
void sendMail(const QString &from, const QString &to, const QString &subject, const QString &body);
private slots: void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body);
private slots:
void readyRead(); void readyRead();
private: private:
QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray()); enum States
{
Rcpt,
EhloSent,
HeloSent,
EhloDone,
EhloGreetReceived,
AuthRequestSent,
AuthSent,
AuthUsernameSent,
Authenticated,
StartTLSSent,
Data,
Init,
Body,
Quit,
Close
};
enum AuthType
{
AuthPlain,
AuthLogin,
AuthCramMD5
};
QByteArray encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix = QByteArray());
void ehlo(); void ehlo();
void helo(); void helo();
void parseEhloResponse(const QByteArray& code, bool continued, const QString& line); void parseEhloResponse(const QByteArray &code, bool continued, const QString &line);
void authenticate(); void authenticate();
void startTLS(); void startTLS();
void authCramMD5(const QByteArray& challenge = QByteArray()); void authCramMD5(const QByteArray &challenge = QByteArray());
void authPlain(); void authPlain();
void authLogin(); void authLogin();
void logError(const QString &msg); void logError(const QString &msg);
private: QByteArray m_message;
enum states { Rcpt, EhloSent, HeloSent, EhloDone, EhloGreetReceived, AuthRequestSent, AuthSent,
AuthUsernameSent, Authenticated, StartTLSSent, Data, Init, Body, Quit, Close };
enum AuthType { AuthPlain, AuthLogin, AuthCramMD5 };
private:
QByteArray message;
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
QSslSocket *socket; QSslSocket *m_socket;
#else #else
QTcpSocket *socket; QTcpSocket *m_socket;
#endif #endif
QString from; QString m_from;
QString rcpt; QString m_rcpt;
QString response; QString m_response;
int state; int m_state;
QHash<QString, QString> extensions; QHash<QString, QString> m_extensions;
QByteArray buffer; QByteArray m_buffer;
bool use_ssl; bool m_useSsl;
AuthType authType; AuthType m_authType;
QString username; QString m_username;
QString password; QString m_password;
}; };
} }
#endif #endif