diff options
Diffstat (limited to '')
-rw-r--r-- | src/CMakeLists.txt | 7 | ||||
-rw-r--r-- | src/Mime.cpp | 233 | ||||
-rw-r--r-- | src/gpg/GpgContext.cpp | 55 | ||||
-rw-r--r-- | src/smtp/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/smtp/emailaddress.cpp | 44 | ||||
-rw-r--r-- | src/smtp/mimeattachment.cpp | 43 | ||||
-rw-r--r-- | src/smtp/mimecontentformatter.cpp | 59 | ||||
-rw-r--r-- | src/smtp/mimefile.cpp | 61 | ||||
-rw-r--r-- | src/smtp/mimehtml.cpp | 46 | ||||
-rw-r--r-- | src/smtp/mimeinlinefile.cpp | 42 | ||||
-rw-r--r-- | src/smtp/mimemessage.cpp | 308 | ||||
-rw-r--r-- | src/smtp/mimemultipart.cpp | 70 | ||||
-rw-r--r-- | src/smtp/mimepart.cpp | 159 | ||||
-rw-r--r-- | src/smtp/mimetext.cpp | 52 | ||||
-rw-r--r-- | src/smtp/quotedprintable.cpp | 62 | ||||
-rw-r--r-- | src/smtp/smtpclient.cpp | 407 | ||||
-rw-r--r-- | src/ui/AttachmentTableModel.cpp | 134 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowSlotUI.cpp | 7 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowUI.cpp | 22 | ||||
-rw-r--r-- | src/ui/widgets/Attachments.cpp | 180 |
20 files changed, 1363 insertions, 634 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 243570e5..03eaac9e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(ALL_SOURCE_FILE) add_subdirectory(gpg) add_subdirectory(ui) +add_subdirectory(smtp) aux_source_directory(. BASE_SOURCE) @@ -121,19 +122,19 @@ endif() IF (MINGW) message(STATUS "Link Application Static Library For MINGW") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl) elseif(APPLE) message(STATUS "Link Application Static Library For macOS") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl) else() message(STATUS "Link Application Static Library For UNIX") target_link_libraries(${AppName} - gpgfrontend-ui gpg + smtp gpgfrontend-ui gpg Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core crypto ssl pthread) endif() diff --git a/src/Mime.cpp b/src/Mime.cpp deleted file mode 100644 index 8e4c8d72..00000000 --- a/src/Mime.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/** - * This file is part of GPGFrontend. - * - * GPGFrontend is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Foobar 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <https://www.gnu.org/licenses/>. - * - * The initial version of the source code is inherited from gpg4usb-team. - * Their source code version also complies with GNU General Public License. - * - * The source code version of this software was modified and released - * by Saturneric<[email protected]> starting on May 12, 2021. - * - */ - -/* TODO: proper import / copyright statement - * - */ - -#include "Mime.h" - -Mime::Mime(QByteArray *message) { - splitParts(message); - /* - mMessage = message; - int bStart = mMessage->indexOf("boundary=\"") + 10 ; - int bEnd = mMessage->indexOf("\"\n", bStart ); - - qDebug() << "bStart: " << bStart << " bEnd: " << bEnd; - mBoundary = new QByteArray(mMessage->mid(bStart, bEnd - bStart)); - qDebug() << "boundary: " << *mBoundary; - - Part *p1 = new Part(); - - int nb = mMessage->indexOf(*mBoundary, bEnd) + mBoundary->length() +1 ; - qDebug() << "nb: " << nb; - int eh = mMessage->indexOf("\n\n", nb); - qDebug() << "eh: " << eh; - QByteArray *header = new QByteArray(mMessage->mid(nb , eh - nb)); - qDebug() << "header:" << header; - - // split header at newlines - foreach(QByteArray tmp , header->split(* "\n")) { - // split lines at : - QList<QByteArray> tmp2 = tmp.split(* ":"); - p1->header.insert(QString(tmp2[0].trimmed()), QString(tmp2[1].trimmed())); - } - - QHashIterator<QString, QString> i(p1->header); - while (i.hasNext()) { - i.next(); - qDebug() << "found: " << i.key() << ":" << i.value() << endl; - } - - int nb2 = mMessage->indexOf(*mBoundary, eh); - - p1->body = mMessage->mid(eh , nb2 - eh); - - QTextCodec *codec = QTextCodec::codecForName("ISO-8859-15"); - QString qs = codec->toUnicode(p1->body); - qDebug() << "body: " << qs; - */ -} - -Mime::~Mime() -= default; - -void Mime::splitParts(QByteArray *message) { - int pos1, pos2, headEnd; - MimePart p_tmp; - - // find the boundary - pos1 = message->indexOf("boundary=\"") + 10; - pos2 = message->indexOf("\"\n", pos1); - QByteArray boundary = message->mid(pos1, pos2 - pos1); - //qDebug() << "boundary: " << boundary; - - while (pos2 > pos1) { - - pos1 = message->indexOf(boundary, pos2) + boundary.length() + 1; - headEnd = message->indexOf("\n\n", pos1); - if (headEnd < 0) - break; - QByteArray header = message->mid(pos1, headEnd - pos1); - - p_tmp.header = parseHeader(&header); - - pos2 = message->indexOf(boundary, headEnd); - p_tmp.body = message->mid(headEnd, pos2 - headEnd); - - mPartList.append(p_tmp); - } -} - -Header Mime::parseHeader(QByteArray *header) { - - QList<HeadElem> ret; - - /** http://www.aspnetmime.com/help/welcome/overviewmimeii.html : - * If a line starts with any white space, that line is said to be 'folded' and is actually - * part of the header above it. - */ - header->replace("\n ", " "); - - //split header at newlines - foreach(QByteArray line, header->split(*"\n")) { - HeadElem elem; - //split lines at : - QList<QByteArray> tmp2 = line.split(*":"); - elem.name = tmp2[0].trimmed(); - if (tmp2[1].contains(';')) { - // split lines at ; - // TODO: what if ; is inside "" - QList<QByteArray> tmp3 = tmp2[1].split(*";"); - elem.value = QString(tmp3.takeFirst().trimmed()); - foreach(QByteArray tmp4, tmp3) { - QList<QByteArray> tmp5 = tmp4.split(*"="); - elem.params.insert(QString(tmp5[0].trimmed()), QString(tmp5[1].trimmed())); - } - } else { - elem.value = tmp2[1].trimmed(); - } - ret.append(elem); - } - return Header(ret); -} - -Header Mime::getHeader(const QByteArray *message) { - int headEnd = message->indexOf("\n\n"); - QByteArray header = message->mid(0, headEnd); - return parseHeader(&header); -} - -bool Mime::isMultipart(QByteArray *message) { - return message->startsWith("Content-Type: multipart/mixed;"); -} - -/** - * if Content-Type is specified, it should be mime - * - */ -bool Mime::isMime(const QByteArray *message) { - return message->startsWith("Content-Type:"); -} - -/*** - * quotedPrintableDecode copied from KCodecs, where it is stated: - - The quoted-printable codec as described in RFC 2045, section 6.7. is by - Rik Hemsley (C) 2001. - - */ - -static const char hexChars[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' -}; - -/******************************** KCodecs ********************************/ -// strchr(3) for broken systems. -static int rikFindChar(const char *_s, const char c) { - const char *s = _s; - - while (true) { - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - if ((0 == *s) || (c == *s)) break; - ++s; - } - - return static_cast<int>(s - _s); -} - -void Mime::quotedPrintableDecode(const QByteArray &in, QByteArray &out) { - // clear out the output buffer - out.resize(0); - if (in.isEmpty()) - return; - - char *cursor; - const char *data; - const size_t length = in.size(); - - data = in.data(); - out.resize(static_cast<int>(length)); - cursor = out.data(); - - for (unsigned int i = 0; i < length; i++) { - char c(in[i]); - - if ('=' == c) { - if (i < length - 2) { - char c1 = in[i + 1]; - char c2 = in[i + 2]; - - if (('\n' == c1) || ('\r' == c1 && '\n' == c2)) { - // Soft line break. No output. - if ('\r' == c1) - i += 2; // CRLF line breaks - else - i += 1; - } else { - // =XX encoded byte. - - int hexChar0 = rikFindChar(hexChars, c1); - int hexChar1 = rikFindChar(hexChars, c2); - - if (hexChar0 < 16 && hexChar1 < 16) { - *cursor++ = char((hexChar0 * 16) | hexChar1); - i += 2; - } - } - } - } else { - *cursor++ = c; - } - } - - out.truncate(static_cast<int>(cursor - out.data())); -} diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp index 028653b7..7ec521a7 100644 --- a/src/gpg/GpgContext.cpp +++ b/src/gpg/GpgContext.cpp @@ -25,7 +25,6 @@ #include "gpg/GpgContext.h" #include <unistd.h> /* contains read/write */ -#include <Mime.h> #ifdef _WIN32 @@ -430,38 +429,6 @@ namespace GpgME { return err; } - - /** - * if this is mime, split text and attachments... - * message contains only text afterwards - */ - void parseMime(QByteArray *message) { - - QString pText; - bool show_ma_dock = false; - - Mime *mime = new Mime(message); - for (MimePart tmp : mime->parts()) { - if (tmp.header.getValue("Content-Type") == "text/plain" && - tmp.header.getValue("Content-Transfer-Encoding") != "base64") { - QByteArray body; - if (tmp.header.getValue("Content-Transfer-Encoding") == "quoted-printable") { - Mime::quotedPrintableDecode(tmp.body, body); - } else { - body = tmp.body; - } - pText.append(QString(body)); - } else { - // TODO - show_ma_dock = true; - } - } - *message = pText.toUtf8(); - if (show_ma_dock) { - // TODO - } - } - /** Decrypt QByteAarray, return QByteArray * mainly from http://basket.kde.org/ (kgpgme.cpp) */ @@ -496,28 +463,6 @@ namespace GpgME { gpgme_data_release(dataOut); } - /* - * 1) is it mime (content-type:) - * 2) parse header - * 2) choose action depending on content-type - */ - if (Mime::isMime(outBuffer)) { - Header header = Mime::getHeader(outBuffer); - // is it multipart, is multipart-parsing enabled - if (header.getValue("Content-Type") == "multipart/mixed" - && settings.value("mime/parseMime").toBool()) { - parseMime(outBuffer); - } else if (header.getValue("Content-Type") == "text/plain" - && settings.value("mime/parseQP").toBool()) { - if (header.getValue("Content-Transfer-Encoding") == "quoted-printable") { - auto *decoded = new QByteArray(); - Mime::quotedPrintableDecode(*outBuffer, *decoded); - //TODO: remove header - outBuffer = decoded; - } - } - } - if (result != nullptr) { *result = m_result; } diff --git a/src/smtp/CMakeLists.txt b/src/smtp/CMakeLists.txt new file mode 100644 index 00000000..8e341f98 --- /dev/null +++ b/src/smtp/CMakeLists.txt @@ -0,0 +1,6 @@ +aux_source_directory(. SMTP_MIME_SOURCE) + +add_library(smtp STATIC ${SMTP_MIME_SOURCE}) + +target_link_libraries(smtp + Qt5::Network Qt5::Core) diff --git a/src/smtp/emailaddress.cpp b/src/smtp/emailaddress.cpp new file mode 100644 index 00000000..c0ecaa0d --- /dev/null +++ b/src/smtp/emailaddress.cpp @@ -0,0 +1,44 @@ +/* + 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 "smtp/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/src/smtp/mimeattachment.cpp b/src/smtp/mimeattachment.cpp new file mode 100644 index 00000000..033eecfb --- /dev/null +++ b/src/smtp/mimeattachment.cpp @@ -0,0 +1,43 @@ +/* + 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 "smtp/mimeattachment.h" +#include <QFileInfo> + +/* [1] Constructors and Destructors */ + +MimeAttachment::MimeAttachment(QFile *file) : MimeFile(file) {} + +MimeAttachment::MimeAttachment(const QByteArray &stream, + const QString &fileName) + : MimeFile(stream, fileName) {} + +MimeAttachment::~MimeAttachment() = default; + +/* [1] --- */ + +/* [2] Protected methods */ + +void MimeAttachment::prepare() { + this->header += "Content-disposition: attachment\r\n"; + + /* !!! IMPORTANT !!! */ + MimeFile::prepare(); +} + +/* [2] --- */ diff --git a/src/smtp/mimecontentformatter.cpp b/src/smtp/mimecontentformatter.cpp new file mode 100644 index 00000000..8f538457 --- /dev/null +++ b/src/smtp/mimecontentformatter.cpp @@ -0,0 +1,59 @@ +/* + 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 "smtp/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 (auto i : content) { + chars++; + if (!quotedPrintable) { + if (chars > max_length) { + out.append("\r\n"); + chars = 1; + } + } else { + if (i == '\n') { // new line + out.append(i); + chars = 0; + continue; + } + + if ((chars > max_length - 1) || + ((i == '=') && (chars > max_length - 3))) { + out.append('='); + out.append("\r\n"); + chars = 1; + } + } + out.append(i); + } + + return out; +} + +void MimeContentFormatter::setMaxLength(int l) { max_length = l; } + +int MimeContentFormatter::getMaxLength() const { return max_length; } diff --git a/src/smtp/mimefile.cpp b/src/smtp/mimefile.cpp new file mode 100644 index 00000000..4f095b84 --- /dev/null +++ b/src/smtp/mimefile.cpp @@ -0,0 +1,61 @@ +/* + 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 "smtp/mimefile.h" +#include <QFileInfo> + +/* [1] Constructors and Destructors */ + +MimeFile::MimeFile(QFile *file) { + this->file = file; + this->cType = "application/octet-stream"; + this->cName = QFileInfo(*file).fileName(); + this->cEncoding = Base64; +} + +MimeFile::MimeFile(const QByteArray &stream, const QString &fileName) { + this->cEncoding = Base64; + this->cType = "application/octet-stream"; + this->file = nullptr; + this->cName = fileName; + this->content = stream; +} + +MimeFile::~MimeFile() { + 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/src/smtp/mimehtml.cpp b/src/smtp/mimehtml.cpp new file mode 100644 index 00000000..9f3a53c3 --- /dev/null +++ b/src/smtp/mimehtml.cpp @@ -0,0 +1,46 @@ +/* + 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 "smtp/mimehtml.h" + +/* [1] Constructors and Destructors */ + +MimeHtml::MimeHtml(const QString &html) : MimeText(html) { + this->cType = "text/html"; +} + +MimeHtml::~MimeHtml() = default; + +/* [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/src/smtp/mimeinlinefile.cpp b/src/smtp/mimeinlinefile.cpp new file mode 100644 index 00000000..a49167e8 --- /dev/null +++ b/src/smtp/mimeinlinefile.cpp @@ -0,0 +1,42 @@ +/* + 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 "smtp/mimeinlinefile.h" + +/* [1] Constructors and Destructors */ + +MimeInlineFile::MimeInlineFile(QFile *f) : MimeFile(f) {} + +MimeInlineFile::~MimeInlineFile() = default; + +/* [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/src/smtp/mimemessage.cpp b/src/smtp/mimemessage.cpp new file mode 100644 index 00000000..cf653e0a --- /dev/null +++ b/src/smtp/mimemessage.cpp @@ -0,0 +1,308 @@ +/* + 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 "smtp/mimemessage.h" +#include "smtp/quotedprintable.h" + +#include <QDateTime> +#include <QLocale> +#include <typeinfo> + +/* [1] Constructors and Destructors */ +MimeMessage::MimeMessage(bool createAutoMimeContent) + : replyTo(nullptr), 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::setReplyTo(EmailAddress *rto) { replyTo = rto; } + +void MimeMessage::setSender(EmailAddress *e) { + this->sender = e; + e->setParent(this); +} + +void MimeMessage::addRecipient(EmailAddress *rcpt, RecipientType type) { + switch (type) { + case To: + recipientsTo << rcpt; + break; + case Cc: + recipientsCc << rcpt; + break; + case Bcc: + recipientsBcc << rcpt; + break; + } + + rcpt->setParent(this); +} + +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::setInReplyTo(const QString &inReplyTo) { + mInReplyTo = inReplyTo; +} + +void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc) { + this->hEncoding = hEnc; +} + +const EmailAddress &MimeMessage::getSender() const { return *sender; } + +const QList<EmailAddress *> & +MimeMessage::getRecipients(RecipientType type) const { + switch (type) { + default: + case To: + return recipientsTo; + case Cc: + return recipientsCc; + case Bcc: + return recipientsBcc; + } +} + +const EmailAddress *MimeMessage::getReplyTo() const { return replyTo; } + +const QString &MimeMessage::getSubject() const { return subject; } + +const QList<MimePart *> &MimeMessage::getParts() const { + if (typeid(*content) == typeid(MimeMultiPart)) { + return ((MimeMultiPart *) content)->getParts(); + } else { + auto *res = new QList<MimePart *>(); + res->append(content); + return *res; + } +} + +/* [2] --- */ + +/* [3] Public Methods */ + +QString MimeMessage::toString() { + QString mime; + + /* =========== MIME HEADER ============ */ + + /* ---------- Sender / From ----------- */ + mime = "From:"; + if (sender->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append(sender->getName().toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append(sender->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + sender->getName(); + } + } + mime += " <" + sender->getAddress() + ">\r\n"; + /* ---------------------------------- */ + + /* ------- Recipients / To ---------- */ + mime += "To:"; + QList<EmailAddress *>::iterator it; + int i; + for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) { + if (i != 0) { + mime += ","; + } + + if ((*it)->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append((*it)->getName().toUtf8()).toBase64() + + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append((*it)->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + mime += "\r\n"; + /* ---------------------------------- */ + + /* ------- Recipients / Cc ---------- */ + if (!recipientsCc.empty()) { + 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().toUtf8()).toBase64() + + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode( + QByteArray().append((*it)->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + if (!recipientsCc.empty()) { + mime += "\r\n"; + } + /* ---------------------------------- */ + + /* ------------ Subject ------------- */ + mime += "Subject: "; + + switch (hEncoding) { + case MimePart::Base64: + mime += "=?utf-8?B?" + QByteArray().append(subject.toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += "=?utf-8?Q?" + + QuotedPrintable::encode(QByteArray().append(subject.toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += subject; + } + mime += "\r\n"; + /* ---------------------------------- */ + + /* ---------- Reply-To -------------- */ + if (replyTo) { + mime += "Reply-To: "; + if (replyTo->getName() != "") { + switch (hEncoding) { + case MimePart::Base64: + mime += " =?utf-8?B?" + + QByteArray().append(replyTo->getName().toUtf8()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + + QuotedPrintable::encode(QByteArray().append(replyTo->getName().toUtf8())) + .replace(' ', "_") + .replace(':', "=3A") + + "?="; + break; + default: + mime += " " + replyTo->getName(); + } + } + mime += " <" + replyTo->getAddress() + ">\r\n"; + } + + /* ---------------------------------- */ + + mime += "MIME-Version: 1.0\r\n"; + if (!mInReplyTo.isEmpty()) { + mime += "In-Reply-To: <" + mInReplyTo + ">\r\n"; + mime += "References: <" + mInReplyTo + ">\r\n"; + } + + QDateTime now = QDateTime::currentDateTime(); +#if QT_VERSION_MAJOR < 5 // Qt4 workaround since RFC2822Date isn't defined + QString shortDayName = + QLocale::c().dayName(now.date().dayOfWeek(), QLocale::ShortFormat); + QString shortMonthName = + QLocale::c().monthName(now.date().month(), QLocale::ShortFormat); + int utcOffset = now.secsTo(QDateTime(now.date(), now.time(), Qt::UTC)) / 60; + char timezoneSign = utcOffset >= 0 ? '+' : '-'; + utcOffset = utcOffset >= 0 ? utcOffset : -utcOffset; + QString timezone = QString("%1%2%3") + .arg(timezoneSign) + .arg(utcOffset / 60, 2, 10, QChar('0')) + .arg(utcOffset % 60, 2, 10, QChar('0')); + mime += QString("Date: %1\r\n") + .arg(now.toString("%1, dd %2 yyyy hh:mm:ss %3") + .arg(shortDayName) + .arg(shortMonthName) + .arg(timezone)); +#else // Qt5 supported + mime += QString("Date: %1\r\n").arg(now.toString(Qt::RFC2822Date)); +#endif // support RFC2822Date + + mime += content->toString(); + return mime; +} + +/* [3] --- */ diff --git a/src/smtp/mimemultipart.cpp b/src/smtp/mimemultipart.cpp new file mode 100644 index 00000000..14a813c2 --- /dev/null +++ b/src/smtp/mimemultipart.cpp @@ -0,0 +1,70 @@ +/* + 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 "smtp/mimemultipart.h" +#include <QCryptographicHash> +#include <QRandomGenerator> +#include <QTime> + +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; + + QRandomGenerator generator; + + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(QByteArray().append((char) generator.generate())); + cBoundary = md5.result().toHex(); +} + +void MimeMultiPart::addPart(MimePart *part) { parts.append(part); } + +const QList<MimePart *> &MimeMultiPart::getParts() const { return parts; } + +void MimeMultiPart::prepare() { + QList<MimePart *>::iterator it; + + content = ""; + for (it = parts.begin(); it != parts.end(); it++) { + content += QString("--" + cBoundary + "\r\n").toUtf8(); + (*it)->prepare(); + content += (*it)->toString().toUtf8(); + }; + + content += QString("--" + cBoundary + "--\r\n").toUtf8(); + + 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/src/smtp/mimepart.cpp b/src/smtp/mimepart.cpp new file mode 100644 index 00000000..5d33884d --- /dev/null +++ b/src/smtp/mimepart.cpp @@ -0,0 +1,159 @@ +/* + 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 "smtp/mimepart.h" +#include "smtp/quotedprintable.h" + +/* [1] Constructors and Destructors */ + +MimePart::MimePart() { + cEncoding = _7Bit; + prepared = false; + cBoundary = ""; +} + +/* [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/src/smtp/mimetext.cpp b/src/smtp/mimetext.cpp new file mode 100644 index 00000000..3726647e --- /dev/null +++ b/src/smtp/mimetext.cpp @@ -0,0 +1,52 @@ +/* + 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 "smtp/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() = default; + +/* [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.toUtf8()); + + /* !!! IMPORTANT !!! */ + MimePart::prepare(); +} + +/* [3] --- */ diff --git a/src/smtp/quotedprintable.cpp b/src/smtp/quotedprintable.cpp new file mode 100644 index 00000000..93e51122 --- /dev/null +++ b/src/smtp/quotedprintable.cpp @@ -0,0 +1,62 @@ +/* + 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 "smtp/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 (char i : input) { + byte = 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/src/smtp/smtpclient.cpp b/src/smtp/smtpclient.cpp new file mode 100644 index 00000000..63202998 --- /dev/null +++ b/src/smtp/smtpclient.cpp @@ -0,0 +1,407 @@ +/* + 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 "smtp/smtpclient.h" + +#include <QByteArray> +#include <QFileInfo> + +/* [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.toUtf8()) + .append((char) 0) + .append(password.toUtf8()) + .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.toUtf8()).toBase64()); + + // Wait for 334 + waitForResponse(); + if (responseCode != 334) { + emit smtpError(AuthenticationFailedError); + return false; + } + + // Send the password in base64 + sendMessage(QByteArray().append(password.toUtf8()).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) { + // Responce Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } catch (SendMessageTimeoutException) { + // Send Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } + + return true; +} + +bool SmtpClient::sendMail(MimeMessage &email) { + try { + // Send the MAIL command with the sender + sendMessage("MAIL FROM:<" + email.getSender().getAddress() + ">"); + + waitForResponse(); + + if (responseCode != 250) + return false; + + // Send RCPT command for each recipient + QList<EmailAddress *>::const_iterator it, itEnd; + // To (primary recipients) + for (it = email.getRecipients().begin(), + itEnd = email.getRecipients().end(); + it != itEnd; ++it) { + + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Cc (carbon copy) + for (it = email.getRecipients(MimeMessage::Cc).begin(), + itEnd = email.getRecipients(MimeMessage::Cc).end(); + it != itEnd; ++it) { + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Bcc (blind carbon copy) + for (it = email.getRecipients(MimeMessage::Bcc).begin(), + itEnd = email.getRecipients(MimeMessage::Bcc).end(); + it != itEnd; ++it) { + sendMessage("RCPT TO:<" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) + return false; + } + + // Send DATA command + sendMessage("DATA"); + waitForResponse(); + + if (responseCode != 354) + return false; + + sendMessage(email.toString()); + + // Send \r\n.\r\n to end the mail data + sendMessage("."); + + waitForResponse(); + + if (responseCode != 250) + return false; + } catch (ResponseTimeoutException) { + return false; + } catch (SendMessageTimeoutException) { + return false; + } + + return true; +} + +void SmtpClient::quit() { + try { + sendMessage("QUIT"); + } catch (SmtpClient::SendMessageTimeoutException) { + // Manually close the connection to the smtp server if message "QUIT" wasn't + // received by the smtp server + if (socket->state() == QAbstractSocket::ConnectedState || + socket->state() == QAbstractSocket::ConnectingState || + socket->state() == QAbstractSocket::HostLookupState) + socket->disconnectFromHost(); + } +} + +/* [3] --- */ + +/* [4] Protected methods */ + +void SmtpClient::waitForResponse() { + 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.leftRef(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) { + 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/src/ui/AttachmentTableModel.cpp b/src/ui/AttachmentTableModel.cpp deleted file mode 100644 index 79cbdf9b..00000000 --- a/src/ui/AttachmentTableModel.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/** - * This file is part of GPGFrontend. - * - * GPGFrontend is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Foobar 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <https://www.gnu.org/licenses/>. - * - * The initial version of the source code is inherited from gpg4usb-team. - * Their source code version also complies with GNU General Public License. - * - * The source code version of this software was modified and released - * by Saturneric<[email protected]> starting on May 12, 2021. - * - */ - -#include "ui/AttachmentTableModel.h" - -#include <utility> - -/** compare with http://doc.qt.nokia.com/4.6/itemviews-addressbook.html - */ - -AttachmentTableModel::AttachmentTableModel(QObject *parent) : - QAbstractTableModel(parent) { -} - -AttachmentTableModel::AttachmentTableModel(QList<MimePart> mimeparts, QObject *parent) : - QAbstractTableModel(parent) { - listOfMimeparts = std::move(mimeparts); -} - - -void AttachmentTableModel::add(const MimePart &mp) { - listOfMimeparts.append(mp); - //QModelIndex changedIndex0 = createIndex(listOfMimeparts.size(), 0); - //QModelIndex changedIndex1 = createIndex(listOfMimeparts.size(), 1); - - //emit(dataChanged(changedIndex0, changedIndex1)); - // TODO: check the data-changed signal - // reset(); -} - -MimePart AttachmentTableModel::getSelectedMimePart(QModelIndex index) { - return listOfMimeparts.at(index.row()); -} - -MimePart AttachmentTableModel::getMimePart(int index) { - return listOfMimeparts.at(index); -} - -/*QList<MimePart> AttachmentTableModel::getSelectedMimeParts(QModelIndexList indexes){ - - foreach(QModelIndex index, indexes) { - qDebug() << "ir: "<< index.row(); - } - -}*/ - -int AttachmentTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return listOfMimeparts.size(); -} - -int AttachmentTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return 2; -} - -QVariant AttachmentTableModel::data(const QModelIndex &index, int role) const { - - //qDebug() << "called, index: " << index.column(); - - if (!index.isValid()) - return QVariant(); - - if (index.row() >= listOfMimeparts.size() || index.row() < 0) - return QVariant(); - - if (role == Qt::DisplayRole) { - MimePart mp = listOfMimeparts.at(index.row()); - - if (index.column() == 0) - return mp.header.getParam("Content-Type", "name"); - if (index.column() == 1) - return mp.header.getValue("Content-Type"); - - } - - // set icon - // TODO more generic matching, e.g. for audio - if (role == Qt::DecorationRole && index.column() == 0) { - MimePart mp = listOfMimeparts.at(index.row()); - QString icon; - if (mp.header.getValue("Content-Type").startsWith("image")) { - icon = ":mimetypes/image-x-generic.png"; - } else { - icon = mp.header.getValue("Content-Type").replace("/", "-"); - icon = ":mimetypes/" + icon + ".png"; - } - if (!QFile::exists(icon)) icon = ":mimetypes/unknown.png"; - return QIcon(icon); - } - - return QVariant(); -} - -QVariant AttachmentTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - //qDebug() << "called, section: " << section; - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) { - switch (section) { - case 0: - return tr("Filename"); - - case 1: - return tr("Contenttype"); - - default: - return QVariant(); - } - } - return QVariant(); -} diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index d95a9bfe..7065e41c 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -145,13 +145,6 @@ void MainWindow::slotOpenSettingsDialog() { importButton->setToolButtonStyle(buttonStyle); fileEncButton->setToolButtonStyle(buttonStyle); - // Mime-settings - if (settings.value("mime/parseMime").toBool()) { - createAttachmentDock(); - } else if (attachmentDockCreated) { - closeAttachmentDock(); - } - // restart mainwindow if necessary if (getRestartNeeded()) { if (edit->maybeSaveAnyTab()) { diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 0f7f1040..21f6e3b7 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -447,26 +447,4 @@ void MainWindow::createDockWindows() { infoBoardDock->setWidget(infoBoard); infoBoardDock->widget()->layout()->setContentsMargins(0, 0, 0, 0); viewMenu->addAction(infoBoardDock->toggleViewAction()); - - /* Attachments-Dockwindow - */ - if (settings.value("mime/parseMime").toBool()) { - createAttachmentDock(); - } -} - -void MainWindow::createAttachmentDock() { - if (attachmentDockCreated) { - return; - } - mAttachments = new Attachments(); - attachmentDock = new QDockWidget(tr("Attached files:"), this); - attachmentDock->setObjectName("AttachmentDock"); - attachmentDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea); - addDockWidget(Qt::LeftDockWidgetArea, attachmentDock); - attachmentDock->setWidget(mAttachments); - // hide till attachment is decrypted - viewMenu->addAction(attachmentDock->toggleViewAction()); - attachmentDock->hide(); - attachmentDockCreated = true; } diff --git a/src/ui/widgets/Attachments.cpp b/src/ui/widgets/Attachments.cpp deleted file mode 100644 index 8f741f66..00000000 --- a/src/ui/widgets/Attachments.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * This file is part of GPGFrontend. - * - * GPGFrontend is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Foobar 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Foobar. If not, see <https://www.gnu.org/licenses/>. - * - * The initial version of the source code is inherited from gpg4usb-team. - * Their source code version also complies with GNU General Public License. - * - * The source code version of this software was modified and released - * by Saturneric<[email protected]> starting on May 12, 2021. - * - */ - -/* TODO: - * - check content encoding (base64 / quoted-printable) and apply appropriate opperation (maybe already in mime.cpp) - * - check memory usage, use less copy operations / more references - * - possibility to clear attachment-view , e.g. with decryption or encrypting a new message - * - save all: like in thunderbird, one folder, all files go there - */ - -/* - * - save, delete (clear) all - * - each line save & clear button - * - attached files to view-menu - * - also an open button, whichs should save file to tmp-folder, and open with correct app (via QDesktopServices) - */ - - -#include "ui/widgets/Attachments.h" - -Attachments::Attachments(QWidget *parent) - : QWidget(parent) { - table = new AttachmentTableModel(this); - - tableView = new QTableView; - tableView->setModel(table); - tableView->setSelectionBehavior(QAbstractItemView::SelectRows); - // only one entry should be selected at time - tableView->setSelectionMode(QAbstractItemView::SingleSelection); - tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); - tableView->setFocusPolicy(Qt::NoFocus); - tableView->setAlternatingRowColors(true); - tableView->verticalHeader()->hide(); - tableView->setShowGrid(false); - tableView->setColumnWidth(0, 300); - tableView->horizontalHeader()->setStretchLastSection(true); - - auto *layout = new QVBoxLayout; - layout->addWidget(tableView); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); - createActions(); - -} - -void Attachments::contextMenuEvent(QContextMenuEvent *event) { - QMenu menu(this); - menu.addAction(saveFileAct); - // enable open with only if allowed by user - if (settings.value("mime/openAttachment").toBool()) - menu.addAction(openFileAct); - - menu.exec(event->globalPos()); -} - -void Attachments::createActions() { - saveFileAct = new QAction(tr("Save File"), this); - saveFileAct->setToolTip(tr("Save this file")); - saveFileAct->setIcon(QIcon(":filesave.png")); - connect(saveFileAct, SIGNAL(triggered()), this, SLOT(slotSaveFile())); - - openFileAct = new QAction(tr("Open File"), this); - openFileAct->setToolTip(tr("Open this file")); - openFileAct->setIcon(QIcon(":fileopen.png")); - connect(openFileAct, SIGNAL(triggered()), this, SLOT(slotOpenFile())); - -} - -void Attachments::slotSaveFile() { - - QModelIndexList indexes = tableView->selectionModel()->selection().indexes(); - - if (indexes.empty()) { - return; - } - - // only singe-selection possible now: TODO: foreach - MimePart mp = table->getMimePart(indexes.at(0).row()); - QString filename = mp.header.getParam("Content-Type", "name"); - // TODO: find out why filename is quoted - filename.chop(1); - filename.remove(0, 1); - // TODO: check if really base64 - saveByteArrayToFile(QByteArray::fromBase64(mp.body), filename); - -} - -void Attachments::saveByteArrayToFile(QByteArray outBuffer, QString filename) { - - //QString path=""; - QString path = std::move(filename); - QString outfileName = QFileDialog::getSaveFileName(this, tr("Save File"), path); - - if (outfileName.isEmpty()) return; - - QFile outfile(outfileName); - if (!outfile.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("File"), - tr("Cannot write file %1:\n%2.") - .arg(outfileName) - .arg(outfile.errorString())); - return; - } - - QDataStream out(&outfile); - out.writeRawData(outBuffer.data(), outBuffer.length()); - outfile.close(); -} - -/** - * WIP: TODO: - * - create attachments dir if not existing - * - ask for cleanup of dir on exit - * - remove code-duplication with saveByteArrayToFile - */ -void Attachments::slotOpenFile() { - - // TODO: make attachmentdir constant or configurable - QString attachmentDir = qApp->applicationDirPath() + "/attachments/"; - //QDir p = QDir(qApp->applicationDirPath() + "/attachments/"); - if (!QDir(attachmentDir).exists()) { - QDir().mkpath(attachmentDir); - } - - QModelIndexList indexes = tableView->selectionModel()->selection().indexes(); - MimePart mp = table->getMimePart(indexes.at(0).row()); - -// qDebug() << "mime: " << mp.header.getValue("Content-Type"); - - QString filename = mp.header.getParam("Content-Type", "name"); - // TODO: find out why filename is quoted -// qDebug() << "file: " << filename; - filename.chop(1); - filename.remove(0, 1); - filename.prepend(attachmentDir); - - // qDebug() << "file: " << filename; - QByteArray outBuffer = QByteArray::fromBase64(mp.body); - - - QFile outfile(filename); - if (!outfile.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("File"), - tr("Cannot write file %1:\n%2.") - .arg(filename) - .arg(outfile.errorString())); - return; - } - - QDataStream out(&outfile); - out.writeRawData(outBuffer.data(), outBuffer.length()); - outfile.close(); - QDesktopServices::openUrl(QUrl("file://" + filename, QUrl::TolerantMode)); -} - -void Attachments::addMimePart(MimePart *mp) { - table->add(*mp); -} - |