mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-14 01:03:08 -07:00
Fix coding style (Issue #2192).
This commit is contained in:
parent
191cdc2849
commit
5f288d228d
12 changed files with 820 additions and 784 deletions
|
@ -42,30 +42,28 @@ QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class IRequestHandler;
|
||||||
|
|
||||||
class IRequestHandler;
|
class Connection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Connection)
|
||||||
|
|
||||||
class Connection : public QObject
|
public:
|
||||||
{
|
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||||
Q_OBJECT
|
~Connection();
|
||||||
Q_DISABLE_COPY(Connection)
|
|
||||||
|
|
||||||
public:
|
private slots:
|
||||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
void read();
|
||||||
~Connection();
|
|
||||||
|
|
||||||
private slots:
|
private:
|
||||||
void read();
|
static bool acceptsGzipEncoding(const QString &encoding);
|
||||||
|
void sendResponse(const Response &response);
|
||||||
private:
|
|
||||||
static bool acceptsGzipEncoding(const QString &encoding);
|
|
||||||
void sendResponse(const Response &response);
|
|
||||||
|
|
||||||
QTcpSocket *m_socket;
|
|
||||||
IRequestHandler *m_requestHandler;
|
|
||||||
QByteArray m_receivedData;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
QTcpSocket *m_socket;
|
||||||
|
IRequestHandler *m_requestHandler;
|
||||||
|
QByteArray m_receivedData;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_CONNECTION_H
|
#endif // HTTP_CONNECTION_H
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -36,39 +36,37 @@
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class RequestParser
|
||||||
class RequestParser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum ErrorCode
|
|
||||||
{
|
{
|
||||||
NoError = 0,
|
public:
|
||||||
IncompleteRequest,
|
enum ErrorCode
|
||||||
BadRequest
|
{
|
||||||
|
NoError = 0,
|
||||||
|
IncompleteRequest,
|
||||||
|
BadRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
// when result != NoError parsed request is undefined
|
||||||
|
// Warning! Header names are converted to lower-case.
|
||||||
|
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RequestParser(uint maxContentLength);
|
||||||
|
|
||||||
|
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
||||||
|
|
||||||
|
bool parseHttpHeader(const QByteArray &data);
|
||||||
|
bool parseStartingLine(const QString &line);
|
||||||
|
bool parseContent(const QByteArray &data);
|
||||||
|
bool parseFormData(const QByteArray &data);
|
||||||
|
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
||||||
|
|
||||||
|
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
||||||
|
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
||||||
|
|
||||||
|
const uint m_maxContentLength;
|
||||||
|
Request m_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
// when result != NoError parsed request is undefined
|
|
||||||
// Warning! Header names are converted to lower-case.
|
|
||||||
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
|
||||||
|
|
||||||
private:
|
|
||||||
RequestParser(uint maxContentLength);
|
|
||||||
|
|
||||||
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
|
||||||
|
|
||||||
bool parseHttpHeader(const QByteArray &data);
|
|
||||||
bool parseStartingLine(const QString &line);
|
|
||||||
bool parseContent(const QByteArray &data);
|
|
||||||
bool parseFormData(const QByteArray &data);
|
|
||||||
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
|
||||||
|
|
||||||
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
|
||||||
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
|
||||||
|
|
||||||
const uint m_maxContentLength;
|
|
||||||
Request m_request;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_REQUESTPARSER_H
|
#endif // HTTP_REQUESTPARSER_H
|
||||||
|
|
|
@ -34,27 +34,25 @@
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class ResponseBuilder : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ResponseBuilder(QObject *parent = 0);
|
||||||
|
|
||||||
class ResponseBuilder : public QObject
|
protected:
|
||||||
{
|
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||||
public:
|
void header(const QString &name, const QString &value);
|
||||||
explicit ResponseBuilder(QObject *parent = 0);
|
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
void clear();
|
||||||
|
|
||||||
protected:
|
Response response() const;
|
||||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
|
||||||
void header(const QString &name, const QString &value);
|
|
||||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
|
||||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
Response response() const;
|
private:
|
||||||
|
void print_impl(const QByteArray &data, const QString &type);
|
||||||
private:
|
|
||||||
void print_impl(const QByteArray &data, const QString &type);
|
|
||||||
|
|
||||||
Response m_response;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
Response m_response;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_RESPONSEBUILDER_H
|
#endif // HTTP_RESPONSEBUILDER_H
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -41,40 +41,38 @@
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class IRequestHandler;
|
||||||
|
class Connection;
|
||||||
|
|
||||||
class IRequestHandler;
|
class Server : public QTcpServer
|
||||||
class Connection;
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Server)
|
||||||
|
|
||||||
class Server : public QTcpServer
|
public:
|
||||||
{
|
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
||||||
Q_OBJECT
|
~Server();
|
||||||
Q_DISABLE_COPY(Server)
|
|
||||||
|
|
||||||
public:
|
#ifndef QT_NO_OPENSSL
|
||||||
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||||
~Server();
|
void disableHttps();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
private:
|
||||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||||
void disableHttps();
|
void incomingConnection(qintptr socketDescriptor);
|
||||||
#endif
|
#else
|
||||||
|
void incomingConnection(int socketDescriptor);
|
||||||
private:
|
#endif
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
void incomingConnection(qintptr socketDescriptor);
|
|
||||||
#else
|
|
||||||
void incomingConnection(int socketDescriptor);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
IRequestHandler *m_requestHandler;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
bool m_https;
|
|
||||||
QSslCertificate m_certificate;
|
|
||||||
QSslKey m_key;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
IRequestHandler *m_requestHandler;
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
bool m_https;
|
||||||
|
QSslCertificate m_certificate;
|
||||||
|
QSslKey m_key;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_SERVER_H
|
#endif // HTTP_SERVER_H
|
||||||
|
|
|
@ -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";
|
QHostAddress clientAddress;
|
||||||
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
|
struct UploadedFile
|
||||||
{
|
{
|
||||||
QHostAddress clientAddress;
|
QString filename; // original filename
|
||||||
};
|
QString type; // MIME type
|
||||||
|
QByteArray data; // File data
|
||||||
|
};
|
||||||
|
|
||||||
struct UploadedFile
|
struct Request
|
||||||
{
|
{
|
||||||
QString filename; // original filename
|
QString method;
|
||||||
QString type; // MIME type
|
QString path;
|
||||||
QByteArray data; // File data
|
QStringMap headers;
|
||||||
};
|
QStringMap gets;
|
||||||
|
QStringMap posts;
|
||||||
|
QMap<QString, UploadedFile> files;
|
||||||
|
};
|
||||||
|
|
||||||
struct Request
|
struct ResponseStatus
|
||||||
{
|
{
|
||||||
QString method;
|
uint code;
|
||||||
QString path;
|
QString text;
|
||||||
QStringMap headers;
|
|
||||||
QStringMap gets;
|
|
||||||
QStringMap posts;
|
|
||||||
QMap<QString, UploadedFile> files;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResponseStatus
|
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||||
{
|
};
|
||||||
uint code;
|
|
||||||
QString text;
|
|
||||||
|
|
||||||
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
struct Response
|
||||||
};
|
{
|
||||||
|
ResponseStatus status;
|
||||||
struct Response
|
QStringMap headers;
|
||||||
{
|
QByteArray content;
|
||||||
ResponseStatus status;
|
|
||||||
QStringMap headers;
|
|
||||||
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
|
||||||
|
|
|
@ -35,265 +35,271 @@
|
||||||
#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());
|
||||||
|
|
||||||
// Start IP checking timer
|
// Start IP checking timer
|
||||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
||||||
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
|
{
|
||||||
Preferences* const pref = Preferences::instance();
|
// Save lastupdate time and last ip
|
||||||
pref->setDNSLastUpd(m_lastIPCheckTime);
|
Preferences *const pref = Preferences::instance();
|
||||||
pref->setDNSLastIP(m_lastIP.toString());
|
pref->setDNSLastUpd(m_lastIPCheckTime);
|
||||||
|
pref->setDNSLastIP(m_lastIP.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSUpdater::checkPublicIP()
|
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"));
|
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
||||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
manager->get(request);
|
||||||
manager->get(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
if (reply->error()) {
|
if (reply->error()) {
|
||||||
// Error
|
// Error
|
||||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||||
} else {
|
|
||||||
// Parse response
|
|
||||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
|
||||||
QString ret = reply->readAll();
|
|
||||||
if (ipregex.indexIn(ret) >= 0) {
|
|
||||||
QString ip_str = ipregex.cap(1);
|
|
||||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
|
||||||
QHostAddress new_ip(ip_str);
|
|
||||||
if (!new_ip.isNull()) {
|
|
||||||
if (m_lastIP != new_ip) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
|
||||||
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
|
||||||
m_lastIP = new_ip;
|
|
||||||
updateDNSService();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
// Clean up
|
// Parse response
|
||||||
reply->deleteLater();
|
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
||||||
sender()->deleteLater();
|
QString ret = reply->readAll();
|
||||||
|
if (ipregex.indexIn(ret) >= 0) {
|
||||||
|
QString ip_str = ipregex.cap(1);
|
||||||
|
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
||||||
|
QHostAddress new_ip(ip_str);
|
||||||
|
if (!new_ip.isNull()) {
|
||||||
|
if (m_lastIP != new_ip) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
||||||
|
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
||||||
|
m_lastIP = new_ip;
|
||||||
|
updateDNSService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean up
|
||||||
|
reply->deleteLater();
|
||||||
|
sender()->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSUpdater::updateDNSService()
|
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());
|
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
||||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
manager->get(request);
|
||||||
manager->get(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl DNSUpdater::getUpdateUrl() const
|
QUrl DNSUpdater::getUpdateUrl() const
|
||||||
{
|
{
|
||||||
QUrl url;
|
QUrl url;
|
||||||
#ifdef QT_NO_OPENSSL
|
#ifdef QT_NO_OPENSSL
|
||||||
url.setScheme("http");
|
url.setScheme("http");
|
||||||
#else
|
#else
|
||||||
url.setScheme("https");
|
url.setScheme("https");
|
||||||
#endif
|
#endif
|
||||||
url.setUserName(m_username);
|
url.setUserName(m_username);
|
||||||
url.setPassword(m_password);
|
url.setPassword(m_password);
|
||||||
|
|
||||||
Q_ASSERT(!m_lastIP.isNull());
|
Q_ASSERT(!m_lastIP.isNull());
|
||||||
// Service specific
|
// Service specific
|
||||||
switch(m_service) {
|
switch(m_service) {
|
||||||
case DNS::DYNDNS:
|
case DNS::DYNDNS:
|
||||||
url.setHost("members.dyndns.org");
|
url.setHost("members.dyndns.org");
|
||||||
break;
|
break;
|
||||||
case DNS::NOIP:
|
case DNS::NOIP:
|
||||||
url.setHost("dynupdate.no-ip.com");
|
url.setHost("dynupdate.no-ip.com");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qWarning() << "Unrecognized Dynamic DNS service!";
|
qWarning() << "Unrecognized Dynamic DNS service!";
|
||||||
Q_ASSERT(0);
|
Q_ASSERT(0);
|
||||||
}
|
}
|
||||||
url.setPath("/nic/update");
|
url.setPath("/nic/update");
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||||
url.addQueryItem("hostname", m_domain);
|
url.addQueryItem("hostname", m_domain);
|
||||||
url.addQueryItem("myip", m_lastIP.toString());
|
url.addQueryItem("myip", m_lastIP.toString());
|
||||||
#else
|
#else
|
||||||
QUrlQuery urlQuery(url);
|
QUrlQuery urlQuery(url);
|
||||||
urlQuery.addQueryItem("hostname", m_domain);
|
urlQuery.addQueryItem("hostname", m_domain);
|
||||||
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
||||||
url.setQuery(urlQuery);
|
url.setQuery(urlQuery);
|
||||||
#endif
|
#endif
|
||||||
Q_ASSERT(url.isValid());
|
Q_ASSERT(url.isValid());
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << url.toString();
|
qDebug() << Q_FUNC_INFO << url.toString();
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
|
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 {
|
}
|
||||||
// Pase reply
|
else {
|
||||||
processIPUpdateReply(reply->readAll());
|
// Pase reply
|
||||||
}
|
processIPUpdateReply(reply->readAll());
|
||||||
// Clean up
|
}
|
||||||
reply->deleteLater();
|
// Clean up
|
||||||
sender()->deleteLater();
|
reply->deleteLater();
|
||||||
|
sender()->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
if (code == "good" || code == "nochg") {
|
if (code == "good" || code == "nochg") {
|
||||||
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();
|
||||||
|
// It will retry in 30 minutes because the timer was not stopped
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Everything bellow is an error, stop updating until the user updates something
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
m_lastIP.clear();
|
m_lastIP.clear();
|
||||||
// It will retry in 30 minutes because the timer was not stopped
|
if (code == "nohost") {
|
||||||
return;
|
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
||||||
}
|
m_state = INVALID_CREDS;
|
||||||
// Everything bellow is an error, stop updating until the user updates something
|
return;
|
||||||
m_ipCheckTimer.stop();
|
}
|
||||||
m_lastIP.clear();
|
if (code == "badauth") {
|
||||||
if (code == "nohost") {
|
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
||||||
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
m_state = INVALID_CREDS;
|
||||||
m_state = INVALID_CREDS;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (code == "badagent") {
|
||||||
if (code == "badauth") {
|
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
||||||
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
Log::CRITICAL);
|
||||||
m_state = INVALID_CREDS;
|
m_state = FATAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (code == "badagent") {
|
if (code == "!donator") {
|
||||||
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
||||||
Log::CRITICAL);
|
Log::CRITICAL);
|
||||||
m_state = FATAL;
|
m_state = FATAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (code == "!donator") {
|
if (code == "abuse") {
|
||||||
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
||||||
Log::CRITICAL);
|
m_state = FATAL;
|
||||||
m_state = FATAL;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
if (code == "abuse") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
|
||||||
m_state = FATAL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
m_service = pref->getDynDNSService();
|
m_service = pref->getDynDNSService();
|
||||||
change = true;
|
change = true;
|
||||||
}
|
|
||||||
if (m_domain != pref->getDynDomainName()) {
|
|
||||||
m_domain = pref->getDynDomainName();
|
|
||||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
|
||||||
if (domain_regex.indexIn(m_domain) < 0) {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
|
||||||
m_lastIP.clear();
|
|
||||||
m_ipCheckTimer.stop();
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
change = true;
|
if (m_domain != pref->getDynDomainName()) {
|
||||||
}
|
m_domain = pref->getDynDomainName();
|
||||||
if (m_username != pref->getDynDNSUsername()) {
|
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
||||||
m_username = pref->getDynDNSUsername();
|
if (domain_regex.indexIn(m_domain) < 0) {
|
||||||
if (m_username.length() < 4) {
|
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
m_lastIP.clear();
|
||||||
m_lastIP.clear();
|
m_ipCheckTimer.stop();
|
||||||
m_ipCheckTimer.stop();
|
m_state = INVALID_CREDS;
|
||||||
m_state = INVALID_CREDS;
|
return;
|
||||||
return;
|
}
|
||||||
|
change = true;
|
||||||
}
|
}
|
||||||
change = true;
|
if (m_username != pref->getDynDNSUsername()) {
|
||||||
}
|
m_username = pref->getDynDNSUsername();
|
||||||
if (m_password != pref->getDynDNSPassword()) {
|
if (m_username.length() < 4) {
|
||||||
m_password = pref->getDynDNSPassword();
|
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
||||||
if (m_password.length() < 4) {
|
m_lastIP.clear();
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
m_ipCheckTimer.stop();
|
||||||
m_lastIP.clear();
|
m_state = INVALID_CREDS;
|
||||||
m_ipCheckTimer.stop();
|
return;
|
||||||
m_state = INVALID_CREDS;
|
}
|
||||||
return;
|
change = true;
|
||||||
|
}
|
||||||
|
if (m_password != pref->getDynDNSPassword()) {
|
||||||
|
m_password = pref->getDynDNSPassword();
|
||||||
|
if (m_password.length() < 4) {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
||||||
|
m_lastIP.clear();
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl DNSUpdater::getRegistrationUrl(int service)
|
QUrl DNSUpdater::getRegistrationUrl(int service)
|
||||||
{
|
{
|
||||||
switch(service) {
|
switch(service) {
|
||||||
case DNS::DYNDNS:
|
case DNS::DYNDNS:
|
||||||
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
||||||
case DNS::NOIP:
|
case DNS::NOIP:
|
||||||
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(0);
|
Q_ASSERT(0);
|
||||||
}
|
}
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,47 +40,51 @@
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
|
// Based on http://www.dyndns.com/developers/specs/
|
||||||
|
class DNSUpdater : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
/*!
|
public:
|
||||||
* Based on http://www.dyndns.com/developers/specs/
|
explicit DNSUpdater(QObject *parent = 0);
|
||||||
*/
|
~DNSUpdater();
|
||||||
class DNSUpdater : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DNSUpdater(QObject *parent = 0);
|
|
||||||
~DNSUpdater();
|
|
||||||
static QUrl getRegistrationUrl(int service);
|
|
||||||
|
|
||||||
public slots:
|
static QUrl getRegistrationUrl(int service);
|
||||||
void updateCredentials();
|
|
||||||
|
|
||||||
private slots:
|
public slots:
|
||||||
void checkPublicIP();
|
void updateCredentials();
|
||||||
void ipRequestFinished(QNetworkReply* reply);
|
|
||||||
void updateDNSService();
|
|
||||||
void ipUpdateFinished(QNetworkReply* reply);
|
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
QUrl getUpdateUrl() const;
|
void checkPublicIP();
|
||||||
void processIPUpdateReply(const QString &reply);
|
void ipRequestFinished(QNetworkReply *reply);
|
||||||
|
void updateDNSService();
|
||||||
|
void ipUpdateFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHostAddress m_lastIP;
|
QUrl getUpdateUrl() const;
|
||||||
QDateTime m_lastIPCheckTime;
|
void processIPUpdateReply(const QString &reply);
|
||||||
QTimer m_ipCheckTimer;
|
|
||||||
int m_state;
|
|
||||||
// Service creds
|
|
||||||
DNS::Service m_service;
|
|
||||||
QString m_domain;
|
|
||||||
QString m_username;
|
|
||||||
QString m_password;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
QHostAddress m_lastIP;
|
||||||
enum State { OK, INVALID_CREDS, FATAL };
|
QDateTime m_lastIPCheckTime;
|
||||||
};
|
QTimer m_ipCheckTimer;
|
||||||
|
int m_state;
|
||||||
|
// Service creds
|
||||||
|
DNS::Service m_service;
|
||||||
|
QString m_domain;
|
||||||
|
QString m_username;
|
||||||
|
QString m_password;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
INVALID_CREDS,
|
||||||
|
FATAL
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DNSUPDATER_H
|
#endif // DNSUPDATER_H
|
||||||
|
|
|
@ -50,433 +50,453 @@
|
||||||
#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 int blockSize = 64; // HMAC-MD5 block size
|
const short DEFAULT_PORT = 25;
|
||||||
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
const short DEFAULT_PORT_SSL = 465;
|
||||||
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
|
||||||
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
{
|
||||||
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
const int blockSize = 64; // HMAC-MD5 block size
|
||||||
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
||||||
|
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < key.length(); i++) {
|
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
||||||
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
||||||
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
||||||
}
|
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
||||||
|
|
||||||
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
|
for (int i = 0; i < key.length(); i++) {
|
||||||
QByteArray total = outerPadding;
|
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
||||||
QByteArray part = innerPadding;
|
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
||||||
part.append(msg);
|
}
|
||||||
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
|
|
||||||
return QCryptographicHash::hash(total, QCryptographicHash::Md5);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray determineFQDN()
|
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
|
||||||
{
|
QByteArray total = outerPadding;
|
||||||
QString hostname = QHostInfo::localHostName();
|
QByteArray part = innerPadding;
|
||||||
if (hostname.isEmpty())
|
part.append(msg);
|
||||||
hostname = "localhost";
|
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
|
||||||
|
return QCryptographicHash::hash(total, QCryptographicHash::Md5);
|
||||||
|
}
|
||||||
|
|
||||||
return hostname.toLocal8Bit();
|
QByteArray determineFQDN()
|
||||||
}
|
{
|
||||||
|
QString hostname = QHostInfo::localHostName();
|
||||||
|
if (hostname.isEmpty())
|
||||||
|
hostname = "localhost";
|
||||||
|
|
||||||
|
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();
|
{
|
||||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
const Preferences* const pref = Preferences::instance();
|
||||||
message = "";
|
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
||||||
message += encode_mime_header("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
m_message = "";
|
||||||
message += encode_mime_header("From", from, latin1);
|
m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
||||||
message += encode_mime_header("Subject", subject, latin1);
|
m_message += encodeMimeHeader("From", from, latin1);
|
||||||
message += encode_mime_header("To", to, latin1);
|
m_message += encodeMimeHeader("Subject", subject, latin1);
|
||||||
message += "MIME-Version: 1.0\r\n";
|
m_message += encodeMimeHeader("To", to, latin1);
|
||||||
message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
m_message += "MIME-Version: 1.0\r\n";
|
||||||
message += "Content-Transfer-Encoding: base64\r\n";
|
m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||||
message += "\r\n";
|
m_message += "Content-Transfer-Encoding: base64\r\n";
|
||||||
// Encode the body in base64
|
m_message += "\r\n";
|
||||||
QString crlf_body = body;
|
// Encode the body in base64
|
||||||
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
QString crlf_body = body;
|
||||||
int ct = b.length();
|
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
||||||
for (int i = 0; i < ct; i += 78)
|
int ct = b.length();
|
||||||
{
|
for (int i = 0; i < ct; i += 78)
|
||||||
message += b.mid(i, 78);
|
m_message += b.mid(i, 78);
|
||||||
}
|
m_from = from;
|
||||||
this->from = from;
|
m_rcpt = to;
|
||||||
rcpt = to;
|
// Authentication
|
||||||
// Authentication
|
if (pref->getMailNotificationSMTPAuth()) {
|
||||||
if (pref->getMailNotificationSMTPAuth()) {
|
m_username = pref->getMailNotificationSMTPUsername();
|
||||||
username = pref->getMailNotificationSMTPUsername();
|
m_password = pref->getMailNotificationSMTPPassword();
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::readyRead()
|
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 = m_buffer.left(pos);
|
||||||
QByteArray line = buffer.left(pos);
|
m_buffer = m_buffer.mid(pos + 2);
|
||||||
buffer = 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.
|
||||||
// We wait until it finishes.
|
// We wait until it finishes.
|
||||||
if (line[3] != ' ')
|
if (line[3] != ' ')
|
||||||
break;
|
break;
|
||||||
// Connection was successful
|
// Connection was successful
|
||||||
ehlo();
|
ehlo();
|
||||||
} else {
|
}
|
||||||
logError("Connection failed, unrecognized reply: "+line);
|
else {
|
||||||
state = Close;
|
logError("Connection failed, unrecognized reply: "+line);
|
||||||
}
|
m_state = Close;
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
case EhloSent:
|
}
|
||||||
case HeloSent:
|
case EhloSent:
|
||||||
case EhloGreetReceived:
|
case HeloSent:
|
||||||
parseEhloResponse(code, line[3] != ' ', line.mid(4));
|
case EhloGreetReceived:
|
||||||
break;
|
parseEhloResponse(code, line[3] != ' ', line.mid(4));
|
||||||
|
break;
|
||||||
#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 {
|
}
|
||||||
authenticate();
|
else {
|
||||||
}
|
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 {
|
}
|
||||||
// Authentication failed!
|
else {
|
||||||
logError("Authentication failed, msg: "+line);
|
// Authentication failed!
|
||||||
state = Close;
|
logError("Authentication failed, msg: "+line);
|
||||||
}
|
m_state = Close;
|
||||||
break;
|
}
|
||||||
case Rcpt:
|
break;
|
||||||
if (code[0] == '2') {
|
case Rcpt:
|
||||||
socket->write("rcpt to:<" + rcpt.toLatin1() + ">\r\n");
|
if (code[0] == '2') {
|
||||||
socket->flush();
|
m_socket->write("rcpt to:<" + m_rcpt.toLatin1() + ">\r\n");
|
||||||
state = Data;
|
m_socket->flush();
|
||||||
} else {
|
m_state = Data;
|
||||||
logError("<mail from> was rejected by server, msg: "+line);
|
}
|
||||||
state = Close;
|
else {
|
||||||
}
|
logError("<mail from> was rejected by server, msg: "+line);
|
||||||
break;
|
m_state = Close;
|
||||||
case Data:
|
}
|
||||||
if (code[0] == '2') {
|
break;
|
||||||
socket->write("data\r\n");
|
case Data:
|
||||||
socket->flush();
|
if (code[0] == '2') {
|
||||||
state = Body;
|
m_socket->write("data\r\n");
|
||||||
} else {
|
m_socket->flush();
|
||||||
logError("<Rcpt to> was rejected by server, msg: "+line);
|
m_state = Body;
|
||||||
state = Close;
|
}
|
||||||
}
|
else {
|
||||||
break;
|
logError("<Rcpt to> was rejected by server, msg: "+line);
|
||||||
case Body:
|
m_state = Close;
|
||||||
if (code[0] == '3') {
|
}
|
||||||
socket->write(message + "\r\n.\r\n");
|
break;
|
||||||
socket->flush();
|
case Body:
|
||||||
state = Quit;
|
if (code[0] == '3') {
|
||||||
} else {
|
m_socket->write(m_message + "\r\n.\r\n");
|
||||||
logError("<data> was rejected by server, msg: "+line);
|
m_socket->flush();
|
||||||
state = Close;
|
m_state = Quit;
|
||||||
}
|
}
|
||||||
break;
|
else {
|
||||||
case Quit:
|
logError("<data> was rejected by server, msg: "+line);
|
||||||
if (code[0] == '2') {
|
m_state = Close;
|
||||||
socket->write("QUIT\r\n");
|
}
|
||||||
socket->flush();
|
break;
|
||||||
// here, we just close.
|
case Quit:
|
||||||
state = Close;
|
if (code[0] == '2') {
|
||||||
} else {
|
m_socket->write("QUIT\r\n");
|
||||||
logError("Message was rejected by the server, error: "+line);
|
m_socket->flush();
|
||||||
state = Close;
|
// here, we just close.
|
||||||
}
|
m_state = Close;
|
||||||
break;
|
}
|
||||||
default:
|
else {
|
||||||
qDebug() << "Disconnecting from host";
|
logError("Message was rejected by the server, error: "+line);
|
||||||
socket->disconnectFromHost();
|
m_state = Close;
|
||||||
return;
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug() << "Disconnecting from host";
|
||||||
|
m_socket->disconnectFromHost();
|
||||||
|
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() + ": ";
|
||||||
if (!prefix.isEmpty()) line += prefix;
|
if (!prefix.isEmpty()) line += prefix;
|
||||||
if (!value.contains("=?") && latin1->canEncode(value)) {
|
if (!value.contains("=?") && latin1->canEncode(value)) {
|
||||||
bool firstWord = true;
|
bool firstWord = true;
|
||||||
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
|
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
|
||||||
if (line.size() > 78) {
|
if (line.size() > 78) {
|
||||||
rv = rv + line + "\r\n";
|
rv = rv + line + "\r\n";
|
||||||
line.clear();
|
line.clear();
|
||||||
}
|
}
|
||||||
if (firstWord)
|
if (firstWord)
|
||||||
line += word;
|
line += word;
|
||||||
else
|
else
|
||||||
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();
|
||||||
// Use base64 encoding
|
// Use base64 encoding
|
||||||
QByteArray base64 = utf8.toBase64();
|
QByteArray base64 = utf8.toBase64();
|
||||||
int ct = base64.length();
|
int ct = base64.length();
|
||||||
line += "=?utf-8?b?";
|
line += "=?utf-8?b?";
|
||||||
for (int i = 0; i < ct; i += 4) {
|
for (int i = 0; i < ct; i += 4) {
|
||||||
/*if (line.length() > 72) {
|
/*if (line.length() > 72) {
|
||||||
rv += line + "?\n\r";
|
rv += line + "?\n\r";
|
||||||
line = " =?utf-8?b?";
|
line = " =?utf-8?b?";
|
||||||
}*/
|
}*/
|
||||||
line = line + base64.mid(i, 4);
|
line = line + base64.mid(i, 4);
|
||||||
|
}
|
||||||
|
line += "?="; // end encoded-word atom
|
||||||
}
|
}
|
||||||
line += "?="; // end encoded-word atom
|
return rv + line + "\r\n";
|
||||||
}
|
|
||||||
return rv + line + "\r\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
}
|
||||||
// Both EHLO and HELO failed, chances are this is NOT
|
else {
|
||||||
// a SMTP server
|
// Both EHLO and HELO failed, chances are this is NOT
|
||||||
logError("Both EHLO and HELO failed, msg: "+line);
|
// a SMTP server
|
||||||
state = Close;
|
logError("Both EHLO and HELO failed, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
if (m_state != EhloGreetReceived) {
|
||||||
if (state != EhloGreetReceived) {
|
if (!continued) {
|
||||||
if (!continued) {
|
// greeting only, no extensions
|
||||||
// greeting only, no extensions
|
qDebug() << "No extension";
|
||||||
qDebug() << "No extension";
|
m_state = EhloDone;
|
||||||
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 {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
|
||||||
|
<< line.section(' ', 1);
|
||||||
|
m_extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
|
||||||
|
if (!continued)
|
||||||
|
m_state = EhloDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state != EhloDone) return;
|
||||||
|
|
||||||
|
if (m_extensions.contains("STARTTLS") && m_useSsl) {
|
||||||
|
qDebug() << "STARTTLS";
|
||||||
|
startTLS();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
authenticate();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
|
|
||||||
<< line.section(' ', 1);
|
|
||||||
extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
|
|
||||||
if (!continued)
|
|
||||||
state = EhloDone;
|
|
||||||
}
|
|
||||||
if (state != EhloDone) return;
|
|
||||||
if (extensions.contains("STARTTLS") && use_ssl) {
|
|
||||||
qDebug() << "STARTTLS";
|
|
||||||
startTLS();
|
|
||||||
} else {
|
|
||||||
authenticate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
else if (auth.contains("PLAIN")) {
|
else if (auth.contains("PLAIN")) {
|
||||||
qDebug() << "Using PLAIN authentication...";
|
qDebug() << "Using PLAIN authentication...";
|
||||||
authPlain();
|
authPlain();
|
||||||
}
|
}
|
||||||
else if (auth.contains("LOGIN")) {
|
else if (auth.contains("LOGIN")) {
|
||||||
qDebug() << "Using LOGIN authentication...";
|
qDebug() << "Using LOGIN authentication...";
|
||||||
authLogin();
|
authLogin();
|
||||||
} else {
|
}
|
||||||
// Skip authentication
|
else {
|
||||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
// Skip authentication
|
||||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||||
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
||||||
state = Authenticated;
|
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
||||||
// At this point the server will not send any response
|
m_state = Authenticated;
|
||||||
// So fill the buffer with a fake one to pass the tests
|
// At this point the server will not send any response
|
||||||
// in readyRead()
|
// So fill the buffer with a fake one to pass the tests
|
||||||
buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
// in readyRead()
|
||||||
}
|
m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::startTLS()
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::logError(const QString &msg)
|
void Smtp::logError(const QString &msg)
|
||||||
{
|
{
|
||||||
qDebug() << "Email Notification Error:" << msg;
|
qDebug() << "Email Notification Error:" << msg;
|
||||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,54 +52,74 @@ QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
|
class Smtp : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
class Smtp : public QObject {
|
public:
|
||||||
Q_OBJECT
|
Smtp(QObject *parent = 0);
|
||||||
|
~Smtp();
|
||||||
|
|
||||||
public:
|
void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body);
|
||||||
Smtp(QObject *parent = 0);
|
|
||||||
~Smtp();
|
|
||||||
void sendMail(const QString &from, const QString &to, const QString &subject, const QString &body);
|
|
||||||
|
|
||||||
private slots:
|
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
|
||||||
void ehlo();
|
{
|
||||||
void helo();
|
Rcpt,
|
||||||
void parseEhloResponse(const QByteArray& code, bool continued, const QString& line);
|
EhloSent,
|
||||||
void authenticate();
|
HeloSent,
|
||||||
void startTLS();
|
EhloDone,
|
||||||
void authCramMD5(const QByteArray& challenge = QByteArray());
|
EhloGreetReceived,
|
||||||
void authPlain();
|
AuthRequestSent,
|
||||||
void authLogin();
|
AuthSent,
|
||||||
void logError(const QString &msg);
|
AuthUsernameSent,
|
||||||
|
Authenticated,
|
||||||
|
StartTLSSent,
|
||||||
|
Data,
|
||||||
|
Init,
|
||||||
|
Body,
|
||||||
|
Quit,
|
||||||
|
Close
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
enum AuthType
|
||||||
enum states { Rcpt, EhloSent, HeloSent, EhloDone, EhloGreetReceived, AuthRequestSent, AuthSent,
|
{
|
||||||
AuthUsernameSent, Authenticated, StartTLSSent, Data, Init, Body, Quit, Close };
|
AuthPlain,
|
||||||
enum AuthType { AuthPlain, AuthLogin, AuthCramMD5 };
|
AuthLogin,
|
||||||
|
AuthCramMD5
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
QByteArray encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix = QByteArray());
|
||||||
QByteArray message;
|
void ehlo();
|
||||||
|
void helo();
|
||||||
|
void parseEhloResponse(const QByteArray &code, bool continued, const QString &line);
|
||||||
|
void authenticate();
|
||||||
|
void startTLS();
|
||||||
|
void authCramMD5(const QByteArray &challenge = QByteArray());
|
||||||
|
void authPlain();
|
||||||
|
void authLogin();
|
||||||
|
void logError(const QString &msg);
|
||||||
|
|
||||||
|
QByteArray m_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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue