Merge pull request #1295 from ctrlaltca/servatrice_email_v2

Refactor servatrice email send
This commit is contained in:
Zach 2015-07-26 21:13:58 -07:00
commit c0bb640a37
49 changed files with 2521 additions and 2574 deletions

View file

@ -16,18 +16,11 @@ SET(servatrice_SOURCES
src/isl_interface.cpp
src/signalhandler.cpp
${VERSION_STRING_CPP}
src/smtp/emailaddress.cpp
src/smtp/mimeattachment.cpp
src/smtp/mimecontentformatter.cpp
src/smtp/mimefile.cpp
src/smtp/mimehtml.cpp
src/smtp/mimeinlinefile.cpp
src/smtp/mimemessage.cpp
src/smtp/mimemultipart.cpp
src/smtp/mimepart.cpp
src/smtp/mimetext.cpp
src/smtp/quotedprintable.cpp
src/smtp/smtpclient.cpp
src/smtpclient.cpp
src/smtp/qxthmac.cpp
src/smtp/qxtmailattachment.cpp
src/smtp/qxtmailmessage.cpp
src/smtp/qxtsmtp.cpp
)
set(servatrice_RESOURCES servatrice.qrc)

View file

@ -0,0 +1,8 @@
-- Servatrice db migration from version 1 to version 2
-- FIX #1281
CREATE TABLE IF NOT EXISTS `cockatrice_activation_emails` (
`name` varchar(35) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
UPDATE cockatrice_schema_version SET version=2 WHERE version=1;

View file

@ -90,11 +90,11 @@ allowpunctuationprefix=false
[smtp]
; Connectin type: currently supported method are "tcp", "ssl" and "tls"
; Connectin type: currently supported method are "tcp" and "ssl"; tls is autodetected if available
connection=tcp
; Auth type: currently supported method are "plain" and "login"
auth=plain
; Accept all certificates: in ssl mode, enable this if your server is using an invalid/self signed certificate
acceptallcerts=false;
; Hostname or IP addres of the smtp server
host=localhost

View file

@ -205,3 +205,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_log` (
KEY `target_id` (`target_id`),
KEY `target_name` (`target_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `cockatrice_activation_emails` (
`name` varchar(35) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

View file

@ -29,6 +29,7 @@
#include "server_logger.h"
#include "settingscache.h"
#include "signalhandler.h"
#include "smtpclient.h"
#include "rng_sfmt.h"
#include "version_string.h"
#include <google/protobuf/stubs/common.h>
@ -38,6 +39,7 @@ ServerLogger *logger;
QThread *loggerThread;
SettingsCache *settingsCache;
SignalHandler *signalhandler;
SmtpClient *smtpClient;
/* Prototypes */
@ -181,6 +183,8 @@ int main(int argc, char *argv[])
testRNG();
if (testHashFunction)
testHash();
smtpClient = new SmtpClient();
Servatrice *server = new Servatrice();
QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection);
@ -200,6 +204,7 @@ int main(int argc, char *argv[])
std::cerr << "-------------------------" << std::endl;
}
delete smtpClient;
delete rng;
delete signalhandler;
delete settingsCache;

View file

@ -4,9 +4,11 @@
class ServerLogger;
class QThread;
class SettingsCache;
class SmtpClient;
extern ServerLogger *logger;
extern QThread *loggerThread;
extern SettingsCache *settingsCache;
extern SmtpClient *smtpClient;
#endif

View file

@ -34,6 +34,7 @@
#include "server_logger.h"
#include "main.h"
#include "decklist.h"
#include "smtpclient.h"
#include "pb/event_server_message.pb.h"
#include "pb/event_server_shutdown.pb.h"
#include "pb/event_connection_closed.pb.h"
@ -473,6 +474,32 @@ void Servatrice::statusUpdate()
query->bindValue(":tx", tx);
query->bindValue(":rx", rx);
servatriceDatabaseInterface->execSqlQuery(query);
// send activation emails
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
if (registrationEnabled && requireEmailForRegistration)
{
QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name");
if (!servatriceDatabaseInterface->execSqlQuery(query))
return;
QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name");
while (query->next()) {
const QString userName = query->value(0).toString();
const QString emailAddress = query->value(1).toString();
const QString token = query->value(2).toString();
if(smtpClient->enqueueActivationTokenMail(userName, emailAddress, token))
{
queryDelete->bindValue(":name", userName);
servatriceDatabaseInterface->execSqlQuery(queryDelete);
}
}
smtpClient->sendAllEmails();
}
}
void Servatrice::scheduleShutdown(const QString &reason, int minutes)

View file

@ -9,7 +9,7 @@
#include "server.h"
#include "server_database_interface.h"
#define DATABASE_SCHEMA_VERSION 1
#define DATABASE_SCHEMA_VERSION 2
class Servatrice;

View file

@ -59,8 +59,6 @@
#include "pb/serverinfo_user.pb.h"
#include "pb/serverinfo_deckstorage.pb.h"
#include "smtp/SmtpMime"
#include "version_string.h"
#include <string>
#include <iostream>
@ -854,8 +852,11 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R
qDebug() << "Accepted register command for user: " << userName;
if(requireEmailForRegistration)
{
// TODO call a slot on another thread to send email
sendActivationTokenMail(userName, emailAddress, token);
QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)");
query->bindValue(":name", userName);
if (!sqlInterface->execSqlQuery(query))
return Response::RespRegistrationFailed;
return Response::RespRegistrationAcceptedNeedsActivation;
} else {
return Response::RespRegistrationAccepted;
@ -865,88 +866,6 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R
}
}
bool ServerSocketInterface::sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token)
{
QString tmp = settingsCache->value("smtp/connection", "tcp").toString();
SmtpClient::ConnectionType connection = SmtpClient::TcpConnection;
if(tmp == "ssl")
connection = SmtpClient::SslConnection;
else if(tmp == "tls")
connection = SmtpClient::TlsConnection;
tmp = settingsCache->value("smtp/auth", "plain").toString();
SmtpClient::AuthMethod auth = SmtpClient::AuthPlain;
if(tmp == "login")
auth = SmtpClient::AuthLogin;
QString host = settingsCache->value("smtp/host", "localhost").toString();
int port = settingsCache->value("smtp/port", 25).toInt();
QString username = settingsCache->value("smtp/username", "").toString();
QString password = settingsCache->value("smtp/password", "").toString();
QString email = settingsCache->value("smtp/email", "").toString();
QString name = settingsCache->value("smtp/name", "").toString();
QString subject = settingsCache->value("smtp/subject", "").toString();
QString body = settingsCache->value("smtp/body", "").toString();
if(email.isEmpty())
{
qDebug() << "[MAIL] Missing email field" << endl;
return false;
}
if(body.isEmpty())
{
qDebug() << "[MAIL] Missing body field" << endl;
return false;
}
if(recipient.isEmpty())
{
qDebug() << "[MAIL] Missing recipient field" << endl;
return false;
}
if(token.isEmpty())
{
qDebug() << "[MAIL] Missing token field" << endl;
return false;
}
SmtpClient smtp(host, port, connection);
smtp.setUser(username);
smtp.setPassword(password);
smtp.setAuthMethod(auth);
MimeMessage message;
EmailAddress sender(email, name);
message.setSender(&sender);
EmailAddress to(recipient, nickname);
message.addRecipient(&to);
message.setSubject(subject);
MimeText text;
text.setText(body.replace("%username", nickname).replace("%token", token));
message.addPart(&text);
// Now we can send the mail
if (!smtp.connectToHost()) {
qDebug() << "[MAIL] Failed to connect to host" << host << "on port" << port;
return false;
}
if (!smtp.login()) {
qDebug() << "[MAIL] Failed to login as " << username;
return false;
}
if (!smtp.sendMail(message)) {
qDebug() << "[MAIL] Failed to send mail to " << recipient;
return false;
}
smtp.quit();
qDebug() << "[MAIL] Sent activation email to " << recipient << " for nickname " << nickname;
return true;
}
bool ServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress)
{
// TODO: implement

View file

@ -108,8 +108,6 @@ private:
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc);
bool sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token);
public:
ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0);
~ServerSocketInterface();

View file

@ -1,31 +0,0 @@
/*
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 SMTPMIME_H
#define SMTPMIME_H
#include "smtpclient.h"
#include "mimepart.h"
#include "mimehtml.h"
#include "mimeattachment.h"
#include "mimemessage.h"
#include "mimetext.h"
#include "mimeinlinefile.h"
#include "mimefile.h"
#endif // SMTPMIME_H

View file

@ -1,60 +0,0 @@
/*
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 "emailaddress.h"
/* [1] Constructors and Destructors */
EmailAddress::EmailAddress(const QString & address, const QString & name)
{
this->address = address;
this->name = name;
}
EmailAddress::~EmailAddress()
{
}
/* [1] --- */
/* [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
{
return name;
}
const QString & EmailAddress::getAddress() const
{
return address;
}
/* [2] --- */

View file

@ -1,61 +0,0 @@
/*
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 EMAILADDRESS_H
#define EMAILADDRESS_H
#include <QObject>
#include "smtpexports.h"
class SMTP_EXPORT EmailAddress : public QObject
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
EmailAddress();
EmailAddress(const QString & address, const QString & name="");
~EmailAddress();
/* [1] --- */
/* [2] Getters and Setters */
void setName(const QString & name);
void setAddress(const QString & address);
const QString & getName() const;
const QString & getAddress() const;
/* [2] --- */
private:
/* [3] Private members */
QString name;
QString address;
/* [3] --- */
};
#endif // EMAILADDRESS_H

View file

@ -1,50 +0,0 @@
/*
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 "mimeattachment.h"
#include <QFileInfo>
/* [1] Constructors and Destructors */
MimeAttachment::MimeAttachment(QFile *file)
: MimeFile(file)
{
}
MimeAttachment::MimeAttachment(const QByteArray& stream, const QString& fileName): MimeFile(stream, fileName)
{
}
MimeAttachment::~MimeAttachment()
{
}
/* [1] --- */
/* [2] Protected methods */
void MimeAttachment::prepare()
{
this->header += "Content-disposition: attachment\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [2] --- */

View file

@ -1,51 +0,0 @@
/*
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 MIMEATTACHMENT_H
#define MIMEATTACHMENT_H
#include <QFile>
#include "mimepart.h"
#include "mimefile.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeAttachment : public MimeFile
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeAttachment(QFile* file);
MimeAttachment(const QByteArray& stream, const QString& fileName);
~MimeAttachment();
/* [1] --- */
protected:
/* [2] Protected methods */
virtual void prepare();
/* [2] --- */
};
#endif // MIMEATTACHMENT_H

View file

@ -1,66 +0,0 @@
/*
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;
}
void MimeContentFormatter::setMaxLength(int l) {
max_length = l;
}
int MimeContentFormatter::getMaxLength() const {
return max_length;
}

View file

@ -1,43 +0,0 @@
/*
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 "smtpexports.h"
class SMTP_EXPORT MimeContentFormatter : public QObject
{
Q_OBJECT
public:
MimeContentFormatter (int max_length = 76);
void setMaxLength(int l);
int getMaxLength() const;
QString format(const QString &content, bool quotedPrintable = false) const;
protected:
int max_length;
};
#endif // MIMECONTENTFORMATTER_H

View file

@ -1,70 +0,0 @@
/*
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 "mimefile.h"
#include <QFileInfo>
/* [1] Constructors and Destructors */
MimeFile::MimeFile(QFile *file)
{
this->file = file;
this->cType = "application/octet-stream";
this->cName = QFileInfo(*file).fileName();
this->cEncoding = Base64;
}
MimeFile::MimeFile(const QByteArray& stream, const QString& fileName)
{
this->cEncoding = Base64;
this->cType = "application/octet-stream";
this->file = 0;
this->cName = fileName;
this->content = stream;
}
MimeFile::~MimeFile()
{
if (file)
delete file;
}
/* [1] --- */
/* [2] Getters and setters */
/* [2] --- */
/* [3] Protected methods */
void MimeFile::prepare()
{
if (this->file)
{
file->open(QIODevice::ReadOnly);
this->content = file->readAll();
file->close();
}
/* !!! IMPORTANT !!!! */
MimePart::prepare();
}
/* [3] --- */

