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.
This commit is contained in:
Tőkés Attila 2012-09-22 20:59:27 +03:00
parent afb66b4fff
commit 9e048495ba
40 changed files with 538 additions and 340 deletions

View File

@ -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 +=

59
src/SMTPEmail.pro Normal file
View File

@ -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 +=

View File

@ -19,9 +19,10 @@
#ifndef EMAILADDRESS_H #ifndef EMAILADDRESS_H
#define EMAILADDRESS_H #define EMAILADDRESS_H
#include "smtpmime_global.h"
#include <QObject> #include <QObject>
class EmailAddress : public QObject class SMTP_MIME_EXPORT EmailAddress : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -24,6 +24,7 @@
MimeAttachment::MimeAttachment(QFile *file) MimeAttachment::MimeAttachment(QFile *file)
: MimeFile(file) : MimeFile(file)
{ {
this->headerLines += "Content-disposition: attachment\r\n";
} }
MimeAttachment::~MimeAttachment() MimeAttachment::~MimeAttachment()
@ -35,12 +36,4 @@ MimeAttachment::~MimeAttachment()
/* [2] Protected methods */ /* [2] Protected methods */
void MimeAttachment::prepare()
{
this->header += "Content-disposition: attachment\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [2] --- */ /* [2] --- */

View File

@ -19,11 +19,13 @@
#ifndef MIMEATTACHMENT_H #ifndef MIMEATTACHMENT_H
#define MIMEATTACHMENT_H #define MIMEATTACHMENT_H
#include <QFile> #include <QFile>
#include "smtpmime_global.h"
#include "mimepart.h" #include "mimepart.h"
#include "mimefile.h" #include "mimefile.h"
class MimeAttachment : public MimeFile class SMTP_MIME_EXPORT MimeAttachment : public MimeFile
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -38,9 +40,6 @@ public:
protected: protected:
/* [2] Protected methods */ /* [2] Protected methods */
virtual void prepare();
/* [2] --- */ /* [2] --- */
}; };

View File

@ -0,0 +1,7 @@
#include "mimebase64encoder.h"
MimeBase64Encoder::MimeBase64Encoder() {}
QByteArray MimeBase64Encoder::encode(const QByteArray &data) {
return data.toBase64();
}

14
src/mimebase64encoder.h Normal file
View File

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

View File

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

15
src/mimebase64formatter.h Normal file
View File

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

View File

@ -0,0 +1,3 @@
#include "mimecontentencoder.h"
MimeContentEncoder::MimeContentEncoder() {}

16
src/mimecontentencoder.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef MIMEENCODER_H
#define MIMEENCODER_H
#include <QObject>
#include <QByteArray>
class MimeContentEncoder : public QObject
{
public:
virtual QByteArray encode(const QByteArray &data) =0;
protected:
MimeContentEncoder();
};
#endif // MIMEENCODER_H

View File

@ -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" #include "mimecontentformatter.h"
MimeContentFormatter::MimeContentFormatter(int max_length) : MimeContentFormatter::MimeContentFormatter(QIODevice *out, int length) :
max_length(max_length) output(out),
{} lineLength(length)
{
QString MimeContentFormatter::format(const QString &content, bool quotedPrintable) const { QIODevice::open(WriteOnly);
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;
} }
void MimeContentFormatter::setMaxLength(int l) { int MimeContentFormatter::getLineLength() const {
max_length = l; return lineLength;
} }
int MimeContentFormatter::getMaxLength() const { void MimeContentFormatter::setLineLength(int l) {
return max_length; lineLength = l;
}
qint64 MimeContentFormatter::readData(char*, qint64) {
return -1;
} }

View File

