diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 18e2db06..6fa529ff 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -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) diff --git a/servatrice/migrations/servatrice_0001_to_0002.sql b/servatrice/migrations/servatrice_0001_to_0002.sql new file mode 100644 index 00000000..fc30b4a1 --- /dev/null +++ b/servatrice/migrations/servatrice_0001_to_0002.sql @@ -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; diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 70078dba..ac0d6707 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -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 diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 0704b05d..af5fd9e4 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -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; diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index db6bfeb8..499b468d 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -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 @@ -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; diff --git a/servatrice/src/main.h b/servatrice/src/main.h index 389b805c..6ce4bb26 100644 --- a/servatrice/src/main.h +++ b/servatrice/src/main.h @@ -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 diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 69bbd44c..b88b4f47 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -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) diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 7e6c5847..e820e8b8 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -9,7 +9,7 @@ #include "server.h" #include "server_database_interface.h" -#define DATABASE_SCHEMA_VERSION 1 +#define DATABASE_SCHEMA_VERSION 2 class Servatrice; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index ee74af5a..abb2997a 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -59,8 +59,6 @@ #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_deckstorage.pb.h" -#include "smtp/SmtpMime" - #include "version_string.h" #include #include @@ -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 diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index df20e118..ccec5519 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -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(); diff --git a/servatrice/src/smtp/SmtpMime b/servatrice/src/smtp/SmtpMime deleted file mode 100644 index 940996b8..00000000 --- a/servatrice/src/smtp/SmtpMime +++ /dev/null @@ -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 diff --git a/servatrice/src/smtp/emailaddress.cpp b/servatrice/src/smtp/emailaddress.cpp deleted file mode 100644 index 9480e5d4..00000000 --- a/servatrice/src/smtp/emailaddress.cpp +++ /dev/null @@ -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] --- */ - diff --git a/servatrice/src/smtp/emailaddress.h b/servatrice/src/smtp/emailaddress.h deleted file mode 100644 index 4ecf6efc..00000000 --- a/servatrice/src/smtp/emailaddress.h +++ /dev/null @@ -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 - -#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 diff --git a/servatrice/src/smtp/mimeattachment.cpp b/servatrice/src/smtp/mimeattachment.cpp deleted file mode 100644 index 0dea6222..00000000 --- a/servatrice/src/smtp/mimeattachment.cpp +++ /dev/null @@ -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 - -/* [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] --- */ diff --git a/servatrice/src/smtp/mimeattachment.h b/servatrice/src/smtp/mimeattachment.h deleted file mode 100644 index 4a92e9a1..00000000 --- a/servatrice/src/smtp/mimeattachment.h +++ /dev/null @@ -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 -#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 diff --git a/servatrice/src/smtp/mimecontentformatter.cpp b/servatrice/src/smtp/mimecontentformatter.cpp deleted file mode 100644 index 7f5a6e43..00000000 --- a/servatrice/src/smtp/mimecontentformatter.cpp +++ /dev/null @@ -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; -} diff --git a/servatrice/src/smtp/mimecontentformatter.h b/servatrice/src/smtp/mimecontentformatter.h deleted file mode 100644 index bf751588..00000000 --- a/servatrice/src/smtp/mimecontentformatter.h +++ /dev/null @@ -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 -#include - -#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 diff --git a/servatrice/src/smtp/mimefile.cpp b/servatrice/src/smtp/mimefile.cpp deleted file mode 100644 index c6f313a8..00000000 --- a/servatrice/src/smtp/mimefile.cpp +++ /dev/null @@ -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 - -/* [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] --- */ - diff --git a/servatrice/src/smtp/mimefile.h b/servatrice/src/smtp/mimefile.h deleted file mode 100644 index 13f444de..00000000 --- a/servatrice/src/smtp/mimefile.h +++ /dev/null @@ -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 - -#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 diff --git a/servatrice/src/smtp/mimehtml.cpp b/servatrice/src/smtp/mimehtml.cpp deleted file mode 100644 index 5594d3a4..00000000 --- a/servatrice/src/smtp/mimehtml.cpp +++ /dev/null @@ -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] --- */ diff --git a/servatrice/src/smtp/mimehtml.h b/servatrice/src/smtp/mimehtml.h deleted file mode 100644 index f3f54300..00000000 --- a/servatrice/src/smtp/mimehtml.h +++ /dev/null @@ -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 diff --git a/servatrice/src/smtp/mimeinlinefile.cpp b/servatrice/src/smtp/mimeinlinefile.cpp deleted file mode 100644 index 0823b0db..00000000 --- a/servatrice/src/smtp/mimeinlinefile.cpp +++ /dev/null @@ -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] --- */ - - - diff --git a/servatrice/src/smtp/mimeinlinefile.h b/servatrice/src/smtp/mimeinlinefile.h deleted file mode 100644 index 98fe3cab..00000000 --- a/servatrice/src/smtp/mimeinlinefile.h +++ /dev/null @@ -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 diff --git a/servatrice/src/smtp/mimemessage.cpp b/servatrice/src/smtp/mimemessage.cpp deleted file mode 100644 index 0c4ebc2d..00000000 --- a/servatrice/src/smtp/mimemessage.cpp +++ /dev/null @@ -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 -#include "quotedprintable.h" -#include - -/* [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 & 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 & MimeMessage::getParts() const -{ - if (typeid(*content) == typeid(MimeMultiPart)) { - return ((MimeMultiPart*) content)->getParts(); - } - else { - QList *res = new QList(); - 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::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] --- */ diff --git a/servatrice/src/smtp/mimemessage.h b/servatrice/src/smtp/mimemessage.h deleted file mode 100644 index 18b4ec3b..00000000 --- a/servatrice/src/smtp/mimemessage.h +++ /dev/null @@ -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 - -#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 & getRecipients(RecipientType type = To) const; - const QString & getSubject() const; - const QList & getParts() const; - - MimePart& getContent(); - void setContent(MimePart *content); - /* [2] --- */ - - - /* [3] Public methods */ - - virtual QString toString(); - - /* [3] --- */ - -protected: - - /* [4] Protected members */ - - EmailAddress* sender; - QList recipientsTo, recipientsCc, recipientsBcc; - QString subject; - MimePart *content; - bool autoMimeContentCreated; - - MimePart::Encoding hEncoding; - - /* [4] --- */ - - -}; - -#endif // MIMEMESSAGE_H diff --git a/servatrice/src/smtp/mimemultipart.cpp b/servatrice/src/smtp/mimemultipart.cpp deleted file mode 100644 index 19d47297..00000000 --- a/servatrice/src/smtp/mimemultipart.cpp +++ /dev/null @@ -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 -#include - -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 & MimeMultiPart::getParts() const { - return parts; -} - -void MimeMultiPart::prepare() { - QList::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; -} diff --git a/servatrice/src/smtp/mimemultipart.h b/servatrice/src/smtp/mimemultipart.h deleted file mode 100644 index a8e9f599..00000000 --- a/servatrice/src/smtp/mimemultipart.h +++ /dev/null @@ -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 & getParts() const; - - /* [2] --- */ - - /* [3] Public methods */ - - void addPart(MimePart *part); - - virtual void prepare(); - - /* [3] --- */ - -protected: - QList< MimePart* > parts; - - MultiPartType type; - -}; - -#endif // MIMEMULTIPART_H diff --git a/servatrice/src/smtp/mimepart.cpp b/servatrice/src/smtp/mimepart.cpp deleted file mode 100644 index 443f863e..00000000 --- a/servatrice/src/smtp/mimepart.cpp +++ /dev/null @@ -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] --- */ diff --git a/servatrice/src/smtp/mimepart.h b/servatrice/src/smtp/mimepart.h deleted file mode 100644 index 8636035d..00000000 --- a/servatrice/src/smtp/mimepart.h +++ /dev/null @@ -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 -#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 diff --git a/servatrice/src/smtp/mimetext.cpp b/servatrice/src/smtp/mimetext.cpp deleted file mode 100644 index 20b1b0a0..00000000 --- a/servatrice/src/smtp/mimetext.cpp +++ /dev/null @@ -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] --- */ diff --git a/servatrice/src/smtp/mimetext.h b/servatrice/src/smtp/mimetext.h deleted file mode 100644 index c0d29835..00000000 --- a/servatrice/src/smtp/mimetext.h +++ /dev/null @@ -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 diff --git a/servatrice/src/smtp/quotedprintable.cpp b/servatrice/src/smtp/quotedprintable.cpp deleted file mode 100644 index aa3eda6a..00000000 --- a/servatrice/src/smtp/quotedprintable.cpp +++ /dev/null @@ -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; -} diff --git a/servatrice/src/smtp/quotedprintable.h b/servatrice/src/smtp/quotedprintable.h deleted file mode 100644 index 03d3acf8..00000000 --- a/servatrice/src/smtp/quotedprintable.h +++ /dev/null @@ -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 -#include - -#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 diff --git a/servatrice/src/smtp/qxtglobal.h b/servatrice/src/smtp/qxtglobal.h new file mode 100644 index 00000000..a5744d88 --- /dev/null +++ b/servatrice/src/smtp/qxtglobal.h @@ -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. + ** + ** + ** + ****************************************************************************/ + +#ifndef QXTGLOBAL_H +#define QXTGLOBAL_H + +#include + +#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 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 +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 +class QxtPrivateInterface +{ + friend class QxtPrivate; +public: + QxtPrivateInterface() + { + pvt = new PVT; + } + ~QxtPrivateInterface() + { + delete pvt; + } + + inline void setPublic(PUB* pub) + { + pvt->QXT_setPublic(pub); + } + inline PVT& operator()() + { + return *static_cast(pvt); + } + inline const PVT& operator()() const + { + return *static_cast(pvt); + } +private: + QxtPrivateInterface(const QxtPrivateInterface&) { } + QxtPrivateInterface& operator=(const QxtPrivateInterface&) { } + QxtPrivate* pvt; +}; + +#endif // QXT_GLOBAL diff --git a/servatrice/src/smtp/qxthmac.cpp b/servatrice/src/smtp/qxthmac.cpp new file mode 100644 index 00000000..12c6ae74 --- /dev/null +++ b/servatrice/src/smtp/qxthmac.cpp @@ -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. + ** + ** + ** + ****************************************************************************/ + +#include "qxthmac.h" +#include + +#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 +{ +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 diff --git a/servatrice/src/smtp/qxthmac.h b/servatrice/src/smtp/qxthmac.h new file mode 100644 index 00000000..8e0d3d4a --- /dev/null +++ b/servatrice/src/smtp/qxthmac.h @@ -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. + ** + ** + ** + ****************************************************************************/ + +#ifndef QXTHMAC_H +#define QXTHMAC_H + +#include + +#if QT_VERSION < 0x040300 +# warning QxtHmac requires Qt 4.3.0 or greater +#else + +#include +#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 diff --git a/servatrice/src/smtp/qxtmail_p.h b/servatrice/src/smtp/qxtmail_p.h new file mode 100644 index 00000000..a9a9dc90 --- /dev/null +++ b/servatrice/src/smtp/qxtmail_p.h @@ -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. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAIL_P_H +#define QXTMAIL_P_H + +#include + +#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 diff --git a/servatrice/src/smtp/qxtmailattachment.cpp b/servatrice/src/smtp/qxtmailattachment.cpp new file mode 100644 index 00000000..59ad464f --- /dev/null +++ b/servatrice/src/smtp/qxtmailattachment.cpp @@ -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. + ** + ** + ** + ****************************************************************************/ + +/*! + * \class QxtMailAttachment + * \inmodule QxtNetwork + * \brief The QxtMailAttachment class represents an attachement to a QxtMailMessage + */ + + + + +#include "qxtmailattachment.h" +#include "qxtmail_p.h" +#include +#include +#include +#include +#include + +struct QxtMailAttachmentPrivate : public QSharedData +{ + QHash extraHeaders; + QString contentType; + QPointer 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(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 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& a) +{ + QHash& 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; +} diff --git a/servatrice/src/smtp/qxtmailattachment.h b/servatrice/src/smtp/qxtmailattachment.h new file mode 100644 index 00000000..8084bbec --- /dev/null +++ b/servatrice/src/smtp/qxtmailattachment.h @@ -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. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAILATTACHMENT_H +#define QXTMAILATTACHMENT_H + +#include "qxtglobal.h" + +#include +#include +#include +#include +#include +#include + +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 extraHeaders() const; + QString extraHeader(const QString&) const; + bool hasExtraHeader(const QString&) const; + void setExtraHeader(const QString& key, const QString& value); + void setExtraHeaders(const QHash&); + void removeExtraHeader(const QString& key); + + QByteArray mimeData(); + +private: + QSharedDataPointer qxt_d; +}; +Q_DECLARE_TYPEINFO(QxtMailAttachment, Q_MOVABLE_TYPE); + +#endif // QXTMAILATTACHMENT_H diff --git a/servatrice/src/smtp/qxtmailmessage.cpp b/servatrice/src/smtp/qxtmailmessage.cpp new file mode 100644 index 00000000..d90740d9 --- /dev/null +++ b/servatrice/src/smtp/qxtmailmessage.cpp @@ -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. + ** + ** + ** + ****************************************************************************/ + + +/*! + * \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 +#include +#include +#include + + +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 extraHeaders; + QHash 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 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& a) +{ + QHash& 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 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 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; +} diff --git a/servatrice/src/smtp/qxtmailmessage.h b/servatrice/src/smtp/qxtmailmessage.h new file mode 100644 index 00000000..5fd71507 --- /dev/null +++ b/servatrice/src/smtp/qxtmailmessage.h @@ -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. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAILMESSAGE_H +#define QXTMAILMESSAGE_H + +#include "qxtglobal.h" +#include "qxtmailattachment.h" + +#include +#include +#include +#include + +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 extraHeaders() const; + QString extraHeader(const QString&) const; + bool hasExtraHeader(const QString&) const; + void setExtraHeader(const QString& key, const QString& value); + void setExtraHeaders(const QHash&); + void removeExtraHeader(const QString& key); + + QHash 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 qxt_d; +}; +Q_DECLARE_TYPEINFO(QxtMailMessage, Q_MOVABLE_TYPE); + +#endif // QXTMAIL_H diff --git a/servatrice/src/smtp/qxtsmtp.cpp b/servatrice/src/smtp/qxtsmtp.cpp new file mode 100644 index 00000000..5ec59f0c --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp.cpp @@ -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. + ** + ** + ** + ****************************************************************************/ + + +/*! + * \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 +#include +#include +#include + +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; +} diff --git a/servatrice/src/smtp/qxtsmtp.h b/servatrice/src/smtp/qxtsmtp.h new file mode 100644 index 00000000..747e22df --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp.h @@ -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. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSMTP_H +#define QXTSMTP_H + +#include +#include +#include + +#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 diff --git a/servatrice/src/smtp/qxtsmtp_p.h b/servatrice/src/smtp/qxtsmtp_p.h new file mode 100644 index 00000000..a8e234b1 --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp_p.h @@ -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. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSMTP_P_H +#define QXTSMTP_P_H + +#include "qxtsmtp.h" +#include +#include +#include +#include + +class QxtSmtpPrivate : public QObject, public QxtPrivate +{ + 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 extensions; + QList > 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 diff --git a/servatrice/src/smtp/smtpclient.cpp b/servatrice/src/smtp/smtpclient.cpp deleted file mode 100644 index f54d275b..00000000 --- a/servatrice/src/smtp/smtpclient.cpp +++ /dev/null @@ -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 -#include - - -/* [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::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] --- */ - - - - diff --git a/servatrice/src/smtp/smtpclient.h b/servatrice/src/smtp/smtpclient.h deleted file mode 100644 index f734aad6..00000000 --- a/servatrice/src/smtp/smtpclient.h +++ /dev/null @@ -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 -#include - -#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 diff --git a/servatrice/src/smtp/smtpexports.h b/servatrice/src/smtp/smtpexports.h deleted file mode 100644 index c9234be5..00000000 --- a/servatrice/src/smtp/smtpexports.h +++ /dev/null @@ -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 diff --git a/servatrice/src/smtpclient.cpp b/servatrice/src/smtpclient.cpp new file mode 100644 index 00000000..aa2f9198 --- /dev/null +++ b/servatrice/src/smtpclient.cpp @@ -0,0 +1,175 @@ +#include "smtpclient.h" +#include "settingscache.h" +#include "smtp/qxtsmtp.h" + +#include +#include + +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); +} diff --git a/servatrice/src/smtpclient.h b/servatrice/src/smtpclient.h new file mode 100644 index 00000000..b2b1ad00 --- /dev/null +++ b/servatrice/src/smtpclient.h @@ -0,0 +1,34 @@ +#ifndef SMTPCLIENT_H +#define SMTPCLIENT_H + +#include + +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 \ No newline at end of file