View file

@ -1,62 +0,0 @@
/*
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 MIMEFILE_H
#define MIMEFILE_H
#include "mimepart.h"
#include <QFile>
#include "smtpexports.h"
class SMTP_EXPORT MimeFile : public MimePart
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeFile(const QByteArray& stream, const QString& fileName);
MimeFile(QFile *f);
~MimeFile();
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
protected:
/* [3] Protected members */
QFile* file;
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEFILE_H

View file

@ -1,57 +0,0 @@
/*
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 "mimehtml.h"
/* [1] Constructors and Destructors */
MimeHtml::MimeHtml(const QString &html) : MimeText(html)
{
this->cType = "text/html";
}
MimeHtml::~MimeHtml() {}
/* [1] --- */
/* [2] Getters and Setters */
void MimeHtml::setHtml(const QString & html)
{
this->text = html;
}
const QString & MimeHtml::getHtml() const
{
return text;
}
/* [2] --- */
/* [3] Protected methods */
void MimeHtml::prepare()
{
/* !!! IMPORTANT !!! */
MimeText::prepare();
}
/* [3] --- */

View file

@ -1,61 +0,0 @@
/*
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 MIMEHTML_H
#define MIMEHTML_H
#include "mimetext.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeHtml : public MimeText
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeHtml(const QString &html = "");
~MimeHtml();
/* [1] --- */
/* [2] Getters and Setters */
void setHtml(const QString & html);
const QString& getHtml() const;
/* [2] --- */
protected:
/* [3] Protected members */
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEHTML_H

View file

@ -1,52 +0,0 @@
/*
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 "mimeinlinefile.h"
/* [1] Constructors and Destructors */
MimeInlineFile::MimeInlineFile(QFile *f)
: MimeFile(f)
{
}
MimeInlineFile::~MimeInlineFile()
{}
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
/* [3] Protected methods */
void MimeInlineFile::prepare()
{
this->header += "Content-Disposition: inline\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [3] --- */

View file

@ -1,56 +0,0 @@
/*
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 MIMEINLINEFILE_H
#define MIMEINLINEFILE_H
#include "mimefile.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeInlineFile : public MimeFile
{
public:
/* [1] Constructors and Destructors */
MimeInlineFile(QFile *f);
~MimeInlineFile();
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
protected:
/* [3] Protected members */
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEINLINEFILE_H

View file

@ -1,257 +0,0 @@
/*
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 "mimemessage.h"
#include <QDateTime>
#include "quotedprintable.h"
#include <typeinfo>
/* [1] Constructors and Destructors */
MimeMessage::MimeMessage(bool createAutoMimeContent) :
hEncoding(MimePart::_8Bit)
{
if (createAutoMimeContent)
this->content = new MimeMultiPart();
autoMimeContentCreated = createAutoMimeContent;
}
MimeMessage::~MimeMessage()
{
if (this->autoMimeContentCreated)
{
this->autoMimeContentCreated = false;
delete (this->content);
}
}
/* [1] --- */
/* [2] Getters and Setters */
MimePart& MimeMessage::getContent() {
return *content;
}
void MimeMessage::setContent(MimePart *content) {
if (this->autoMimeContentCreated)
{
this->autoMimeContentCreated = false;
delete (this->content);
}
this->content = content;
}
void MimeMessage::setSender(EmailAddress* e)
{
this->sender = e;
}
void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type)
{
switch (type)
{
case To:
recipientsTo << rcpt;
break;
case Cc:
recipientsCc << rcpt;
break;
case Bcc:
recipientsBcc << rcpt;
break;
}
}
void MimeMessage::addTo(EmailAddress* rcpt) {
this->recipientsTo << rcpt;
}
void MimeMessage::addCc(EmailAddress* rcpt) {
this->recipientsCc << rcpt;
}
void MimeMessage::addBcc(EmailAddress* rcpt) {
this->recipientsBcc << rcpt;
}
void MimeMessage::setSubject(const QString & subject)
{
this->subject = subject;
}
void MimeMessage::addPart(MimePart *part)
{
if (typeid(*content) == typeid(MimeMultiPart)) {
((MimeMultiPart*) content)->addPart(part);
};
}
void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc)
{
this->hEncoding = hEnc;
}
const EmailAddress & MimeMessage::getSender() const
{
return *sender;
}
const QList<EmailAddress*> & MimeMessage::getRecipients(RecipientType type) const
{
switch (type)
{
default:
case To:
return recipientsTo;
case Cc:
return recipientsCc;
case Bcc:
return recipientsBcc;
}
}
const QString & MimeMessage::getSubject() const
{
return subject;
}
const QList<MimePart*> & MimeMessage::getParts() const
{
if (typeid(*content) == typeid(MimeMultiPart)) {
return ((MimeMultiPart*) content)->getParts();
}
else {
QList<MimePart*> *res = new QList<MimePart*>();
res->append(content);
return *res;
}
}
/* [2] --- */
/* [3] Public Methods */
QString MimeMessage::toString()
{
QString mime;
/* =========== 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";
/* ---------------------------------- */
/* ------- Recipients / To ---------- */
mime += "To:";
QList<EmailAddress*>::iterator it; int i;
for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++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() + ">";
}
mime += "\r\n";
/* ---------------------------------- */
/* ------- Recipients / Cc ---------- */
if (recipientsCc.size() != 0) {
mime += "Cc:";
}
for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++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 (recipientsCc.size() != 0) {
mime += "\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;
}
/* ---------------------------------- */
mime += "\r\n";
mime += "MIME-Version: 1.0\r\n";
mime += content->toString();
return mime;
}
/* [3] --- */

View file

@ -1,92 +0,0 @@
/*
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 MIMEMESSAGE_H
#define MIMEMESSAGE_H
#include "mimepart.h"
#include "mimemultipart.h"
#include "emailaddress.h"
#include <QList>
#include "smtpexports.h"
class SMTP_EXPORT MimeMessage : public QObject
{
public:
enum RecipientType {
To, // primary
Cc, // carbon copy
Bcc // blind carbon copy
};
/* [1] Constructors and Destructors */
MimeMessage(bool createAutoMimeConent = true);
~MimeMessage();
/* [1] --- */
/* [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 addPart(MimePart* part);
void setHeaderEncoding(MimePart::Encoding);
const EmailAddress & getSender() const;
const QList<EmailAddress*> & getRecipients(RecipientType type = To) const;
const QString & getSubject() const;
const QList<MimePart*> & getParts() const;
MimePart& getContent();
void setContent(MimePart *content);
/* [2] --- */
/* [3] Public methods */
virtual QString toString();
/* [3] --- */
protected:
/* [4] Protected members */
EmailAddress* sender;
QList<EmailAddress*> recipientsTo, recipientsCc, recipientsBcc;
QString subject;
MimePart *content;
bool autoMimeContentCreated;
MimePart::Encoding hEncoding;
/* [4] --- */
};
#endif // MIMEMESSAGE_H

View file

@ -1,78 +0,0 @@
/*
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 "mimemultipart.h"
#include <QTime>
#include <QCryptographicHash>
const QString MULTI_PART_NAMES[] = {
"multipart/mixed", // Mixed
"multipart/digest", // Digest
"multipart/alternative", // Alternative
"multipart/related", // Related
"multipart/report", // Report
"multipart/signed", // Signed
"multipart/encrypted" // Encrypted
};
MimeMultiPart::MimeMultiPart(MultiPartType type)
{
this->type = type;
this->cType = MULTI_PART_NAMES[this->type];
this->cEncoding = _8Bit;
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(QByteArray().append(qrand()));
cBoundary = md5.result().toHex();
}
MimeMultiPart::~MimeMultiPart() {
}
void MimeMultiPart::addPart(MimePart *part) {
parts.append(part);
}
const QList<MimePart*> & MimeMultiPart::getParts() const {
return parts;
}
void MimeMultiPart::prepare() {
QList<MimePart*>::iterator it;
content = "";
for (it = parts.begin(); it != parts.end(); it++) {
content += "--" + cBoundary + "\r\n";
(*it)->prepare();
content += (*it)->toString();
};
content += "--" + cBoundary + "--\r\n";
MimePart::prepare();
}
void MimeMultiPart::setMimeType(const MultiPartType type) {
this->type = type;
this->cType = MULTI_PART_NAMES[type];
}
MimeMultiPart::MultiPartType MimeMultiPart::getMimeType() const {
return type;
}

View file

@ -1,75 +0,0 @@
/*
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 MIMEMULTIPART_H
#define MIMEMULTIPART_H
#include "mimepart.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeMultiPart : public MimePart
{
Q_OBJECT
public:
/* [0] Enums */
enum MultiPartType {
Mixed = 0, // RFC 2046, section 5.1.3
Digest = 1, // RFC 2046, section 5.1.5
Alternative = 2, // RFC 2046, section 5.1.4
Related = 3, // RFC 2387
Report = 4, // RFC 6522
Signed = 5, // RFC 1847, section 2.1
Encrypted = 6 // RFC 1847, section 2.2
};
/* [0] --- */
/* [1] Constructors and Destructors */
MimeMultiPart(const MultiPartType type = Related);
~MimeMultiPart();
/* [1] --- */
/* [2] Getters and Setters */
void setMimeType(const MultiPartType type);
MultiPartType getMimeType() const;
const QList<MimePart*> & getParts() const;
/* [2] --- */
/* [3] Public methods */
void addPart(MimePart *part);
virtual void prepare();
/* [3] --- */
protected:
QList< MimePart* > parts;
MultiPartType type;
};
#endif // MIMEMULTIPART_H

View file

