|
|
@ -12,19 +12,33 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include <QTextStream> |
|
|
|
#include <QTextStream> |
|
|
|
#include <QTcpSocket> |
|
|
|
#include <QTcpSocket> |
|
|
|
|
|
|
|
#include <QTextCodec> |
|
|
|
#include <QDebug> |
|
|
|
#include <QDebug> |
|
|
|
|
|
|
|
#include <QHostAddress> |
|
|
|
|
|
|
|
#include <QNetworkInterface> |
|
|
|
|
|
|
|
|
|
|
|
Smtp::Smtp(const QString &from, const QString &to, const QString &subject, const QString &body) { |
|
|
|
Smtp::Smtp(const QString &from, const QString &to, const QString &subject, const QString &body) { |
|
|
|
socket = new QTcpSocket(this); |
|
|
|
socket = new QTcpSocket(this); |
|
|
|
|
|
|
|
|
|
|
|
connect( socket, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); |
|
|
|
connect( socket, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); |
|
|
|
message = "To: " + to + "\n"; |
|
|
|
QTextCodec* latin1 = QTextCodec::codecForName("latin1"); |
|
|
|
message.append("From: " + from + "\n"); |
|
|
|
message = ""; |
|
|
|
message.append("Subject: " + subject + "\n"); |
|
|
|
message += encode_mime_header("Date", QDateTime::currentDateTimeUtc().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1); |
|
|
|
message.append(body); |
|
|
|
message += encode_mime_header("From", from, latin1); |
|
|
|
message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) ); |
|
|
|
message += encode_mime_header("Subject", subject, latin1); |
|
|
|
message.replace( QString::fromLatin1( "\r\n.\r\n" ), |
|
|
|
message += encode_mime_header("To", to, latin1); |
|
|
|
QString::fromLatin1( "\r\n..\r\n" ) ); |
|
|
|
message += "MIME-Version: 1.0\r\n"; |
|
|
|
|
|
|
|
message += "Content-Type: text/plain; charset=UTF-8\r\n"; |
|
|
|
|
|
|
|
message += "Content-Transfer-Encoding: base64\r\n"; |
|
|
|
|
|
|
|
message += "\r\n"; |
|
|
|
|
|
|
|
// Encode the body in base64
|
|
|
|
|
|
|
|
QString crlf_body = body; |
|
|
|
|
|
|
|
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64(); |
|
|
|
|
|
|
|
int ct = b.length(); |
|
|
|
|
|
|
|
for (int i = 0; i < ct; i += 78) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
message += b.mid(i, 78); |
|
|
|
|
|
|
|
} |
|
|
|
this->from = from; |
|
|
|
this->from = from; |
|
|
|
rcpt = to; |
|
|
|
rcpt = to; |
|
|
|
state = Init; |
|
|
|
state = Init; |
|
|
@ -38,6 +52,45 @@ Smtp::Smtp(const QString &from, const QString &to, const QString &subject, const |
|
|
|
t = new QTextStream(socket); |
|
|
|
t = new QTextStream(socket); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
QByteArray rv = ""; |
|
|
|
|
|
|
|
QByteArray line = key.toAscii() + ": "; |
|
|
|
|
|
|
|
if (!prefix.isEmpty()) line += prefix; |
|
|
|
|
|
|
|
if (!value.contains("=?") && latin1->canEncode(value)) { |
|
|
|
|
|
|
|
bool firstWord = true; |
|
|
|
|
|
|
|
foreach(const QByteArray& word, value.toAscii().split(' ')) { |
|
|
|
|
|
|
|
if (line.size() > 78) { |
|
|
|
|
|
|
|
rv = rv + line + "\r\n"; |
|
|
|
|
|
|
|
line.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (firstWord) |
|
|
|
|
|
|
|
line += word; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
line += " " + word; |
|
|
|
|
|
|
|
firstWord = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// The text cannot be losslessly encoded as Latin-1. Therefore, we
|
|
|
|
|
|
|
|
// must use base64 encoding.
|
|
|
|
|
|
|
|
QByteArray utf8 = value.toUtf8(); |
|
|
|
|
|
|
|
int ct = utf8.length(); |
|
|
|
|
|
|
|
// Use base64 encoding
|
|
|
|
|
|
|
|
QByteArray base64 = utf8.toBase64(); |
|
|
|
|
|
|
|
ct = base64.length(); |
|
|
|
|
|
|
|
line += "=?utf-8?b?"; |
|
|
|
|
|
|
|
for (int i = 0; i < ct; i += 4) { |
|
|
|
|
|
|
|
/*if (line.length() > 72) {
|
|
|
|
|
|
|
|
rv += line + "?\n\r"; |
|
|
|
|
|
|
|
line = " =?utf-8?b?"; |
|
|
|
|
|
|
|
}*/ |
|
|
|
|
|
|
|
line = line + base64.mid(i, 4); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
line += "?="; // end encoded-word atom
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return rv + line + "\r\n"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Smtp::~Smtp() |
|
|
|
Smtp::~Smtp() |
|
|
|
{ |
|
|
|
{ |
|
|
|
if(t) |
|
|
|
if(t) |
|
|
@ -67,31 +120,46 @@ void Smtp::readyRead() |
|
|
|
if ( state == Init && responseLine[0] == '2' ) |
|
|
|
if ( state == Init && responseLine[0] == '2' ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// banner was okay, let's go on
|
|
|
|
// banner was okay, let's go on
|
|
|
|
|
|
|
|
QByteArray address = "127.0.0.1"; |
|
|
|
*t << "HELO there\r\n"; |
|
|
|
foreach(const QHostAddress& addr, QNetworkInterface::allAddresses()) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (addr == QHostAddress::LocalHost || addr == QHostAddress::LocalHostIPv6) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
address = addr.toString().toAscii(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
*t << "ehlo "+ address + "\r\n"; |
|
|
|
t->flush(); |
|
|
|
t->flush(); |
|
|
|
|
|
|
|
|
|
|
|
state = Mail; |
|
|
|
state = Mail; |
|
|
|
} |
|
|
|
} |
|
|
|
else if ( state == Mail && responseLine[0] == '2' ) |
|
|
|
else if ( state == Mail || state == Mail2 ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// HELO response was okay (well, it has to be)
|
|
|
|
if(responseLine[0] == '2') { |
|
|
|
|
|
|
|
// EHLO response was okay (well, it has to be)
|
|
|
|
*t << "MAIL FROM: " << from << "\r\n"; |
|
|
|
*t << "mail from:<" << from << ">\r\n"; |
|
|
|
t->flush(); |
|
|
|
t->flush(); |
|
|
|
state = Rcpt; |
|
|
|
state = Rcpt; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if(state == Mail) { |
|
|
|
|
|
|
|
// ehlo did not work, try helo instead
|
|
|
|
|
|
|
|
*t << "helo\r\n"; |
|
|
|
|
|
|
|
t->flush(); |
|
|
|
|
|
|
|
state = Mail2; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if ( state == Rcpt && responseLine[0] == '2' ) |
|
|
|
else if ( state == Rcpt && responseLine[0] == '2' ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
*t << "RCPT TO: " << rcpt << "\r\n"; //r
|
|
|
|
*t << "rcpt to:<" << rcpt << ">\r\n"; //r
|
|
|
|
t->flush(); |
|
|
|
t->flush(); |
|
|
|
state = Data; |
|
|
|
state = Data; |
|
|
|
} |
|
|
|
} |
|
|
|
else if ( state == Data && responseLine[0] == '2' ) |
|
|
|
else if ( state == Data && responseLine[0] == '2' ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
*t << "DATA\r\n"; |
|
|
|
*t << "data\r\n"; |
|
|
|
t->flush(); |
|
|
|
t->flush(); |
|
|
|
state = Body; |
|
|
|
state = Body; |
|
|
|
} |
|
|
|
} |
|
|
|