From 9e048495bae992f555a44af0c3780938062cd5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C5=91k=C3=A9s=20Attila?= Date: Sat, 22 Sep 2012 20:59:27 +0300 Subject: [PATCH] State machine implementation terminated. Added some functions (waitForReadyConnected, waitForAuthenticated, waitForMailSent) synchronous waiting. Added writeToDevice() function to MimeMessage and MimePart (and subclasses), now these are used in SmtpClient instead of toString(). Added some unit tests. --- SMTPEmail.pro | 46 -------------- src/SMTPEmail.pro | 59 ++++++++++++++++++ src/emailaddress.h | 3 +- src/mimeattachment.cpp | 9 +-- src/mimeattachment.h | 7 +-- src/mimebase64encoder.cpp | 7 +++ src/mimebase64encoder.h | 14 +++++ src/mimebase64formatter.cpp | 17 +++++ src/mimebase64formatter.h | 15 +++++ src/mimecontentencoder.cpp | 3 + src/mimecontentencoder.h | 16 +++++ src/mimecontentformatter.cpp | 72 ++++----------------- src/mimecontentformatter.h | 35 +++-------- src/mimefile.cpp | 9 +-- src/mimefile.h | 8 ++- src/mimehtml.cpp | 6 -- src/mimehtml.h | 5 +- src/mimeinlinefile.cpp | 9 +-- src/mimeinlinefile.h | 5 +- src/mimemessage.cpp | 118 +++++++++++++---------------------- src/mimemessage.h | 15 +++-- src/mimemultipart.cpp | 16 ++--- src/mimemultipart.h | 7 ++- src/mimepart.cpp | 91 +++++++++++++++------------ src/mimepart.h | 23 +++---- src/mimeqpencoder.cpp | 8 +++ src/mimeqpencoder.h | 14 +++++ src/mimeqpformatter.cpp | 29 +++++++++ src/mimeqpformatter.h | 15 +++++ src/mimetext.cpp | 10 +-- src/mimetext.h | 5 +- src/quotedprintable.cpp | 6 +- src/quotedprintable.h | 3 +- src/smtpclient.cpp | 23 ++++--- src/smtpclient.h | 11 ++-- src/smtpmime_global.h | 10 +++ test/connectiontest.cpp | 55 ++++++++++++++++ test/connectiontest.h | 21 +++++++ test/main.cpp | 26 ++++++++ test/test.pro | 27 ++++++++ 40 files changed, 538 insertions(+), 340 deletions(-) delete mode 100644 SMTPEmail.pro create mode 100644 src/SMTPEmail.pro create mode 100644 src/mimebase64encoder.cpp create mode 100644 src/mimebase64encoder.h create mode 100644 src/mimebase64formatter.cpp create mode 100644 src/mimebase64formatter.h create mode 100644 src/mimecontentencoder.cpp create mode 100644 src/mimecontentencoder.h create mode 100644 src/mimeqpencoder.cpp create mode 100644 src/mimeqpencoder.h create mode 100644 src/mimeqpformatter.cpp create mode 100644 src/mimeqpformatter.h create mode 100644 src/smtpmime_global.h create mode 100644 test/connectiontest.cpp create mode 100644 test/connectiontest.h create mode 100644 test/main.cpp create mode 100644 test/test.pro diff --git a/SMTPEmail.pro b/SMTPEmail.pro deleted file mode 100644 index 2fc74d6..0000000 --- a/SMTPEmail.pro +++ /dev/null @@ -1,46 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2011-08-11T20:59:25 -# -#------------------------------------------------- - -QT += core gui network - -TARGET = SMTPEmail -TEMPLATE = app - - -SOURCES += \ - src/emailaddress.cpp \ - src/mimeattachment.cpp \ - src/mimefile.cpp \ - src/mimehtml.cpp \ - src/mimeinlinefile.cpp \ - src/mimemessage.cpp \ - src/mimepart.cpp \ - src/mimetext.cpp \ - src/smtpclient.cpp \ - src/quotedprintable.cpp \ - src/mimemultipart.cpp \ - src/mimecontentformatter.cpp - -HEADERS += \ - src/emailaddress.h \ - src/mimeattachment.h \ - src/mimefile.h \ - src/mimehtml.h \ - src/mimeinlinefile.h \ - src/mimemessage.h \ - src/mimepart.h \ - src/mimetext.h \ - src/smtpclient.h \ - src/SmtpMime \ - src/quotedprintable.h \ - src/mimemultipart.h \ - src/mimecontentformatter.h - -OTHER_FILES += \ - LICENSE \ - README.md - -FORMS += diff --git a/src/SMTPEmail.pro b/src/SMTPEmail.pro new file mode 100644 index 0000000..4abab5f --- /dev/null +++ b/src/SMTPEmail.pro @@ -0,0 +1,59 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-08-11T20:59:25 +# +#------------------------------------------------- + +QT += core network + +TARGET = SmtpMime +TEMPLATE = lib + +DEFINE += SMTP_MIME_LIBRARY + +SOURCES += \ + emailaddress.cpp \ + mimeattachment.cpp \ + mimefile.cpp \ + mimehtml.cpp \ + mimeinlinefile.cpp \ + mimemessage.cpp \ + mimepart.cpp \ + mimetext.cpp \ + smtpclient.cpp \ + quotedprintable.cpp \ + mimemultipart.cpp \ + mimecontentencoder.cpp \ + mimebase64encoder.cpp \ + mimeqpencoder.cpp \ + mimeqpformatter.cpp \ + mimebase64formatter.cpp \ + mimecontentformatter.cpp + +HEADERS += \ + emailaddress.h \ + mimeattachment.h \ + mimefile.h \ + mimehtml.h \ + mimeinlinefile.h \ + mimemessage.h \ + mimepart.h \ + mimetext.h \ + smtpclient.h \ + SmtpMime \ + quotedprintable.h \ + mimemultipart.h \ + smtpmime_global.h \ + mimecontentencoder.h \ + mimebase64encoder.h \ + mimeqpencoder.h \ + mimeqpformatter.h \ + mimepart.cpp.autosave \ + mimebase64formatter.h \ + mimecontentformatter.h + +OTHER_FILES += \ + LICENSE \ + README.md + +FORMS += diff --git a/src/emailaddress.h b/src/emailaddress.h index 957ba2b..44f1a2e 100644 --- a/src/emailaddress.h +++ b/src/emailaddress.h @@ -19,9 +19,10 @@ #ifndef EMAILADDRESS_H #define EMAILADDRESS_H +#include "smtpmime_global.h" #include -class EmailAddress : public QObject +class SMTP_MIME_EXPORT EmailAddress : public QObject { Q_OBJECT public: diff --git a/src/mimeattachment.cpp b/src/mimeattachment.cpp index eac36f7..fd2b46c 100644 --- a/src/mimeattachment.cpp +++ b/src/mimeattachment.cpp @@ -24,6 +24,7 @@ MimeAttachment::MimeAttachment(QFile *file) : MimeFile(file) { + this->headerLines += "Content-disposition: attachment\r\n"; } MimeAttachment::~MimeAttachment() @@ -35,12 +36,4 @@ MimeAttachment::~MimeAttachment() /* [2] Protected methods */ -void MimeAttachment::prepare() -{ - this->header += "Content-disposition: attachment\r\n"; - - /* !!! IMPORTANT !!! */ - MimeFile::prepare(); -} - /* [2] --- */ diff --git a/src/mimeattachment.h b/src/mimeattachment.h index 4b79678..90a5780 100644 --- a/src/mimeattachment.h +++ b/src/mimeattachment.h @@ -19,11 +19,13 @@ #ifndef MIMEATTACHMENT_H #define MIMEATTACHMENT_H + #include +#include "smtpmime_global.h" #include "mimepart.h" #include "mimefile.h" -class MimeAttachment : public MimeFile +class SMTP_MIME_EXPORT MimeAttachment : public MimeFile { Q_OBJECT public: @@ -38,9 +40,6 @@ public: protected: /* [2] Protected methods */ - - virtual void prepare(); - /* [2] --- */ }; diff --git a/src/mimebase64encoder.cpp b/src/mimebase64encoder.cpp new file mode 100644 index 0000000..4ce73ff --- /dev/null +++ b/src/mimebase64encoder.cpp @@ -0,0 +1,7 @@ +#include "mimebase64encoder.h" + +MimeBase64Encoder::MimeBase64Encoder() {} + +QByteArray MimeBase64Encoder::encode(const QByteArray &data) { + return data.toBase64(); +} diff --git a/src/mimebase64encoder.h b/src/mimebase64encoder.h new file mode 100644 index 0000000..826b7d4 --- /dev/null +++ b/src/mimebase64encoder.h @@ -0,0 +1,14 @@ +#ifndef MIMEBASE64ENCODER_H +#define MIMEBASE64ENCODER_H + +#include "mimecontentencoder.h" + +class MimeBase64Encoder : public MimeContentEncoder +{ +public: + MimeBase64Encoder(); + + QByteArray encode(const QByteArray &data); +}; + +#endif // MIMEBASE64ENCODER_H diff --git a/src/mimebase64formatter.cpp b/src/mimebase64formatter.cpp new file mode 100644 index 0000000..b9b67c8 --- /dev/null +++ b/src/mimebase64formatter.cpp @@ -0,0 +1,17 @@ +#include "mimebase64formatter.h" + +MimeBase64Formatter::MimeBase64Formatter(QIODevice *out) : + MimeContentFormatter(out) {} + +qint64 MimeBase64Formatter::writeData(const char *data, qint64 maxLength) { + qDebug("called"); + int lines = (maxLength - 1) / lineLength + 1; + for (int i = 1; i < lines; ++i) { + output->write(data, lineLength); + output->write("\r\n"); + data += lineLength; + } + output->write(data, maxLength - (lines - 1) * lineLength); + output->write("\r\n"); + return maxLength; +} diff --git a/src/mimebase64formatter.h b/src/mimebase64formatter.h new file mode 100644 index 0000000..c1d227b --- /dev/null +++ b/src/mimebase64formatter.h @@ -0,0 +1,15 @@ +#ifndef MIMEBASE64FORMATTER_H +#define MIMEBASE64FORMATTER_H + +#include "mimecontentformatter.h" + +class MimeBase64Formatter : public MimeContentFormatter +{ +public: + MimeBase64Formatter(QIODevice*); + +protected: + virtual qint64 writeData(const char *data, qint64 len); +}; + +#endif // MIMEBASE64FORMATTER_H diff --git a/src/mimecontentencoder.cpp b/src/mimecontentencoder.cpp new file mode 100644 index 0000000..b718b7e --- /dev/null +++ b/src/mimecontentencoder.cpp @@ -0,0 +1,3 @@ +#include "mimecontentencoder.h" + +MimeContentEncoder::MimeContentEncoder() {} diff --git a/src/mimecontentencoder.h b/src/mimecontentencoder.h new file mode 100644 index 0000000..2cf577e --- /dev/null +++ b/src/mimecontentencoder.h @@ -0,0 +1,16 @@ +#ifndef MIMEENCODER_H +#define MIMEENCODER_H + +#include +#include + +class MimeContentEncoder : public QObject +{ +public: + virtual QByteArray encode(const QByteArray &data) =0; + +protected: + MimeContentEncoder(); +}; + +#endif // MIMEENCODER_H diff --git a/src/mimecontentformatter.cpp b/src/mimecontentformatter.cpp index 7f5a6e4..c1ab674 100644 --- a/src/mimecontentformatter.cpp +++ b/src/mimecontentformatter.cpp @@ -1,66 +1,20 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - #include "mimecontentformatter.h" -MimeContentFormatter::MimeContentFormatter(int max_length) : - max_length(max_length) -{} - -QString MimeContentFormatter::format(const QString &content, bool quotedPrintable) const { - - QString out; - - int chars = 0; - for (int i = 0; i < content.length() ; ++i) { - chars++; - if (!quotedPrintable) { - if (chars > max_length) { - out.append("\r\n"); - chars = 1; - } - } - else { - if (content[i] == '\n') { // new line - out.append(content[i]); - chars = 0; - continue; - } - - if ((chars > max_length - 1) - || ((content[i] == '=') && (chars > max_length - 3) )) { - out.append('='); - out.append("\r\n"); - chars = 1; - } - - } - out.append(content[i]); - } - - return out; - +MimeContentFormatter::MimeContentFormatter(QIODevice *out, int length) : + output(out), + lineLength(length) +{ + QIODevice::open(WriteOnly); } -void MimeContentFormatter::setMaxLength(int l) { - max_length = l; +int MimeContentFormatter::getLineLength() const { + return lineLength; } -int MimeContentFormatter::getMaxLength() const { - return max_length; +void MimeContentFormatter::setLineLength(int l) { + lineLength = l; +} + +qint64 MimeContentFormatter::readData(char*, qint64) { + return -1; } diff --git a/src/mimecontentformatter.h b/src/mimecontentformatter.h index 8180cb6..5912f11 100644 --- a/src/mimecontentformatter.h +++ b/src/mimecontentformatter.h @@ -1,41 +1,24 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - #ifndef MIMECONTENTFORMATTER_H #define MIMECONTENTFORMATTER_H #include -#include +#include -class MimeContentFormatter : public QObject +class MimeContentFormatter : public QIODevice { Q_OBJECT public: - MimeContentFormatter (int max_length = 76); + MimeContentFormatter(QIODevice *device, int lineLength = 76); - void setMaxLength(int l); - int getMaxLength() const; - - QString format(const QString &content, bool quotedPrintable = false) const; + int getLineLength() const; + void setLineLength(int l); protected: - int max_length; + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len) = 0; + QIODevice *output; + int lineLength; }; #endif // MIMECONTENTFORMATTER_H diff --git a/src/mimefile.cpp b/src/mimefile.cpp index 59fb5bf..d981a4b 100644 --- a/src/mimefile.cpp +++ b/src/mimefile.cpp @@ -44,14 +44,15 @@ MimeFile::~MimeFile() /* [3] Protected methods */ -void MimeFile::prepare() -{ + +void MimeFile::writeContent(QIODevice &device) { file->open(QIODevice::ReadOnly); this->content = file->readAll(); file->close(); - /* !!! IMPORTANT !!!! */ - MimePart::prepare(); + MimePart::writeContent(device); + + this->content.clear(); } /* [3] --- */ diff --git a/src/mimefile.h b/src/mimefile.h index b72e947..8540c77 100644 --- a/src/mimefile.h +++ b/src/mimefile.h @@ -19,10 +19,11 @@ #ifndef MIMEFILE_H #define MIMEFILE_H -#include "mimepart.h" #include +#include "mimepart.h" +#include "smtpmime_global.h" -class MimeFile : public MimePart +class SMTP_MIME_EXPORT MimeFile : public MimePart { Q_OBJECT public: @@ -50,7 +51,8 @@ protected: /* [4] Protected methods */ - virtual void prepare(); + void writeContent(QIODevice &device); + /* [4] --- */ diff --git a/src/mimehtml.cpp b/src/mimehtml.cpp index 5594d3a..e65adf6 100644 --- a/src/mimehtml.cpp +++ b/src/mimehtml.cpp @@ -48,10 +48,4 @@ const QString & MimeHtml::getHtml() const /* [3] Protected methods */ -void MimeHtml::prepare() -{ - /* !!! IMPORTANT !!! */ - MimeText::prepare(); -} - /* [3] --- */ diff --git a/src/mimehtml.h b/src/mimehtml.h index 037c05f..5d59bc4 100644 --- a/src/mimehtml.h +++ b/src/mimehtml.h @@ -19,9 +19,10 @@ #ifndef MIMEHTML_H #define MIMEHTML_H +#include "smtpmime_global.h" #include "mimetext.h" -class MimeHtml : public MimeText +class SMTP_MIME_EXPORT MimeHtml : public MimeText { Q_OBJECT public: @@ -51,8 +52,6 @@ protected: /* [4] Protected methods */ - virtual void prepare(); - /* [4] --- */ }; diff --git a/src/mimeinlinefile.cpp b/src/mimeinlinefile.cpp index 0823b0d..a275691 100644 --- a/src/mimeinlinefile.cpp +++ b/src/mimeinlinefile.cpp @@ -23,6 +23,7 @@ MimeInlineFile::MimeInlineFile(QFile *f) : MimeFile(f) { + this->headerLines += "Content-Disposition: inline\r\n"; } MimeInlineFile::~MimeInlineFile() @@ -38,14 +39,6 @@ MimeInlineFile::~MimeInlineFile() /* [3] Protected methods */ -void MimeInlineFile::prepare() -{ - this->header += "Content-Disposition: inline\r\n"; - - /* !!! IMPORTANT !!! */ - MimeFile::prepare(); -} - /* [3] --- */ diff --git a/src/mimeinlinefile.h b/src/mimeinlinefile.h index 6a7444e..8a3187e 100644 --- a/src/mimeinlinefile.h +++ b/src/mimeinlinefile.h @@ -19,9 +19,10 @@ #ifndef MIMEINLINEFILE_H #define MIMEINLINEFILE_H +#include "smtpmime_global.h" #include "mimefile.h" -class MimeInlineFile : public MimeFile +class SMTP_MIME_EXPORT MimeInlineFile : public MimeFile { public: @@ -46,8 +47,6 @@ protected: /* [4] Protected methods */ - virtual void prepare(); - /* [4] --- */ }; diff --git a/src/mimemessage.cpp b/src/mimemessage.cpp index f156363..3eae06e 100644 --- a/src/mimemessage.cpp +++ b/src/mimemessage.cpp @@ -18,7 +18,9 @@ #include "mimemessage.h" +#include #include +#include #include "quotedprintable.h" #include @@ -140,117 +142,87 @@ const QList & MimeMessage::getParts() const QString MimeMessage::toString() { - QString mimeString; - QTextStream out(&mimeString); - writeToStream(out); - return mimeString; + QBuffer out; + out.open(QIODevice::WriteOnly); + writeToDevice(out); + return QString(out.buffer()); } -void MimeMessage::writeToStream(QTextStream &out) { +QString MimeMessage::formatAddress(EmailAddress *address, MimePart::Encoding encoding) { + QString result; + if (address->getName() != "") + { + switch (encoding) + { + case MimePart::Base64: + result.append(" =?utf-8?B?" + QByteArray().append(address->getName()).toBase64() + "?="); + break; + case MimePart::QuotedPrintable: + result.append(" =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(address->getName())).replace(' ', "_").replace(':',"=3A") + "?="); + break; + default: + result.append(" " + address->getName()); + } + } + result.append(" <" + address->getAddress() + ">"); + return result; +} + +void MimeMessage::writeToDevice(QIODevice &out) { /* =========== MIME HEADER ============ */ /* ---------- Sender / From ----------- */ - out << "From:"; - if (sender->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - out << " =?utf-8?B?" + QByteArray().append(sender->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - out << " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(sender->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - out << " " + sender->getName(); - } - } - out << " <" + sender->getAddress() + ">\r\n"; + QString header; + header.append("From:" + formatAddress(sender, hEncoding) + "\r\n"); /* ---------------------------------- */ /* ------- Recipients / To ---------- */ - out << "To:"; + header.append("To:"); QList::iterator it; int i; for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) { - if (i != 0) { out << ","; } - - if ((*it)->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - out << " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - out << " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - out << " " + (*it)->getName(); - } - } - out << " <" + (*it)->getAddress() + ">"; + if (i != 0) { header.append(","); } + header.append(formatAddress(*it, hEncoding)); } - out << "\r\n"; + header.append("\r\n"); /* ---------------------------------- */ /* ------- Recipients / Cc ---------- */ if (recipientsCc.size() != 0) { - out << "Cc:"; + header.append("Cc:"); } for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i) { - if (i != 0) { out << ","; } - - if ((*it)->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - out << " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - out << " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - out << " " + (*it)->getName(); - } - } - out << " <" + (*it)->getAddress() + ">"; + if (i != 0) { header.append(","); } + header.append(formatAddress(*it, hEncoding)); } if (recipientsCc.size() != 0) { - out << "\r\n"; + header.append("\r\n"); } /* ---------------------------------- */ /* ------------ Subject ------------- */ - out << "Subject: "; + header.append("Subject: "); switch (hEncoding) { case MimePart::Base64: - out << "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?="; + header.append("=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?="); break; case MimePart::QuotedPrintable: - out << "=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?="; + header.append("=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?="); break; default: - out << subject; + header.append(subject); } /* ---------------------------------- */ - out << "\r\n"; - out << "MIME-Version: 1.0\r\n"; + header.append("\r\n"); + header.append("MIME-Version: 1.0\r\n"); - out << content->toString(); + out.write(header.toAscii()); + content->writeToDevice(out); } -void MimeMessage::writeToDevice(QIODevice &device) { - QTextStream out (&device); - writeToStream(out); - out.flush(); -} - - /* [3] --- */ diff --git a/src/mimemessage.h b/src/mimemessage.h index d136756..7f7edc6 100644 --- a/src/mimemessage.h +++ b/src/mimemessage.h @@ -19,13 +19,15 @@ #ifndef MIMEMESSAGE_H #define MIMEMESSAGE_H -#include "mimepart.h" -#include "mimemultipart.h" -#include "emailaddress.h" #include #include -class MimeMessage : public QObject +#include "smtpmime_global.h" +#include "mimepart.h" +#include "mimemultipart.h" +#include "emailaddress.h" + +class SMTP_MIME_EXPORT MimeMessage : public QObject { public: @@ -68,8 +70,7 @@ public: /* [3] Public methods */ virtual QString toString(); - virtual void writeToStream(QTextStream &stream); - virtual void writeToDevice(QIODevice &device); + void writeToDevice(QIODevice &device); /* [3] --- */ @@ -84,6 +85,8 @@ protected: MimePart::Encoding hEncoding; + static QString formatAddress(EmailAddress*, MimePart::Encoding); + /* [4] --- */ diff --git a/src/mimemultipart.cpp b/src/mimemultipart.cpp index 19d4729..b3a70d1 100644 --- a/src/mimemultipart.cpp +++ b/src/mimemultipart.cpp @@ -53,21 +53,23 @@ const QList & MimeMultiPart::getParts() const { return parts; } -void MimeMultiPart::prepare() { +void MimeMultiPart::writeContent(QIODevice &device) { QList::iterator it; content = ""; for (it = parts.begin(); it != parts.end(); it++) { - content += "--" + cBoundary + "\r\n"; - (*it)->prepare(); - content += (*it)->toString(); + device.write("--" ); + device.write(cBoundary.toAscii()); + device.write("\r\n"); + (*it)->writeToDevice(device); }; - content += "--" + cBoundary + "--\r\n"; - - MimePart::prepare(); + device.write("--"); + device.write(cBoundary.toAscii()); + device.write("--\r\n"); } + void MimeMultiPart::setMimeType(const MultiPartType type) { this->type = type; this->cType = MULTI_PART_NAMES[type]; diff --git a/src/mimemultipart.h b/src/mimemultipart.h index 8a85b6f..6355347 100644 --- a/src/mimemultipart.h +++ b/src/mimemultipart.h @@ -19,9 +19,12 @@ #ifndef MIMEMULTIPART_H #define MIMEMULTIPART_H +#include +#include +#include "smtpmime_global.h" #include "mimepart.h" -class MimeMultiPart : public MimePart +class SMTP_MIME_EXPORT MimeMultiPart : public MimePart { Q_OBJECT public: @@ -59,7 +62,7 @@ public: void addPart(MimePart *part); - virtual void prepare(); + void writeContent(QIODevice &device); /* [3] --- */ diff --git a/src/mimepart.cpp b/src/mimepart.cpp index 07ff662..1e85da9 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -16,8 +16,13 @@ See the LICENSE file for more details. */ +#include #include "mimepart.h" #include "quotedprintable.h" +#include "mimebase64formatter.h" +#include "mimeqpformatter.h" +#include "mimebase64encoder.h" +#include "mimeqpencoder.h" /* [1] Constructors and Destructors */ @@ -45,17 +50,17 @@ void MimePart::setContent(const QByteArray & content) void MimePart::setHeader(const QString & header) { - this->header = header; + this->headerLines = header; } void MimePart::addHeaderLine(const QString & line) { - this->header += line + "\r\n"; + this->headerLines += line + "\r\n"; } const QString& MimePart::getHeader() const { - return header; + return headerLines; } const QByteArray& MimePart::getContent() const @@ -113,9 +118,12 @@ MimePart::Encoding MimePart::getEncoding() const return this->cEncoding; } -MimeContentFormatter& MimePart::getContentFormatter() -{ - return this->formatter; +void MimePart::setMaxLineLength(const int length) { + maxLineLength = length; +} + +int MimePart::getMaxLineLength() const { + return maxLineLength; } /* [2] --- */ @@ -125,90 +133,91 @@ MimeContentFormatter& MimePart::getContentFormatter() QString MimePart::toString() { - if (!prepared) - prepare(); - - return mimeString; + QBuffer out; + out.open(QIODevice::WriteOnly); + writeToDevice(out); + return QString(out.buffer()); } -/* [3] --- */ - - -/* [4] Protected methods */ - -void MimePart::prepare() -{ - mimeString = QString(); +void MimePart::writeToDevice(QIODevice &device) { + QString header; /* === Header Prepare === */ /* Content-Type */ - mimeString.append("Content-Type: ").append(cType); + header.append("Content-Type: ").append(cType); if (cName != "") - mimeString.append("; name=\"").append(cName).append("\""); + header.append("; name=\"").append(cName).append("\""); if (cCharset != "") - mimeString.append("; charset=").append(cCharset); + header.append("; charset=").append(cCharset); if (cBoundary != "") - mimeString.append("; boundary=").append(cBoundary); + header.append("; boundary=").append(cBoundary); - mimeString.append("\r\n"); + header.append("\r\n"); /* ------------ */ /* Content-Transfer-Encoding */ - mimeString.append("Content-Transfer-Encoding: "); + header.append("Content-Transfer-Encoding: "); switch (cEncoding) { case _7Bit: - mimeString.append("7bit\r\n"); + header.append("7bit\r\n"); break; case _8Bit: - mimeString.append("8bit\r\n"); + header.append("8bit\r\n"); break; case Base64: - mimeString.append("base64\r\n"); + header.append("base64\r\n"); break; case QuotedPrintable: - mimeString.append("quoted-printable\r\n"); + header.append("quoted-printable\r\n"); break; } /* ------------------------ */ /* Content-Id */ if (cId != NULL) - mimeString.append("Content-ID: <").append(cId).append(">\r\n"); + header.append("Content-ID: <").append(cId).append(">\r\n"); /* ---------- */ - /* Addition header lines */ + /* Additional header lines */ - mimeString.append(header).append("\r\n"); + header.append(headerLines).append("\r\n"); /* ------------------------- */ /* === End of Header Prepare === */ - /* === Content === */ + device.write(header.toAscii()); + + writeContent(device); +} + + + +/* [3] --- */ + + +/* [4] Protected methods */ + +void MimePart::writeContent(QIODevice &device) { switch (cEncoding) { case _7Bit: - mimeString.append(QString(content).toAscii()); - break; case _8Bit: - mimeString.append(content); + device.write(content); break; case Base64: - mimeString.append(formatter.format(content.toBase64())); + MimeBase64Formatter(&device).write(MimeBase64Encoder().encode(content)); break; case QuotedPrintable: - mimeString.append(formatter.format(QuotedPrintable::encode(content), true)); + MimeQPFormatter(&device).write(MimeQpEncoder().encode(content)); break; } - mimeString.append("\r\n"); - /* === End of Content === */ - - prepared = true; + device.write("\r\n"); } /* [4] --- */ diff --git a/src/mimepart.h b/src/mimepart.h index 3eda975..49bbb19 100644 --- a/src/mimepart.h +++ b/src/mimepart.h @@ -20,9 +20,10 @@ #define MIMEPART_H #include -#include "mimecontentformatter.h" +#include +#include "smtpmime_global.h" -class MimePart : public QObject +class SMTP_MIME_EXPORT MimePart : public QObject { Q_OBJECT public: @@ -53,7 +54,7 @@ public: const QByteArray& getContent() const; void setContent(const QByteArray & content); - void setHeader(const QString & header); + void setHeader(const QString & headerLines); void addHeaderLine(const QString & line); @@ -72,7 +73,8 @@ public: void setEncoding(Encoding enc); Encoding getEncoding() const; - MimeContentFormatter& getContentFormatter(); + void setMaxLineLength(const int length); + int getMaxLineLength() const; /* [2] --- */ @@ -80,18 +82,15 @@ public: /* [3] Public methods */ virtual QString toString(); - - virtual void prepare(); + void writeToDevice(QIODevice &device); /* [3] --- */ - - protected: /* [4] Protected members */ - QString header; + QString headerLines; QByteArray content; QString cId; @@ -101,12 +100,14 @@ protected: QString cBoundary; Encoding cEncoding; + int maxLineLength; + QString mimeString; bool prepared; - MimeContentFormatter formatter; - /* [4] --- */ + + virtual void writeContent(QIODevice &device); }; #endif // MIMEPART_H diff --git a/src/mimeqpencoder.cpp b/src/mimeqpencoder.cpp new file mode 100644 index 0000000..5d7e89e --- /dev/null +++ b/src/mimeqpencoder.cpp @@ -0,0 +1,8 @@ +#include "mimeqpencoder.h" +#include "quotedprintable.h" + +MimeQpEncoder::MimeQpEncoder() {} + +QByteArray MimeQpEncoder::encode(const QByteArray &data) { + return QuotedPrintable::encode(data).toAscii(); +} diff --git a/src/mimeqpencoder.h b/src/mimeqpencoder.h new file mode 100644 index 0000000..31f9a2c --- /dev/null +++ b/src/mimeqpencoder.h @@ -0,0 +1,14 @@ +#ifndef MIMEQPENCODER_H +#define MIMEQPENCODER_H + +#include "mimecontentencoder.h" + +class MimeQpEncoder : public MimeContentEncoder +{ +public: + MimeQpEncoder(); + + QByteArray encode(const QByteArray &data); +}; + +#endif // MIMEQPENCODER_H diff --git a/src/mimeqpformatter.cpp b/src/mimeqpformatter.cpp new file mode 100644 index 0000000..5177029 --- /dev/null +++ b/src/mimeqpformatter.cpp @@ -0,0 +1,29 @@ +#include "mimeqpformatter.h" + +MimeQPFormatter::MimeQPFormatter(QIODevice *output) : + MimeContentFormatter(output) {} + +qint64 MimeQPFormatter::writeData(const char *data, qint64 maxLength) { + int chars = 0; + const char *start = data; + for (int i = 0; i < maxLength; ++i) { + chars++; + if (data[i] == '\n') { + output->write(start, chars); + start += chars; + chars = 0; + } else if ((chars > lineLength - 3) && (data[i] == '=')) { + output->write(start, chars - 1); + output->write("=\r\n="); + start += chars; + chars = 0; + } else if (chars == lineLength - 1) { + output->write(start, chars); + output->write("=\r\n"); + start += chars; + chars = 0; + } + } + output->write(start, chars); + return maxLength; +} diff --git a/src/mimeqpformatter.h b/src/mimeqpformatter.h new file mode 100644 index 0000000..eb0356c --- /dev/null +++ b/src/mimeqpformatter.h @@ -0,0 +1,15 @@ +#ifndef MIMEQPFORMATTER_H +#define MIMEQPFORMATTER_H + +#include "mimecontentformatter.h" + +class MimeQPFormatter : public MimeContentFormatter +{ +public: + MimeQPFormatter(QIODevice*); + +protected: + virtual qint64 writeData(const char *data, qint64 len); +}; + +#endif // MIMEQPFORMATTER_H diff --git a/src/mimetext.cpp b/src/mimetext.cpp index 20b1b0a..338cf1e 100644 --- a/src/mimetext.cpp +++ b/src/mimetext.cpp @@ -50,13 +50,9 @@ const QString & MimeText::getText() const /* [3] Protected Methods */ -void MimeText::prepare() -{ - this->content.clear(); - this->content.append(text); - - /* !!! IMPORTANT !!! */ - MimePart::prepare(); +void MimeText::writeContent(QIODevice &device) { + this->content = text.toAscii(); + MimePart::writeContent(device); } /* [3] --- */ diff --git a/src/mimetext.h b/src/mimetext.h index 124ad5b..eb75894 100644 --- a/src/mimetext.h +++ b/src/mimetext.h @@ -19,9 +19,10 @@ #ifndef MIMETEXT_H #define MIMETEXT_H +#include "smtpmime_global.h" #include "mimepart.h" -class MimeText : public MimePart +class SMTP_MIME_EXPORT MimeText : public MimePart { public: @@ -51,7 +52,7 @@ protected: /* [4] Protected methods */ - void prepare(); + void writeContent(QIODevice &device); /* [4] --- */ diff --git a/src/quotedprintable.cpp b/src/quotedprintable.cpp index 52b5f13..e7e130c 100644 --- a/src/quotedprintable.cpp +++ b/src/quotedprintable.cpp @@ -29,12 +29,10 @@ QString& QuotedPrintable::encode(const QByteArray &input) { byte = input[i]; - if ((byte == 0x20) || (byte >= 33) && (byte <= 126) && (byte != 61)) - { + if ((byte == 0x20) || (byte >= 33) && (byte <= 126) && (byte != 61)) { output->append(byte); } - else - { + else { output->append('='); output->append(hex[((byte >> 4) & 0x0F)]); output->append(hex[(byte & 0x0F)]); diff --git a/src/quotedprintable.h b/src/quotedprintable.h index 9c74946..5a54dc4 100644 --- a/src/quotedprintable.h +++ b/src/quotedprintable.h @@ -21,8 +21,9 @@ #include #include +#include "smtpmime_global.h" -class QuotedPrintable : public QObject +class SMTP_MIME_EXPORT QuotedPrintable : public QObject { Q_OBJECT public: diff --git a/src/smtpclient.cpp b/src/smtpclient.cpp index 7419cb5..27858f2 100644 --- a/src/smtpclient.cpp +++ b/src/smtpclient.cpp @@ -182,6 +182,7 @@ bool SmtpClient::login(const QString &user, const QString &password, AuthMethod bool SmtpClient::sendMail(MimeMessage& email) { this->email = &email; + this->rcptType = 0; changeState(MailSendingState); return true; @@ -238,10 +239,10 @@ void SmtpClient::changeState(ClientState state) { } #else // emit all in debug mode + qDebug() << "State:" << state; emit stateChanged(state); #endif - switch (state) { case ConnectingState: @@ -373,8 +374,8 @@ void SmtpClient::changeState(ClientState state) { break; case _MAIL_4_SEND_DATA: - sendMessage(email->toString()); - sendMessage("."); + email->writeToDevice(*socket); + sendMessage("\r\n."); break; case _READY_MailSent: @@ -528,6 +529,9 @@ void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state) { } void SmtpClient::socketError(QAbstractSocket::SocketError socketError) { +#ifndef QT_NO_DEBUG + qDebug() << "SocketError:" << socketError << socket->error(); +#endif emit error(SocketError); } @@ -538,7 +542,13 @@ void SmtpClient::socketReadyRead() while (socket->canReadLine()) { // Save the server's response responseLine = socket->readLine(); - tempResponse += responseLine; + tempResponse += responseLine; + } + + // Is this the last line of the response + if (responseLine[3] == ' ') { + responseText = tempResponse; + tempResponse = ""; // Extract the respose code from the server's responce (first 3 digits) responseCode = responseLine.left(3).toInt(); @@ -554,13 +564,8 @@ void SmtpClient::socketReadyRead() emit error(ClientError); return; } - } - // Is this the last line of the response - if (responseLine[3] == ' ') { - responseText = tempResponse; processResponse(); - } } diff --git a/src/smtpclient.h b/src/smtpclient.h index 2753d2f..74bbcae 100644 --- a/src/smtpclient.h +++ b/src/smtpclient.h @@ -22,11 +22,11 @@ #include #include #include - +#include "smtpmime_global.h" #include "mimemessage.h" -class SmtpClient : public QObject +class SMTP_MIME_EXPORT SmtpClient : public QObject { Q_OBJECT public: @@ -52,9 +52,9 @@ public: enum ConnectionType { - TcpConnection, - SslConnection, - TlsConnection // STARTTLS + TcpConnection = 0, + SslConnection = 1, + TlsConnection = 2 // STARTTLS }; enum ClientState { @@ -136,7 +136,6 @@ public: QTcpSocket* getSocket(); - /* [2] --- */ diff --git a/src/smtpmime_global.h b/src/smtpmime_global.h new file mode 100644 index 0000000..d3bc902 --- /dev/null +++ b/src/smtpmime_global.h @@ -0,0 +1,10 @@ +#ifndef SMTPMIME_GLOBAL_H +#define SMTPMIME_GLOBAL_H + +#ifdef SMTP_MIME_LIBRARY +#define SMTP_MIME_EXPORT Q_DECL_EXPORT +#else +#define SMTP_MIME_EXPORT Q_DECL_IMPORT +#endif + +#endif // SMTPMIME_GLOBAL_H diff --git a/test/connectiontest.cpp b/test/connectiontest.cpp new file mode 100644 index 0000000..254d6aa --- /dev/null +++ b/test/connectiontest.cpp @@ -0,0 +1,55 @@ +#include "connectiontest.h" +#include +#include +#include "../src/smtpclient.h" + +ConnectionTest::ConnectionTest(QObject *parent) : + QObject(parent) {} + + +void ConnectionTest::init() { + //qDebug() << "Init..."; +} + +void ConnectionTest::testConnect() { + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(int, connectionType); + + const SmtpClient::ConnectionType cTypes[] = { + SmtpClient::TcpConnection, + SmtpClient::SslConnection, + SmtpClient::TcpConnection + }; + + SmtpClient::ConnectionType cType = cTypes[--connectionType]; + + SmtpClient smtp(host, port, cType); + smtp.connectToHost(); + + QCOMPARE(smtp.waitForReadyConnected(5000), true); +} + +void ConnectionTest::testConnect_data() { + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("connectionType"); + + QFile file("../connect_data.txt"); + file.open(QIODevice::ReadOnly); + QTextStream in(&file); + + while (!in.atEnd()) { + QString host; + int port; + int connectionType; + in >> host >> port >> connectionType; + if (!host.isEmpty()) { + QTest::newRow(QString("%1:%2").arg(host).arg(port).toLocal8Bit().data()) << host << port << connectionType; + } + } + file.close(); +} + +void ConnectionTest::cleanup() { +} diff --git a/test/connectiontest.h b/test/connectiontest.h new file mode 100644 index 0000000..d23e7f8 --- /dev/null +++ b/test/connectiontest.h @@ -0,0 +1,21 @@ +#ifndef CONNECTIONTEST_H +#define CONNECTIONTEST_H + +#include + +class ConnectionTest : public QObject +{ + Q_OBJECT +public: + ConnectionTest(QObject *parent = 0); + +private slots: + + void init(); + void cleanup(); + + void testConnect(); + void testConnect_data(); +}; + +#endif // CONNECTIONTEST_H diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..3d3900b --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include "connectiontest.h" + +bool success = true; + +void runTest(QObject *test) { + int retVal = QTest::qExec(test); + success &= retVal == 0; +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + runTest(new ConnectionTest()); + + if (success) + qDebug() << "SUCCESS"; + else + qDebug() << "FAIL"; + + return success; +} diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..499b18e --- /dev/null +++ b/test/test.pro @@ -0,0 +1,27 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2012-09-22T16:39:45 +# +#------------------------------------------------- + +QT += testlib gui + +TARGET = test +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + + +SOURCES += main.cpp \ + connectiontest.cpp + +HEADERS += \ + connectiontest.h + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../bin/lib/release/ -lSmtpMime +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../bin/lib/debug/ -lSmtpMime +else:unix:!symbian: LIBS += -L$$PWD/../bin/lib/release/ -lSmtpMime + +INCLUDEPATH += $$PWD/../bin/lib/release +DEPENDPATH += $$PWD/../bin/lib/release