@ -1,214 +0,0 @@
/*
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 "mimepart.h"
#include "quotedprintable.h"
/* [1] Constructors and Destructors */
MimePart::MimePart()
{
cEncoding = _7Bit;
prepared = false;
cBoundary = "";
}
MimePart::~MimePart()
{
return;
}
/* [1] --- */
/* [2] Getters and Setters */
void MimePart::setContent(const QByteArray & content)
{
this->content = content;
}
void MimePart::setHeader(const QString & header)
{
this->header = header;
}
void MimePart::addHeaderLine(const QString & line)
{
this->header += line + "\r\n";
}
const QString& MimePart::getHeader() const
{
return header;
}
const QByteArray& MimePart::getContent() const
{
return content;
}
void MimePart::setContentId(const QString & cId)
{
this->cId = cId;
}
const QString & MimePart::getContentId() const
{
return this->cId;
}
void MimePart::setContentName(const QString & cName)
{
this->cName = cName;
}
const QString & MimePart::getContentName() const
{
return this->cName;
}
void MimePart::setContentType(const QString & cType)
{
this->cType = cType;
}
const QString & MimePart::getContentType() const
{
return this->cType;
}
void MimePart::setCharset(const QString & charset)
{
this->cCharset = charset;
}
const QString & MimePart::getCharset() const
{
return this->cCharset;
}
void MimePart::setEncoding(Encoding enc)
{
this->cEncoding = enc;
}
MimePart::Encoding MimePart::getEncoding() const
{
return this->cEncoding;
}
MimeContentFormatter& MimePart::getContentFormatter()
{
return this->formatter;
}
/* [2] --- */
/* [3] Public methods */
QString MimePart::toString()
{
if (!prepared)
prepare();
return mimeString;
}
/* [3] --- */
/* [4] Protected methods */
void MimePart::prepare()
{
mimeString = QString();
/* === Header Prepare === */
/* Content-Type */
mimeString.append("Content-Type: ").append(cType);
if (cName != "")
mimeString.append("; name=\"").append(cName).append("\"");
if (cCharset != "")
mimeString.append("; charset=").append(cCharset);
if (cBoundary != "")
mimeString.append("; boundary=").append(cBoundary);
mimeString.append("\r\n");
/* ------------ */
/* Content-Transfer-Encoding */
mimeString.append("Content-Transfer-Encoding: ");
switch (cEncoding)
{
case _7Bit:
mimeString.append("7bit\r\n");
break;
case _8Bit:
mimeString.append("8bit\r\n");
break;
case Base64:
mimeString.append("base64\r\n");
break;
case QuotedPrintable:
mimeString.append("quoted-printable\r\n");
break;
}
/* ------------------------ */
/* Content-Id */
if (cId != NULL)
mimeString.append("Content-ID: <").append(cId).append(">\r\n");
/* ---------- */
/* Addition header lines */
mimeString.append(header).append("\r\n");
/* ------------------------- */
/* === End of Header Prepare === */
/* === Content === */
switch (cEncoding)
{
case _7Bit:
mimeString.append(QString(content).toLatin1());
break;
case _8Bit:
mimeString.append(content);
break;
case Base64:
mimeString.append(formatter.format(content.toBase64()));
break;
case QuotedPrintable:
mimeString.append(formatter.format(QuotedPrintable::encode(content), true));
break;
}
mimeString.append("\r\n");
/* === End of Content === */
prepared = true;
}
/* [4] --- */

View file

@ -1,114 +0,0 @@
/*
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 MIMEPART_H
#define MIMEPART_H
#include <QObject>
#include "mimecontentformatter.h"
#include "smtpexports.h"
class SMTP_EXPORT MimePart : public QObject
{
Q_OBJECT
public:
/* [0] Enumerations */
enum Encoding {
_7Bit,
_8Bit,
Base64,
QuotedPrintable
};
/* [0] --- */
/* [1] Constructors and Destructors */
MimePart();
~MimePart();
/* [1] --- */
/* [2] Getters and Setters */
const QString& getHeader() const;
const QByteArray& getContent() const;
void setContent(const QByteArray & content);
void setHeader(const QString & header);
void addHeaderLine(const QString & line);
void setContentId(const QString & cId);
const QString & getContentId() const;
void setContentName(const QString & cName);
const QString & getContentName() const;
void setContentType(const QString & cType);
const QString & getContentType() const;
void setCharset(const QString & charset);
const QString & getCharset() const;
void setEncoding(Encoding enc);
Encoding getEncoding() const;
MimeContentFormatter& getContentFormatter();
/* [2] --- */
/* [3] Public methods */
virtual QString toString();
virtual void prepare();
/* [3] --- */
protected:
/* [4] Protected members */
QString header;
QByteArray content;
QString cId;
QString cName;
QString cType;
QString cCharset;
QString cBoundary;
Encoding cEncoding;
QString mimeString;
bool prepared;
MimeContentFormatter formatter;
/* [4] --- */
};
#endif // MIMEPART_H

View file

@ -1,62 +0,0 @@
/*
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 "mimetext.h"
/* [1] Constructors and Destructors */
MimeText::MimeText(const QString &txt)
{
this->text = txt;
this->cType = "text/plain";
this->cCharset = "utf-8";
this->cEncoding = _8Bit;
}
MimeText::~MimeText() { }
/* [1] --- */
/* [2] Getters and Setters */
void MimeText::setText(const QString & text)
{
this->text = text;
}
const QString & MimeText::getText() const
{
return text;
}
/* [2] --- */
/* [3] Protected Methods */
void MimeText::prepare()
{
this->content.clear();
this->content.append(text);
/* !!! IMPORTANT !!! */
MimePart::prepare();
}
/* [3] --- */

View file

@ -1,62 +0,0 @@
/*
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 MIMETEXT_H
#define MIMETEXT_H
#include "mimepart.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeText : public MimePart
{
public:
/* [1] Constructors and Destructors */
MimeText(const QString &text = "");
~MimeText();
/* [1] --- */
/* [2] Getters and Setters*/
void setText(const QString & text);
const QString & getText() const;
/* [2] --- */
protected:
/* [3] Protected members */
QString text;
/* [3] --- */
/* [4] Protected methods */
void prepare();
/* [4] --- */
};
#endif // MIMETEXT_H

View file

@ -1,69 +0,0 @@
/*
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 "quotedprintable.h"
QString QuotedPrintable::encode(const QByteArray &input)
{
QString output;
char byte;
const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0; i < input.length() ; ++i)
{
byte = input[i];
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)]);
}
}
return output;
}
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};
QByteArray output;
for (int i = 0; i < input.length(); ++i)
{
if (input.at(i).toLatin1() == '=')
{
output.append((hexVal[input.at(i + 1).toLatin1() - '0'] << 4) + hexVal[input.at(i + 2).toLatin1() - '0']);
i += 2;
}
else
{
output.append(input.at(i).toLatin1());
}
}
return output;
}

View file

@ -1,39 +0,0 @@
/*
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 QUOTEDPRINTABLE_H
#define QUOTEDPRINTABLE_H
#include <QObject>
#include <QByteArray>
#include "smtpexports.h"
class SMTP_EXPORT QuotedPrintable : public QObject
{
Q_OBJECT
public:
static QString encode(const QByteArray &input);
static QByteArray decode(const QString &input);
private:
QuotedPrintable();
};
#endif // QUOTEDPRINTABLE_H

View file

@ -0,0 +1,207 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtCore module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTGLOBAL_H
#define QXTGLOBAL_H
#include <QtGlobal>
#define QXT_VERSION 0x000602
#define QXT_VERSION_STR "0.6.2"
//--------------------------global macros------------------------------
#ifndef QXT_NO_MACROS
#endif // QXT_NO_MACROS
//--------------------------export macros------------------------------
#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_CORE)
# define QXT_CORE_EXPORT Q_DECL_EXPORT
# else
# define QXT_CORE_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_CORE_EXPORT
#endif // BUILD_QXT_CORE
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_GUI)
# define QXT_GUI_EXPORT Q_DECL_EXPORT
# else
# define QXT_GUI_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_GUI_EXPORT
#endif // BUILD_QXT_GUI
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_NETWORK)
# define QXT_NETWORK_EXPORT Q_DECL_EXPORT
# else
# define QXT_NETWORK_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_NETWORK_EXPORT
#endif // BUILD_QXT_NETWORK
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_SQL)
# define QXT_SQL_EXPORT Q_DECL_EXPORT
# else
# define QXT_SQL_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_SQL_EXPORT
#endif // BUILD_QXT_SQL
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_WEB)
# define QXT_WEB_EXPORT Q_DECL_EXPORT
# else
# define QXT_WEB_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_WEB_EXPORT
#endif // BUILD_QXT_WEB
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_BERKELEY)
# define QXT_BERKELEY_EXPORT Q_DECL_EXPORT
# else
# define QXT_BERKELEY_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_BERKELEY_EXPORT
#endif // BUILD_QXT_BERKELEY
#if !defined(QXT_STATIC)
# if defined(BUILD_QXT_ZEROCONF)
# define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT
# else
# define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_ZEROCONF_EXPORT
#endif // QXT_ZEROCONF_EXPORT
#if defined BUILD_QXT_CORE || defined BUILD_QXT_GUI || defined BUILD_QXT_SQL || defined BUILD_QXT_NETWORK || defined BUILD_QXT_WEB || defined BUILD_QXT_BERKELEY || defined BUILD_QXT_ZEROCONF
# define BUILD_QXT
#endif
QXT_CORE_EXPORT const char* qxtVersion();
#ifndef QT_BEGIN_NAMESPACE
#define QT_BEGIN_NAMESPACE
#endif
#ifndef QT_END_NAMESPACE
#define QT_END_NAMESPACE
#endif
#ifndef QT_FORWARD_DECLARE_CLASS
#define QT_FORWARD_DECLARE_CLASS(Class) class Class;
#endif
/****************************************************************************
** This file is derived from code bearing the following notice:
** The sole author of this file, Adam Higerd, has explicitly disclaimed all
** copyright interest and protection for the content within. This file has
** been placed in the public domain according to United States copyright
** statute and case law. In jurisdictions where this public domain dedication
** is not legally recognized, anyone who receives a copy of this file is
** permitted to use, modify, duplicate, and redistribute this file, in whole
** or in part, with no restrictions or conditions. In these jurisdictions,
** this file shall be copyright (C) 2006-2008 by Adam Higerd.
****************************************************************************/
#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface<PUB, PUB##Private> qxt_d;
#define QXT_DECLARE_PUBLIC(PUB) friend class PUB;
#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this);
#define QXT_D(PUB) PUB##Private& d = qxt_d()
#define QXT_P(PUB) PUB& p = qxt_p()
template <typename PUB>
class QxtPrivate
{
public:
virtual ~QxtPrivate()
{}
inline void QXT_setPublic(PUB* pub)
{
qxt_p_ptr = pub;
}
protected:
inline PUB& qxt_p()
{
return *qxt_p_ptr;
}
inline const PUB& qxt_p() const
{
return *qxt_p_ptr;
}
private:
PUB* qxt_p_ptr;
};
template <typename PUB, typename PVT>
class QxtPrivateInterface
{
friend class QxtPrivate<PUB>;
public:
QxtPrivateInterface()
{
pvt = new PVT;
}
~QxtPrivateInterface()
{
delete pvt;
}
inline void setPublic(PUB* pub)
{
pvt->QXT_setPublic(pub);
}
inline PVT& operator()()
{
return *static_cast<PVT*>(pvt);
}
inline const PVT& operator()() const
{
return *static_cast<PVT*>(pvt);
}
private:
QxtPrivateInterface(const QxtPrivateInterface&) { }
QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
QxtPrivate<PUB>* pvt;
};
#endif // QXT_GLOBAL

View file