@ -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 #ifndef MIMECONTENTFORMATTER_H
#define MIMECONTENTFORMATTER_H #define MIMECONTENTFORMATTER_H
#include <QObject> #include <QObject>
#include <QByteArray> #include <QIODevice>
class MimeContentFormatter : public QObject class MimeContentFormatter : public QIODevice
{ {
Q_OBJECT Q_OBJECT
public: public:
MimeContentFormatter (int max_length = 76); MimeContentFormatter(QIODevice *device, int lineLength = 76);
void setMaxLength(int l); int getLineLength() const;
int getMaxLength() const; void setLineLength(int l);
QString format(const QString &content, bool quotedPrintable = false) const;
protected: 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 #endif // MIMECONTENTFORMATTER_H

View File

@ -44,14 +44,15 @@ MimeFile::~MimeFile()
/* [3] Protected methods */ /* [3] Protected methods */
void MimeFile::prepare()
{ void MimeFile::writeContent(QIODevice &device) {
file->open(QIODevice::ReadOnly); file->open(QIODevice::ReadOnly);
this->content = file->readAll(); this->content = file->readAll();
file->close(); file->close();
/* !!! IMPORTANT !!!! */ MimePart::writeContent(device);
MimePart::prepare();
this->content.clear();
} }
/* [3] --- */ /* [3] --- */

View File

@ -19,10 +19,11 @@
#ifndef MIMEFILE_H #ifndef MIMEFILE_H
#define MIMEFILE_H #define MIMEFILE_H
#include "mimepart.h"
#include <QFile> #include <QFile>
#include "mimepart.h"
#include "smtpmime_global.h"
class MimeFile : public MimePart class SMTP_MIME_EXPORT MimeFile : public MimePart
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -50,7 +51,8 @@ protected:
/* [4] Protected methods */ /* [4] Protected methods */
virtual void prepare(); void writeContent(QIODevice &device);
/* [4] --- */ /* [4] --- */

View File

@ -48,10 +48,4 @@ const QString & MimeHtml::getHtml() const
/* [3] Protected methods */ /* [3] Protected methods */
void MimeHtml::prepare()
{
/* !!! IMPORTANT !!! */
MimeText::prepare();
}
/* [3] --- */ /* [3] --- */

View File

@ -19,9 +19,10 @@
#ifndef MIMEHTML_H #ifndef MIMEHTML_H
#define MIMEHTML_H #define MIMEHTML_H
#include "smtpmime_global.h"
#include "mimetext.h" #include "mimetext.h"
class MimeHtml : public MimeText class SMTP_MIME_EXPORT MimeHtml : public MimeText
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -51,8 +52,6 @@ protected:
/* [4] Protected methods */ /* [4] Protected methods */
virtual void prepare();
/* [4] --- */ /* [4] --- */
}; };

View File

