From 615e08b74fa95863b859771a72f31c5f3d54e4e6 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Sat, 15 Jan 2011 16:53:37 +0000 Subject: [PATCH] Added unicode support to email notification Improved compatibility with various SMTP servers --- src/smtp.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++--------- src/smtp.h | 9 ++++- 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/smtp.cpp b/src/smtp.cpp index 4e0df7930..5d72128ba 100644 --- a/src/smtp.cpp +++ b/src/smtp.cpp @@ -12,19 +12,33 @@ #include #include +#include #include +#include +#include Smtp::Smtp(const QString &from, const QString &to, const QString &subject, const QString &body) { socket = new QTcpSocket(this); connect( socket, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); - message = "To: " + to + "\n"; - message.append("From: " + from + "\n"); - message.append("Subject: " + subject + "\n"); - message.append(body); - message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) ); - message.replace( QString::fromLatin1( "\r\n.\r\n" ), - QString::fromLatin1( "\r\n..\r\n" ) ); + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); + message = ""; + message += encode_mime_header("Date", QDateTime::currentDateTimeUtc().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1); + message += encode_mime_header("From", from, latin1); + message += encode_mime_header("Subject", subject, latin1); + message += encode_mime_header("To", to, latin1); + 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; rcpt = to; state = Init; @@ -38,6 +52,45 @@ Smtp::Smtp(const QString &from, const QString &to, const QString &subject, const 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() { if(t) @@ -67,31 +120,46 @@ void Smtp::readyRead() if ( state == Init && responseLine[0] == '2' ) { // banner was okay, let's go on - - *t << "HELO there\r\n"; + QByteArray address = "127.0.0.1"; + 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(); state = Mail; } - else if ( state == Mail && responseLine[0] == '2' ) + else if ( state == Mail || state == Mail2 ) { - // HELO response was okay (well, it has to be) - - *t << "MAIL FROM: " << from << "\r\n"; - t->flush(); - state = Rcpt; + if(responseLine[0] == '2') { + // EHLO response was okay (well, it has to be) + *t << "mail from:<" << from << ">\r\n"; + t->flush(); + 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' ) { - *t << "RCPT TO: " << rcpt << "\r\n"; //r + *t << "rcpt to:<" << rcpt << ">\r\n"; //r t->flush(); state = Data; } else if ( state == Data && responseLine[0] == '2' ) { - *t << "DATA\r\n"; + *t << "data\r\n"; t->flush(); state = Body; } diff --git a/src/smtp.h b/src/smtp.h index fbeb224ca..a16e05585 100644 --- a/src/smtp.h +++ b/src/smtp.h @@ -14,9 +14,11 @@ #include #include +#include struct QTextStream; struct QTcpSocket; +class QTextCodec; class Smtp : public QObject { Q_OBJECT @@ -29,13 +31,16 @@ private slots: void readyRead(); private: - QString message; + QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray()); + +private: + QByteArray message; QTextStream *t; QTcpSocket *socket; QString from; QString rcpt; QString response; - enum states{Rcpt,Mail,Data,Init,Body,Quit,Close}; + enum states{Rcpt,Mail,Mail2,Data,Init,Body,Quit,Close}; int state; };