@ -0,0 +1,211 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtCore module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#include "qxthmac.h"
#include <QtGlobal>
#if QT_VERSION >= 0x040300
/*
\class QxtHmac
\inmodule QxtCore
\brief The QxtHmac class calculates keyed-Hash Message Authentication Codes
HMAC is a well-known algorithm for generating a message authentication code (MAC) that can be used to verify the
integrity and authenticity of a message.
This class requires Qt 4.3.0 or greater.
To verify a message, the sender creates a MAC using a key, which is a secret known only to the sender and recipient,
and the content of the message. This MAC is then sent along with the message. The recipient then creates another MAC
using the shared key and the content of the message. If the two codes match, the message is verified.
HMAC has been used as a password encryption scheme. The final output of the HMAC algorithm depends on the shared key
and an inner hash. This inner hash is generated from the message content and the key. To use HMAC as a password
scheme, the key should be the username; the message should be the user's password. The authenticating party (for
instance, a login server) only needs to store this inner hash generated by the innerHash() function. When requesting
authentication, the user calculates a HMAC using this key and message and sends his username and this HMAC to the
authenticator. The authenticator can then use verify() using the provided HMAC and the stored inner hash. When using
this scheme, the password is never stored or transmitted in plain text.
*/
#ifndef QXT_DOXYGEN_RUN
class QxtHmacPrivate : public QxtPrivate<QxtHmac>
{
public:
QXT_DECLARE_PUBLIC(QxtHmac)
QxtHmacPrivate() : ohash(0), ihash(0) {}
~QxtHmacPrivate()
{
// deleting NULL is safe, so no tests are needed here
delete ohash;
delete ihash;
}
QCryptographicHash* ohash;
QCryptographicHash* ihash;
QByteArray opad, ipad, result;
QCryptographicHash::Algorithm algorithm;
};
#endif
/*!
* Constructs a QxtHmac object using the specified algorithm.
*/
QxtHmac::QxtHmac(QCryptographicHash::Algorithm algorithm)
{
QXT_INIT_PRIVATE(QxtHmac);
qxt_d().ohash = new QCryptographicHash(algorithm);
qxt_d().ihash = new QCryptographicHash(algorithm);
qxt_d().algorithm = algorithm;
}
/*!
* Sets the shared secret key for the message authentication code.
*
* Any data that had been processed using addData() will be discarded.
*/
void QxtHmac::setKey(QByteArray key)
{
// We make the assumption that all hashes use a 512-bit block size; as of Qt 4.4.0 this is true of all supported hash functions
QxtHmacPrivate* d = &qxt_d();
d->opad = QByteArray(64, 0x5c);
d->ipad = QByteArray(64, 0x36);
if (key.size() > 64)
{
key = QCryptographicHash::hash(key, d->algorithm);
}
for (int i = key.size() - 1; i >= 0; --i)
{
d->opad[i] = d->opad[i] ^ key[i];
d->ipad[i] = d->ipad[i] ^ key[i];
}
reset();
}
/*!
* Resets the object.
*
* Any data that had been processed using addData() will be discarded.
* The key, if set, will be preserved.
*/
void QxtHmac::reset()
{
QxtHmacPrivate* d = &qxt_d();
d->ihash->reset();
d->ihash->addData(d->ipad);
}
/*!
* Returns the inner hash of the HMAC function.
*
* This hash can be stored in lieu of the shared secret on the authenticating side
* and used for verifying an HMAC code. When used in this manner, HMAC can be used
* to provide a form of secure password authentication. See the documentation above
* for details.
*/
QByteArray QxtHmac::innerHash() const
{
return qxt_d().ihash->result();
}
/*!
* Returns the authentication code for the message.
*/
QByteArray QxtHmac::result()
{
QxtHmacPrivate* d = &qxt_d();
Q_ASSERT(d->opad.size());
if (d->result.size())
return d->result;
d->ohash->reset();
d->ohash->addData(d->opad);
d->ohash->addData(innerHash());
d->result = d->ohash->result();
return d->result;
}
/*!
* Verifies the authentication code against a known inner hash.
*
* \sa innerHash()
*/
bool QxtHmac::verify(const QByteArray& otherInner)
{
result(); // populates d->result
QxtHmacPrivate* d = &qxt_d();
d->ohash->reset();
d->ohash->addData(d->opad);
d->ohash->addData(otherInner);
return d->result == d->ohash->result();
}
/*!
* Adds the provided data to the message to be authenticated.
*/
void QxtHmac::addData(const char* data, int length)
{
Q_ASSERT(qxt_d().opad.size());
qxt_d().ihash->addData(data, length);
qxt_d().result.clear();
}
/*!
* Adds the provided data to the message to be authenticated.
*/
void QxtHmac::addData(const QByteArray& data)
{
addData(data.constData(), data.size());
}
/*!
* Returns the HMAC of the provided data using the specified key and hashing algorithm.
*/
QByteArray QxtHmac::hash(const QByteArray& key, const QByteArray& data, Algorithm algorithm)
{
QxtHmac hmac(algorithm);
hmac.setKey(key);
hmac.addData(data);
return hmac.result();
}
/*!
* Verifies a HMAC against a known key and inner hash using the specified hashing algorithm.
*/
bool QxtHmac::verify(const QByteArray& key, const QByteArray& hmac, const QByteArray& inner, Algorithm algorithm)
{
QxtHmac calc(algorithm);
calc.setKey(key);
QxtHmacPrivate* d = &calc.qxt_d();
d->ohash->reset();
d->ohash->addData(d->opad);
d->ohash->addData(inner);
return hmac == d->ohash->result();
}
#endif

View file

@ -0,0 +1,64 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtCore module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTHMAC_H
#define QXTHMAC_H
#include <QtGlobal>
#if QT_VERSION < 0x040300
# warning QxtHmac requires Qt 4.3.0 or greater
#else
#include <QCryptographicHash>
#include "qxtglobal.h"
class QxtHmacPrivate;
class QXT_CORE_EXPORT QxtHmac
{
public:
typedef QCryptographicHash::Algorithm Algorithm;
QxtHmac(QCryptographicHash::Algorithm algorithm);
void setKey(QByteArray key);
void reset();
void addData(const char* data, int length);
void addData(const QByteArray& data);
QByteArray innerHash() const;
QByteArray result();
bool verify(const QByteArray& otherInner);
static QByteArray hash(const QByteArray& key, const QByteArray& data, Algorithm algorithm);
static bool verify(const QByteArray& key, const QByteArray& hmac, const QByteArray& inner, Algorithm algorithm);
private:
QXT_DECLARE_PRIVATE(QxtHmac)
};
#endif
#endif

View file

@ -0,0 +1,34 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTMAIL_P_H
#define QXTMAIL_P_H
#include <QByteArray>
#define QXT_MUST_QP(x) (x < char(32) || x > char(126) || x == '=' || x == '?')
QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1,
const QByteArray& prefix = QByteArray());
#endif // QXTMAIL_P_H

View file

@ -0,0 +1,211 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
/*!
* \class QxtMailAttachment
* \inmodule QxtNetwork
* \brief The QxtMailAttachment class represents an attachement to a QxtMailMessage
*/
#include "qxtmailattachment.h"
#include "qxtmail_p.h"
#include <QTextCodec>
#include <QBuffer>
#include <QPointer>
#include <QFile>
#include <QtDebug>
struct QxtMailAttachmentPrivate : public QSharedData
{
QHash<QString, QString> extraHeaders;
QString contentType;
QPointer<QIODevice> content;
bool deleteContent;
QxtMailAttachmentPrivate()
{
content = 0;
deleteContent = false;
contentType = "text/plain";
}
~QxtMailAttachmentPrivate()
{
if (deleteContent && content)
content->deleteLater();
deleteContent = false;
content = 0;
}
};
QxtMailAttachment::QxtMailAttachment()
{
qxt_d = new QxtMailAttachmentPrivate;
}
QxtMailAttachment::QxtMailAttachment(const QxtMailAttachment& other) : qxt_d(other.qxt_d)
{
// trivial copy constructor
}
QxtMailAttachment::QxtMailAttachment(const QByteArray& content, const QString& contentType)
{
qxt_d = new QxtMailAttachmentPrivate;
setContentType(contentType);
setContent(content);
}
QxtMailAttachment::QxtMailAttachment(QIODevice* content, const QString& contentType)
{
qxt_d = new QxtMailAttachmentPrivate;
setContentType(contentType);
setContent(content);
}
QxtMailAttachment& QxtMailAttachment::operator=(const QxtMailAttachment & other)
{
qxt_d = other.qxt_d;
return *this;
}
QxtMailAttachment::~QxtMailAttachment()
{
// trivial destructor
}
QIODevice* QxtMailAttachment::content() const
{
return qxt_d->content;
}
void QxtMailAttachment::setContent(const QByteArray& content)
{
if (qxt_d->deleteContent && qxt_d->content)
qxt_d->content->deleteLater();
qxt_d->content = new QBuffer;
static_cast<QBuffer*>(qxt_d->content.data())->setData(content);
}
void QxtMailAttachment::setContent(QIODevice* content)
{
if (qxt_d->deleteContent && qxt_d->content)
qxt_d->content->deleteLater();
qxt_d->content = content;
}
bool QxtMailAttachment::deleteContent() const
{
return qxt_d->deleteContent;
}
void QxtMailAttachment::setDeleteContent(bool enable)
{
qxt_d->deleteContent = enable;
}
QString QxtMailAttachment::contentType() const
{
return qxt_d->contentType;
}
void QxtMailAttachment::setContentType(const QString& contentType)
{
qxt_d->contentType = contentType;
}
QHash<QString, QString> QxtMailAttachment::extraHeaders() const
{
return qxt_d->extraHeaders;
}
QString QxtMailAttachment::extraHeader(const QString& key) const
{
return qxt_d->extraHeaders[key.toLower()];
}
bool QxtMailAttachment::hasExtraHeader(const QString& key) const
{
return qxt_d->extraHeaders.contains(key.toLower());
}
void QxtMailAttachment::setExtraHeader(const QString& key, const QString& value)
{
qxt_d->extraHeaders[key.toLower()] = value;
}
void QxtMailAttachment::setExtraHeaders(const QHash<QString, QString>& a)
{
QHash<QString, QString>& headers = qxt_d->extraHeaders;
headers.clear();
foreach(const QString& key, a.keys())
{
headers[key.toLower()] = a[key];
}
}
void QxtMailAttachment::removeExtraHeader(const QString& key)
{
qxt_d->extraHeaders.remove(key.toLower());
}
QByteArray QxtMailAttachment::mimeData()
{
QIODevice* c = content();
if (!c)
{
qWarning() << "QxtMailAttachment::mimeData(): Content not set or already output";
return QByteArray();
}
if (!c->isOpen() && !c->open(QIODevice::ReadOnly))
{
qWarning() << "QxtMailAttachment::mimeData(): Cannot open content for reading";
return QByteArray();
}
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
QByteArray rv = "Content-Type: " + qxt_d->contentType.toLatin1() + "\r\nContent-Transfer-Encoding: base64\r\n";
foreach(const QString& r, qxt_d->extraHeaders.keys())
{
rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1);
}
rv += "\r\n";
while (!c->atEnd())
{
rv += c->read(57).toBase64() + "\r\n";
}
setContent((QIODevice*)0);
return rv;
}
QxtMailAttachment QxtMailAttachment::fromFile(const QString& filename)
{
QxtMailAttachment rv(new QFile(filename));
rv.setDeleteContent(true);
return rv;
}

View file

@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTMAILATTACHMENT_H
#define QXTMAILATTACHMENT_H
#include "qxtglobal.h"
#include <QStringList>
#include <QHash>
#include <QByteArray>
#include <QMetaType>
#include <QSharedDataPointer>
#include <QIODevice>
class QxtMailAttachmentPrivate;
class QXT_NETWORK_EXPORT QxtMailAttachment
{
public:
QxtMailAttachment();
QxtMailAttachment(const QxtMailAttachment& other);
QxtMailAttachment(const QByteArray& content, const QString& contentType = QString("application/octet-stream"));
QxtMailAttachment(QIODevice* content, const QString& contentType = QString("application/octet-stream"));
QxtMailAttachment& operator=(const QxtMailAttachment& other);
~QxtMailAttachment();
static QxtMailAttachment fromFile(const QString& filename);
QIODevice* content() const;
void setContent(const QByteArray& content);
void setContent(QIODevice* content);
bool deleteContent() const;
void setDeleteContent(bool enable);
QString contentType() const;
void setContentType(const QString& contentType);
QHash<QString, QString> extraHeaders() const;
QString extraHeader(const QString&) const;
bool hasExtraHeader(const QString&) const;
void setExtraHeader(const QString& key, const QString& value);
void setExtraHeaders(const QHash<QString, QString>&);
void removeExtraHeader(const QString& key);
QByteArray mimeData();
private:
QSharedDataPointer<QxtMailAttachmentPrivate> qxt_d;
};
Q_DECLARE_TYPEINFO(QxtMailAttachment, Q_MOVABLE_TYPE);
#endif // QXTMAILATTACHMENT_H