@ -23,6 +23,7 @@
MimeInlineFile::MimeInlineFile(QFile *f) MimeInlineFile::MimeInlineFile(QFile *f)
: MimeFile(f) : MimeFile(f)
{ {
this->headerLines += "Content-Disposition: inline\r\n";
} }
MimeInlineFile::~MimeInlineFile() MimeInlineFile::~MimeInlineFile()
@ -38,14 +39,6 @@ MimeInlineFile::~MimeInlineFile()
/* [3] Protected methods */ /* [3] Protected methods */
void MimeInlineFile::prepare()
{
this->header += "Content-Disposition: inline\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [3] --- */ /* [3] --- */

View File

@ -19,9 +19,10 @@
#ifndef MIMEINLINEFILE_H #ifndef MIMEINLINEFILE_H
#define MIMEINLINEFILE_H #define MIMEINLINEFILE_H
#include "smtpmime_global.h"
#include "mimefile.h" #include "mimefile.h"
class MimeInlineFile : public MimeFile class SMTP_MIME_EXPORT MimeInlineFile : public MimeFile
{ {
public: public:
@ -46,8 +47,6 @@ protected:
/* [4] Protected methods */ /* [4] Protected methods */
virtual void prepare();
/* [4] --- */ /* [4] --- */
}; };

View File

@ -18,7 +18,9 @@
#include "mimemessage.h" #include "mimemessage.h"
#include <QDebug>
#include <QDateTime> #include <QDateTime>
#include <QBuffer>
#include "quotedprintable.h" #include "quotedprintable.h"
#include <typeinfo> #include <typeinfo>
@ -140,117 +142,87 @@ const QList<MimePart*> & MimeMessage::getParts() const
QString MimeMessage::toString() QString MimeMessage::toString()
{ {
QString mimeString; QBuffer out;
QTextStream out(&mimeString); out.open(QIODevice::WriteOnly);
writeToStream(out); writeToDevice(out);
return mimeString; 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 ============ */ /* =========== MIME HEADER ============ */
/* ---------- Sender / From ----------- */ /* ---------- Sender / From ----------- */
out << "From:"; QString header;
if (sender->getName() != "") header.append("From:" + formatAddress(sender, hEncoding) + "\r\n");
{
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";
/* ---------------------------------- */ /* ---------------------------------- */
/* ------- Recipients / To ---------- */ /* ------- Recipients / To ---------- */
out << "To:"; header.append("To:");
QList<EmailAddress*>::iterator it; int i; QList<EmailAddress*>::iterator it; int i;
for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i)
{ {
if (i != 0) { out << ","; } if (i != 0) { header.append(","); }
header.append(formatAddress(*it, hEncoding));
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();
} }
} header.append("\r\n");
out << " <" + (*it)->getAddress() + ">";
}
out << "\r\n";
/* ---------------------------------- */ /* ---------------------------------- */
/* ------- Recipients / Cc ---------- */ /* ------- Recipients / Cc ---------- */
if (recipientsCc.size() != 0) { if (recipientsCc.size() != 0) {
out << "Cc:"; header.append("Cc:");
} }
for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i) for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i)
{ {
if (i != 0) { out << ","; } if (i != 0) { header.append(","); }
header.append(formatAddress(*it, hEncoding));
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 (recipientsCc.size() != 0) { if (recipientsCc.size() != 0) {
out << "\r\n"; header.append("\r\n");
} }
/* ---------------------------------- */ /* ---------------------------------- */
/* ------------ Subject ------------- */ /* ------------ Subject ------------- */
out << "Subject: "; header.append("Subject: ");
switch (hEncoding) switch (hEncoding)
{ {
case MimePart::Base64: case MimePart::Base64:
out << "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?="; header.append("=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?=");
break; break;
case MimePart::QuotedPrintable: 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; break;
default: default:
out << subject; header.append(subject);
} }
/* ---------------------------------- */ /* ---------------------------------- */
out << "\r\n"; header.append("\r\n");
out << "MIME-Version: 1.0\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] --- */ /* [3] --- */

View File

@ -19,13 +19,15 @@
#ifndef MIMEMESSAGE_H #ifndef MIMEMESSAGE_H
#define MIMEMESSAGE_H #define MIMEMESSAGE_H
#include "mimepart.h"
#include "mimemultipart.h"
#include "emailaddress.h"
#include <QList> #include <QList>
#include <QTextStream> #include <QTextStream>
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: public:
@ -68,8 +70,7 @@ public:
/* [3] Public methods */ /* [3] Public methods */
virtual QString toString(); virtual QString toString();
virtual void writeToStream(QTextStream &stream); void writeToDevice(QIODevice &device);
virtual void writeToDevice(QIODevice &device);
/* [3] --- */ /* [3] --- */
@ -84,6 +85,8 @@ protected:
MimePart::Encoding hEncoding; MimePart::Encoding hEncoding;
static QString formatAddress(EmailAddress*, MimePart::Encoding);
/* [4] --- */ /* [4] --- */

View File

@ -53,21 +53,23 @@ const QList<MimePart*> & MimeMultiPart::getParts() const {
return parts; return parts;
} }
void MimeMultiPart::prepare() { void MimeMultiPart::writeContent(QIODevice &device) {
QList<MimePart*>::iterator it; QList<MimePart*>::iterator it;
content = ""; content = "";
for (it = parts.begin(); it != parts.end(); it++) { for (it = parts.begin(); it != parts.end(); it++) {
content += "--" + cBoundary + "\r\n"; device.write("--" );
(*it)->prepare(); device.write(cBoundary.toAscii());
content += (*it)->toString(); device.write("\r\n");
(*it)->writeToDevice(device);
}; };
content += "--" + cBoundary + "--\r\n"; device.write("--");
device.write(cBoundary.toAscii());
MimePart::prepare(); device.write("--\r\n");
} }
void MimeMultiPart::setMimeType(const MultiPartType type) { void MimeMultiPart::setMimeType(const MultiPartType type) {
this->type = type; this->type = type;
this->cType = MULTI_PART_NAMES[type]; this->cType = MULTI_PART_NAMES[type];

View File

@ -19,9 +19,12 @@
#ifndef MIMEMULTIPART_H #ifndef MIMEMULTIPART_H
#define MIMEMULTIPART_H #define MIMEMULTIPART_H
#include <QList>
#include <QTextStream>
#include "smtpmime_global.h"
#include "mimepart.h" #include "mimepart.h"
class MimeMultiPart : public MimePart class SMTP_MIME_EXPORT MimeMultiPart : public MimePart
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -59,7 +62,7 @@ public:
void addPart(MimePart *part); void addPart(MimePart *part);
virtual void prepare(); void writeContent(QIODevice &device);
/* [3] --- */ /* [3] --- */

View File

@ -16,8 +16,13 @@
See the LICENSE file for more details. See the LICENSE file for more details.
*/ */
#include <QBuffer>
#include "mimepart.h" #include "mimepart.h"
#include "quotedprintable.h" #include "quotedprintable.h"
#include "mimebase64formatter.h"
#include "mimeqpformatter.h"
#include "mimebase64encoder.h"
#include "mimeqpencoder.h"
/* [1] Constructors and Destructors */ /* [1] Constructors and Destructors */
@ -45,17 +50,17 @@ void MimePart::setContent(const QByteArray & content)
void MimePart::setHeader(const QString & header) void MimePart::setHeader(const QString & header)
{ {
this->header = header; this->headerLines = header;
} }
void MimePart::addHeaderLine(const QString & line) void MimePart::addHeaderLine(const QString & line)
{ {
this->header += line + "\r\n"; this->headerLines += line + "\r\n";
} }
const QString& MimePart::getHeader() const const QString& MimePart::getHeader() const
{ {
return header; return headerLines;
} }
const QByteArray& MimePart::getContent() const const QByteArray& MimePart::getContent() const
@ -113,9 +118,12 @@ MimePart::Encoding MimePart::getEncoding() const
return this->cEncoding; return this->cEncoding;
} }
MimeContentFormatter& MimePart::getContentFormatter() void MimePart::setMaxLineLength(const int length) {
{ maxLineLength = length;
return this->formatter; }
int MimePart::getMaxLineLength() const {
return maxLineLength;
} }
/* [2] --- */ /* [2] --- */
@ -125,90 +133,91 @@ MimeContentFormatter& MimePart::getContentFormatter()
QString MimePart::toString() QString MimePart::toString()
{ {
if (!prepared) QBuffer out;
prepare(); out.open(QIODevice::WriteOnly);
writeToDevice(out);
return mimeString; return QString(out.buffer());
} }
/* [3] --- */ void MimePart::writeToDevice(QIODevice &device) {
QString header;
/* [4] Protected methods */
void MimePart::prepare()
{
mimeString = QString();
/* === Header Prepare === */ /* === Header Prepare === */
/* Content-Type */ /* Content-Type */
mimeString.append("Content-Type: ").append(cType); header.append("Content-Type: ").append(cType);
if (cName != "") if (cName != "")
mimeString.append("; name=\"").append(cName).append("\""); header.append("; name=\"").append(cName).append("\"");
if (cCharset != "") if (cCharset != "")
mimeString.append("; charset=").append(cCharset); header.append("; charset=").append(cCharset);
if (cBoundary != "") if (cBoundary != "")
mimeString.append("; boundary=").append(cBoundary); header.append("; boundary=").append(cBoundary);
mimeString.append("\r\n"); header.append("\r\n");
/* ------------ */ /* ------------ */
/* Content-Transfer-Encoding */ /* Content-Transfer-Encoding */
mimeString.append("Content-Transfer-Encoding: "); header.append("Content-Transfer-Encoding: ");
switch (cEncoding) switch (cEncoding)
{ {
case _7Bit: case _7Bit:
mimeString.append("7bit\r\n"); header.append("7bit\r\n");
break; break;
case _8Bit: case _8Bit:
mimeString.append("8bit\r\n"); header.append("8bit\r\n");
break; break;
case Base64: case Base64:
mimeString.append("base64\r\n"); header.append("base64\r\n");
break; break;
case QuotedPrintable: case QuotedPrintable:
mimeString.append("quoted-printable\r\n"); header.append("quoted-printable\r\n");
break; break;
} }
/* ------------------------ */ /* ------------------------ */
/* Content-Id */ /* Content-Id */
if (cId != NULL) 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 === */ /* === End of Header Prepare === */
/* === Content === */ device.write(header.toAscii());
writeContent(device);
}
/* [3] --- */
/* [4] Protected methods */
void MimePart::writeContent(QIODevice &device) {
switch (cEncoding) switch (cEncoding)
{ {
case _7Bit: case _7Bit:
mimeString.append(QString(content).toAscii());
break;
case _8Bit: case _8Bit:
mimeString.append(content); device.write(content);
break; break;
case Base64: case Base64:
mimeString.append(formatter.format(content.toBase64())); MimeBase64Formatter(&device).write(MimeBase64Encoder().encode(content));
break; break;
case QuotedPrintable: case QuotedPrintable:
mimeString.append(formatter.format(QuotedPrintable::encode(content), true)); MimeQPFormatter(&device).write(MimeQpEncoder().encode(content));
break; break;
} }
mimeString.append("\r\n"); device.write("\r\n");
/* === End of Content === */
prepared = true;
} }
/* [4] --- */ /* [4] --- */

View File

@ -20,9 +20,10 @@
#define MIMEPART_H #define MIMEPART_H
#include <QObject> #include <QObject>
#include "mimecontentformatter.h" #include <QTextStream>
#include "smtpmime_global.h"
class MimePart : public QObject class SMTP_MIME_EXPORT MimePart : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -53,7 +54,7 @@ public:
const QByteArray& getContent() const; const QByteArray& getContent() const;
void setContent(const QByteArray & content); void setContent(const QByteArray & content);
void setHeader(const QString & header); void setHeader(const QString & headerLines);
void addHeaderLine(const QString & line); void addHeaderLine(const QString & line);
@ -72,7 +73,8 @@ public:
void setEncoding(Encoding enc); void setEncoding(Encoding enc);
Encoding getEncoding() const; Encoding getEncoding() const;
MimeContentFormatter& getContentFormatter(); void setMaxLineLength(const int length);
int getMaxLineLength() const;
/* [2] --- */ /* [2] --- */
@ -80,18 +82,15 @@ public:
/* [3] Public methods */ /* [3] Public methods */
virtual QString toString(); virtual QString toString();
void writeToDevice(QIODevice &device);
virtual void prepare();
/* [3] --- */ /* [3] --- */
protected: protected:
/* [4] Protected members */ /* [4] Protected members */
QString header; QString headerLines;
QByteArray content; QByteArray content;
QString cId; QString cId;
@ -101,12 +100,14 @@ protected:
QString cBoundary; QString cBoundary;
Encoding cEncoding; Encoding cEncoding;
int maxLineLength;
QString mimeString; QString mimeString;
bool prepared; bool prepared;
MimeContentFormatter formatter;
/* [4] --- */ /* [4] --- */
virtual void writeContent(QIODevice &device);
}; };
#endif // MIMEPART_H #endif // MIMEPART_H

