File indexing completed on 2024-11-24 04:53:23

0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
0002    Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com>
0003 
0004    This file is part of the Trojita Qt IMAP e-mail client,
0005    http://trojita.flaska.net/
0006 
0007    This program is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU General Public License as
0009    published by the Free Software Foundation; either version 2 of
0010    the License or (at your option) version 3 or any later version
0011    accepted by the membership of KDE e.V. (or its successor approved
0012    by the membership of KDE e.V.), which shall act as a proxy
0013    defined in Section 14 of version 3 of the license.
0014 
0015    This program is distributed in the hope that it will be useful,
0016    but WITHOUT ANY WARRANTY; without even the implied warranty of
0017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018    GNU General Public License for more details.
0019 
0020    You should have received a copy of the GNU General Public License
0021    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0022 */
0023 #include "SMTP.h"
0024 #include "UiUtils/Formatting.h"
0025 
0026 namespace MSA
0027 {
0028 
0029 SMTP::SMTP(QObject *parent, const QString &host, quint16 port, bool encryptedConnect, bool startTls, bool auth,
0030            const QString &user):
0031     AbstractMSA(parent), host(host), port(port),
0032     encryptedConnect(encryptedConnect), startTls(startTls), auth(auth),
0033     user(user), failed(false), isWaitingForPassword(false), sendingMode(MODE_SMTP_INVALID)
0034 {
0035     qwwSmtp = new QwwSmtpClient(this);
0036     // FIXME: handle SSL errors in the same way as we handle IMAP TLS errors, with key pinning, etc.
0037     connect(qwwSmtp, &QwwSmtpClient::sslErrors, this, &SMTP::handleSslErrors);
0038     connect(qwwSmtp, &QwwSmtpClient::connected, this, &AbstractMSA::sending);
0039     connect(qwwSmtp, &QwwSmtpClient::done, this, &SMTP::handleDone);
0040     connect(qwwSmtp, &QwwSmtpClient::socketError, this, &SMTP::handleError);
0041     connect(qwwSmtp, &QwwSmtpClient::logReceived, this, [this](const QByteArray& data) {
0042         emit logged(Common::LogKind::LOG_IO_READ, QStringLiteral("SMTP"), QString::fromUtf8(data));
0043     });
0044     connect(qwwSmtp, &QwwSmtpClient::logSent, this, [this](const QByteArray& data) {
0045         emit logged(Common::LogKind::LOG_IO_WRITTEN, QStringLiteral("SMTP"), QString::fromUtf8(data));
0046     });
0047 }
0048 
0049 void SMTP::cancel()
0050 {
0051     qwwSmtp->disconnectFromHost();
0052     if (!failed) {
0053         failed = true;
0054         emit error(tr("Sending of the message was cancelled"));
0055     }
0056 }
0057 
0058 void SMTP::handleDone(bool ok)
0059 {
0060     if (failed) {
0061         // This is a duplicate notification. The QwwSmtpClient is known to send contradicting results, see e.g. bug 321272.
0062         return;
0063     }
0064     if (ok) {
0065         emit sent();
0066     } else {
0067         failed = true;
0068         if (qwwSmtp->errorString().isEmpty())
0069             emit error(tr("Sending of the message failed."));
0070         else
0071             emit error(tr("Sending of the message failed with the following error: %1").arg(qwwSmtp->errorString()));
0072     }
0073 }
0074 
0075 void SMTP::handleError(QAbstractSocket::SocketError err, const QString &msg)
0076 {
0077     Q_UNUSED(err);
0078     failed = true;
0079     emit error(msg);
0080 }
0081 
0082 void SMTP::handleSslErrors(const QList<QSslError>& errors)
0083 {
0084     auto msg = UiUtils::Formatting::sslErrorsToHtml(errors);
0085     emit error(tr("<p>Cannot send message due to an SSL/TLS error</p>\n%1").arg(msg));
0086 }
0087 
0088 void SMTP::setPassword(const QString &password)
0089 {
0090     pass = password;
0091     if (isWaitingForPassword)
0092         sendContinueGotPassword();
0093 }
0094 
0095 void SMTP::sendMail(const QByteArray &from, const QList<QByteArray> &to, const QByteArray &data)
0096 {
0097     this->from = from;
0098     this->to = to;
0099     this->data = data;
0100     this->sendingMode = MODE_SMTP_DATA;
0101     this->isWaitingForPassword = true;
0102     emit progressMax(data.size());
0103     emit progress(0);
0104     emit connecting();
0105     if (!auth || !pass.isEmpty()) {
0106         sendContinueGotPassword();
0107         return;
0108     }
0109     emit passwordRequested(user, host);
0110 }
0111 
0112 void SMTP::sendContinueGotPassword()
0113 {
0114     isWaitingForPassword = false;
0115     if (encryptedConnect)
0116         qwwSmtp->connectToHostEncrypted(host, port);
0117     else
0118         qwwSmtp->connectToHost(host, port);
0119     if (startTls)
0120         qwwSmtp->startTls();
0121     if (auth)
0122         qwwSmtp->authenticate(user, pass, QwwSmtpClient::AuthAny);
0123     emit sending(); // FIXME: later
0124     switch (sendingMode) {
0125     case MODE_SMTP_DATA:
0126         {
0127             //RFC5321 specifies to prepend a period to lines starting with a period in section 4.5.2
0128             if (data.startsWith('.'))
0129                 data.prepend('.');
0130             data.replace("\n.", "\n..");
0131             qwwSmtp->sendMail(from, to, data);
0132         }
0133         break;
0134     case MODE_SMTP_BURL:
0135         qwwSmtp->sendMailBurl(from, to, data);
0136         break;
0137     default:
0138         failed = true;
0139         emit error(tr("Unknown SMTP mode"));
0140         break;
0141     }
0142     qwwSmtp->disconnectFromHost();
0143 }
0144 
0145 bool SMTP::supportsBurl() const
0146 {
0147     return true;
0148 }
0149 
0150 void SMTP::sendBurl(const QByteArray &from, const QList<QByteArray> &to, const QByteArray &imapUrl)
0151 {
0152     this->from = from;
0153     this->to = to;
0154     this->data = imapUrl;
0155     this->sendingMode = MODE_SMTP_BURL;
0156     this->isWaitingForPassword = true;
0157     emit progressMax(1);
0158     emit progress(0);
0159     emit connecting();
0160     if (!auth || !pass.isEmpty()) {
0161         sendContinueGotPassword();
0162         return;
0163     }
0164     emit passwordRequested(user, host);
0165 }
0166 
0167 SMTPFactory::SMTPFactory(const QString &host, quint16 port, bool encryptedConnect, bool startTls,
0168                          bool auth, const QString &user):
0169     m_host(host), m_port(port), m_encryptedConnect(encryptedConnect), m_startTls(startTls),
0170     m_auth(auth), m_user(user)
0171 {
0172 }
0173 
0174 SMTPFactory::~SMTPFactory()
0175 {
0176 }
0177 
0178 AbstractMSA *SMTPFactory::create(QObject *parent) const
0179 {
0180     return new SMTP(parent, m_host, m_port, m_encryptedConnect, m_startTls, m_auth, m_user);
0181 }
0182 
0183 }