View file

@ -0,0 +1,537 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
/*!
* \class QxtMailMessage
* \inmodule QxtNetwork
* \brief The QxtMailMessage class encapsulates an e-mail according to RFC 2822 and related specifications
* TODO: {implicitshared}
*/
#include "qxtmailmessage.h"
#include "qxtmail_p.h"
#include <QTextCodec>
#include <QUuid>
#include <QDir>
#include <QtDebug>
struct QxtMailMessagePrivate : public QSharedData
{
QxtMailMessagePrivate() {}
QxtMailMessagePrivate(const QxtMailMessagePrivate& other)
: QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc),
subject(other.subject), body(other.body), sender(other.sender),
extraHeaders(other.extraHeaders), attachments(other.attachments) {}
QStringList rcptTo, rcptCc, rcptBcc;
QString subject, body, sender;
QHash<QString, QString> extraHeaders;
QHash<QString, QxtMailAttachment> attachments;
mutable QByteArray boundary;
};
QxtMailMessage::QxtMailMessage()
{
qxt_d = new QxtMailMessagePrivate;
}
QxtMailMessage::QxtMailMessage(const QxtMailMessage& other) : qxt_d(other.qxt_d)
{
// trivial copy constructor
}
QxtMailMessage::QxtMailMessage(const QString& sender, const QString& recipient)
{
qxt_d = new QxtMailMessagePrivate;
setSender(sender);
addRecipient(recipient);
}
QxtMailMessage::~QxtMailMessage()
{
// trivial destructor
}
QxtMailMessage& QxtMailMessage::operator=(const QxtMailMessage & other)
{
qxt_d = other.qxt_d;
return *this;
}
QString QxtMailMessage::sender() const
{
return qxt_d->sender;
}
void QxtMailMessage::setSender(const QString& a)
{
qxt_d->sender = a;
}
QString QxtMailMessage::subject() const
{
return qxt_d->subject;
}
void QxtMailMessage::setSubject(const QString& a)
{
qxt_d->subject = a;
}
QString QxtMailMessage::body() const
{
return qxt_d->body;
}
void QxtMailMessage::setBody(const QString& a)
{
qxt_d->body = a;
}
QStringList QxtMailMessage::recipients(QxtMailMessage::RecipientType type) const
{
if (type == Bcc)
return qxt_d->rcptBcc;
if (type == Cc)
return qxt_d->rcptCc;
return qxt_d->rcptTo;
}
void QxtMailMessage::addRecipient(const QString& a, QxtMailMessage::RecipientType type)
{
if (type == Bcc)
qxt_d->rcptBcc.append(a);
else if (type == Cc)
qxt_d->rcptCc.append(a);
else
qxt_d->rcptTo.append(a);
}
void QxtMailMessage::removeRecipient(const QString& a)
{
qxt_d->rcptTo.removeAll(a);
qxt_d->rcptCc.removeAll(a);
qxt_d->rcptBcc.removeAll(a);
}
QHash<QString, QString> QxtMailMessage::extraHeaders() const
{
return qxt_d->extraHeaders;
}
QString QxtMailMessage::extraHeader(const QString& key) const
{
return qxt_d->extraHeaders[key.toLower()];
}
bool QxtMailMessage::hasExtraHeader(const QString& key) const
{
return qxt_d->extraHeaders.contains(key.toLower());
}
void QxtMailMessage::setExtraHeader(const QString& key, const QString& value)
{
qxt_d->extraHeaders[key.toLower()] = value;
}
void QxtMailMessage::setExtraHeaders(const QHash<QString, QString>& a)
{
QHash<QString, QString>& headers = qxt_d->extraHeaders;
headers.clear();
foreach(const QString& key, a.keys())
{
headers[key.toLower()] = a[key];
}
}
void QxtMailMessage::removeExtraHeader(const QString& key)
{
qxt_d->extraHeaders.remove(key.toLower());
}
QHash<QString, QxtMailAttachment> QxtMailMessage::attachments() const
{
return qxt_d->attachments;
}
QxtMailAttachment QxtMailMessage::attachment(const QString& filename) const
{
return qxt_d->attachments[filename];
}
void QxtMailMessage::addAttachment(const QString& filename, const QxtMailAttachment& attach)
{
if (qxt_d->attachments.contains(filename))
{
qWarning() << "QxtMailMessage::addAttachment: " << filename << " already in use";
int i = 1;
while (qxt_d->attachments.contains(filename + "." + QString::number(i)))
{
i++;
}
qxt_d->attachments[filename+"."+QString::number(i)] = attach;
}
else
{
qxt_d->attachments[filename] = attach;
}
}
void QxtMailMessage::removeAttachment(const QString& filename)
{
qxt_d->attachments.remove(filename);
}
QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix)
{
QByteArray rv = "";
QByteArray line = key.toLatin1() + ": ";
if (!prefix.isEmpty()) line += prefix;
if (!value.contains("=?") && latin1->canEncode(value))
{
bool firstWord = true;
foreach(const QByteArray& word, value.toLatin1().split(' '))
{
if (line.size() > 78)
{
rv = rv + line + "\r\n";
line.clear();
}
if (firstWord)
line += word;
else
line += " " + word;
firstWord = false;
}
}
else
{
// The text cannot be losslessly encoded as Latin-1. Therefore, we
// must use quoted-printable or base64 encoding. This is a quick
// heuristic based on the first 100 characters to see which
// encoding to use.
QByteArray utf8 = value.toUtf8();
int ct = utf8.length();
int nonAscii = 0;
for (int i = 0; i < ct && i < 100; i++)
{
if (QXT_MUST_QP(utf8[i])) nonAscii++;
}
if (nonAscii > 20)
{
// more than 20%-ish non-ASCII characters: use base64
QByteArray base64 = utf8.toBase64();
ct = base64.length();
line += "=?utf-8?b?";
for (int i = 0; i < ct; i += 4)
{
if (line.length() > 72)
{
rv += line + "?\r\n";
line = " =?utf-8?b?";
}
line = line + base64.mid(i, 4);
}
}
else
{
// otherwise use Q-encoding
line += "=?utf-8?q?";
for (int i = 0; i < ct; i++)
{
if (line.length() > 73)
{
rv += line + "?\r\n";
line = " =?utf-8?q?";
}
if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ')
{
line += "=" + utf8.mid(i, 1).toHex().toUpper();
}
else
{
line += utf8[i];
}
}
}
line += "?="; // end encoded-word atom
}
return rv + line + "\r\n";
}
QByteArray QxtMailMessage::rfc2822() const
{
// Use quoted-printable if requested
bool useQuotedPrintable = (extraHeader("Content-Transfer-Encoding").toLower() == "quoted-printable");
// Use base64 if requested
bool useBase64 = (extraHeader("Content-Transfer-Encoding").toLower() == "base64");
// Check to see if plain text is ASCII-clean; assume it isn't if QP or base64 was requested
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
bool bodyIsAscii = latin1->canEncode(body()) && !useQuotedPrintable && !useBase64;
QHash<QString, QxtMailAttachment> attach = attachments();
QByteArray rv;
if (!sender().isEmpty() && !hasExtraHeader("From"))
{
rv += qxt_fold_mime_header("From", sender(), latin1);
}
if (!qxt_d->rcptTo.isEmpty())
{
rv += qxt_fold_mime_header("To", qxt_d->rcptTo.join(", "), latin1);
}
if (!qxt_d->rcptCc.isEmpty())
{
rv += qxt_fold_mime_header("Cc", qxt_d->rcptCc.join(", "), latin1);
}
if (!subject().isEmpty())
{
rv += qxt_fold_mime_header("Subject", subject(), latin1);
}
if (!bodyIsAscii)
{
if (!hasExtraHeader("MIME-Version") && !attach.count())
rv += "MIME-Version: 1.0\r\n";
// If no transfer encoding has been requested, guess.
// Heuristic: If >20% of the first 100 characters aren't
// 7-bit clean, use base64, otherwise use Q-P.
if(!bodyIsAscii && !useQuotedPrintable && !useBase64)
{
QString b = body();
int nonAscii = 0;
int ct = b.length();
for (int i = 0; i < ct && i < 100; i++)
{
if (QXT_MUST_QP(b[i])) nonAscii++;
}
useQuotedPrintable = !(nonAscii > 20);
useBase64 = !useQuotedPrintable;
}
}
if (attach.count())
{
if (qxt_d->boundary.isEmpty())
qxt_d->boundary = QUuid::createUuid().toString().toLatin1().replace("{", "").replace("}", "");
if (!hasExtraHeader("MIME-Version"))
rv += "MIME-Version: 1.0\r\n";
if (!hasExtraHeader("Content-Type"))
rv += "Content-Type: multipart/mixed; boundary=" + qxt_d->boundary + "\r\n";
}
else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding"))
{
if (!useQuotedPrintable)
{
// base64
rv += "Content-Transfer-Encoding: base64\r\n";
}
else
{
// quoted-printable
rv += "Content-Transfer-Encoding: quoted-printable\r\n";
}
}
foreach(const QString& r, qxt_d->extraHeaders.keys())
{
if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count())
{
// Since we're in multipart mode, we'll be outputting this later
continue;
}
rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1);
}
rv += "\r\n";
if (attach.count())
{
// we're going to have attachments, so output the lead-in for the message body
rv += "This is a message with multiple parts in MIME format.\r\n";
rv += "--" + qxt_d->boundary + "\r\nContent-Type: ";
if (hasExtraHeader("Content-Type"))
rv += extraHeader("Content-Type") + "\r\n";
else
rv += "text/plain; charset=UTF-8\r\n";
if (hasExtraHeader("Content-Transfer-Encoding"))
{
rv += "Content-Transfer-Encoding: " + extraHeader("Content-Transfer-Encoding") + "\r\n";
}
else if (!bodyIsAscii)
{
if (!useQuotedPrintable)
{
// base64
rv += "Content-Transfer-Encoding: base64\r\n";
}
else
{
// quoted-printable
rv += "Content-Transfer-Encoding: quoted-printable\r\n";
}
}
rv += "\r\n";
}
if (bodyIsAscii)
{
QByteArray b = latin1->fromUnicode(body());
int len = b.length();
QByteArray line = "";
QByteArray word = "";
for (int i = 0; i < len; i++)
{
if (b[i] == '\n' || b[i] == '\r')
{
if (line.isEmpty())
{
line = word;
word = "";
}
else if (line.length() + word.length() + 1 <= 78)
{
line = line + ' ' + word;
word = "";
}
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1])
{
// If we're looking at a CRLF pair, skip the second half
i++;
}
line = word;
}
else if (b[i] == ' ')
{
if (line.length() + word.length() + 1 > 78)
{
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
line = word;
}
else if (line.isEmpty())
{
line = word;
}
else
{
line = line + ' ' + word;
}
word = "";
}
else
{
word += b[i];
}
}
if (line.length() + word.length() + 1 > 78)
{
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
line = word;
}
else if (!word.isEmpty())
{
line += ' ' + word;
}
if(!line.isEmpty()) {
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
}
}
else if (useQuotedPrintable)
{
QByteArray b = body().toUtf8();
int ct = b.length();
QByteArray line;
for (int i = 0; i < ct; i++)
{
if(b[i] == '\n' || b[i] == '\r')
{
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
line = "";
if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1])
{
// If we're looking at a CRLF pair, skip the second half
i++;
}
}
else if (line.length() > 74)
{
rv += line + "=\r\n";
line = "";
}
if (QXT_MUST_QP(b[i]))
{
line += "=" + b.mid(i, 1).toHex().toUpper();
}
else
{
line += b[i];
}
}
if(!line.isEmpty()) {
if(line[0] == '.')
rv += ".";
rv += line + "\r\n";
}
}
else /* base64 */
{
QByteArray b = body().toUtf8().toBase64();
int ct = b.length();
for (int i = 0; i < ct; i += 78)
{
rv += b.mid(i, 78) + "\r\n";
}
}
if (attach.count())
{
foreach(const QString& filename, attach.keys())
{
rv += "--" + qxt_d->boundary + "\r\n";
rv += qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), latin1, "attachment; filename=");
rv += attach[filename].mimeData();
}
rv += "--" + qxt_d->boundary + "--\r\n";
}
return rv;
}