8
src/mimeqpencoder.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "mimeqpencoder.h"
#include "quotedprintable.h"
MimeQpEncoder::MimeQpEncoder() {}
QByteArray MimeQpEncoder::encode(const QByteArray &data) {
return QuotedPrintable::encode(data).toAscii();
}

14
src/mimeqpencoder.h Normal file
View File

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

29
src/mimeqpformatter.cpp Normal file
View File

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

15
src/mimeqpformatter.h Normal file
View File

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

View File

@ -50,13 +50,9 @@ const QString & MimeText::getText() const
/* [3] Protected Methods */ /* [3] Protected Methods */
void MimeText::prepare() void MimeText::writeContent(QIODevice &device) {
{ this->content = text.toAscii();
this->content.clear(); MimePart::writeContent(device);
this->content.append(text);
/* !!! IMPORTANT !!! */
MimePart::prepare();
} }
/* [3] --- */ /* [3] --- */

View File

@ -19,9 +19,10 @@
#ifndef MIMETEXT_H #ifndef MIMETEXT_H
#define MIMETEXT_H #define MIMETEXT_H
#include "smtpmime_global.h"
#include "mimepart.h" #include "mimepart.h"
class MimeText : public MimePart class SMTP_MIME_EXPORT MimeText : public MimePart
{ {
public: public:
@ -51,7 +52,7 @@ protected:
/* [4] Protected methods */ /* [4] Protected methods */
void prepare(); void writeContent(QIODevice &device);
/* [4] --- */ /* [4] --- */

View File

@ -29,12 +29,10 @@ QString& QuotedPrintable::encode(const QByteArray &input)
{ {
byte = input[i]; byte = input[i];
if ((byte == 0x20) || (byte >= 33) && (byte <= 126) && (byte != 61)) if ((byte == 0x20) || (byte >= 33) && (byte <= 126) && (byte != 61)) {
{
output->append(byte); output->append(byte);
} }
else else {
{
output->append('='); output->append('=');
output->append(hex[((byte >> 4) & 0x0F)]); output->append(hex[((byte >> 4) & 0x0F)]);
output->append(hex[(byte & 0x0F)]); output->append(hex[(byte & 0x0F)]);

View File

@ -21,8 +21,9 @@
#include <QObject> #include <QObject>
#include <QByteArray> #include <QByteArray>
#include "smtpmime_global.h"
class QuotedPrintable : public QObject class SMTP_MIME_EXPORT QuotedPrintable : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -182,6 +182,7 @@ bool SmtpClient::login(const QString &user, const QString &password, AuthMethod
bool SmtpClient::sendMail(MimeMessage& email) bool SmtpClient::sendMail(MimeMessage& email)
{ {
this->email = &email; this->email = &email;
this->rcptType = 0;
changeState(MailSendingState); changeState(MailSendingState);
return true; return true;
@ -238,10 +239,10 @@ void SmtpClient::changeState(ClientState state) {
} }
#else #else
// emit all in debug mode // emit all in debug mode
qDebug() << "State:" << state;
emit stateChanged(state); emit stateChanged(state);
#endif #endif
switch (state) switch (state)
{ {
case ConnectingState: case ConnectingState:
@ -373,8 +374,8 @@ void SmtpClient::changeState(ClientState state) {
break; break;
case _MAIL_4_SEND_DATA: case _MAIL_4_SEND_DATA:
sendMessage(email->toString()); email->writeToDevice(*socket);
sendMessage("."); sendMessage("\r\n.");
break; break;
case _READY_MailSent: case _READY_MailSent:
@ -528,6 +529,9 @@ void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state) {
} }
void SmtpClient::socketError(QAbstractSocket::SocketError socketError) { void SmtpClient::socketError(QAbstractSocket::SocketError socketError) {
#ifndef QT_NO_DEBUG
qDebug() << "SocketError:" << socketError << socket->error();
#endif
emit error(SocketError); emit error(SocketError);
} }
@ -539,6 +543,12 @@ void SmtpClient::socketReadyRead()
// Save the server's response // Save the server's response
responseLine = socket->readLine(); 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) // Extract the respose code from the server's responce (first 3 digits)
responseCode = responseLine.left(3).toInt(); responseCode = responseLine.left(3).toInt();
@ -554,13 +564,8 @@ void SmtpClient::socketReadyRead()
emit error(ClientError); emit error(ClientError);
return; return;
} }
}
// Is this the last line of the response
if (responseLine[3] == ' ') {
responseText = tempResponse;
processResponse(); processResponse();
} }
} }

View File

@ -22,11 +22,11 @@
#include <QObject> #include <QObject>
#include <QtNetwork/QSslSocket> #include <QtNetwork/QSslSocket>
#include <QEventLoop> #include <QEventLoop>
#include "smtpmime_global.h"
#include "mimemessage.h" #include "mimemessage.h"
class SmtpClient : public QObject class SMTP_MIME_EXPORT SmtpClient : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -52,9 +52,9 @@ public:
enum ConnectionType enum ConnectionType
{ {
TcpConnection, TcpConnection = 0,
SslConnection, SslConnection = 1,
TlsConnection // STARTTLS TlsConnection = 2 // STARTTLS
}; };
enum ClientState { enum ClientState {
@ -136,7 +136,6 @@ public:
QTcpSocket* getSocket(); QTcpSocket* getSocket();
/* [2] --- */ /* [2] --- */

10
src/smtpmime_global.h Normal file
View File

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

55
test/connectiontest.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "connectiontest.h"
#include <QDebug>
#include <QtTest/QtTest>
#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<QString>("host");
QTest::addColumn<int>("port");
QTest::addColumn<int>("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() {
}

21
test/connectiontest.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CONNECTIONTEST_H
#define CONNECTIONTEST_H
#include <QObject>
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

26
test/main.cpp Normal file
View File

@ -0,0 +1,26 @@
#include <QtGui/QApplication>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <QDebug>
#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;
}

27
test/test.pro Normal file
View File

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