Added unicode support to email notification

Improved compatibility with various SMTP servers
This commit is contained in:
Christophe Dumez 2011-01-15 16:53:37 +00:00
parent 4644b5cf15
commit 615e08b74f
2 changed files with 92 additions and 19 deletions

View file

@ -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;
} }

View file

@ -14,9 +14,11 @@
#include <QString> #include <QString>
#include <QObject> #include <QObject>
#include <QByteArray>
struct QTextStream; struct QTextStream;
struct QTcpSocket; struct QTcpSocket;
class QTextCodec;
class Smtp : public QObject { class Smtp : public QObject {
Q_OBJECT Q_OBJECT
@ -29,13 +31,16 @@ private slots:
void readyRead(); void readyRead();
private: private:
QString message; QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray());
private:
QByteArray message;
QTextStream *t; QTextStream *t;
QTcpSocket *socket; QTcpSocket *socket;
QString from; QString from;
QString rcpt; QString rcpt;
QString response; QString response;
enum states{Rcpt,Mail,Data,Init,Body,Quit,Close}; enum states{Rcpt,Mail,Mail2,Data,Init,Body,Quit,Close};
int state; int state;
}; };