View file

@ -0,0 +1,85 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTMAILMESSAGE_H
#define QXTMAILMESSAGE_H
#include "qxtglobal.h"
#include "qxtmailattachment.h"
#include <QStringList>
#include <QHash>
#include <QMetaType>
#include <QSharedDataPointer>
struct QxtMailMessagePrivate;
class QXT_NETWORK_EXPORT QxtMailMessage
{
public:
enum RecipientType
{
To,
Cc,
Bcc
};
QxtMailMessage();
QxtMailMessage(const QxtMailMessage& other);
QxtMailMessage(const QString& sender, const QString& recipient);
QxtMailMessage& operator=(const QxtMailMessage& other);
~QxtMailMessage();
QString sender() const;
void setSender(const QString&);
QString subject() const;
void setSubject(const QString&);
QString body() const;
void setBody(const QString&);
QStringList recipients(RecipientType type = To) const;
void addRecipient(const QString&, RecipientType type = To);
void removeRecipient(const QString&);
QHash<QString, QString> extraHeaders() const;
QString extraHeader(const QString&) const;
bool hasExtraHeader(const QString&) const;
void setExtraHeader(const QString& key, const QString& value);
void setExtraHeaders(const QHash<QString, QString>&);
void removeExtraHeader(const QString& key);
QHash<QString, QxtMailAttachment> attachments() const;
QxtMailAttachment attachment(const QString& filename) const;
void addAttachment(const QString& filename, const QxtMailAttachment& attach);
void removeAttachment(const QString& filename);
QByteArray rfc2822() const;
private:
QSharedDataPointer<QxtMailMessagePrivate> qxt_d;
};
Q_DECLARE_TYPEINFO(QxtMailMessage, Q_MOVABLE_TYPE);
#endif // QXTMAIL_H

View file

@ -0,0 +1,617 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
/*!
* \class QxtSmtp
* \inmodule QxtNetwork
* \brief The QxtSmtp class implements the SMTP protocol for sending email
*/
#include "qxtsmtp.h"
#include "qxtsmtp_p.h"
#include "qxthmac.h"
#include <QStringList>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QSslSocket>
QxtSmtpPrivate::QxtSmtpPrivate() : QObject(0)
{
// empty ctor
}
QxtSmtp::QxtSmtp(QObject* parent) : QObject(parent)
{
QXT_INIT_PRIVATE(QxtSmtp);
qxt_d().state = QxtSmtpPrivate::Disconnected;
qxt_d().nextID = 0;
qxt_d().socket = new QSslSocket(this);
QObject::connect(socket(), SIGNAL(encrypted()), this, SIGNAL(encrypted()));
//QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo()));
QObject::connect(socket(), SIGNAL(connected()), this, SIGNAL(connected()));
QObject::connect(socket(), SIGNAL(disconnected()), this, SIGNAL(disconnected()));
QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), SLOT(socketError(QAbstractSocket::SocketError)));
QObject::connect(this, SIGNAL(authenticated()), &qxt_d(), SLOT(sendNext()));
QObject::connect(socket(), SIGNAL(readyRead()), &qxt_d(), SLOT(socketRead()));
}
QByteArray QxtSmtp::username() const
{
return qxt_d().username;
}
void QxtSmtp::setUsername(const QByteArray& username)
{
qxt_d().username = username;
}
QByteArray QxtSmtp::password() const
{
return qxt_d().password;
}
void QxtSmtp::setPassword(const QByteArray& password)
{
qxt_d().password = password;
}
int QxtSmtp::send(const QxtMailMessage& message)
{
int messageID = ++qxt_d().nextID;
qxt_d().pending.append(qMakePair(messageID, message));
if (qxt_d().state == QxtSmtpPrivate::Waiting)
qxt_d().sendNext();
return messageID;
}
int QxtSmtp::pendingMessages() const
{
return qxt_d().pending.count();
}
QTcpSocket* QxtSmtp::socket() const
{
return qxt_d().socket;
}
void QxtSmtp::connectToHost(const QString& hostName, quint16 port)
{
qxt_d().useSecure = false;
qxt_d().state = QxtSmtpPrivate::StartState;
socket()->connectToHost(hostName, port);
}
void QxtSmtp::connectToHost(const QHostAddress& address, quint16 port)
{
connectToHost(address.toString(), port);
}
void QxtSmtp::disconnectFromHost()
{
socket()->disconnectFromHost();
}
bool QxtSmtp::startTlsDisabled() const
{
return qxt_d().disableStartTLS;
}
void QxtSmtp::setStartTlsDisabled(bool disable)
{
qxt_d().disableStartTLS = disable;
}
QSslSocket* QxtSmtp::sslSocket() const
{
return qxt_d().socket;
}
void QxtSmtp::connectToSecureHost(const QString& hostName, quint16 port)
{
qxt_d().useSecure = true;
qxt_d().state = QxtSmtpPrivate::StartState;
sslSocket()->connectToHostEncrypted(hostName, port);
}
void QxtSmtp::connectToSecureHost(const QHostAddress& address, quint16 port)
{
connectToSecureHost(address.toString(), port);
}
bool QxtSmtp::hasExtension(const QString& extension)
{
return qxt_d().extensions.contains(extension);
}
QString QxtSmtp::extensionData(const QString& extension)
{
return qxt_d().extensions[extension];
}
void QxtSmtpPrivate::socketError(QAbstractSocket::SocketError err)
{
if (err == QAbstractSocket::SslHandshakeFailedError)
{
emit qxt_p().encryptionFailed();
emit qxt_p().encryptionFailed( socket->errorString().toLatin1() );
}
else if (state == StartState)
{
emit qxt_p().connectionFailed();
emit qxt_p().connectionFailed( socket->errorString().toLatin1() );
}
}
void QxtSmtpPrivate::socketRead()
{
buffer += socket->readAll();
while (true)
{
int pos = buffer.indexOf("\r\n");
if (pos < 0) return;
QByteArray line = buffer.left(pos);
buffer = buffer.mid(pos + 2);
QByteArray code = line.left(3);
switch (state)
{
case StartState:
if (code[0] != '2')
{
socket->disconnectFromHost();
}
else
{
ehlo();
}
break;
case HeloSent:
case EhloSent:
case EhloGreetReceived:
parseEhlo(code, (line[3] != ' '), line.mid(4));
break;
case StartTLSSent:
if (code == "220")
{
socket->startClientEncryption();
ehlo();
}
else
{
authenticate();
}
break;
case AuthRequestSent:
case AuthUsernameSent:
if (authType == AuthPlain) authPlain();
else if (authType == AuthLogin) authLogin();
else authCramMD5(line.mid(4));
break;
case AuthSent:
if (code[0] == '2')
{
state = Authenticated;
emit qxt_p().authenticated();
}
else
{
state = Disconnected;
emit qxt_p().authenticationFailed();
emit qxt_p().authenticationFailed( line );
emit socket->disconnectFromHost();
}
break;
case MailToSent:
case RcptAckPending:
if (code[0] != '2') {
emit qxt_p().mailFailed( pending.first().first, code.toInt() );
emit qxt_p().mailFailed(pending.first().first, code.toInt(), line);
// pending.removeFirst();
// DO NOT remove it, the body sent state needs this message to assigned the next mail failed message that will
// the sendNext
// a reset will be sent to clear things out
sendNext();
state = BodySent;
}
else
sendNextRcpt(code, line);
break;
case SendingBody:
sendBody(code, line);
break;
case BodySent:
if ( pending.count() )
{
// if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty,
// you will get into this state and then crash because no check is done. CHeck added but shouldnt
// be necessary since I commented out the removeFirst
if (code[0] != '2')
{
emit qxt_p().mailFailed(pending.first().first, code.toInt() );
emit qxt_p().mailFailed(pending.first().first, code.toInt(), line);
}
else
emit qxt_p().mailSent(pending.first().first);
pending.removeFirst();
}
sendNext();
break;
case Resetting:
if (code[0] != '2') {
emit qxt_p().connectionFailed();
emit qxt_p().connectionFailed( line );
}
else {
state = Waiting;
sendNext();
}
break;
case Disconnected:
case EhloExtensionsReceived:
case EhloDone:
case Authenticated:
case Waiting:
// only to make compiler happy
break;
}
}
}
void QxtSmtpPrivate::ehlo()
{
QByteArray address = "127.0.0.1";
foreach(const QHostAddress& addr, QNetworkInterface::allAddresses())
{
if (addr == QHostAddress::LocalHost || addr == QHostAddress::LocalHostIPv6)
continue;
address = addr.toString().toLatin1();
break;
}
socket->write("ehlo " + address + "\r\n");
extensions.clear();
state = EhloSent;
}
void QxtSmtpPrivate::parseEhlo(const QByteArray& code, bool cont, const QString& line)
{
if (code != "250")
{
// error!
if (state != HeloSent)
{
// maybe let's try HELO
socket->write("helo\r\n");
state = HeloSent;
}
else
{
// nope
socket->write("QUIT\r\n");
socket->flush();
socket->disconnectFromHost();
}
return;
}
else if (state != EhloGreetReceived)
{
if (!cont)
{
// greeting only, no extensions
state = EhloDone;
}
else
{
// greeting followed by extensions
state = EhloGreetReceived;
return;
}
}
else
{
extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
if (!cont)
state = EhloDone;
}
if (state != EhloDone) return;
if (extensions.contains("STARTTLS") && !disableStartTLS)
{
startTLS();
}
else
{
authenticate();
}
}
void QxtSmtpPrivate::startTLS()
{
socket->write("starttls\r\n");
state = StartTLSSent;
}
void QxtSmtpPrivate::authenticate()
{
if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty())
{
state = Authenticated;
emit qxt_p().authenticated();
}
else
{
QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
if (auth.contains("CRAM-MD5"))
{
authCramMD5();
}
else if (auth.contains("PLAIN"))
{
authPlain();
}
else if (auth.contains("LOGIN"))
{
authLogin();
}
else
{
state = Authenticated;
emit qxt_p().authenticated();
}
}
}
void QxtSmtpPrivate::authCramMD5(const QByteArray& challenge)
{
if (state != AuthRequestSent)
{
socket->write("auth cram-md5\r\n");
authType = AuthCramMD5;
state = AuthRequestSent;
}
else
{
QxtHmac hmac(QCryptographicHash::Md5);
hmac.setKey(password);
hmac.addData(QByteArray::fromBase64(challenge));
QByteArray response = username + ' ' + hmac.result().toHex();
socket->write(response.toBase64() + "\r\n");
state = AuthSent;
}
}
void QxtSmtpPrivate::authPlain()
{
if (state != AuthRequestSent)
{
socket->write("auth plain\r\n");
authType = AuthPlain;
state = AuthRequestSent;
}
else
{
QByteArray auth;
auth += '\0';
auth += username;
auth += '\0';
auth += password;
socket->write(auth.toBase64() + "\r\n");
state = AuthSent;
}
}
void QxtSmtpPrivate::authLogin()
{
if (state != AuthRequestSent && state != AuthUsernameSent)
{
socket->write("auth login\r\n");
authType = AuthLogin;
state = AuthRequestSent;
}
else if (state == AuthRequestSent)
{
socket->write(username.toBase64() + "\r\n");
state = AuthUsernameSent;
}
else
{
socket->write(password.toBase64() + "\r\n");
state = AuthSent;
}
}
static QByteArray qxt_extract_address(const QString& address)
{
int parenDepth = 0;
int addrStart = -1;
bool inQuote = false;
int ct = address.length();
for (int i = 0; i < ct; i++)
{
QChar ch = address[i];
if (inQuote)
{
if (ch == '"')
inQuote = false;
}
else if (addrStart != -1)
{
if (ch == '>')
return address.mid(addrStart, (i - addrStart)).toLatin1();
}
else if (ch == '(')
{
parenDepth++;
}
else if (ch == ')')
{
parenDepth--;
if (parenDepth < 0) parenDepth = 0;
}
else if (ch == '"')
{
if (parenDepth == 0)
inQuote = true;
}
else if (ch == '<')
{
if (!inQuote && parenDepth == 0)
addrStart = i + 1;
}
}
return address.toLatin1();
}
void QxtSmtpPrivate::sendNext()
{
if (state == Disconnected)
{
// leave the mail in the queue if not ready to send
return;
}
if (pending.isEmpty())
{
// if there are no additional mails to send, finish up
state = Waiting;
emit qxt_p().finished();
return;
}
if(state != Waiting) {
state = Resetting;
socket->write("rset\r\n");
return;
}
const QxtMailMessage& msg = pending.first().second;
rcptNumber = rcptAck = mailAck = 0;
recipients = msg.recipients(QxtMailMessage::To) +
msg.recipients(QxtMailMessage::Cc) +
msg.recipients(QxtMailMessage::Bcc);
if (recipients.count() == 0)
{
// can't send an e-mail with no recipients
emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients );
emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray( "e-mail has no recipients" ) );
pending.removeFirst();
sendNext();
return;
}
// We explicitly use lowercase keywords because for some reason gmail
// interprets any string starting with an uppercase R as a request
// to renegotiate the SSL connection.
socket->write("mail from:<" + qxt_extract_address(msg.sender()) + ">\r\n");
if (extensions.contains("PIPELINING")) // almost all do nowadays
{
foreach(const QString& rcpt, recipients)
{
socket->write("rcpt to:<" + qxt_extract_address(rcpt) + ">\r\n");
}
state = RcptAckPending;
}
else
{
state = MailToSent;
}
}
void QxtSmtpPrivate::sendNextRcpt(const QByteArray& code, const QByteArray&line)
{
int messageID = pending.first().first;
const QxtMailMessage& msg = pending.first().second;
if (code[0] != '2')
{
// on failure, emit a warning signal
if (!mailAck)
{
emit qxt_p().senderRejected(messageID, msg.sender());
emit qxt_p().senderRejected(messageID, msg.sender(), line );
}
else
{
emit qxt_p().recipientRejected(messageID, msg.sender());
emit qxt_p().recipientRejected(messageID, msg.sender(), line);
}
}
else if (!mailAck)
{
mailAck = true;
}
else
{
rcptAck++;
}
if (rcptNumber == recipients.count())
{
// all recipients have been sent
if (rcptAck == 0)
{
// no recipients were considered valid
emit qxt_p().mailFailed(messageID, code.toInt() );
emit qxt_p().mailFailed(messageID, code.toInt(), line);
pending.removeFirst();
sendNext();
}
else
{
// at least one recipient was acknowledged, send mail body
socket->write("data\r\n");
state = SendingBody;
}
}
else if (state != RcptAckPending)
{
// send the next recipient unless we're only waiting on acks
socket->write("rcpt to:<" + qxt_extract_address(recipients[rcptNumber]) + ">\r\n");
rcptNumber++;
}
else
{
// If we're only waiting on acks, just count them
rcptNumber++;
}
}
void QxtSmtpPrivate::sendBody(const QByteArray& code, const QByteArray & line)
{
int messageID = pending.first().first;
const QxtMailMessage& msg = pending.first().second;
if (code[0] != '3')
{
emit qxt_p().mailFailed(messageID, code.toInt() );
emit qxt_p().mailFailed(messageID, code.toInt(), line);
pending.removeFirst();
sendNext();
return;
}
socket->write(msg.rfc2822());
socket->write(".\r\n");
state = BodySent;
}

