Compare commits

...

49 Commits
v1.1 ... dev

Author SHA1 Message Date
Attila Tőkés
6564518cad Update README.md 2017-06-01 20:04:31 +03:00
Attila Tőkés
0c10ed6daa Update .travis.yml 2017-06-01 20:01:46 +03:00
Attila Tőkés
145720277c Issue #35 - fix compiler warnings 2017-06-01 18:29:13 +03:00
Attila Tőkés
6ea91ad3cb Issue #41 - remove extra space from MAIL FROM and RCPT TO commands 2017-06-01 18:19:37 +03:00
Attila Tőkés
ee14ff7e00 Demos - fix library name in Windows builds 2017-06-01 17:43:47 +03:00
Attila Tőkés
b90aa7cfc3 Integrate changes from olehs's repository:
https://github.com/olehs/SmtpClient-for-Qt
2014-11-10 22:30:13 +02:00
Tőkés Attila
ace683a658 Add travis-ci.org build status 2014-11-07 22:50:49 +02:00
Attila Tőkés
ba39a3ec76 Fix travis-ci.org build 2014-11-07 22:44:28 +02:00
Attila Tőkés
aeb946a1fa Build on travis-ci.org 2014-11-07 22:38:42 +02:00
Attila Tőkés
a42aa8e7ed Set up demo projects. 2014-11-07 21:19:17 +02:00
Attila Tőkés
9897598885 Code and API cleanup. 2014-11-06 20:38:30 +02:00
Tőkés Attila
e049dfd51b Merge pull request #29 from naihil/dev
Small changes.
2014-11-01 22:55:57 +02:00
Anatoliy Golubev
e0e091e5e0 fixes #12 (Inconsistent dll linkage with MSVS 2010) 2014-08-15 17:40:34 +04:00
Anatoliy Golubev
84e662e352 Ignore Qt Creator`s .pro.user files 2014-08-15 17:30:38 +04:00
Attila Tőkés
95ed484e7e Added MimeByteArrayAttachment.
Removed some unused fields in SmtpClient.
2014-02-21 17:12:32 +02:00
Tőkés Attila
058c6bd7ce Update README.md 2014-02-16 19:46:44 +02:00
Tőkés Attila
f3216a5bf3 Updated README for version 2.0 2014-02-16 19:46:03 +02:00
Tőkés Attila
38dbcebcf5 Merge pull request #18 from Kaffeine/dev
Do not link against qt gui.
2014-02-16 18:48:48 +02:00
Attila Tőkés
f12e1b6512 Fixed waitFor* methods in SmtpClient. Updated test data. 2014-02-16 18:42:33 +02:00
Alexandr Akulich
77a590d6f6 Do not link against qt gui. 2013-10-02 16:01:35 +06:00
Attila Tőkés
45e6340ff3 Added more debug level logging. 2013-08-06 22:36:50 +03:00
Tőkés Attila
07140fee85 Merge pull request #11 from sjinks/dev
Qt 5 compatibility
2013-08-04 10:19:58 -07:00
Vladimir Kolesnikov
4ff42edc4f Qt 5 compatibility 2013-02-14 01:27:00 +02:00
Tőkés Attila
208b072ad3 Merge pull request #10 from sjinks/dev
Fixed some bugs and memory leaks
2013-02-11 12:29:50 -08:00
Vladimir Kolesnikov
40d69fc592 Make sure to stay within hexVal bounds 2013-02-10 22:18:41 +02:00
Vladimir Kolesnikov
22f768ca99 Fix -Wunused-parameter warning 2013-02-10 17:10:30 +02:00
Vladimir Kolesnikov
e792db9fa8 Do not derive MimePart from QObject 2013-02-10 17:06:52 +02:00
Vladimir Kolesnikov
1dd96f9bd0 Do not derive EmailAddress from QObject 2013-02-10 16:50:09 +02:00
Vladimir Kolesnikov
968cad3328 Fix crash in decode() on malformed input 2013-02-10 16:43:24 +02:00
Vladimir Kolesnikov
7b42551a20 Fix -Wsequence-point warning 2013-02-10 16:25:44 +02:00
Vladimir Kolesnikov
8a0282b6c1 Fix -Wlogical-op-parentheses warning 2013-02-10 16:24:06 +02:00
Vladimir Kolesnikov
6339318cde Tests to run can be specified in command line 2013-02-10 16:10:38 +02:00
Vladimir Kolesnikov
8156bc0a95 Fix memory leaks: do not allocate results on heap 2013-02-10 15:51:58 +02:00
Vladimir Kolesnikov
8967ae4aad Convert QuotedPrintable class to namespace 2013-02-10 15:47:48 +02:00
Vladimir Kolesnikov
4e2d1446dd Fix memory leak 2013-02-10 15:41:39 +02:00
Vladimir Kolesnikov
49a43f2370 Make test app console 2013-02-10 15:40:10 +02:00
Vladimir Kolesnikov
3b4ec583fa Removed mimepart.cpp.autosave from the project 2013-02-10 15:30:08 +02:00
Vladimir Kolesnikov
38f06fcb48 Added .gitignore 2013-02-10 15:29:19 +02:00
Tőkés Attila
949e1ee82a Maked setHost() and setPassword() deprecated. Some bug fixes. 2012-10-28 21:48:04 +02:00
Tőkés Attila
34883564f5 Added some state check to the connectToHost, login, sendMail,
waitForReadyConnected, waitForAuthenticated, waitForMailSent functions.
2012-10-28 21:31:34 +02:00
Tőkés Attila
49080a5a0c Added test test/connect_data.txt. 2012-10-15 21:31:16 +03:00
Tőkés Attila
baa3cb2293 Added some documentation. 2012-10-15 21:24:39 +03:00
Tőkés Attila
9e048495ba 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.
2012-09-22 20:59:27 +03:00
Attila Tőkés
afb66b4fff Added support for synchronous mode: waitForReadyConnected(),
waitForAuthenticated(), waitForMailSent().
The waitForResponse() funtion was deleted (no more needed).
2012-09-08 21:32:39 +03:00
Attila Tőkés
297f2f8178 First working version of the state machine implementation. 2012-09-08 00:07:45 +03:00
Attila Tőkés
618d583551 state machine implementation, connectToHost and login working 2012-09-07 00:28:07 +03:00
Attila Tőkés
b0e7a31b64 Started the state machine implementation. 2012-09-05 00:11:24 +03:00
Attila Tőkés
04b7054aa9 Added states for state machine implementation of SmtpClient. 2012-09-02 20:48:18 +03:00
Tőkés Attila
24ca613e47 First step of stream based implementation. 2012-09-02 20:34:07 +03:00
59 changed files with 1611 additions and 884 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.o
*.so
*.so.*
Makefile
moc_*.cpp
*.pro.user*

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
sudo: required
dist: trusty
before_install:
- sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa
- sudo apt-get update -qq
- sudo apt-get install qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev
- sudo apt-get install qt5-default qttools5-dev-tools
script:
- qmake -project
- qmake src/SMTPEmail.pro
- make

View File

@ -1,8 +1,17 @@
SMTP Client for Qt (C++) - Version 1.1
SMTP Client for Qt (C++) - Version 2.0 [![Build Status](https://travis-ci.org/bluetiger9/SmtpClient-for-Qt.svg?branch=dev)](https://travis-ci.org/bluetiger9/SmtpClient-for-Qt)
=============================================
The SmtpClient for Qt is small library writen for Qt 4 (C++ version) that allows application to send complex emails (plain text, html, attachments, inline files, etc.) using the Simple Mail Transfer Protocol (SMTP).
##New in version 2.0:
- Asynchronous & Synchronous working mode
- Qt5 compatibility
- Building as a shared library
- code of SmtpClient refactored and partially rewrited
##New in version 1.1:
- TLS (STARTTLS) connection is now supported
@ -14,7 +23,7 @@ The SmtpClient for Qt is small library writen for Qt 4 (C++ version) that allows
- output compilant with RFC2045
## SMPT Client for Qt supports
## SMTP Client for Qt supports
- TCP and SSL connections to SMTP servers
@ -77,8 +86,14 @@ int main(int argc, char *argv[])
// Now we can send the mail
smtp.connectToHost();
smtp.waitForReadyConnected();
smtp.login();
smtp.waitForAuthenticated();
smtp.sendMail(message);
smtp.waitForMailSent();
smtp.quit();
}
@ -91,4 +106,4 @@ For more examples see the [Wiki/Examples](https://github.com/bluetiger9/SmtpClie
This project (all files including the demos/examples) is licensed under the GNU LGPL, version 2.1.
**Copyright (c) 2011 - Tőkés Attila**
**Copyright (c) 2014 - Tőkés Attila**

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

View File

@ -1,52 +0,0 @@
#include <QtGui/QApplication>
#include "../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// This is a first demo application of the SmtpClient for Qt project
// First we need to create an SmtpClient object
// We will use the Gmail's smtp server (smtp.gmail.com, port 465, ssl)
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
// We need to set the username (your email address) and password
// for smtp authentification.
smtp.setUser("your_email_address@gmail.com");
smtp.setPassword("your_password");
// Now we create a MimeMessage object. This is the email.
MimeMessage message;
message.setSender(new EmailAddress("your_email_address@gmail.com", "Your Name"));
message.addRecipient(new EmailAddress("recipient@host.com", "Recipient's Name"));
message.setSubject("SmtpClient for Qt - Demo");
// Now add some text to the email.
// First we create a MimeText object.
MimeText text;
text.setText("Hi,\nThis is a simple email message.\n");
// Now add it to the mail
message.addPart(&text);
// Now we can send the mail
smtp.connectToHost();
smtp.login();
smtp.sendMail(message);
smtp.quit();
}

57
demos/demo1/demo1.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <QtCore>
#include "../../src/SmtpMime"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// This is a first demo application of the SmtpClient for Qt project
// Now we create a MimeMessage object. This is the email.
MimeMessage message;
EmailAddress sender("your_email_address@host.com", "Your Name");
message.setSender(sender);
EmailAddress to("recipient@host.com", "Recipient's Name");
message.addRecipient(to);
message.setSubject("SmtpClient for Qt - Demo");
// Now add some text to the email.
// First we create a MimeText object.
MimeText text;
text.setText("Hi,\nThis is a simple email message.\n");
// Now add it to the mail
message.addPart(&text);
// Now we can send the mail
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.connectToHost();
if (!smtp.waitForReadyConnected()) {
qDebug() << "Failed to connect to host!" << endl;
return -1;
}
smtp.login("your_email_address@host.com", "your_password");
if (!smtp.waitForAuthenticated()) {
qDebug() << "Failed to login!" << endl;
return -2;
}
smtp.sendMail(message);
if (!smtp.waitForMailSent()) {
qDebug() << "Failed to send mail!" << endl;
return -3;
}
smtp.quit();
}

29
demos/demo1/demo1.pro Normal file
View File

@ -0,0 +1,29 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-10-30T22:19:03
#
#-------------------------------------------------
QT += core
QT -= gui
TARGET = demo1
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += \
demo1.cpp
# Location of SMTP Library
SMTP_LIBRARY_LOCATION = $$PWD/../../../build/SMTPEmail-Desktop-Debug
win32:CONFIG(release, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/release/ -lSMTPMime
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/debug/ -lSMTPMime
else:unix: LIBS += -L$$SMTP_LIBRARY_LOCATION -lSmtpMime
INCLUDEPATH += $$SMTP_LIBRARY_LOCATION
DEPENDPATH += $$SMTP_LIBRARY_LOCATION

View File

@ -14,12 +14,11 @@
See the LICENSE file for more details.
*/
#include <QtGui/QApplication>
#include <QtWidgets>
#include "sendemail.h"
#include "../../src/SmtpMime"
#include <iostream>
using namespace std;

32
demos/demo2/demo2.pro Normal file
View File

@ -0,0 +1,32 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-10-30T22:48:32
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = demo2
TEMPLATE = app
SOURCES += \
demo2.cpp \
sendemail.cpp
# Location of SMTP Library
SMTP_LIBRARY_LOCATION = $$PWD/../../../build/SMTPEmail-Desktop-Debug
win32:CONFIG(release, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/release/ -lSMTPMime
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/debug/ -lSMTPMime
else:unix: LIBS += -L$$SMTP_LIBRARY_LOCATION -lSmtpMime
INCLUDEPATH += $$SMTP_LIBRARY_LOCATION
DEPENDPATH += $$SMTP_LIBRARY_LOCATION
HEADERS += \
sendemail.h
FORMS += \
sendemail.ui

View File

@ -37,7 +37,7 @@ SendEmail::~SendEmail()
delete ui;
}
EmailAddress* SendEmail::stringToEmail(const QString &str)
EmailAddress SendEmail::stringToEmail(const QString &str)
{
int p1 = str.indexOf("<");
int p2 = str.indexOf(">");
@ -45,11 +45,11 @@ EmailAddress* SendEmail::stringToEmail(const QString &str)
if (p1 == -1)
{
// no name, only email address
return new EmailAddress(str);
return EmailAddress(str);
}
else
{
return new EmailAddress(str.mid(p1 + 1, p2 - p1 - 1), str.left(p1));
return EmailAddress(str.mid(p1 + 1, p2 - p1 - 1), str.left(p1));
}
}
@ -59,11 +59,8 @@ void SendEmail::on_addAttachment_clicked()
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFiles);
if (dialog.exec())
ui->attachments->addItems(dialog.selectedFiles());
}
void SendEmail::on_sendEmail_clicked()
@ -75,7 +72,7 @@ void SendEmail::on_sendEmail_clicked()
QString user = ui->username->text();
QString password = ui->password->text();
EmailAddress *sender = stringToEmail(ui->sender->text());
EmailAddress sender = stringToEmail(ui->sender->text());
QStringList rcptStringList = ui->recipients->text().split(';');
@ -102,20 +99,25 @@ void SendEmail::on_sendEmail_clicked()
message.addPart(new MimeAttachment(new QFile(ui->attachments->item(i)->text())));
}
if (!smtp.connectToHost())
smtp.connectToHost();
if (!smtp.waitForReadyConnected())
{
errorMessage("Connection Failed");
return;
}
if (auth)
if (!smtp.login(user, password))
{
smtp.login(user, password);
if (!smtp.waitForAuthenticated())
{
errorMessage("Authentification Failed");
return;
}
}
if (!smtp.sendMail(message))
smtp.sendMail(message);
if (!smtp.waitForMailSent())
{
errorMessage("Mail sending failed");
return;
@ -123,7 +125,7 @@ void SendEmail::on_sendEmail_clicked()
else
{
QMessageBox okMessage (this);
okMessage.setText("The email was succesfully sended.");
okMessage.setText("The email was succesfully sent.");
okMessage.exec();
}

View File

@ -21,8 +21,6 @@
#include "../../src/SmtpMime"
namespace Ui {
class SendEmail;
}
@ -35,7 +33,7 @@ public:
explicit SendEmail(QWidget *parent = 0);
~SendEmail();
static EmailAddress * stringToEmail(const QString & str);
static EmailAddress stringToEmail(const QString & str);
private slots:
void on_addAttachment_clicked();

View File

@ -14,26 +14,23 @@
See the LICENSE file for more details.
*/
#include <QtGui/QApplication>
#include "../src/SmtpMime"
#include <QtCore>
#include "../../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// First create the SmtpClient object and set the user and the password.
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.setUser("your_email@gmail.com");
smtp.setPassword("your_password");
QCoreApplication a(argc, argv);
// Create a MimeMessage
MimeMessage message;
message.setSender(new EmailAddress("your_email@gmail.com", "Your Name"));
message.addRecipient(new EmailAddress("recipient@host.com", "Recipient's Name"));
EmailAddress sender("your_email_address@host.com", "Your Name");
message.setSender(sender);
EmailAddress to("recipient@host.com", "Recipient's Name");
message.addRecipient(to);
message.setSubject("SmtpClient for Qt - Demo");
// Add some text
@ -51,13 +48,30 @@ int main(int argc, char *argv[])
message.addPart(&attachment);
// Add an another attachment
message.addPart(new MimeAttachment(new QFile("document.pdf")));
MimeAttachment document(new QFile("document.pdf"));
message.addPart(&document);
// Now we can send the mail
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.connectToHost();
smtp.login();
if (!smtp.waitForReadyConnected()) {
qDebug() << "Failed to connect to host!" << endl;
return -1;
}
smtp.login("your_email_address@host.com", "your_password");
if (!smtp.waitForAuthenticated()) {
qDebug() << "Failed to login!" << endl;
return -2;
}
smtp.sendMail(message);
if (!smtp.waitForMailSent()) {
qDebug() << "Failed to send mail!" << endl;
return -3;
}
smtp.quit();
}

28
demos/demo3/demo3.pro Normal file
View File

@ -0,0 +1,28 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-10-30T22:20:42
#
#-------------------------------------------------
QT += core
QT -= gui
TARGET = demo3
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += \
demo3.cpp
# Location of SMTP Library
SMTP_LIBRARY_LOCATION = $$PWD/../../../build/SMTPEmail-Desktop-Debug
win32:CONFIG(release, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/release/ -lSMTPMime
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/debug/ -lSMTPMime
else:unix: LIBS += -L$$SMTP_LIBRARY_LOCATION -lSmtpMime
INCLUDEPATH += $$SMTP_LIBRARY_LOCATION
DEPENDPATH += $$SMTP_LIBRARY_LOCATION

View File

@ -14,28 +14,25 @@
See the LICENSE file for more details.
*/
#include <QtGui/QApplication>
#include "../src/SmtpMime"
#include <QtCore>
#include "../../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// First create the SmtpClient object and set the user and the password.
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.setUser("your_email@gmail.com");
smtp.setPassword("your_password");
QCoreApplication a(argc, argv);
// Create a MimeMessage
MimeMessage message;
message.setSender(new EmailAddress("your_email@gmail.com", "Your Name"));
message.addRecipient(new EmailAddress("recipient@host.com", "Recipient's Name"));
message.setSubject("SmtpClient for Qt - Example 3 - Html email with images");
EmailAddress sender("your_email_address@host.com", "Your Name");
message.setSender(sender);
EmailAddress to("recipient@host.com", "Recipient's Name");
message.addRecipient(to);
message.setSubject("SmtpClient for Qt - Example 3 - Html email with images");
// Now we need to create a MimeHtml object for HTML content
MimeHtml html;
@ -62,11 +59,26 @@ int main(int argc, char *argv[])
message.addPart(&image1);
message.addPart(&image2);
// Now the email can be sended
// Now we can send the mail
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.connectToHost();
smtp.login();
if (!smtp.waitForReadyConnected()) {
qDebug() << "Failed to connect to host!" << endl;
return -1;
}
smtp.login("your_email_address@host.com", "your_password");
if (!smtp.waitForAuthenticated()) {
qDebug() << "Failed to login!" << endl;
return -2;
}
smtp.sendMail(message);
if (!smtp.waitForMailSent()) {
qDebug() << "Failed to send mail!" << endl;
return -3;
}
smtp.quit();
}

28
demos/demo4/demo4.pro Normal file
View File

@ -0,0 +1,28 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-10-30T22:20:54
#
#-------------------------------------------------
QT += core
QT -= gui
TARGET = demo4
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += \
demo4.cpp
# Location of SMTP Library
SMTP_LIBRARY_LOCATION = $$PWD/../../../build/SMTPEmail-Desktop-Debug
win32:CONFIG(release, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/release/ -lSMTPMime
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SMTP_LIBRARY_LOCATION/debug/ -lSMTPMime
else:unix: LIBS += -L$$SMTP_LIBRARY_LOCATION -lSmtpMime
INCLUDEPATH += $$SMTP_LIBRARY_LOCATION
DEPENDPATH += $$SMTP_LIBRARY_LOCATION

58
src/SMTPEmail.pro Normal file
View File

@ -0,0 +1,58 @@
#-------------------------------------------------
#
# Project created by QtCreator 2011-08-11T20:59:25
#
#-------------------------------------------------
QT = core network
TARGET = SmtpMime
TEMPLATE = lib
DEFINES += 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 \
mimebase64formatter.h \
mimecontentformatter.h
OTHER_FILES += \
LICENSE \
README.md
FORMS +=

View File

@ -27,5 +27,6 @@
#include "mimetext.h"
#include "mimeinlinefile.h"
#include "mimefile.h"
#include "mimebytearrayattachment.h"
#endif // SMTPMIME_H

View File

@ -21,9 +21,13 @@
/* [1] Constructors and Destructors */
EmailAddress::EmailAddress(const QString & address, const QString & name)
: address(address), name(name)
{
}
EmailAddress::EmailAddress(const EmailAddress &other)
: address(other.address), name(other.name)
{
this->address = address;
this->name = name;
}
EmailAddress::~EmailAddress()
@ -35,23 +39,13 @@ EmailAddress::~EmailAddress()
/* [2] Getters and Setters */
void EmailAddress::setName(const QString & name)
{
this->name = name;
}
void EmailAddress::setAddress(const QString & address)
{
this->address = address;
}
const QString & EmailAddress::getName() const
QString EmailAddress::getName() const
{
return name;
}
const QString & EmailAddress::getAddress() const
QString EmailAddress::getAddress() const
{
return address;
}

View File

@ -19,17 +19,17 @@
#ifndef EMAILADDRESS_H
#define EMAILADDRESS_H
#include <QObject>
#include "smtpmime_global.h"
#include <QString>
class EmailAddress : public QObject
class SMTP_MIME_EXPORT EmailAddress
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
EmailAddress();
EmailAddress(const QString & address, const QString & name="");
EmailAddress(const QString & address = "", const QString & name = "");
EmailAddress(const EmailAddress &other);
~EmailAddress();
@ -37,11 +37,9 @@ public:
/* [2] Getters and Setters */
void setName(const QString & name);
void setAddress(const QString & address);
const QString & getName() const;
const QString & getAddress() const;
QString getAddress() const;
QString getName() const;
/* [2] --- */
@ -50,8 +48,8 @@ private:
/* [3] Private members */
QString name;
QString address;
QString name;
/* [3] --- */
};

View File

@ -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] --- */

View File

@ -19,13 +19,13 @@
#ifndef MIMEATTACHMENT_H
#define MIMEATTACHMENT_H
#include <QFile>
#include "smtpmime_global.h"
#include "mimepart.h"
#include "mimefile.h"
class MimeAttachment : public MimeFile
class SMTP_MIME_EXPORT MimeAttachment : public MimeFile
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
@ -38,9 +38,6 @@ public:
protected:
/* [2] Protected methods */
virtual void prepare();
/* [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,16 @@
#include "mimebase64formatter.h"
MimeBase64Formatter::MimeBase64Formatter(QIODevice *out) :
MimeContentFormatter(out) {}
qint64 MimeBase64Formatter::writeData(const char *data, qint64 maxLength) {
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,13 @@
#include "mimebytearrayattachment.h"
MimeByteArrayAttachment::MimeByteArrayAttachment(const QString& name, const QByteArray &content) :
MimePart()
{
this->cType = "application/octet-stream";
this->cEncoding = Base64;
this->cName = name,
this->content = content;
}
MimeByteArrayAttachment::~MimeByteArrayAttachment() {}

View File

@ -0,0 +1,20 @@
#ifndef MIMEBYTEARRAYATTACHMENT_H
#define MIMEBYTEARRAYATTACHMENT_H
#include "smtpmime_global.h"
#include "mimepart.h"
class SMTP_MIME_EXPORT MimeByteArrayAttachment : public MimePart
{
public:
/* [1] Constructors and Destructors */
MimeByteArrayAttachment(const QString& name, const QByteArray &content);
~MimeByteArrayAttachment();
/* [1] --- */
};
#endif // MIMEBYTEARRAYATTACHMENT_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"
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;
}

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
#define MIMECONTENTFORMATTER_H
#include <QObject>
#include <QByteArray>
#include <QIODevice>
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

View File

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

View File

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

View File

@ -37,7 +37,7 @@ void MimeHtml::setHtml(const QString & html)
this->text = html;
}
const QString & MimeHtml::getHtml() const
QString MimeHtml::getHtml() const
{
return text;
}
@ -48,10 +48,4 @@ const QString & MimeHtml::getHtml() const
/* [3] Protected methods */
void MimeHtml::prepare()
{
/* !!! IMPORTANT !!! */
MimeText::prepare();
}
/* [3] --- */

View File

@ -19,11 +19,11 @@
#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:
/* [1] Constructors and Destructors */
@ -38,7 +38,7 @@ public:
void setHtml(const QString & html);
const QString& getHtml() const;
QString getHtml() const;
/* [2] --- */
@ -51,8 +51,6 @@ protected:
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};

View File

@ -23,6 +23,7 @@
MimeInlineFile::MimeInlineFile(QFile *f)
: MimeFile(f)
{
addHeaderLine("Content-Disposition: inline");
}
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] --- */

View File

@ -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] --- */
};

View File

@ -18,7 +18,9 @@
#include "mimemessage.h"
#include <QDebug>
#include <QDateTime>
#include <QBuffer>
#include "quotedprintable.h"
#include <typeinfo>
@ -47,12 +49,12 @@ void MimeMessage::setContent(MimePart *content) {
this->content = content;
}
void MimeMessage::setSender(EmailAddress* e)
void MimeMessage::setSender(const EmailAddress &sender)
{
this->sender = e;
this->sender = sender;
}
void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type)
void MimeMessage::addRecipient(const EmailAddress &rcpt, RecipientType type)
{
switch (type)
{
@ -68,18 +70,23 @@ void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type)
}
}
void MimeMessage::addTo(EmailAddress* rcpt) {
void MimeMessage::addTo(const EmailAddress &rcpt) {
this->recipientsTo << rcpt;
}
void MimeMessage::addCc(EmailAddress* rcpt) {
void MimeMessage::addCc(const EmailAddress &rcpt) {
this->recipientsCc << rcpt;
}
void MimeMessage::addBcc(EmailAddress* rcpt) {
void MimeMessage::addBcc(const EmailAddress &rcpt) {
this->recipientsBcc << rcpt;
}
void MimeMessage::addCustomHeader(const QString &header)
{
this->customHeaders << header;
}
void MimeMessage::setSubject(const QString & subject)
{
this->subject = subject;
@ -97,12 +104,12 @@ void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc)
this->hEncoding = hEnc;
}
const EmailAddress & MimeMessage::getSender() const
EmailAddress MimeMessage::getSender() const
{
return *sender;
return sender;
}
const QList<EmailAddress*> & MimeMessage::getRecipients(RecipientType type) const
const QList<EmailAddress> & MimeMessage::getRecipients(RecipientType type) const
{
switch (type)
{
@ -116,7 +123,7 @@ const QList<EmailAddress*> & MimeMessage::getRecipients(RecipientType type) cons
}
}
const QString & MimeMessage::getSubject() const
QString MimeMessage::getSubject() const
{
return subject;
}
@ -138,109 +145,88 @@ const QList<MimePart*> & MimeMessage::getParts() const
/* [3] Public Methods */
QString MimeMessage::toString()
QString MimeMessage::toString() const
{
QString mime;
QBuffer out;
out.open(QIODevice::WriteOnly);
writeToDevice(out);
return QString(out.buffer());
}
QByteArray MimeMessage::formatAddress(const EmailAddress &address, MimePart::Encoding encoding) {
QByteArray result;
result.append(format(address.getName(), encoding));
result.append(" <" + address.getAddress() + ">");
return result;
}
QByteArray MimeMessage::format(const QString &text, MimePart::Encoding encoding)
{
QByteArray result;
if (!text.isEmpty())
{
switch (encoding)
{
case MimePart::Base64:
result.append(" =?utf-8?B?" + text.toUtf8().toBase64() + "?=");
break;
case MimePart::QuotedPrintable:
result.append(" =?utf-8?Q?" + QuotedPrintable::encode(text.toUtf8()).toLocal8Bit().replace(' ', "_").replace(':',"=3A") + "?=");
break;
default:
result.append(" ").append(text.toLocal8Bit());
}
}
return result;
}
void MimeMessage::writeToDevice(QIODevice &out) const {
/* =========== MIME HEADER ============ */
/* ---------- Sender / From ----------- */
mime = "From:";
if (sender->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append(sender->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(sender->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + sender->getName();
}
}
mime += " <" + sender->getAddress() + ">\r\n";
QByteArray header;
header.append("From:" + formatAddress(sender, hEncoding) + "\r\n");
/* ---------------------------------- */
/* ------- Recipients / To ---------- */
mime += "To:";
QList<EmailAddress*>::iterator it; int i;
for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i)
header.append("To:");
for (int i = 0; i<recipientsTo.size(); ++i)
{
if (i != 0) { mime += ","; }
if ((*it)->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + (*it)->getName();
}
}
mime += " <" + (*it)->getAddress() + ">";
if (i != 0) { header.append(","); }
header.append(formatAddress(recipientsTo.at(i), hEncoding));
}
mime += "\r\n";
header.append("\r\n");
/* ---------------------------------- */
/* ------- Recipients / Cc ---------- */
if (recipientsCc.size() != 0) {
mime += "Cc:";
header.append("Cc:");
}
for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i)
for (int i = 0; i<recipientsCc.size(); ++i)
{
if (i != 0) { mime += ","; }
if ((*it)->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + (*it)->getName();
}
}
mime += " <" + (*it)->getAddress() + ">";
if (i != 0) { header.append(","); }
header.append(formatAddress(recipientsCc.at(i), hEncoding));
}
if (recipientsCc.size() != 0) {
mime += "\r\n";
header.append("\r\n");
}
/* ---------------------------------- */
/* ------------ Subject ------------- */
mime += "Subject: ";
switch (hEncoding)
{
case MimePart::Base64:
mime += "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += "=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += subject;
}
header.append("Subject: ");
header.append(format(subject, hEncoding));
header.append("\r\n");
/* ---------------------------------- */
mime += "\r\n";
mime += "MIME-Version: 1.0\r\n";
foreach (QString hdr, customHeaders) {
header.append(hdr.toLocal8Bit());
header.append("\r\n");
}
mime += content->toString();
return mime;
header.append("MIME-Version: 1.0\r\n");
out.write(header);
content->writeToDevice(out);
}
/* [3] --- */

View File

@ -19,12 +19,15 @@
#ifndef MIMEMESSAGE_H
#define MIMEMESSAGE_H
#include <QStringList>
#include <QTextStream>
#include "smtpmime_global.h"
#include "mimepart.h"
#include "mimemultipart.h"
#include "emailaddress.h"
#include <QList>
class MimeMessage : public QObject
class SMTP_MIME_EXPORT MimeMessage : public QObject
{
public:
@ -36,7 +39,7 @@ public:
/* [1] Constructors and Destructors */
MimeMessage(bool createAutoMimeConent = true);
MimeMessage(bool createAutoMimeContent = true);
~MimeMessage();
/* [1] --- */
@ -44,19 +47,21 @@ public:
/* [2] Getters and Setters */
void setSender(EmailAddress* e);
void addRecipient(EmailAddress* rcpt, RecipientType type = To);
void addTo(EmailAddress* rcpt);
void addCc(EmailAddress* rcpt);
void addBcc(EmailAddress* rcpt);
void setSubject(const QString & subject);
void setSender(const EmailAddress &sndr);
void addRecipient(const EmailAddress &rcpt, RecipientType type = To);
void addTo(const EmailAddress &rcpt);
void addCc(const EmailAddress &rcpt);
void addBcc(const EmailAddress &rcpt);
void addCustomHeader(const QString &hdr);
void setSubject(const QString &subject);
void addPart(MimePart* part);
void setHeaderEncoding(MimePart::Encoding);
const EmailAddress & getSender() const;
const QList<EmailAddress*> & getRecipients(RecipientType type = To) const;
const QString & getSubject() const;
EmailAddress getSender() const;
const QList<EmailAddress> &getRecipients(RecipientType type = To) const;
QString getSubject() const;
const QStringList &getCustomHeaders() const;
const QList<MimePart*> & getParts() const;
MimePart& getContent();
@ -66,7 +71,8 @@ public:
/* [3] Public methods */
virtual QString toString();
virtual QString toString() const;
void writeToDevice(QIODevice &device) const;
/* [3] --- */
@ -74,13 +80,17 @@ protected:
/* [4] Protected members */
EmailAddress* sender;
QList<EmailAddress*> recipientsTo, recipientsCc, recipientsBcc;
EmailAddress sender;
QList<EmailAddress> recipientsTo, recipientsCc, recipientsBcc;
QString subject;
QStringList customHeaders;
MimePart *content;
MimePart::Encoding hEncoding;
static QByteArray format(const QString &text, MimePart::Encoding encoding);
static QByteArray formatAddress(const EmailAddress &address, MimePart::Encoding encoding);
/* [4] --- */

View File

@ -17,6 +17,7 @@
*/
#include "mimemultipart.h"
#include <QIODevice>
#include <QTime>
#include <QCryptographicHash>
@ -42,7 +43,9 @@ MimeMultiPart::MimeMultiPart(MultiPartType type)
}
MimeMultiPart::~MimeMultiPart() {
foreach (MimePart *part, parts) {
delete part;
}
}
void MimeMultiPart::addPart(MimePart *part) {
@ -53,21 +56,22 @@ const QList<MimePart*> & MimeMultiPart::getParts() const {
return parts;
}
void MimeMultiPart::prepare() {
QList<MimePart*>::iterator it;
void MimeMultiPart::writeContent(QIODevice &device) const {
QList<MimePart*>::const_iterator it;
content = "";
for (it = parts.begin(); it != parts.end(); it++) {
content += "--" + cBoundary + "\r\n";
(*it)->prepare();
content += (*it)->toString();
for (it = parts.constBegin(); it != parts.constEnd(); it++) {
device.write("--" );
device.write(cBoundary.toLatin1());
device.write("\r\n");
(*it)->writeToDevice(device);
};
content += "--" + cBoundary + "--\r\n";
MimePart::prepare();
device.write("--");
device.write(cBoundary.toLatin1());
device.write("--\r\n");
}
void MimeMultiPart::setMimeType(const MultiPartType type) {
this->type = type;
this->cType = MULTI_PART_NAMES[type];

View File

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

View File

@ -16,8 +16,13 @@
See the LICENSE file for more details.
*/
#include <QBuffer>
#include "mimepart.h"
#include "quotedprintable.h"
#include "mimebase64formatter.h"
#include "mimeqpformatter.h"
#include "mimebase64encoder.h"
#include "mimeqpencoder.h"
/* [1] Constructors and Destructors */
@ -45,20 +50,20 @@ 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
QString MimePart::getHeader() const
{
return header;
return headerLines;
}
const QByteArray& MimePart::getContent() const
QByteArray MimePart::getContent() const
{
return content;
}
@ -68,7 +73,7 @@ void MimePart::setContentId(const QString & cId)
this->cId = cId;
}
const QString & MimePart::getContentId() const
QString MimePart::getContentId() const
{
return this->cId;
}
@ -78,7 +83,7 @@ void MimePart::setContentName(const QString & cName)
this->cName = cName;
}
const QString & MimePart::getContentName() const
QString MimePart::getContentName() const
{
return this->cName;
}
@ -88,7 +93,7 @@ void MimePart::setContentType(const QString & cType)
this->cType = cType;
}
const QString & MimePart::getContentType() const
QString MimePart::getContentType() const
{
return this->cType;
}
@ -98,7 +103,7 @@ void MimePart::setCharset(const QString & charset)
this->cCharset = charset;
}
const QString & MimePart::getCharset() const
QString MimePart::getCharset() const
{
return this->cCharset;
}
@ -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] --- */
@ -123,92 +131,97 @@ MimeContentFormatter& MimePart::getContentFormatter()
/* [3] Public methods */
QString MimePart::toString()
QString MimePart::toString() const
{
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) const {
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.toLatin1());
writeContent(device);
}
/* [3] --- */
/* [4] Protected methods */
void MimePart::writeContent(QIODevice &device) const {
this->writeContent(device, content);
}
void MimePart::writeContent(QIODevice &device, const QByteArray &content) const {
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] --- */

View File

@ -19,12 +19,14 @@
#ifndef MIMEPART_H
#define MIMEPART_H
#include <QObject>
#include "mimecontentformatter.h"
#include "smtpmime_global.h"
#include <QByteArray>
#include <QString>
class MimePart : public QObject
class QIODevice;
class SMTP_MIME_EXPORT MimePart
{
Q_OBJECT
public:
/* [0] Enumerations */
@ -42,56 +44,54 @@ public:
/* [1] Constructors and Destructors */
MimePart();
~MimePart();
virtual ~MimePart();
/* [1] --- */
/* [2] Getters and Setters */
const QString& getHeader() const;
const QByteArray& getContent() const;
void setContent(const QByteArray & content);
void setHeader(const QString & header);
QByteArray getContent() const;
void setHeader(const QString & headerLines);
QString getHeader() const;
void addHeaderLine(const QString & line);
void setContentId(const QString & cId);
const QString & getContentId() const;
QString getContentId() const;
void setContentName(const QString & cName);
const QString & getContentName() const;
QString getContentName() const;
void setContentType(const QString & cType);
const QString & getContentType() const;
QString getContentType() const;
void setCharset(const QString & charset);
const QString & getCharset() const;
QString getCharset() const;
void setEncoding(Encoding enc);
Encoding getEncoding() const;
MimeContentFormatter& getContentFormatter();
void setMaxLineLength(const int length);
int getMaxLineLength() const;
/* [2] --- */
/* [3] Public methods */
virtual QString toString();
virtual void prepare();
virtual QString toString() const;
void writeToDevice(QIODevice &device) const;
/* [3] --- */
protected:
/* [4] Protected members */
QString header;
QString headerLines;
QByteArray content;
QString cId;
@ -101,12 +101,15 @@ protected:
QString cBoundary;
Encoding cEncoding;
int maxLineLength;
QString mimeString;
bool prepared;
MimeContentFormatter formatter;
/* [4] --- */
virtual void writeContent(QIODevice &device) const;
void writeContent(QIODevice &device, const QByteArray &content) const;
};
#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).toLatin1();
}

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,8 @@ 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) const {
MimePart::writeContent(device, text.toLocal8Bit());
}
/* [3] --- */

View File

@ -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) const;
/* [4] --- */

View File

@ -18,51 +18,62 @@
#include "quotedprintable.h"
QString& QuotedPrintable::encode(const QByteArray &input)
QString QuotedPrintable::encode(const QByteArray &input)
{
QString *output = new QString();
static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char byte;
const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
QString output;
for (int i = 0; i < input.length() ; ++i)
{
byte = input[i];
const char byte = input[i];
if ((byte == 0x20) || (byte >= 33) && (byte <= 126) && (byte != 61))
{
output->append(byte);
if ((byte == 0x20) || ((byte >= 33) && (byte <= 126) && (byte != 61))) {
output.append(byte);
}
else
{
output->append('=');
output->append(hex[((byte >> 4) & 0x0F)]);
output->append(hex[(byte & 0x0F)]);
else {
output.append('=').append(hex[((byte >> 4) & 0x0F)]).append(hex[(byte & 0x0F)]);
}
}
return *output;
return output;
}
QByteArray& QuotedPrintable::decode(const QString &input)
QByteArray QuotedPrintable::decode(const QString &input)
{
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15};
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
static const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15};
QByteArray *output = new QByteArray();
QByteArray output;
for (int i = 0; i < input.length(); ++i)
int len = input.length();
int i;
for (i = 0; i < len-2; ++i)
{
if (input.at(i).toAscii() == '=')
if (input.at(i).toLatin1() == '=')
{
output->append((hexVal[input.at(++i).toAscii() - '0'] << 4) + hexVal[input.at(++i).toAscii() - '0']);
int x = input.at(i+1).toLatin1() - '0';
int y = input.at(i+2).toLatin1() - '0';
if (x >= 0 && y >= 0 && x < 23 && y < 23) {
output.append(char((hexVal[x] << 4) + hexVal[y]));
}
else {
output.append('=').append(char(x + '0')).append(char(y + '0'));
}
i += 2;
}
else
{
output->append(input.at(i).toAscii());
output.append(input.at(i).toLatin1());
}
}
return *output;
while (i<len) {
output.append(input.at(i).toLatin1());
++i;
}
return output;
}

View File

@ -19,19 +19,13 @@
#ifndef QUOTEDPRINTABLE_H
#define QUOTEDPRINTABLE_H
#include <QObject>
#include <QByteArray>
#include <QString>
#include "smtpmime_global.h"
class QuotedPrintable : public QObject
{
Q_OBJECT
public:
static QString& encode(const QByteArray &input);
static QByteArray& decode(const QString &input);
private:
QuotedPrintable();
};
namespace QuotedPrintable {
SMTP_MIME_EXPORT QString encode(const QByteArray &input);
SMTP_MIME_EXPORT QByteArray decode(const QString &input);
}
#endif // QUOTEDPRINTABLE_H

View File

@ -20,21 +20,24 @@
#include <QFileInfo>
#include <QByteArray>
#include <QTimer>
#include <QEventLoop>
#include <QMetaEnum>
/* [1] Constructors and destructors */
SmtpClient::SmtpClient(const QString & host, int port, ConnectionType connectionType) :
state(UnconnectedState),
host(host),
port(port),
name("localhost"),
authMethod(AuthPlain),
connectionTimeout(5000),
responseTimeout(5000)
isReadyConnected(false),
isAuthenticated(false),
isMailSent(false),
isReset(false)
{
setConnectionType(connectionType);
this->host = host;
this->port = port;
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
@ -49,32 +52,179 @@ SmtpClient::~SmtpClient() {}
/* [2] Getters and Setters */
void SmtpClient::setUser(const QString &user)
/**
* @brief Returns the host name of the server.
*/
QString SmtpClient::getHost() const
{
this->user = user;
return this->host;
}
void SmtpClient::setPassword(const QString &password)
/**
* @brief Return the port.
*/
int SmtpClient::getPort() const
{
this->password = password;
return this->port;
}
void SmtpClient::setAuthMethod(AuthMethod method)
/**
* @brief Returns the connection type used.
*/
SmtpClient::ConnectionType SmtpClient::getConnectionType() const
{
this->authMethod = method;
return connectionType;
}
void SmtpClient::setHost(QString &host)
/**
* @brief Returns the client's name.
*/
QString SmtpClient::getName() const
{
this->host = host;
return this->name;
}
void SmtpClient::setPort(int port)
/**
* @brief Sets the client's name. This name is sent by the EHLO command.
*/
void SmtpClient::setName(const QString &name)
{
this->port = port;
this->name = name;
}
/**
* @brief Returns the last response of the server.
*/
QString SmtpClient::getResponseText() const
{
return responseText;
}
/**
* @brief Returns the last response code recived by the client.
*/
int SmtpClient::getResponseCode() const
{
return responseCode;
}
/**
* @brief Return the socket used by the client. The type of the of the
* connection is QTcpConnection in case of TcpConnection, and QSslSocket
* for SslConnection and TlsConnection.
*/
QTcpSocket* SmtpClient::getSocket() {
return socket;
}
/* [2] --- */
/* [3] Public methods */
void SmtpClient::connectToHost()
{
if (state != UnconnectedState)
return;
changeState(ConnectingState);
}
void SmtpClient::login()
{
if (!isReadyConnected || isAuthenticated)
return;
changeState(AuthenticatingState);
}
void SmtpClient::login(const QString &user, const QString &password, AuthMethod method)
{
this->authInfo = AuthInfo(user, password, method);
login();
}
void SmtpClient::sendMail(const MimeMessage & email)
{
if (!isReadyConnected)
return;
isMailSent = false;
this->email = &email;
this->rcptType = 0;
changeState(MailSendingState);
}
void SmtpClient::quit()
{
changeState(DisconnectingState);
}
void SmtpClient::reset()
{
if (!isReadyConnected)
return;
isReset = false;
changeState(ResetState);
}
bool SmtpClient::waitForReadyConnected(int msec) {
if (state == UnconnectedState)
return false;
if (isReadyConnected)
return true;
waitForEvent(msec, SIGNAL(readyConnected()));
return isReadyConnected;
}
bool SmtpClient::waitForAuthenticated(int msec) {
if (!isReadyConnected)
return false;
if (isAuthenticated)
return true;
waitForEvent(msec, SIGNAL(authenticated()));
return isAuthenticated;
}
bool SmtpClient::waitForMailSent(int msec) {
if (!isReadyConnected)
return false;
if (isMailSent)
return true;
waitForEvent(msec, SIGNAL(mailSent()));
return isMailSent;
}
bool SmtpClient::waitForReset(int msec)
{
if (!isReadyConnected)
return false;
if (isReset)
return true;
waitForEvent(msec, SIGNAL(mailReset()));
return isReset;
}
/* [3] --- */
/* [4] Protected methods */
void SmtpClient::setConnectionType(ConnectionType ct)
{
this->connectionType = ct;
@ -87,338 +237,418 @@ void SmtpClient::setConnectionType(ConnectionType ct)
case SslConnection:
case TlsConnection:
socket = new QSslSocket(this);
}
}
const QString& SmtpClient::getHost() const
{
return this->host;
}
const QString& SmtpClient::getUser() const
{
return this->user;
}
const QString& SmtpClient::getPassword() const
{
return this->password;
}
SmtpClient::AuthMethod SmtpClient::getAuthMethod() const
{
return this->authMethod;
}
int SmtpClient::getPort() const
{
return this->port;
}
SmtpClient::ConnectionType SmtpClient::getConnectionType() const
{
return connectionType;
}
const QString& SmtpClient::getName() const
{
return this->name;
}
void SmtpClient::setName(const QString &name)
{
this->name = name;
}
const QString & SmtpClient::getResponseText() const
{
return responseText;
}
int SmtpClient::getResponseCode() const
{
return responseCode;
}
QTcpSocket* SmtpClient::getSocket() {
return socket;
}
/* [2] --- */
/* [3] Public methods */
bool SmtpClient::connectToHost()
{
switch (connectionType)
{
case TlsConnection:
case TcpConnection:
socket->connectToHost(host, port);
connect(socket, SIGNAL(encrypted()),
this, SLOT(socketEncrypted()));
break;
case SslConnection:
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
break;
}
}
// Tries to connect to server
if (!socket->waitForConnected(connectionTimeout))
{
emit smtpError(ConnectionTimeoutError);
return false;
void SmtpClient::changeState(SmtpClient::ClientState state) {
this->state = state;
#ifdef QT_NO_DEBUG
// Emit stateChanged signal only for non-internal states
if (state <= DisconnectingState) {
emit stateChanged(state);
}
#else
// emit all in debug mode
qDebug() << "[SmtpClient] State:" << staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ClientState")).valueToKey(state);
emit stateChanged(state);
#endif
try
switch (state)
{
// Wait for the server's response
waitForResponse();
// If the response code is not 220 (Service ready)
// means that is something wrong with the server
if (responseCode != 220)
case ConnectingState:
switch (connectionType)
{
emit smtpError(ServerError);
return false;
case TlsConnection:
case TcpConnection:
socket->connectToHost(host, port);
break;
case SslConnection:
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
break;
}
break;
// Send a EHLO/HELO message to the server
// The client's first command must be EHLO/HELO
case AuthenticatingState:
isAuthenticated = false;
changeState(authInfo.authMethod == AuthPlain ? _AUTH_PLAIN_0 : _AUTH_LOGIN_0);
break;
case MailSendingState:
isMailSent = false;
changeState(_MAIL_0_FROM);
break;
case DisconnectingState:
sendMessage("QUIT");
socket->disconnectFromHost();
break;
case ResetState:
sendMessage("RSET");
break;
case _EHLO_State:
// Service ready. Send EHLO message and change the state
sendMessage("EHLO " + name);
break;
// Wait for the server's response
waitForResponse();
case _READY_Connected:
isReadyConnected = true;
changeState(ReadyState);
emit readyConnected();
break;
/* --- TLS --- */
case _TLS_State:
changeState(_TLS_0_STARTTLS);
break;
case _TLS_0_STARTTLS:
// send a request to start TLS handshake
sendMessage("STARTTLS");
break;
case _TLS_1_ENCRYPT:
((QSslSocket*) socket)->startClientEncryption();
break;
case _TLS_2_EHLO:
// Send EHLO one more time
sendMessage("EHLO " + name);
break;
case _READY_Encrypted:
changeState(_READY_Connected);
break;
/* --- AUTH --- */
case _AUTH_PLAIN_0:
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(authInfo.username)
.append((char) 0).append(authInfo.password).toBase64());
break;
case _AUTH_LOGIN_0:
sendMessage("AUTH LOGIN");
break;
case _AUTH_LOGIN_1_USER:
// Send the username in base64
sendMessage(QByteArray().append(authInfo.username).toBase64());
break;
case _AUTH_LOGIN_2_PASS:
// Send the password in base64
sendMessage(QByteArray().append(authInfo.password).toBase64());
break;
case _READY_Authenticated:
isAuthenticated = true;
authInfo = AuthInfo();
changeState(ReadyState);
emit authenticated();
break;
/* --- MAIL --- */
case _MAIL_0_FROM:
sendMessage("MAIL FROM:<" + email->getSender().getAddress() + ">");
break;
case _MAIL_1_RCPT_INIT:
rcptType++;
const QList<EmailAddress> *addressList;
switch (rcptType)
{
case _TO:
addressList = &email->getRecipients(MimeMessage::To);
break;
case _CC:
addressList = &email->getRecipients(MimeMessage::Cc);
break;
case _BCC:
addressList = &email->getRecipients(MimeMessage::Bcc);
break;
default:
changeState(_MAIL_3_DATA);
return;
}
addressIt = addressList->constBegin();
addressItEnd = addressList->constEnd();
changeState(_MAIL_2_RCPT);
break;
case _MAIL_2_RCPT:
if (addressIt != addressItEnd) {
sendMessage("RCPT TO:<" + addressIt->getAddress() + ">");
addressIt++;
} else {
changeState(_MAIL_1_RCPT_INIT);
}
break;
case _MAIL_3_DATA:
sendMessage("DATA");
break;
case _MAIL_4_SEND_DATA:
email->writeToDevice(*socket);
#ifndef QT_NO_DEBUG
qDebug() << "[Socket] OUT:";
qDebug() << email->toString();
#endif
sendMessage("\r\n.");
break;
case _READY_MailSent:
isMailSent = true;
changeState(ReadyState);
emit mailSent();
break;
default:
;
}
}
void SmtpClient::processResponse() {
switch (state)
{
case ConnectedState:
// Just connected to the server. Wait for 220 (Service ready)
if (responseCode != 220) {
emitError(ServerError);
return;
}
changeState(_EHLO_State);
break;
case ResetState:
if (responseCode != 250) {
emitError(ServerError);
return;
}
emit mailReset();
changeState(ReadyState);
break;
case _EHLO_State:
// The response code needs to be 250.
if (responseCode != 250) {
emit smtpError(ServerError);
return false;
emitError(ServerError);
return;
}
if (connectionType == TlsConnection) {
// send a request to start TLS handshake
sendMessage("STARTTLS");
changeState((connectionType != TlsConnection) ? _READY_Connected : _TLS_State);
break;
// Wait for the server's response
waitForResponse();
// The response code needs to be 220.
if (responseCode != 220) {
emit smtpError(ServerError);
return false;
};
((QSslSocket*) socket)->startClientEncryption();
if (!((QSslSocket*) socket)->waitForEncrypted(connectionTimeout)) {
qDebug() << ((QSslSocket*) socket)->errorString();
emit SmtpError(ConnectionTimeoutError);
return false;
}
// Send ELHO one more time
sendMessage("EHLO " + name);
// Wait for the server's response
waitForResponse();
// The response code needs to be 250.
if (responseCode != 250) {
emit smtpError(ServerError);
return false;
}
/* --- TLS --- */
case _TLS_0_STARTTLS:
// The response code needs to be 220.
if (responseCode != 220) {
emitError(ServerError);
return;
}
}
catch (ResponseTimeoutException)
{
return false;
}
changeState(_TLS_1_ENCRYPT);
break;
// If no errors occured the function returns true.
return true;
}
bool SmtpClient::login()
{
return login(user, password, authMethod);
}
bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method)
{
try {
if (method == AuthPlain)
{
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user).append((char) 0).append(password).toBase64());
// Wait for the server's response
waitForResponse();
// If the response is not 235 then the authentication was faild
if (responseCode != 235)
{
emit smtpError(AuthenticationFailedError);
return false;
}
case _TLS_2_EHLO:
// The response code needs to be 250.
if (responseCode != 250) {
emitError(ServerError);
return;
}
else if (method == AuthLogin)
{
// Sending command: AUTH LOGIN
sendMessage("AUTH LOGIN");
changeState(_READY_Encrypted);
break;
// Wait for 334 response code
waitForResponse();
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
// Send the username in base64
sendMessage(QByteArray().append(user).toBase64());
// Wait for 334
waitForResponse();
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
// Send the password in base64
sendMessage(QByteArray().append(password).toBase64());
// Wait for the server's responce
waitForResponse();
// If the response is not 235 then the authentication was faild
if (responseCode != 235)
{
emit smtpError(AuthenticationFailedError);
return false;
}
/* --- AUTH --- */
case _AUTH_PLAIN_0:
// If the response is not 235 then the authentication was failed
if (responseCode != 235) {
emitError(AuthenticationError);
return;
}
}
catch (ResponseTimeoutException e)
{
// Responce Timeout exceeded
emit smtpError(AuthenticationFailedError);
return false;
}
changeState(_READY_Authenticated);
break;
return true;
}
bool SmtpClient::sendMail(MimeMessage& email)
{
try
{
// Send the MAIL command with the sender
sendMessage("MAIL FROM: <" + email.getSender().getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
// Send RCPT command for each recipient
QList<EmailAddress*>::const_iterator it, itEnd;
// To (primary recipients)
for (it = email.getRecipients().begin(), itEnd = email.getRecipients().end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
case _AUTH_LOGIN_0:
if (responseCode != 334) {
emitError(AuthenticationError);
return;
}
changeState(_AUTH_LOGIN_1_USER);
break;
// Cc (carbon copy)
for (it = email.getRecipients(MimeMessage::Cc).begin(), itEnd = email.getRecipients(MimeMessage::Cc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
case _AUTH_LOGIN_1_USER:
if (responseCode != 334) {
emitError(AuthenticationError);
return;
}
changeState(_AUTH_LOGIN_2_PASS);
break;
// Bcc (blind carbon copy)
for (it = email.getRecipients(MimeMessage::Bcc).begin(), itEnd = email.getRecipients(MimeMessage::Bcc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
case _AUTH_LOGIN_2_PASS:
if (responseCode != 235) {
emitError(AuthenticationError);
return;
}
changeState(_READY_Authenticated);
break;
// Send DATA command
sendMessage("DATA");
waitForResponse();
/* --- MAIL --- */
case _MAIL_0_FROM:
if (responseCode != 250) {
emitError(MailSendingError);
return;
}
changeState(_MAIL_1_RCPT_INIT);
break;
if (responseCode != 354) return false;
case _MAIL_2_RCPT:
if (responseCode != 250) {
emitError(MailSendingError);
return;
}
changeState(_MAIL_2_RCPT);
break;
sendMessage(email.toString());
case _MAIL_3_DATA:
if (responseCode != 354) {
emitError(MailSendingError);
return;
}
changeState(_MAIL_4_SEND_DATA);
break;
// Send \r\n.\r\n to end the mail data
sendMessage(".");
case _MAIL_4_SEND_DATA:
if (responseCode != 250) {
emitError(MailSendingError);
return;
}
changeState(_READY_MailSent);
break;
waitForResponse();
if (responseCode != 250) return false;
default:
;
}
catch (ResponseTimeoutException)
{
return false;
}
return true;
}
void SmtpClient::quit()
{
sendMessage("QUIT");
}
/* [3] --- */
/* [4] Protected methods */
void SmtpClient::waitForResponse() throw (ResponseTimeoutException)
{
if (!socket->waitForReadyRead(responseTimeout))
{
emit smtpError(ResponseTimeoutError);
throw ResponseTimeoutException();
}
// Save the server's response
responseText = socket->readAll();
// Extract the respose code from the server's responce (first 3 digits)
responseCode = responseText.left(3).toInt();
if (responseCode / 100 == 4)
emit smtpError(ServerError);
if (responseCode / 100 == 5)
emit smtpError(ClientError);
}
void SmtpClient::sendMessage(const QString &text)
{
#ifndef QT_NO_DEBUG
qDebug() << "[Socket] OUT:" << text;
#endif
socket->flush();
socket->write(text.toUtf8() + "\r\n");
}
void SmtpClient::emitError(SmtpClient::SmtpError e)
{
emit error(e);
}
void SmtpClient::waitForEvent(int msec, const char *successSignal)
{
QEventLoop loop;
QObject::connect(this, successSignal, &loop, SLOT(quit()));
QObject::connect(this, SIGNAL(error(SmtpClient::SmtpError)), &loop, SLOT(quit()));
if(msec > 0)
{
QTimer timer;
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start(msec);
}
loop.exec();
}
/* [4] --- */
/* [5] Slots for the socket's signals */
void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state)
{
void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state) {
#ifndef QT_NO_DEBUG
qDebug() << "[Socket] State:" << state;
#endif
switch (state)
{
case QAbstractSocket::ConnectedState:
changeState(ConnectedState);
break;
case QAbstractSocket::UnconnectedState:
changeState(UnconnectedState);
break;
default:
;
}
}
void SmtpClient::socketError(QAbstractSocket::SocketError socketError)
{
void SmtpClient::socketError(QAbstractSocket::SocketError socketError) {
#ifndef QT_NO_DEBUG
qDebug() << "[Socket] ERROR:" << socketError;
#else
Q_UNUSED(socketError);
#endif
emit error(SocketError);
}
void SmtpClient::socketReadyRead()
{
QString responseLine;
while (socket->canReadLine()) {
// Save the server's response
responseLine = socket->readLine();
tempResponse += responseLine;
#ifndef QT_NO_DEBUG
qDebug() << "[Socket] IN: " << responseLine;
#endif
}
// 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();
// Check for server error
if (responseCode / 100 == 4) {
emitError(ServerError);
return;
}
// Check for client error
if (responseCode / 100 == 5) {
emitError(ClientError);
return;
}
processResponse();
}
}
void SmtpClient::socketEncrypted() {
if (state == _TLS_1_ENCRYPT) {
changeState(_TLS_2_EHLO);
}
}
/* [5] --- */

View File

@ -21,13 +21,15 @@
#include <QObject>
#include <QtNetwork/QSslSocket>
#include <QEventLoop>
#include "smtpmime_global.h"
#include "mimemessage.h"
class SmtpClient : public QObject
class SMTP_MIME_EXPORT SmtpClient : public QObject
{
Q_OBJECT
Q_ENUMS (AuthMethod SmtpError ConnectionType ClientState)
public:
/* [0] Enumerations */
@ -40,18 +42,60 @@ public:
enum SmtpError
{
ConnectionTimeoutError,
ResponseTimeoutError,
AuthenticationFailedError,
ServerError, // 4xx smtp error
ClientError // 5xx smtp error
ConnectionTimeoutError = 0,
ResponseTimeoutError = 1,
AuthenticationError = 2,
MailSendingError = 3,
ServerError = 4, // 4xx smtp error
ClientError = 5, // 5xx smtp error
SocketError = 6
};
enum ConnectionType
{
TcpConnection,
SslConnection,
TlsConnection // STARTTLS
TcpConnection = 0,
SslConnection = 1,
TlsConnection = 2 // STARTTLS
};
enum ClientState {
UnconnectedState = 0,
ConnectingState = 1,
ConnectedState = 2,
ReadyState = 3,
AuthenticatingState = 4,
MailSendingState = 5,
DisconnectingState = 6,
ResetState = 7,
/* Internal States */
_EHLO_State = 50,
_TLS_State = 51,
_READY_Connected = 52,
_READY_Authenticated = 53,
_READY_MailSent = 54,
_READY_Encrypted = 55,
/* Internal Substates */
// TLS
_TLS_0_STARTTLS = 60,
_TLS_1_ENCRYPT = 61,
_TLS_2_EHLO = 62,
// AUTH
_AUTH_PLAIN_0 = 70,
_AUTH_LOGIN_0 = 71,
_AUTH_LOGIN_1_USER = 72,
_AUTH_LOGIN_2_PASS = 73,
// MAIL
_MAIL_0_FROM = 81,
_MAIL_1_RCPT_INIT = 82,
_MAIL_2_RCPT = 83,
_MAIL_3_DATA = 84,
_MAIL_4_SEND_DATA = 85
};
/* [0] --- */
@ -59,7 +103,7 @@ public:
/* [1] Constructors and Destructors */
SmtpClient(const QString & host = "locahost", int port = 25, ConnectionType ct = TcpConnection);
SmtpClient(const QString & host = "localhost", int port = 25, ConnectionType ct = TcpConnection);
~SmtpClient();
@ -68,47 +112,36 @@ public:
/* [2] Getters and Setters */
const QString& getHost() const;
void setHost(QString &host);
QString getHost() const;
int getPort() const;
void setPort(int port);
ConnectionType getConnectionType() const;
const QString& getName() const;
QString getName() const;
void setName(const QString &name);
ConnectionType getConnectionType() const;
void setConnectionType(ConnectionType ct);
const QString & getUser() const;
void setUser(const QString &host);
const QString & getPassword() const;
void setPassword(const QString &password);
SmtpClient::AuthMethod getAuthMethod() const;
void setAuthMethod(AuthMethod method);
const QString & getResponseText() const;
QString getResponseText() const;
int getResponseCode() const;
QTcpSocket* getSocket();
/* [2] --- */
/* [3] Public methods */
bool connectToHost();
bool login();
bool login(const QString &user, const QString &password, AuthMethod method = AuthLogin);
bool sendMail(MimeMessage& email);
void connectToHost();
void login(const QString &user, const QString &password, AuthMethod method = AuthLogin);
void sendMail(const MimeMessage & email);
void quit();
void reset();
bool isConnected();
bool isLogged();
bool waitForReadyConnected(int msec = 30000);
bool waitForAuthenticated(int msec = 30000);
bool waitForMailSent(int msec = 30000);
bool waitForReset(int msec = 30000);
/* [3] --- */
@ -116,34 +149,54 @@ protected:
/* [4] Protected members */
QTcpSocket *socket;
struct AuthInfo {
QString username;
QString password;
AuthMethod authMethod;
QString host;
int port;
AuthInfo(const QString & username = "", const QString &password = "", AuthMethod authMethod = AuthPlain) :
username(username), password(password), authMethod(authMethod) {}
};
QTcpSocket *socket;
ClientState state;
const QString host;
const int port;
ConnectionType connectionType;
QString name;
QString user;
QString password;
AuthMethod authMethod;
int connectionTimeout;
int responseTimeout;
AuthInfo authInfo;
QString responseText;
QString tempResponse;
int responseCode;
bool isReadyConnected;
bool isAuthenticated;
bool isMailSent;
bool isReset;
class ResponseTimeoutException {};
const MimeMessage *email;
int rcptType;
enum _RcptType { _TO = 1, _CC = 2, _BCC = 3};
QList<EmailAddress>::const_iterator addressIt;
QList<EmailAddress>::const_iterator addressItEnd;
/* [4] --- */
/* [5] Protected methods */
void waitForResponse() throw (ResponseTimeoutException);
void login();
void setConnectionType(ConnectionType ct);
void changeState(ClientState state);
void processResponse();
void sendMessage(const QString &text);
void emitError(SmtpClient::SmtpError e);
void waitForEvent(int msec, const char *successSignal);
/* [5] --- */
@ -154,6 +207,7 @@ protected slots:
void socketStateChanged(QAbstractSocket::SocketState state);
void socketError(QAbstractSocket::SocketError error);
void socketReadyRead();
void socketEncrypted();
/* [6] --- */
@ -162,7 +216,14 @@ signals:
/* [7] Signals */
void smtpError(SmtpError e);
void error(SmtpClient::SmtpError e);
void stateChanged(SmtpClient::ClientState s);
void connected();
void readyConnected();
void authenticated();
void mailSent();
void mailReset();
void disconnected();
/* [7] --- */

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

10
test/connect_data.txt Normal file
View File

@ -0,0 +1,10 @@
smtp.gmail.com 25 1
smtp.gmail.com 465 2
smtp.gmail.com 587 3
smtp.mail.yahoo.com 25 1
smtp.mail.yahoo.com 465 2
smtp.1and1.com 25 1
smtp.1and1.com 465 2
smtp.1and1.com 587 3
smtp.live.com 25 1
smtp.live.com 587 3

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 <QCoreApplication>
#include <QtTest/QTest>
#include <QDebug>
#include "connectiontest.h"
bool success = true;
static void runTest(QObject *test, int argc, char** argv) {
int retVal = QTest::qExec(test, argc, argv);
delete test;
success &= retVal == 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
runTest(new ConnectionTest(), argc, argv);
if (success)
qDebug() << "SUCCESS";
else
qDebug() << "FAIL";
return success;
}

28
test/test.pro Normal file
View File

@ -0,0 +1,28 @@
#-------------------------------------------------
#
# Project created by QtCreator 2012-09-22T16:39:45
#
#-------------------------------------------------
QT += testlib
QT -= 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