View file

@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTSMTP_H
#define QXTSMTP_H
#include <QObject>
#include <QHostAddress>
#include <QString>
#include "qxtglobal.h"
#include "qxtmailmessage.h"
class QTcpSocket;
class QSslSocket;
class QxtSmtpPrivate;
class QXT_NETWORK_EXPORT QxtSmtp : public QObject
{
Q_OBJECT
public:
enum SmtpError
{
NoError,
NoRecipients,
CommandUnrecognized = 500,
SyntaxError,
CommandNotImplemented,
BadSequence,
ParameterNotImplemented,
MailboxUnavailable = 550,
UserNotLocal,
MessageTooLarge,
InvalidMailboxName,
TransactionFailed
};
QxtSmtp(QObject* parent = 0);
QByteArray username() const;
void setUsername(const QByteArray& name);
QByteArray password() const;
void setPassword(const QByteArray& password);
int send(const QxtMailMessage& message);
int pendingMessages() const;
QTcpSocket* socket() const;
void connectToHost(const QString& hostName, quint16 port = 25);
void connectToHost(const QHostAddress& address, quint16 port = 25);
void disconnectFromHost();
bool startTlsDisabled() const;
void setStartTlsDisabled(bool disable);
QSslSocket* sslSocket() const;
void connectToSecureHost(const QString& hostName, quint16 port = 465);
void connectToSecureHost(const QHostAddress& address, quint16 port = 465);
bool hasExtension(const QString& extension);
QString extensionData(const QString& extension);
Q_SIGNALS:
void connected();
void connectionFailed();
void connectionFailed( const QByteArray & msg );
void encrypted();
void encryptionFailed();
void encryptionFailed( const QByteArray & msg );
void authenticated();
void authenticationFailed();
void authenticationFailed( const QByteArray & msg );
void senderRejected(int mailID, const QString& address );
void senderRejected(int mailID, const QString& address, const QByteArray & msg );
void recipientRejected(int mailID, const QString& address );
void recipientRejected(int mailID, const QString& address, const QByteArray & msg );
void mailFailed(int mailID, int errorCode);
void mailFailed(int mailID, int errorCode, const QByteArray & msg);
void mailSent(int mailID);
void finished();
void disconnected();
private:
QXT_DECLARE_PRIVATE(QxtSmtp)
};
#endif // QXTSMTP_H

View file

@ -0,0 +1,102 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtWeb module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
#ifndef QXTSMTP_P_H
#define QXTSMTP_P_H
#include "qxtsmtp.h"
#include <QHash>
#include <QString>
#include <QList>
#include <QPair>
class QxtSmtpPrivate : public QObject, public QxtPrivate<QxtSmtp>
{
Q_OBJECT
public:
QxtSmtpPrivate();
QXT_DECLARE_PUBLIC(QxtSmtp)
enum SmtpState
{
Disconnected,
StartState,
EhloSent,
EhloGreetReceived,
EhloExtensionsReceived,
EhloDone,
HeloSent,
StartTLSSent,
AuthRequestSent,
AuthUsernameSent,
AuthSent,
Authenticated,
MailToSent,
RcptAckPending,
SendingBody,
BodySent,
Waiting,
Resetting
};
enum AuthType
{
AuthPlain,
AuthLogin,
AuthCramMD5
};
bool useSecure, disableStartTLS;
SmtpState state;// rather then an int use the enum. makes sure invalid states are entered at compile time, and makes debugging easier
AuthType authType;
QByteArray buffer, username, password;
QHash<QString, QString> extensions;
QList<QPair<int, QxtMailMessage> > pending;
QStringList recipients;
int nextID, rcptNumber, rcptAck;
bool mailAck;
QSslSocket* socket;
void parseEhlo(const QByteArray& code, bool cont, const QString& line);
void startTLS();
void authenticate();
void authCramMD5(const QByteArray& challenge = QByteArray());
void authPlain();
void authLogin();
void sendNextRcpt(const QByteArray& code, const QByteArray & line);
void sendBody(const QByteArray& code, const QByteArray & line);
public slots:
void socketError(QAbstractSocket::SocketError err);
void socketRead();
void ehlo();
void sendNext();
};
#endif // QXTSMTP_P_H

View file

@ -1,489 +0,0 @@
/*
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 "smtpclient.h"
#include <QFileInfo>
#include <QByteArray>
/* [1] Constructors and destructors */
SmtpClient::SmtpClient(const QString & host, int port, ConnectionType connectionType) :
socket(NULL),
name("localhost"),
authMethod(AuthPlain),
connectionTimeout(5000),
responseTimeout(5000),
sendMessageTimeout(60000)
{
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)),
this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(socket, SIGNAL(readyRead()),
this, SLOT(socketReadyRead()));
}
SmtpClient::~SmtpClient() {
if (socket)
delete socket;
}
/* [1] --- */
/* [2] Getters and Setters */
void SmtpClient::setUser(const QString &user)
{
this->user = user;
}
void SmtpClient::setPassword(const QString &password)
{
this->password = password;
}
void SmtpClient::setAuthMethod(AuthMethod method)
{
this->authMethod = method;
}
void SmtpClient::setHost(const QString &host)
{
this->host = host;
}
void SmtpClient::setPort(int port)
{
this->port = port;
}
void SmtpClient::setConnectionType(ConnectionType ct)
{
this->connectionType = ct;
if (socket)
delete socket;
switch (connectionType)
{
case TcpConnection:
socket = new QTcpSocket(this);
break;
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;
}
int SmtpClient::getConnectionTimeout() const
{
return connectionTimeout;
}
void SmtpClient::setConnectionTimeout(int msec)
{
connectionTimeout = msec;
}
int SmtpClient::getResponseTimeout() const
{
return responseTimeout;
}
void SmtpClient::setResponseTimeout(int msec)
{
responseTimeout = msec;
}
int SmtpClient::getSendMessageTimeout() const
{
return sendMessageTimeout;
}
void SmtpClient::setSendMessageTimeout(int msec)
{
sendMessageTimeout = msec;
}
/* [2] --- */
/* [3] Public methods */
bool SmtpClient::connectToHost()
{
switch (connectionType)
{
case TlsConnection:
case TcpConnection:
socket->connectToHost(host, port);
break;
case SslConnection:
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
break;
}
// Tries to connect to server
if (!socket->waitForConnected(connectionTimeout))
{
emit smtpError(ConnectionTimeoutError);
return false;
}
try
{
// 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)
{
emit smtpError(ServerError);
return false;
}
// Send a EHLO/HELO message to the server
// The client's first command must be EHLO/HELO
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;
}
if (connectionType == TlsConnection) {
// send a request to start TLS handshake
sendMessage("STARTTLS");
// 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;
}
}
}
catch (ResponseTimeoutException)
{
return false;
}
catch (SendMessageTimeoutException)
{
return false;
}
// 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;
}
}
else if (method == AuthLogin)
{
// Sending command: AUTH LOGIN
sendMessage("AUTH LOGIN");
// 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;
}
}
}
catch (ResponseTimeoutException e)
{
// Responce Timeout exceeded
emit smtpError(AuthenticationFailedError);
return false;
}
catch (SendMessageTimeoutException)
{
// Send Timeout exceeded
emit smtpError(AuthenticationFailedError);
return false;
}
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;
}
// 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;
}
// 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;
}
// Send DATA command
sendMessage("DATA");
waitForResponse();
if (responseCode != 354) return false;
sendMessage(email.toString());
// Send \r\n.\r\n to end the mail data
sendMessage(".");
waitForResponse();
if (responseCode != 250) return false;
}
catch (ResponseTimeoutException)
{
return false;
}
catch (SendMessageTimeoutException)
{
return false;
}
return true;
}
void SmtpClient::quit()
{
sendMessage("QUIT");
}
/* [3] --- */
/* [4] Protected methods */
void SmtpClient::waitForResponse() throw (ResponseTimeoutException)
{
do {
if (!socket->waitForReadyRead(responseTimeout))
{
emit smtpError(ResponseTimeoutError);
throw ResponseTimeoutException();
}
while (socket->canReadLine()) {
// Save the server's response
responseText = socket->readLine();
// 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);
if (responseText[3] == ' ') { return; }
}
} while (true);
}
void SmtpClient::sendMessage(const QString &text) throw (SendMessageTimeoutException)
{
socket->write(text.toUtf8() + "\r\n");
if (! socket->waitForBytesWritten(sendMessageTimeout))
{
emit smtpError(SendDataTimeoutError);
throw SendMessageTimeoutException();
}
}
/* [4] --- */
/* [5] Slots for the socket's signals */
void SmtpClient::socketStateChanged(QAbstractSocket::SocketState /* state */)
{
}
void SmtpClient::socketError(QAbstractSocket::SocketError /* socketError */)
{
}
void SmtpClient::socketReadyRead()
{
}
/* [5] --- */

View file

@ -1,184 +0,0 @@
/*
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 SMTPCLIENT_H
#define SMTPCLIENT_H
#include <QObject>
#include <QtNetwork/QSslSocket>
#include "mimemessage.h"
#include "smtpexports.h"
class SMTP_EXPORT SmtpClient : public QObject
{
Q_OBJECT
public:
/* [0] Enumerations */
enum AuthMethod
{
AuthPlain,
AuthLogin
};
enum SmtpError
{
ConnectionTimeoutError,
ResponseTimeoutError,
SendDataTimeoutError,
AuthenticationFailedError,
ServerError, // 4xx smtp error
ClientError // 5xx smtp error
};
enum ConnectionType
{
TcpConnection,
SslConnection,
TlsConnection // STARTTLS
};
/* [0] --- */
/* [1] Constructors and Destructors */
SmtpClient(const QString & host = "localhost", int port = 25, ConnectionType ct = TcpConnection);
~SmtpClient();
/* [1] --- */
/* [2] Getters and Setters */
const QString& getHost() const;
void setHost(const QString &host);
int getPort() const;
void setPort(int port);
const QString& getName() const;
void setName(const QString &name);
ConnectionType getConnectionType() const;
void setConnectionType(ConnectionType ct);
const QString & getUser() const;
void setUser(const QString &user);
const QString & getPassword() const;
void setPassword(const QString &password);
SmtpClient::AuthMethod getAuthMethod() const;
void setAuthMethod(AuthMethod method);
const QString & getResponseText() const;
int getResponseCode() const;
int getConnectionTimeout() const;
void setConnectionTimeout(int msec);
int getResponseTimeout() const;
void setResponseTimeout(int msec);
int getSendMessageTimeout() const;
void setSendMessageTimeout(int msec);
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 quit();
/* [3] --- */
protected:
/* [4] Protected members */
QTcpSocket *socket;
QString host;
int port;
ConnectionType connectionType;
QString name;
QString user;
QString password;
AuthMethod authMethod;
int connectionTimeout;
int responseTimeout;
int sendMessageTimeout;
QString responseText;
int responseCode;
class ResponseTimeoutException {};
class SendMessageTimeoutException {};
/* [4] --- */
/* [5] Protected methods */
void waitForResponse() throw (ResponseTimeoutException);
void sendMessage(const QString &text) throw (SendMessageTimeoutException);
/* [5] --- */
protected slots:
/* [6] Protected slots */
void socketStateChanged(QAbstractSocket::SocketState state);
void socketError(QAbstractSocket::SocketError error);
void socketReadyRead();
/* [6] --- */
signals:
/* [7] Signals */
void smtpError(SmtpClient::SmtpError e);
/* [7] --- */
};
#endif // SMTPCLIENT_H

View file

@ -1,15 +0,0 @@
#ifndef SMTPEXPORTS_H
#define SMTPEXPORTS_H
/*
#ifdef SMTP_BUILD
#define SMTP_EXPORT Q_DECL_EXPORT
#else
#define SMTP_EXPORT Q_DECL_IMPORT
#endif
*/
// Servatrice compiles this statically in, so there's no need to declare exports
#define SMTP_EXPORT
#endif // SMTPEXPORTS_H

View file

@ -0,0 +1,175 @@
#include "smtpclient.h"
#include "settingscache.h"
#include "smtp/qxtsmtp.h"
#include <QTcpSocket>
#include <QSslSocket>
SmtpClient::SmtpClient(QObject *parent)
: QObject(parent)
{
smtp = new QxtSmtp(this);
connect(smtp, SIGNAL(authenticated()), this, SLOT(authenticated()));
connect(smtp, SIGNAL(authenticationFailed(const QByteArray &)), this, SLOT(authenticationFailed(const QByteArray &)));
connect(smtp, SIGNAL(connected()), this, SLOT(connected()));
connect(smtp, SIGNAL(connectionFailed(const QByteArray &)), this, SLOT(connectionFailed(const QByteArray &)));
connect(smtp, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(smtp, SIGNAL(encrypted()), this, SLOT(encrypted()));
connect(smtp, SIGNAL(encryptionFailed(const QByteArray &)), this, SLOT(encryptionFailed(const QByteArray &)));
connect(smtp, SIGNAL(finished()), this, SLOT(finished()));
connect(smtp, SIGNAL(mailFailed(int, int, const QByteArray &)), this, SLOT(mailFailed(int, int, const QByteArray &)));
connect(smtp, SIGNAL(mailSent(int)), this, SLOT(mailSent(int)));
connect(smtp, SIGNAL(recipientRejected(int, const QString &, const QByteArray &)), this, SLOT(recipientRejected(int, const QString &, const QByteArray &)));
connect(smtp, SIGNAL(senderRejected(int, const QString &, const QByteArray &)), this, SLOT(senderRejected(int, const QString &, const QByteArray &)));
}
SmtpClient::~SmtpClient()
{
if(smtp)
{
delete smtp;
smtp = 0;
}
}
bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token)
{
QString email = settingsCache->value("smtp/email", "").toString();
QString name = settingsCache->value("smtp/name", "").toString();
QString subject = settingsCache->value("smtp/subject", "").toString();
QString body = settingsCache->value("smtp/body", "").toString();
if(email.isEmpty())
{
qDebug() << "[MAIL] Missing sender email in configuration";
return false;
}
if(subject.isEmpty())
{
qDebug() << "[MAIL] Missing subject field in configuration";
return false;
}
if(body.isEmpty())
{
qDebug() << "[MAIL] Missing body field in configuration";
return false;
}
if(recipient.isEmpty())
{
qDebug() << "[MAIL] Missing recipient field for user " << nickname;
return false;
}
if(token.isEmpty())
{
qDebug() << "[MAIL] Missing token field for user " << nickname;
return false;
}
QxtMailMessage message;
message.setSender(name + " <" + email + ">");
message.addRecipient(recipient);
message.setSubject(subject);
message.setBody(body.replace("%username", nickname).replace("%token", token));
int id = smtp->send(message);
qDebug() << "[MAIL] Enqueued mail to" << recipient << "as" << id;
return true;
}
void SmtpClient::sendAllEmails()
{
// still connected from the previous round
if(smtp->socket()->state() == QAbstractSocket::ConnectedState)
return;
if(smtp->pendingMessages() == 0)
return;
QString connectionType = settingsCache->value("smtp/connection", "tcp").toString();
QString host = settingsCache->value("smtp/host", "localhost").toString();
int port = settingsCache->value("smtp/port", 25).toInt();
QByteArray username = settingsCache->value("smtp/username", "").toByteArray();
QByteArray password = settingsCache->value("smtp/password", "").toByteArray();
bool acceptAllCerts = settingsCache->value("smtp/acceptallcerts", false).toBool();
smtp->setUsername(username);
smtp->setPassword(password);
// Connect
if(connectionType == "ssl")
{
if(acceptAllCerts)
smtp->sslSocket()->setPeerVerifyMode(QSslSocket::QueryPeer);
smtp->connectToSecureHost(host, port);
} else {
smtp->connectToHost(host, port);
}
}
void SmtpClient::authenticated()
{
qDebug() << "[MAIL] authenticated";
}
void SmtpClient::authenticationFailed(const QByteArray & msg)
{
qDebug() << "[MAIL] authenticationFailed" << QString(msg);
}
void SmtpClient::connected()
{
qDebug() << "[MAIL] connected";
}
void SmtpClient::connectionFailed(const QByteArray & msg)
{
qDebug() << "[MAIL] connectionFailed" << QString(msg);
}
void SmtpClient::disconnected()
{
qDebug() << "[MAIL] disconnected";
}
void SmtpClient::encrypted()
{
qDebug() << "[MAIL] encrypted";
}
void SmtpClient::encryptionFailed(const QByteArray & msg)
{
qDebug() << "[MAIL] encryptionFailed" << QString(msg);
qDebug() << "[MAIL] Try enabling the \"acceptallcerts\" option in servatrice.ini";
}
void SmtpClient::finished()
{
qDebug() << "[MAIL] finished";
smtp->disconnectFromHost();
}
void SmtpClient::mailFailed(int mailID, int errorCode, const QByteArray & msg)
{
qDebug() << "[MAIL] mailFailed id=" << mailID << " errorCode=" << errorCode << "msg=" << QString(msg);
}
void SmtpClient::mailSent(int mailID)
{
qDebug() << "[MAIL] mailSent" << mailID;
}
void SmtpClient::recipientRejected(int mailID, const QString & address, const QByteArray & msg)
{
qDebug() << "[MAIL] recipientRejected id=" << mailID << " address=" << address << "msg=" << QString(msg);
}
void SmtpClient::senderRejected(int mailID, const QString & address, const QByteArray & msg)
{
qDebug() << "[MAIL] senderRejected id=" << mailID << " address=" << address << "msg=" << QString(msg);
}

View file

@ -0,0 +1,34 @@
#ifndef SMTPCLIENT_H
#define SMTPCLIENT_H
#include <QObject>
class QxtSmtp;
class QxtMailMessage;
class SmtpClient : public QObject {
Q_OBJECT
public:
SmtpClient(QObject *parent = 0);
~SmtpClient();
protected:
QxtSmtp *smtp;
public slots:
bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token);
void sendAllEmails();
protected slots:
void authenticated();
void authenticationFailed(const QByteArray & msg);
void connected();
void connectionFailed(const QByteArray & msg);
void disconnected();
void encrypted();
void encryptionFailed(const QByteArray & msg);
void finished();
void mailFailed(int mailID, int errorCode, const QByteArray & msg);
void mailSent(int mailID);
void recipientRejected(int mailID, const QString & address, const QByteArray & msg);
void senderRejected(int mailID, const QString & address, const QByteArray & msg);
};
#endif