Git commit b2198b447ab2c4d048035aeacd71a8b05f3c53df by Daniel Vr=C3=A1til.
Committed on 31/07/2017 at 19:37.
Pushed by dvratil into branch 'master'.
Replace SMTP KIO Slave with KSMTP library
Instead of using a KIO Slave for sending emails via SMTP, for which
KIO wasn't really designed, use a KSMTP library. This has no
functional or performance impacts, but improves maintainability
of our SMTP code and opens doors to further improvements (like
native Gmail authentication)
Differential Revision: https://phabricator.kde.org/D6879
M +2 -1 CMakeLists.txt
D +0 -1 kioslave/.krazy
D +0 -3 kioslave/CMakeLists.txt
D +0 -1 kioslave/doc/CMakeLists.txt
D +0 -3 kioslave/doc/smtp/CMakeLists.txt
D +0 -23 kioslave/doc/smtp/index.docbook
D +0 -2 kioslave/src/CMakeLists.txt
D +0 -55 kioslave/src/common.h
D +0 -47 kioslave/src/smtp/CMakeLists.txt
D +0 -2 kioslave/src/smtp/Messages.sh
D +0 -11 kioslave/src/smtp/TODO
D +0 -121 kioslave/src/smtp/capabilities.cpp
D +0 -86 kioslave/src/smtp/capabilities.h
D +0 -648 kioslave/src/smtp/command.cpp
D +0 -331 kioslave/src/smtp/command.h
D +0 -33 kioslave/src/smtp/compliance.txt
D +0 -88 kioslave/src/smtp/kioslavesession.cpp
D +0 -49 kioslave/src/smtp/kioslavesession.h
D +0 -193 kioslave/src/smtp/request.cpp
D +0 -192 kioslave/src/smtp/request.h
D +0 -165 kioslave/src/smtp/response.cpp
D +0 -178 kioslave/src/smtp/response.h
D +0 -652 kioslave/src/smtp/smtp.cpp
D +0 -125 kioslave/src/smtp/smtp.h
D +0 -16 kioslave/src/smtp/smtp.protocol
D +0 -16 kioslave/src/smtp/smtps.protocol
D +0 -61 kioslave/src/smtp/smtpsessioninterface.cpp
D +0 -92 kioslave/src/smtp/smtpsessioninterface.h
D +0 -71 kioslave/src/smtp/tests/CMakeLists.txt
D +0 -131 kioslave/src/smtp/tests/fakesession.h
D +0 -211 kioslave/src/smtp/tests/interactivesmtpserver.cpp
D +0 -85 kioslave/src/smtp/tests/interactivesmtpserver.h
D +0 -83 kioslave/src/smtp/tests/requesttest.cpp
D +0 -37 kioslave/src/smtp/tests/requesttest.h
D +0 -26 kioslave/src/smtp/tests/test_capabilities.cpp
D +0 -676 kioslave/src/smtp/tests/test_commands.cpp
D +0 -89 kioslave/src/smtp/tests/test_headergeneration.cpp
D +0 -106 kioslave/src/smtp/tests/test_responseparser.cpp
D +0 -32 kioslave/src/smtp/tests/test_responseparser.h
D +0 -124 kioslave/src/smtp/transactionstate.cpp
D +0 -237 kioslave/src/smtp/transactionstate.h
M +6 -1 src/kmailtransport/plugins/smtp/CMakeLists.txt
A +19 -0 src/kmailtransport/plugins/smtp/autotests/CMakeLists.txt
A +232 -0 src/kmailtransport/plugins/smtp/autotests/fakeserver.cpp =
[License: LGPL (v2.1+)]
A +67 -0 src/kmailtransport/plugins/smtp/autotests/fakeserver.h [=
License: LGPL (v2.1+)]
A +132 -0 src/kmailtransport/plugins/smtp/autotests/smtpjobtest.cpp =
[License: LGPL (v2+)]
A +33 -0 src/kmailtransport/plugins/smtp/sessionuiproxy.h [Licens=
e: GPL (v2)]
M +171 -156 src/kmailtransport/plugins/smtp/smtpjob.cpp
M +4 -4 src/kmailtransport/plugins/smtp/smtpjob.h
https://commits.kde.org/kmailtransport/b2198b447ab2c4d048035aeacd71a8b05f3c=
53df
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 188c465..8161bbc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,7 @@ set(KMAILTRANSPORT_LIB_VERSION ${PIM_VERSION})
set(KMIME_LIB_VERSION "5.6.40")
set(AKONADI_LIB_VERSION "5.6.40")
set(AKONADIMIME_LIB_VERSION "5.6.40")
+set(KSMTP_LIB_VERSION "5.6.40")
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5MailTranspo=
rt")
=
########### Find packages ###########
@@ -42,6 +43,7 @@ find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED)
find_package(KF5Akonadi ${AKONADI_LIB_VERSION} CONFIG REQUIRED)
find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED)
+find_package(KPimSMTP ${KSMTP_LIB_VERSION} CONFIG VERSION)
=
add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII")
add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
@@ -61,7 +63,6 @@ endif(BUILD_TESTING)
########### Targets ###########
add_subdirectory(cmake)
add_subdirectory(src)
-add_subdirectory(kioslave)
=
install( FILES kmailtransport.renamecategories kmailtransport.categories D=
ESTINATION ${KDE_INSTALL_CONFDIR} )
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/kioslave/.krazy b/kioslave/.krazy
deleted file mode 100644
index 0b16e7f..0000000
--- a/kioslave/.krazy
+++ /dev/null
@@ -1 +0,0 @@
-SKIP /tests/
diff --git a/kioslave/CMakeLists.txt b/kioslave/CMakeLists.txt
deleted file mode 100644
index ec4e2e8..0000000
--- a/kioslave/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII")
-add_subdirectory(src)
-add_subdirectory(doc)
diff --git a/kioslave/doc/CMakeLists.txt b/kioslave/doc/CMakeLists.txt
deleted file mode 100644
index 1751e50..0000000
--- a/kioslave/doc/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(smtp)
diff --git a/kioslave/doc/smtp/CMakeLists.txt b/kioslave/doc/smtp/CMakeList=
s.txt
deleted file mode 100644
index 11d31fa..0000000
--- a/kioslave/doc/smtp/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-########### install files ###############
-kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_=
DOCBUNDLEDIR}/en SUBDIR kioslave5/smtp)
-
diff --git a/kioslave/doc/smtp/index.docbook b/kioslave/doc/smtp/index.docb=
ook
deleted file mode 100644
index 1939851..0000000
--- a/kioslave/doc/smtp/index.docbook
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-]>
- =
-
-smtp
-
-
-&Ferdinand.Gassauer; &Ferdinand.Gassauer.mail;
-
-
-
-
-A protocol to send mail from the client workstation to the mail server.
-
-
- See : Simple Mail Transfer=
Protocol .
-
-
-
diff --git a/kioslave/src/CMakeLists.txt b/kioslave/src/CMakeLists.txt
deleted file mode 100644
index 50ed732..0000000
--- a/kioslave/src/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-add_subdirectory(smtp)
-
diff --git a/kioslave/src/common.h b/kioslave/src/common.h
deleted file mode 100644
index 0b581e2..0000000
--- a/kioslave/src/common.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* This file is part of the KDE project
- Copyright (C) 2008 Jaros=C5=82aw Staniek
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-#ifndef _KIOSLAVE_COMMON_H
-#define _KIOSLAVE_COMMON_H
-
-#include
-#include
-#include
-#include
-
-extern "C" {
-#include
-}
-
-inline bool initSASL()
-{
-#ifdef Q_OS_WIN //krazy:exclude=3Dcpp
- for (const auto &path : QCoreApplication::libraryPaths()) {
- QDir dir(path);
- if (dir.exists(QStringLiteral("sasl2"))) {
- auto libInstallPath =3D QFile::encodeName(dir.absoluteFilePath=
(QStringLiteral("sasl2")));
- if (sasl_set_path(SASL_PATH_TYPE_PLUGIN, libInstallPath.data()=
) !=3D SASL_OK) {
- fprintf(stderr, "SASL path initialization failed!\n");
- return false;
- }
- break;
- }
- }
-#endif
-
- if (sasl_client_init(NULL) !=3D SASL_OK) {
- fprintf(stderr, "SASL library initialization failed!\n");
- return false;
- }
- return true;
-}
-
-#endif
diff --git a/kioslave/src/smtp/CMakeLists.txt b/kioslave/src/smtp/CMakeList=
s.txt
deleted file mode 100644
index e13aa63..0000000
--- a/kioslave/src/smtp/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-if (BUILD_TESTING)
-add_subdirectory(tests)
-endif() =
-set(smtp_optional_includes)
-set(smtp_optional_libs)
-
-if (Sasl2_FOUND)
- set(smtp_optional_includes ${smtp_optional_includes} ${Sasl2_INCLUDE_DI=
RS})
- set(smtp_optional_libs ${smtp_optional_libs} ${Sasl2_LIBRARIES})
-endif()
-
-
-include_directories( ${smtp_optional_includes} )
-
-
-########### next target ###############
-
-set(kio_smtp_PART_SRCS
- smtp.cpp
- request.cpp
- response.cpp
- capabilities.cpp
- command.cpp
- transactionstate.cpp
- smtpsessioninterface.cpp
- kioslavesession.cpp
-)
-
-ecm_qt_declare_logging_category(kio_smtp_PART_SRCS HEADER smtp_debug.h IDE=
NTIFIER SMTP_LOG CATEGORY_NAME org.kde.pim.smtp)
-
-add_library(kio_smtp MODULE ${kio_smtp_PART_SRCS})
-
-
-target_link_libraries(kio_smtp KF5::KIOCore KF5::I18n Qt5::Network ${smtp=
_optional_libs})
-if (WIN32)
- target_link_libraries(kio_smtp ws2_32)
-endif()
-set_target_properties(kio_smtp PROPERTIES OUTPUT_NAME "smtp")
-
-install(TARGETS kio_smtp DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kio/ )
-
-########### install files ###############
-
-install( FILES smtp.protocol smtps.protocol DESTINATION ${KDE_INSTALL_KS=
ERVICES5DIR} )
-
diff --git a/kioslave/src/smtp/Messages.sh b/kioslave/src/smtp/Messages.sh
deleted file mode 100644
index c0be647..0000000
--- a/kioslave/src/smtp/Messages.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-$XGETTEXT *.cpp -o $podir/kio_smtp.pot
diff --git a/kioslave/src/smtp/TODO b/kioslave/src/smtp/TODO
deleted file mode 100644
index cad79f1..0000000
--- a/kioslave/src/smtp/TODO
+++ /dev/null
@@ -1,11 +0,0 @@
-1. Double check the error handling and review error message in various
- failure modes.
-2. Implement the CHUNKING extension (rfc 3030; as soon as I find an
- SMTP server that supports it).
-3. Better error message (translated standard meanings of the known
- response codes, ENHANCEDSTATUSCODES extension (rfc2034)).
-4. (KIO) MultiPutJob to support pipelining across messages.
-5. Ged rid of slave's header generation after checking who on earth
- uses that...
-
-and further refactoring to make the code pleasant to look at ;-)
diff --git a/kioslave/src/smtp/capabilities.cpp b/kioslave/src/smtp/capabil=
ities.cpp
deleted file mode 100644
index 489a35c..0000000
--- a/kioslave/src/smtp/capabilities.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/* -*- c++ -*-
- capabilities.cc
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include "capabilities.h"
-#include "response.h"
-
-namespace KioSMTP {
-Capabilities Capabilities::fromResponse(const Response &ehlo)
-{
- Capabilities c;
-
- // first, check whether the response was valid and indicates success:
- if (!ehlo.isOk()
- || ehlo.code() / 10 !=3D 25 // ### restrict to 250 only?
- || ehlo.lines().empty()) {
- return c;
- }
-
- QCStringList l =3D ehlo.lines();
-
- for (QCStringList::const_iterator it =3D ++l.constBegin(), end(l.const=
End()); it !=3D end; ++it) {
- c.add(QString::fromLatin1(*it));
- }
-
- return c;
-}
-
-void Capabilities::add(const QString &cap, bool replace)
-{
- QStringList tokens =3D cap.toUpper().split(QLatin1Char(' '));
- if (tokens.empty()) {
- return;
- }
- QString name =3D tokens.front();
- tokens.pop_front();
- add(name, tokens, replace);
-}
-
-void Capabilities::add(const QString &name, const QStringList &args, bool =
replace)
-{
- if (replace) {
- mCapabilities[name] =3D args;
- } else {
- mCapabilities[name] +=3D args;
- }
-}
-
-QString Capabilities::createSpecialResponse(bool tls) const
-{
- QStringList result;
- if (tls) {
- result.push_back(QStringLiteral("STARTTLS"));
- }
- result +=3D saslMethodsQSL();
- if (have("PIPELINING")) {
- result.push_back(QStringLiteral("PIPELINING"));
- }
- if (have("8BITMIME")) {
- result.push_back(QStringLiteral("8BITMIME"));
- }
- if (have("SIZE")) {
- bool ok =3D false;
- unsigned int size =3D 0;
- if (!mCapabilities[QStringLiteral("SIZE")].isEmpty()) {
- size =3D mCapabilities[QStringLiteral("SIZE")].front().toUInt(=
&ok);
- }
- if (ok && !size) {
- result.push_back(QStringLiteral("SIZE=3D*")); // any size
- } else if (ok) {
- result.push_back(QStringLiteral("SIZE=3D%1").arg(size)); //=
fixed max
- } else {
- result.push_back(QStringLiteral("SIZE")); // indetermined
- }
- }
- return result.join(QLatin1Char(' '));
-}
-
-QStringList Capabilities::saslMethodsQSL() const
-{
- QStringList result;
- for (QMap::const_iterator it =3D mCapabilities.b=
egin(), end(mCapabilities.end());
- it !=3D end; ++it) {
- if (it.key() =3D=3D QLatin1String("AUTH")) {
- result +=3D it.value();
- } else if (it.key().startsWith(QLatin1String("AUTH=3D"))) {
- result.push_back(it.key().mid(qstrlen("AUTH=3D")));
- result +=3D it.value();
- }
- }
- result.removeDuplicates();
- return result;
-}
-} // namespace KioSMTP
diff --git a/kioslave/src/smtp/capabilities.h b/kioslave/src/smtp/capabilit=
ies.h
deleted file mode 100644
index fac2f9c..0000000
--- a/kioslave/src/smtp/capabilities.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- c++ -*-
- capabilities.h
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#ifndef __KIOSMTP_CAPABILITIES_H__
-#define __KIOSMTP_CAPABILITIES_H__
-
-#include
-
-#include
-
-namespace KioSMTP {
-class Response;
-
-class Capabilities
-{
-public:
- Capabilities()
- {
- }
-
- static Capabilities fromResponse(const Response &response);
-
- void add(const QString &cap, bool replace =3D false);
- void add(const QString &name, const QStringList &args, bool replace =
=3D false);
- void clear()
- {
- mCapabilities.clear();
- }
-
- bool have(const QString &cap) const
- {
- return mCapabilities.find(cap.toUpper()) !=3D mCapabilities.end();
- }
-
- bool have(const QByteArray &cap) const
- {
- return have(QString::fromLatin1(cap));
- }
-
- bool have(const char *cap) const
- {
- return have(QString::fromLatin1(cap));
- }
-
- QString asMetaDataString() const;
-
- QString authMethodMetaData() const;
-
- QString createSpecialResponse(bool tls) const;
-
- QStringList saslMethodsQSL() const;
-private:
-
- QMap mCapabilities;
-};
-} // namespace KioSMTP
-
-#endif // __KIOSMTP_CAPABILITIES_H__
diff --git a/kioslave/src/smtp/command.cpp b/kioslave/src/smtp/command.cpp
deleted file mode 100644
index 60d0bb7..0000000
--- a/kioslave/src/smtp/command.cpp
+++ /dev/null
@@ -1,648 +0,0 @@
-/* -*- c++ -*-
- command.cc
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include "command.h"
-#include "smtp_debug.h"
-
-#include "smtpsessioninterface.h"
-#include "response.h"
-#include "transactionstate.h"
-
-#include
-#include // for test_commands, where SMTPProtocol is not=
derived from TCPSlaveBase
-
-#include
-
-#include
-
-namespace KioSMTP {
-static const sasl_callback_t callbacks[] =3D {
- { SASL_CB_ECHOPROMPT, nullptr, nullptr },
- { SASL_CB_NOECHOPROMPT, nullptr, nullptr },
- { SASL_CB_GETREALM, nullptr, nullptr },
- { SASL_CB_USER, nullptr, nullptr },
- { SASL_CB_AUTHNAME, nullptr, nullptr },
- { SASL_CB_PASS, nullptr, nullptr },
- { SASL_CB_CANON_USER, nullptr, nullptr },
- { SASL_CB_LIST_END, nullptr, nullptr }
-};
-
-//
-// Command (base class)
-//
-
-Command::Command(SMTPSessionInterface *smtp, int flags)
- : mSMTP(smtp)
- , mComplete(false)
- , mNeedResponse(false)
- , mFlags(flags)
-{
- assert(smtp);
-}
-
-Command::~Command()
-{
-}
-
-bool Command::processResponse(const Response &r, TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D false;
- return r.isOk();
-}
-
-void Command::ungetCommandLine(const QByteArray &cmdLine, TransactionState=
*ts)
-{
- Q_UNUSED(cmdLine)
- Q_UNUSED(ts)
- mComplete =3D false;
-}
-
-Command *Command::createSimpleCommand(int which, SMTPSessionInterface *smt=
p)
-{
- switch (which) {
- case STARTTLS:
- return new StartTLSCommand(smtp);
- case DATA:
- return new DataCommand(smtp);
- case NOOP:
- return new NoopCommand(smtp);
- case RSET:
- return new RsetCommand(smtp);
- case QUIT:
- return new QuitCommand(smtp);
- default:
- return nullptr;
- }
-}
-
-//
-// relay methods:
-//
-
-void Command::parseFeatures(const Response &r)
-{
- mSMTP->parseFeatures(r);
-}
-
-int Command::startSsl()
-{
- return mSMTP->startSsl();
-}
-
-bool Command::haveCapability(const char *cap) const
-{
- return mSMTP->haveCapability(cap);
-}
-
-//
-// EHLO / HELO
-//
-
-QByteArray EHLOCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse =3D true;
- mComplete =3D mEHLONotSupported;
- const char *cmd =3D mEHLONotSupported ? "HELO " : "EHLO ";
- return cmd + QUrl::toAce(mHostname) + "\r\n"; //krazy:exclude=3Dqcla=
sses
-}
-
-bool EHLOCommand::processResponse(const Response &r, TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse =3D false;
- // "command not {recognized,implemented}" response:
- if (r.code() =3D=3D 500 || r.code() =3D=3D 502) {
- if (mEHLONotSupported) { // HELO failed...
- mSMTP->error(KIO::ERR_INTERNAL_SERVER,
- i18n("The server rejected both EHLO and HELO comm=
ands "
- "as unknown or unimplemented.\n"
- "Please contact the server's system administ=
rator."));
- return false;
- }
- mEHLONotSupported =3D true; // EHLO failed, but that's ok.
- return true;
- }
- mComplete =3D true;
- if (r.code() / 10 =3D=3D 25) { // 25x: success
- parseFeatures(r);
- return true;
- }
- mSMTP->error(KIO::ERR_UNKNOWN,
- i18n("Unexpected server response to %1 command.\n%2",
- (mEHLONotSupported ? QStringLiteral("HELO") : QStrin=
gLiteral("EHLO")),
- r.errorMessage()));
- return false;
-}
-
-//
-// STARTTLS - rfc 3207
-//
-
-QByteArray StartTLSCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- return QByteArrayLiteral("STARTTLS\r\n");
-}
-
-bool StartTLSCommand::processResponse(const Response &r, TransactionState =
*ts)
-{
- Q_UNUSED(ts)
- mNeedResponse =3D false;
- if (r.code() !=3D 220) {
- mSMTP->error(r.errorCode(),
- i18n("Your SMTP server does not support TLS. "
- "Disable TLS, if you want to connect "
- "without encryption."));
- return false;
- }
-
- if (startSsl()) {
- return true;
- } else {
- //qCDebug(SMTP_LOG) << "TLS negotiation failed!";
- mSMTP->informationMessageBox(
- i18n("Your SMTP server claims to "
- "support TLS, but negotiation "
- "was unsuccessful.\nYou can "
- "disable TLS in the SMTP account settings dialog."),
- i18n("Connection Failed"));
- return false;
- }
-}
-
-#define SASLERROR mSMTP->error(KIO::ERR_COULD_NOT_AUTHENTICATE, \
- i18n("An error occurred during authenticati=
on: %1", \
- QString::fromUtf8(sasl_errdetail(conn)=
)));
-
-//
-// AUTH - rfc 2554
-//
-AuthCommand::AuthCommand(SMTPSessionInterface *smtp, const char *mechanism=
s, const QString &aFQDN, KIO::AuthInfo &ai)
- : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
- , mAi(&ai)
- , mFirstTime(true)
-{
- mMechusing =3D nullptr;
- int result;
- conn =3D nullptr;
- client_interact =3D nullptr;
- mOut =3D nullptr;
- mOutlen =3D 0;
- mOneStep =3D false;
-
- const QByteArray ba =3D aFQDN.toLatin1();
- result =3D sasl_client_new("smtp", ba.constData(),
- nullptr, nullptr, callbacks, 0, &conn);
- if (result !=3D SASL_OK) {
- SASLERROR
- return;
- }
- do {
- result =3D sasl_client_start(conn, mechanisms,
- &client_interact, &mOut, &mOutlen, &mMe=
chusing);
-
- if (result =3D=3D SASL_INTERACT) {
- if (!saslInteract(client_interact)) {
- return;
- }
- }
- } while (result =3D=3D SASL_INTERACT);
- if (result !=3D SASL_CONTINUE && result !=3D SASL_OK) {
- SASLERROR
- return;
- }
- if (result =3D=3D SASL_OK) {
- mOneStep =3D true;
- }
- qCDebug(SMTP_LOG) << "Mechanism: " << mMechusing << " one step: " << m=
OneStep;
-}
-
-AuthCommand::~AuthCommand()
-{
- if (conn) {
- qCDebug(SMTP_LOG) << "dispose sasl connection";
- sasl_dispose(&conn);
- conn =3D nullptr;
- }
-}
-
-bool AuthCommand::saslInteract(void *in)
-{
- qCDebug(SMTP_LOG) << "saslInteract: ";
- sasl_interact_t *interact =3D (sasl_interact_t *)in;
-
- //some mechanisms do not require username && pass, so don't need a pop=
up
- //window for getting this info
- for (; interact->id !=3D SASL_CB_LIST_END; ++interact) {
- if (interact->id =3D=3D SASL_CB_AUTHNAME
- || interact->id =3D=3D SASL_CB_PASS) {
- if (mAi->username.isEmpty() || mAi->password.isEmpty()) {
- if (!mSMTP->openPasswordDialog(*mAi)) {
- mSMTP->error(KIO::ERR_ABORTED, i18n("No authentication=
details supplied."));
- return false;
- }
- }
- break;
- }
- }
-
- interact =3D (sasl_interact_t *)in;
- while (interact->id !=3D SASL_CB_LIST_END) {
- switch (interact->id) {
- case SASL_CB_USER:
- case SASL_CB_AUTHNAME:
- {
- qCDebug(SMTP_LOG) << "SASL_CB_[USER|AUTHNAME]: " << mAi->usern=
ame;
- const QByteArray baUserName =3D mAi->username.toUtf8();
- interact->result =3D strdup(baUserName.constData());
- interact->len =3D strlen((const char *)interact->result);
- break;
- }
- case SASL_CB_PASS:
- {
- qCDebug(SMTP_LOG) << "SASL_CB_PASS: [HIDDEN]";
- const QByteArray baPassword =3D mAi->password.toUtf8();
- interact->result =3D strdup(baPassword.constData());
- interact->len =3D strlen((const char *)interact->result);
- break;
- }
- default:
- interact->result =3D nullptr;
- interact->len =3D 0;
- break;
- }
- interact++;
- }
- return true;
-}
-
-bool AuthCommand::doNotExecute(const TransactionState *ts) const
-{
- Q_UNUSED(ts)
- return !mMechusing;
-}
-
-void AuthCommand::ungetCommandLine(const QByteArray &s, TransactionState *=
ts)
-{
- Q_UNUSED(ts)
- mUngetSASLResponse =3D s;
- mComplete =3D false;
-}
-
-QByteArray AuthCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse =3D true;
- QByteArray cmd;
-
- QByteArray challenge;
- if (!mUngetSASLResponse.isNull()) {
- // implement un-ungetCommandLine
- cmd =3D mUngetSASLResponse;
- mUngetSASLResponse =3D nullptr;
- } else if (mFirstTime) {
- QString firstCommand =3D QLatin1String("AUTH ") + QString::fromLat=
in1(mMechusing);
-
- challenge =3D QByteArray::fromRawData(mOut, mOutlen).toBase64();
- if (!challenge.isEmpty()) {
- firstCommand +=3D QLatin1Char(' ');
- firstCommand +=3D QString::fromLatin1(challenge.data(), challe=
nge.size());
- }
- cmd =3D firstCommand.toLatin1();
-
- if (mOneStep) {
- mComplete =3D true;
- }
- } else {
-// qCDebug(SMTP_LOG) << "SS: '" << mLastChallenge << "'";
- challenge =3D QByteArray::fromBase64(mLastChallenge);
- int result;
- do {
- result =3D sasl_client_step(conn, challenge.isEmpty() ? nullpt=
r : challenge.data(),
- challenge.size(),
- &client_interact,
- &mOut, &mOutlen);
- if (result =3D=3D SASL_INTERACT) {
- if (!saslInteract(client_interact)) {
- return "";
- }
- }
- } while (result =3D=3D SASL_INTERACT);
- if (result !=3D SASL_CONTINUE && result !=3D SASL_OK) {
- qCDebug(SMTP_LOG) << "sasl_client_step failed with: " << resul=
t;
- SASLERROR
- return "";
- }
- cmd =3D QByteArray::fromRawData(mOut, mOutlen).toBase64();
-
-// qCDebug(SMTP_LOG) << "CC: '" << cmd << "'";
- mComplete =3D (result =3D=3D SASL_OK);
- }
- cmd +=3D QByteArrayLiteral("\r\n");
- return cmd;
-}
-
-bool AuthCommand::processResponse(const Response &r, TransactionState *ts)
-{
- Q_UNUSED(ts)
- if (!r.isOk()) {
- if (mFirstTime) {
- if (haveCapability("AUTH")) {
- QString chooseADifferentMsg(i18n("Choose a different authe=
ntication method."));
- mSMTP->error(KIO::ERR_COULD_NOT_LOGIN,
- (mMechusing ? i18n("Your SMTP server does not=
support %1.", QString::fromLatin1(mMechusing))
- : i18n("Your SMTP server does not support (u=
nspecified method)."))
- + QLatin1Char('\n') + chooseADifferentMsg + Q=
Latin1Char('\n') + r.errorMessage());
- } else {
- mSMTP->error(KIO::ERR_COULD_NOT_LOGIN,
- i18n("Your SMTP server does not support authe=
ntication.\n"
- "%1", r.errorMessage()));
- }
- } else {
- mSMTP->error(KIO::ERR_COULD_NOT_LOGIN,
- i18n("Authentication failed.\n"
- "Most likely the password is wrong.\n"
- "%1", r.errorMessage()));
- }
- return false;
- }
- mFirstTime =3D false;
- mLastChallenge =3D r.lines().at(0); // ### better join all lines with =
\n?
- mNeedResponse =3D false;
- return true;
-}
-
-//
-// MAIL FROM:
-//
-
-QByteArray MailFromCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- QByteArray cmdLine =3D QByteArrayLiteral("MAIL FROM:<") + mAddr + '>';
- if (m8Bit && haveCapability("8BITMIME")) {
- cmdLine +=3D QByteArrayLiteral(" BODY=3D8BITMIME");
- }
- if (mSize && haveCapability("SIZE")) {
- cmdLine +=3D QByteArrayLiteral(" SIZE=3D") + QByteArray().setNum(m=
Size);
- }
- return cmdLine + QByteArrayLiteral("\r\n");
-}
-
-bool MailFromCommand::processResponse(const Response &r, TransactionState =
*ts)
-{
- assert(ts);
- mNeedResponse =3D false;
-
- if (r.code() =3D=3D 250) {
- return true;
- }
-
- ts->setMailFromFailed(QString::fromLatin1(mAddr), r);
- return false;
-}
-
-//
-// RCPT TO:
-//
-
-QByteArray RcptToCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- return QByteArrayLiteral("RCPT TO:<") + mAddr + QByteArrayLiteral(">\r=
\n");
-}
-
-bool RcptToCommand::processResponse(const Response &r, TransactionState *t=
s)
-{
- assert(ts);
- mNeedResponse =3D false;
-
- if (r.code() =3D=3D 250) {
- ts->setRecipientAccepted();
- return true;
- }
-
- ts->addRejectedRecipient(QString::fromLatin1(mAddr), r.errorMessage());
- return false;
-}
-
-//
-// DATA (only initial processing!)
-//
-
-QByteArray DataCommand::nextCommandLine(TransactionState *ts)
-{
- assert(ts);
- mComplete =3D true;
- mNeedResponse =3D true;
- ts->setDataCommandIssued(true);
- return QByteArrayLiteral("DATA\r\n");
-}
-
-void DataCommand::ungetCommandLine(const QByteArray &cmd, TransactionState=
*ts)
-{
- Q_UNUSED(cmd)
- assert(ts);
- mComplete =3D false;
- ts->setDataCommandIssued(false);
-}
-
-bool DataCommand::processResponse(const Response &r, TransactionState *ts)
-{
- assert(ts);
- mNeedResponse =3D false;
-
- if (r.code() =3D=3D 354) {
- ts->setDataCommandSucceeded(true, r);
- return true;
- }
-
- ts->setDataCommandSucceeded(false, r);
- return false;
-}
-
-//
-// DATA (data transfer)
-//
-void TransferCommand::ungetCommandLine(const QByteArray &cmd, TransactionS=
tate *ts)
-{
- Q_UNUSED(ts)
- if (cmd.isEmpty()) {
- return; // don't change state when we can't detect the unget in
- }
- // the next nextCommandLine !!
- mWasComplete =3D mComplete;
- mComplete =3D false;
- mNeedResponse =3D false;
- mUngetBuffer =3D cmd;
-}
-
-bool TransferCommand::doNotExecute(const TransactionState *ts) const
-{
- assert(ts);
- return ts->failed();
-}
-
-QByteArray TransferCommand::nextCommandLine(TransactionState *ts)
-{
- assert(ts); // let's rely on it ( at least for the moment )
- assert(!isComplete());
- assert(!ts->failed());
-
- static const QByteArray dotCRLF =3D QByteArrayLiteral(".\r\n");
- static const QByteArray CRLFdotCRLF =3D QByteArrayLiteral("\r\n.\r\n");
-
- if (!mUngetBuffer.isEmpty()) {
- const QByteArray ret =3D mUngetBuffer;
- mUngetBuffer =3D nullptr;
- if (mWasComplete) {
- mComplete =3D true;
- mNeedResponse =3D true;
- }
- return ret; // don't prepare(), it's slave-generated or already pr=
epare()d
- }
-
- // normal processing:
-
- qCDebug(SMTP_LOG) << "requesting data";
- mSMTP->dataReq();
- QByteArray ba;
- int result =3D mSMTP->readData(ba);
- qCDebug(SMTP_LOG) << "got " << result << " bytes";
- if (result > 0) {
- return prepare(ba);
- } else if (result < 0) {
- ts->setFailedFatally(KIO::ERR_INTERNAL,
- i18n("Could not read data from application.")=
);
- mComplete =3D true;
- mNeedResponse =3D true;
- return nullptr;
- }
- mComplete =3D true;
- mNeedResponse =3D true;
- return mLastChar =3D=3D '\n' ? dotCRLF : CRLFdotCRLF;
-}
-
-bool TransferCommand::processResponse(const Response &r, TransactionState =
*ts)
-{
- mNeedResponse =3D false;
- assert(ts);
- ts->setComplete();
- if (!r.isOk()) {
- ts->setFailed();
- mSMTP->error(r.errorCode(),
- i18n("The message content was not accepted.\n"
- "%1", r.errorMessage()));
- return false;
- }
- return true;
-}
-
-static QByteArray dotstuff_lf2crlf(const QByteArray &ba, char &last)
-{
- QByteArray result(ba.size() * 2 + 1, 0); // worst case: repeated "[.=
]\n"
- const char *s =3D ba.data();
- const char *const send =3D ba.data() + ba.size();
- char *d =3D result.data();
-
- while (s < send) {
- const char ch =3D *s++;
- if (ch =3D=3D '\n' && last !=3D '\r') {
- *d++ =3D '\r'; // lf2crlf
- } else if (ch =3D=3D '.' && last =3D=3D '\n') {
- *d++ =3D '.'; // dotstuff
- }
- last =3D *d++ =3D ch;
- }
-
- result.truncate(d - result.data());
- return result;
-}
-
-QByteArray TransferCommand::prepare(const QByteArray &ba)
-{
- if (ba.isEmpty()) {
- return nullptr;
- }
- if (mSMTP->lf2crlfAndDotStuffingRequested()) {
- qCDebug(SMTP_LOG) << "performing dotstuffing and LF->CRLF transfor=
mation";
- return dotstuff_lf2crlf(ba, mLastChar);
- } else {
- mLastChar =3D ba[ba.size() - 1];
- return ba;
- }
-}
-
-//
-// NOOP
-//
-
-QByteArray NoopCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- return QByteArrayLiteral("NOOP\r\n");
-}
-
-//
-// RSET
-//
-
-QByteArray RsetCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- return QByteArrayLiteral("RSET\r\n");
-}
-
-//
-// QUIT
-//
-
-QByteArray QuitCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete =3D true;
- mNeedResponse =3D true;
- return QByteArrayLiteral("QUIT\r\n");
-}
-} // namespace KioSMTP
diff --git a/kioslave/src/smtp/command.h b/kioslave/src/smtp/command.h
deleted file mode 100644
index 1bbc0c8..0000000
--- a/kioslave/src/smtp/command.h
+++ /dev/null
@@ -1,331 +0,0 @@
-/* -*- c++ -*-
- command.h
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#ifndef __KIOSMTP_COMMAND_H__
-#define __KIOSMTP_COMMAND_H__
-
-// workaround a bug in Cyrus-SASL 2.1.26 which is missing sys/types.h
-// include in sasl.h
-#include
-extern "C" {
-#include
-}
-
-#include
-
-namespace KioSMTP {
-class Response;
-class TransactionState;
-class SMTPSessionInterface;
-
-/**
- * @short Represents an SMTP command
- *
- * Semantics: A command consists of a series of "command lines"
- * (though that's stretching it a bit for @ref TransferJob and @ref
- * AuthCommand) and responses. There's typically one response for
- * one command line and the command is completed.
- *
- * However, some commands consist of a dialog (command line,
- * response, command line, response,...) where each successive
- * command line is dependant on the previously received response
- * (and thus those commands are not pipelinable). That's why each
- * command signals completion by having it's @ref #isComplete()
- * method return true @em after the last command line to be sent,
- * but @em before the last response to receive. @ref AuthCommand is
- * the principal representative of this kind of command. Because
- * @ref EHLOCommand automatically falls back to HELO in case EHLO
- * isn't supported, it is also of this kind. If completion is
- * signalled before the first command line is issued, it is not to
- * be executed at all.
- *
- * Other commands need to send multiple "command lines" before
- * receiving a single (final) response. @ref TransferCommand is the
- * only representative of this kind of "command". That's why each
- * command signals whether it now expects a response before being
- * able to issue the next command line (if any) by having it's @ref
- * #needsResponse() method return true.
- *
- * Commands whose @ref #nextCommandLine() does not support being
- * called multiple times in a row without changing command state,
- * must reimplement @ref #ungetCommandLine().
- **/
-class Command
-{
-public:
- enum Flags {
- OnlyLastInPipeline =3D 1,
- OnlyFirstInPipeline =3D 2,
- CloseConnectionOnError =3D 4
- };
-
- explicit Command(SMTPSessionInterface *smtp, int flags =3D 0);
- virtual ~Command();
-
- enum Type {
- STARTTLS,
- DATA,
- NOOP,
- RSET,
- QUIT
- };
-
- static Command *createSimpleCommand(int which, SMTPSessionInterface *s=
mtp);
-
- virtual QByteArray nextCommandLine(TransactionState *ts =3D nullptr) =
=3D 0;
- /* Reimplement this if your @ref #nextCommandLine() implementation
- changes state other than @ref mComplete. The default
- implementation just resets @ref mComplete to false. */
- virtual void ungetCommandLine(const QByteArray &cmdLine, TransactionSt=
ate *ts =3D nullptr);
- /* Reimplement this if your command need more sophisicated
- response processing than just checking for @ref
- Response::isOk(). The default implementation sets @ref
- mComplete to true, @ref mNeedResponse to false and returns
- whether the response isOk(). */
- virtual bool processResponse(const Response &response, TransactionStat=
e *ts =3D nullptr);
-
- virtual bool doNotExecute(const TransactionState *ts) const
- {
- Q_UNUSED(ts)
- return false;
- }
-
- bool isComplete() const
- {
- return mComplete;
- }
-
- /**
- * @return whether the command expects a response now. Some
- * commands (most notably AUTH) may consist of a series of
- * commands and associated responses until they are
- * complete. Others (most notably @ref TransferCommand usually
- * send multiple "command lines" before expecting a response.
- */
- bool needsResponse() const
- {
- return mNeedResponse;
- }
-
- /**
- * @return whether an error in executing this command is so fatal
- * that closing the connection is the only option
- */
- bool closeConnectionOnError() const
- {
- return mFlags & CloseConnectionOnError;
- }
-
- bool mustBeLastInPipeline() const
- {
- return mFlags & OnlyLastInPipeline;
- }
-
- bool mustBeFirstInPipeline() const
- {
- return mFlags & OnlyFirstInPipeline;
- }
-
-protected:
- SMTPSessionInterface *mSMTP;
- bool mComplete;
- bool mNeedResponse;
- const int mFlags;
-
-protected:
- // only relay methods to enable access to slave-protected methods
- // for subclasses of Command:
- void parseFeatures(const Response &r);
- int startSsl();
- bool haveCapability(const char *cap) const;
-};
-
-class EHLOCommand : public Command
-{
-public:
- EHLOCommand(SMTPSessionInterface *smtp, const QString &hostname)
- : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
- , mEHLONotSupported(false)
- , mHostname(hostname.trimmed())
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-private:
- bool mEHLONotSupported;
- QString mHostname;
-};
-
-class StartTLSCommand : public Command
-{
-public:
- StartTLSCommand(SMTPSessionInterface *smtp)
- : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-};
-
-class AuthCommand : public Command
-{
-public:
- AuthCommand(SMTPSessionInterface *smtp, const char *mechanisms, const =
QString &aFQDN, KIO::AuthInfo &ai);
- ~AuthCommand();
- bool doNotExecute(const TransactionState *ts) const override;
- QByteArray nextCommandLine(TransactionState *ts) override;
- void ungetCommandLine(const QByteArray &cmdLine, TransactionState *ts)=
override;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-private:
- bool saslInteract(void *in);
-
- sasl_conn_t *conn;
- sasl_interact_t *client_interact;
- const char *mOut;
- uint mOutlen;
- bool mOneStep;
-
- const char *mMechusing;
- KIO::AuthInfo *mAi;
- QByteArray mLastChallenge;
- QByteArray mUngetSASLResponse;
- bool mFirstTime;
-};
-
-class MailFromCommand : public Command
-{
-public:
- MailFromCommand(SMTPSessionInterface *smtp, const QByteArray &addr, bo=
ol eightBit =3D false, unsigned int size =3D 0)
- : Command(smtp)
- , mAddr(addr)
- , m8Bit(eightBit)
- , mSize(size)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-private:
- QByteArray mAddr;
- bool m8Bit;
- unsigned int mSize;
-};
-
-class RcptToCommand : public Command
-{
-public:
- RcptToCommand(SMTPSessionInterface *smtp, const QByteArray &addr)
- : Command(smtp)
- , mAddr(addr)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-private:
- QByteArray mAddr;
-};
-
-/** Handles only the initial intermediate response and compltetes at
- the point where the mail contents need to be sent */
-class DataCommand : public Command
-{
-public:
- DataCommand(SMTPSessionInterface *smtp)
- : Command(smtp, OnlyLastInPipeline)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- void ungetCommandLine(const QByteArray &cmd, TransactionState *ts) ove=
rride;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-};
-
-/** Handles the data transfer following a successful DATA command */
-class TransferCommand : public Command
-{
-public:
- TransferCommand(SMTPSessionInterface *smtp, const QByteArray &initialB=
uffer)
- : Command(smtp, OnlyFirstInPipeline)
- , mUngetBuffer(initialBuffer)
- , mLastChar('\n')
- , mWasComplete(false)
- {
- }
-
- bool doNotExecute(const TransactionState *ts) const override;
- QByteArray nextCommandLine(TransactionState *ts) override;
- void ungetCommandLine(const QByteArray &cmd, TransactionState *ts) ove=
rride;
- bool processResponse(const Response &response, TransactionState *ts) o=
verride;
-private:
- QByteArray prepare(const QByteArray &ba);
- QByteArray mUngetBuffer;
- char mLastChar;
- bool mWasComplete; // ... before ungetting
-};
-
-class NoopCommand : public Command
-{
-public:
- NoopCommand(SMTPSessionInterface *smtp)
- : Command(smtp, OnlyLastInPipeline)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
-};
-
-class RsetCommand : public Command
-{
-public:
- RsetCommand(SMTPSessionInterface *smtp)
- : Command(smtp, CloseConnectionOnError)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
-};
-
-class QuitCommand : public Command
-{
-public:
- QuitCommand(SMTPSessionInterface *smtp)
- : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
-};
-} // namespace KioSMTP
-
-#endif // __KIOSMTP_COMMAND_H__
diff --git a/kioslave/src/smtp/compliance.txt b/kioslave/src/smtp/complianc=
e.txt
deleted file mode 100644
index b6b9874..0000000
--- a/kioslave/src/smtp/compliance.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-The SMTP kioslave currently conforms to the following SMTP-related RFCs:
-
-Base Spec:
-2821 Simple Mail Transfer Protocol. J. Klensin, Ed.. April 2001.
- (Format: TXT=3D192504 bytes) (Obsoletes RFC0821, RFC0974, RFC1869)
- (Status: PROPOSED STANDARD)
-
-Encryption/Auth:
-3207 SMTP Service Extension for Secure SMTP over Transport Layer
- Security. P. Hoffman. February 2002. (Format: TXT=3D18679 bytes)
- (Obsoletes RFC2487) (Status: PROPOSED STANDARD)
-
-2554 SMTP Service Extension for Authentication. J. Myers. March 1999.
- (Format: TXT=3D20534 bytes) (Status: PROPOSED STANDARD)
-(with all SASL mechanisms supported by KDESasl)
-
-General:
-1652 SMTP Service Extension for 8bit-MIMEtransport. J. Klensin, N.
- Freed, M. Rose, E. Stefferud, D. Crocker. July 1994. (Format:
- TXT=3D11842 bytes) (Obsoletes RFC1426) (Status: DRAFT STANDARD)
-
-1870 SMTP Service Extension for Message Size Declaration. J. Klensin,
- N. Freed, K. Moore. November 1995. (Format: TXT=3D18226 bytes)
- (Obsoletes RFC1653) (Also STD0010) (Status: STANDARD)
-
-2920 SMTP Service Extension for Command Pipelining. N. Freed.
- September 2000. (Format: TXT=3D17065 bytes) (Obsoletes RFC2197) (Also
- STD0060) (Status: STANDARD)
-
-Known shortcomings:
-- Doesn't enforce the CRLF lineending convention on user-supplied data.
-- Due to the lack of a Mulit_Put_ in the KIO infrastructure, pipelining
- across messages isn't supported.
diff --git a/kioslave/src/smtp/kioslavesession.cpp b/kioslave/src/smtp/kios=
lavesession.cpp
deleted file mode 100644
index 3c770bd..0000000
--- a/kioslave/src/smtp/kioslavesession.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- Copyright (c) 2010 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#include "kioslavesession.h"
-
-using namespace KioSMTP;
-
-KioSMTP::KioSlaveSession::KioSlaveSession(SMTPProtocol *protocol)
- : m_protocol(protocol)
-{
-}
-
-void KioSMTP::KioSlaveSession::error(int id, const QString &msg)
-{
- m_protocol->error(id, msg);
-}
-
-void KioSlaveSession::informationMessageBox(const QString &msg, const QStr=
ing &caption)
-{
- m_protocol->messageBox(KIO::SlaveBase::Information, msg, caption);
-}
-
-bool KioSMTP::KioSlaveSession::openPasswordDialog(KIO::AuthInfo &authInfo)
-{
- return m_protocol->openPasswordDialog(authInfo);
-}
-
-void KioSMTP::KioSlaveSession::dataReq()
-{
- m_protocol->dataReq();
-}
-
-int KioSMTP::KioSlaveSession::readData(QByteArray &ba)
-{
- return m_protocol->readData(ba);
-}
-
-bool KioSMTP::KioSlaveSession::startSsl()
-{
- return m_protocol->startSsl();
-}
-
-bool KioSlaveSession::eightBitMimeRequested() const
-{
- return m_protocol->metaData(QStringLiteral("8bitmime")) =3D=3D QLatin1=
String("on");
-}
-
-bool KioSlaveSession::lf2crlfAndDotStuffingRequested() const
-{
- return m_protocol->metaData(QStringLiteral("lf2crlf+dotstuff")) =3D=3D=
QLatin1String("slave");
-}
-
-bool KioSlaveSession::pipeliningRequested() const
-{
- return m_protocol->metaData(QStringLiteral("pipelining")) !=3D QLatin1=
String("off");
-}
-
-QString KioSlaveSession::requestedSaslMethod() const
-{
- return m_protocol->metaData(QStringLiteral("sasl"));
-}
-
-KioSMTP::SMTPSessionInterface::TLSRequestState KioSMTP::KioSlaveSession::t=
lsRequested() const
-{
- if (m_protocol->metaData(QStringLiteral("tls")) =3D=3D QLatin1String("=
off")) {
- return ForceNoTLS;
- }
- if (m_protocol->metaData(QStringLiteral("tls")) =3D=3D QLatin1String("=
on")) {
- return ForceTLS;
- }
- return UseTLSIfAvailable;
-}
diff --git a/kioslave/src/smtp/kioslavesession.h b/kioslave/src/smtp/kiosla=
vesession.h
deleted file mode 100644
index 772b82c..0000000
--- a/kioslave/src/smtp/kioslavesession.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- Copyright (c) 2010 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#ifndef KIOSMTP_KIOSLAVESESSION_H
-#define KIOSMTP_KIOSLAVESESSION_H
-
-#include "smtpsessioninterface.h"
-#include "smtp.h"
-
-namespace KioSMTP {
-class KioSlaveSession : public SMTPSessionInterface
-{
-public:
- explicit KioSlaveSession(SMTPProtocol *protocol);
- void error(int id, const QString &msg) override;
- void informationMessageBox(const QString &msg, const QString &caption)=
override;
- bool openPasswordDialog(KIO::AuthInfo &authInfo) override;
- void dataReq() override;
- int readData(QByteArray &ba) override;
- bool startSsl() override;
-
- QString requestedSaslMethod() const override;
- bool eightBitMimeRequested() const override;
- bool lf2crlfAndDotStuffingRequested() const override;
- bool pipeliningRequested() const override;
- TLSRequestState tlsRequested() const override;
-
-private:
- SMTPProtocol *m_protocol;
-};
-}
-
-#endif
diff --git a/kioslave/src/smtp/request.cpp b/kioslave/src/smtp/request.cpp
deleted file mode 100644
index edbca5c..0000000
--- a/kioslave/src/smtp/request.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/* -*- c++ -*-
- request.cc
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include "request.h"
-#include "smtp_debug.h"
-
-#include
-#include
-#include
-
-#include
-
-namespace KioSMTP {
-Request Request::fromURL(const QUrl &url)
-{
- Request request;
-
- const QStringList query =3D url.query().split(QLatin1Char('&'));
-#ifndef NDEBUG
- qCDebug(SMTP_LOG) << "Parsing request from query:\n" << query.join(QLa=
tin1Char('\n'));
-#endif
- for (QStringList::const_iterator it =3D query.begin(), end(query.end()=
); it !=3D end; ++it) {
- int equalsPos =3D (*it).indexOf(QLatin1Char('=3D'));
- if (equalsPos <=3D 0) {
- continue;
- }
-
- const QString key =3D (*it).left(equalsPos).toLower();
- const QString value =3D QUrl::fromPercentEncoding((*it).mid(equals=
Pos + 1).toLatin1()); //krazy:exclude=3Dqclasses
-
- if (key =3D=3D QLatin1String("to")) {
- request.addTo(value);
- } else if (key =3D=3D QLatin1String("cc")) {
- request.addCc(value);
- } else if (key =3D=3D QLatin1String("bcc")) {
- request.addBcc(value);
- } else if (key =3D=3D QLatin1String("headers")) {
- request.setEmitHeaders(value =3D=3D QLatin1String("0"));
- request.setEmitHeaders(false); // ### ???
- } else if (key =3D=3D QLatin1String("subject")) {
- request.setSubject(value);
- } else if (key =3D=3D QLatin1String("from")) {
- request.setFromAddress(value);
- } else if (key =3D=3D QLatin1String("profile")) {
- request.setProfileName(value);
- } else if (key =3D=3D QLatin1String("hostname")) {
- request.setHeloHostname(value);
- } else if (key =3D=3D QLatin1String("body")) {
- request.set8BitBody(value.toUpper() =3D=3D QLatin1String("8BIT=
"));
- } else if (key =3D=3D QLatin1String("size")) {
- request.setSize(value.toUInt());
- } else {
- qCWarning(SMTP_LOG) << "while parsing query: unknown query ite=
m \""
- << key << "\" with value \"" << value << "=
\"" << endl;
- }
- }
-
- return request;
-}
-
-QByteArray Request::heloHostnameCString() const
-{
- return QUrl::toAce(heloHostname()); //krazy:exclude=3Dqclasses
-}
-
-static bool isUsAscii(const QString &s)
-{
- for (int i =3D 0; i < s.length(); ++i) {
- if (s[i].unicode() > 127) {
- return false;
- }
- }
- return true;
-}
-
-static inline bool isSpecial(char ch)
-{
- static const QByteArray specials =3D "()<>[]:;@\\,.\"";
- return specials.indexOf(ch) >=3D 0;
-}
-
-static inline bool needsQuoting(char ch)
-{
- return ch =3D=3D '\\' || ch =3D=3D '"' || ch =3D=3D '\n';
-}
-
-static inline QByteArray rfc2047Encode(const QString &s)
-{
- QByteArray r =3D s.trimmed().toUtf8().toBase64();
- return "=3D?utf-8?b?" + r + "?=3D"; // use base64 since that always gi=
ves a valid encoded-word
-}
-
-static QByteArray quote(const QString &s)
-{
- assert(isUsAscii(s));
-
- QByteArray r(s.length() * 2, 0);
- bool needsQuotes =3D false;
-
- unsigned int j =3D 0;
- for (int i =3D 0; i < s.length(); ++i) {
- char ch =3D s[i].toLatin1();
- if (isSpecial(ch)) {
- if (needsQuoting(ch)) {
- r[j++] =3D '\\';
- }
- needsQuotes =3D true;
- }
- r[j++] =3D ch;
- }
- r.truncate(j);
-
- if (needsQuotes) {
- return '"' + r + '"';
- } else {
- return r;
- }
-}
-
-static QByteArray formatFromAddress(const QString &fromRealName, const QSt=
ring &fromAddress)
-{
- if (fromRealName.isEmpty()) {
- return fromAddress.toLatin1(); // no real name: return "joe@use=
r.org"
- }
-
- // return "Joe User ", "\"User, Joe\" "
- // or "=3D?utf-8?q?Joe_User?=3D ", depending on real nam=
e's nature.
- QByteArray r =3D isUsAscii(fromRealName) ? quote(fromRealName) : rfc20=
47Encode(fromRealName);
- return r + " <" + fromAddress.toLatin1() + '>';
-}
-
-static QByteArray formatSubject(QString s)
-{
- if (isUsAscii(s)) {
- return s.remove(QLatin1Char('\n')).toLatin1(); // don't break h=
eader folding,
- } else {
- // so remove any line break
- // that happen to be around
- return rfc2047Encode(s);
- }
-}
-
-QByteArray Request::headerFields(const QString &fromRealName) const
-{
- if (!emitHeaders()) {
- return nullptr;
- }
-
- assert(hasFromAddress()); // should have been checked for by
- // caller (MAIL FROM comes before DATA)
-
- QByteArray result =3D QByteArrayLiteral("From: ") + formatFromAddress(=
fromRealName, fromAddress()) + QByteArrayLiteral("\r\n");
-
- if (!subject().isEmpty()) {
- result +=3D QByteArrayLiteral("Subject: ") + formatSubject(subject=
()) + QByteArrayLiteral("\r\n");
- }
- if (!to().empty()) {
- result +=3D QByteArrayLiteral("To: ") + to().join(QStringLiteral("=
,\r\n\t") /* line folding */).toLatin1() + "\r\n";
- }
- if (!cc().empty()) {
- result +=3D QByteArrayLiteral("Cc: ") + cc().join(QStringLiteral("=
,\r\n\t") /* line folding */).toLatin1() + "\r\n";
- }
- return result;
-}
-} // namespace KioSMTP
diff --git a/kioslave/src/smtp/request.h b/kioslave/src/smtp/request.h
deleted file mode 100644
index 35ed528..0000000
--- a/kioslave/src/smtp/request.h
+++ /dev/null
@@ -1,192 +0,0 @@
-/* -*- c++ -*-
- request.h
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#ifndef __KIOSMTP_REQUEST_H__
-#define __KIOSMTP_REQUEST_H__
-
-#include
-#include
-
-class QUrl;
-
-namespace KioSMTP {
-class Request
-{
-public:
- Request()
- : mSubject(QStringLiteral("missing subject"))
- , mEmitHeaders(true)
- , m8Bit(false)
- , mSize(0)
- {
- }
-
- static Request fromURL(const QUrl &url);
-
- QString profileName() const
- {
- return mProfileName;
- }
-
- void setProfileName(const QString &profileName)
- {
- mProfileName =3D profileName;
- }
-
- bool hasProfile() const
- {
- return !profileName().isNull();
- }
-
- QString subject() const
- {
- return mSubject;
- }
-
- void setSubject(const QString &subject)
- {
- mSubject =3D subject;
- }
-
- QString fromAddress() const
- {
- return mFromAddress;
- }
-
- void setFromAddress(const QString &fromAddress)
- {
- mFromAddress =3D fromAddress;
- }
-
- bool hasFromAddress() const
- {
- return !mFromAddress.isEmpty();
- }
-
- QStringList recipients() const
- {
- return to() + cc() + bcc();
- }
-
- bool hasRecipients() const
- {
- return !to().empty() || !cc().empty() || !bcc().empty();
- }
-
- QStringList to() const
- {
- return mTo;
- }
-
- QStringList cc() const
- {
- return mCc;
- }
-
- QStringList bcc() const
- {
- return mBcc;
- }
-
- void addTo(const QString &to)
- {
- mTo.push_back(to);
- }
-
- void addCc(const QString &cc)
- {
- mCc.push_back(cc);
- }
-
- void addBcc(const QString &bcc)
- {
- mBcc.push_back(bcc);
- }
-
- QString heloHostname() const
- {
- return mHeloHostname;
- }
-
- QByteArray heloHostnameCString() const;
- void setHeloHostname(const QString &hostname)
- {
- mHeloHostname =3D hostname;
- }
-
- bool emitHeaders() const
- {
- return mEmitHeaders;
- }
-
- void setEmitHeaders(bool emitHeaders)
- {
- mEmitHeaders =3D emitHeaders;
- }
-
- bool is8BitBody() const
- {
- return m8Bit;
- }
-
- void set8BitBody(bool a8Bit)
- {
- m8Bit =3D a8Bit;
- }
-
- unsigned int size() const
- {
- return mSize;
- }
-
- void setSize(unsigned int size)
- {
- mSize =3D size;
- }
-
- /**
- * If @ref #emitHeaders() is true, returns the rfc2822
- * serialization of the header fields "To", "Cc", "Subject" and
- * "From", as determined by the respective settings. If @ref
- * #emitHeaders() is false, returns a null string.
- */
- QByteArray headerFields(const QString &fromRealName =3D QString()) con=
st;
-
-private:
- QStringList mTo, mCc, mBcc;
- QString mProfileName, mSubject, mFromAddress, mHeloHostname;
- bool mEmitHeaders;
- bool m8Bit;
- unsigned int mSize;
-};
-} // namespace KioSMTP
-
-#endif // __KIOSMTP_REQUEST_H__
diff --git a/kioslave/src/smtp/response.cpp b/kioslave/src/smtp/response.cpp
deleted file mode 100644
index c12f824..0000000
--- a/kioslave/src/smtp/response.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/* -*- c++ -*-
- response.cc
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include "response.h"
-
-#include
-#include
-
-#include
-
-namespace KioSMTP {
-void Response::parseLine(const char *line, int len)
-{
- if (!isWellFormed()) {
- return; // don't bother
- }
-
- if (isComplete()) {
- // if the response is already complete, there can't be another line
- mValid =3D false;
- }
-
- if (len > 1 && line[len - 1] =3D=3D '\n' && line[len - 2] =3D=3D '\r')=
{
- len -=3D 2;
- }
-
- if (len < 3) {
- // can't be valid - too short
- mValid =3D false;
- mWellFormed =3D false;
- return;
- }
-
- bool ok =3D false;
- unsigned int code =3D QByteArray(line, 3).toUInt(&ok);
- if (!ok || code < 100 || code > 559) {
- // not a number or number out of range
- mValid =3D false;
- if (!ok || code < 100) {
- mWellFormed =3D false;
- }
- return;
- }
- if (mCode && code !=3D mCode) {
- // different codes in one response are not allowed.
- mValid =3D false;
- return;
- }
- mCode =3D code;
-
- if (len =3D=3D 3 || line[3] =3D=3D ' ') {
- mSawLastLine =3D true;
- } else if (line[3] !=3D '-') {
- // code must be followed by either SP or hyphen (len =3D=3D 3 is
- // also accepted since broken servers exist); all else is
- // invalid
- mValid =3D false;
- mWellFormed =3D false;
- return;
- }
-
- mLines.push_back(len > 4 ? QByteArray(line + 4, len - 4).trimmed() : Q=
ByteArray());
-}
-
-// hackishly fixing QCStringList flaws...
-static QByteArray join(char sep, const QCStringList &list)
-{
- if (list.empty()) {
- return QByteArray();
- }
- QByteArray result =3D list.front();
- for (QCStringList::const_iterator it =3D ++list.begin(), end(list.end(=
)); it !=3D end; ++it) {
- result +=3D sep + *it;
- }
- return result;
-}
-
-QString Response::errorMessage() const
-{
- QString msg;
- if (lines().count() > 1) {
- msg =3D i18n("The server responded:\n%1", QString::fromLatin1(join=
('\n', lines())));
- } else {
- msg =3D i18n("The server responded: \"%1\"", QString::fromLatin1(l=
ines().at(0)));
- }
- if (first() =3D=3D 4) {
- msg +=3D QLatin1Char('\n') + i18n("This is a temporary failure. Yo=
u may try again later.");
- }
- return msg;
-}
-
-int Response::errorCode() const
-{
- switch (code()) {
- case 421: // Service not available, closing transmission channel
- case 454: // TLS not available due to temporary reason
- // Temporary authentication failure
- case 554: // Transaction failed / No SMTP service here / No valid reci=
pients
- return KIO::ERR_SERVICE_NOT_AVAILABLE;
-
- case 451: // Requested action aborted: local error in processing
- return KIO::ERR_INTERNAL_SERVER;
-
- case 452: // Requested action not taken: insufficient system storage
- case 552: // Requested mail action aborted: exceeded storage allocation
- return KIO::ERR_DISK_FULL;
-
- case 500: // Syntax error, command unrecognized
- case 501: // Syntax error in parameters or arguments
- case 502: // Command not implemented
- case 503: // Bad sequence of commands
- case 504: // Command parameter not implemented
- return KIO::ERR_INTERNAL;
-
- case 450: // Requested mail action not taken: mailbox unavailable
- case 550: // Requested action not taken: mailbox unavailable
- case 551: // User not local; please try
- case 553: // Requested action not taken: mailbox name not allowed
- return KIO::ERR_DOES_NOT_EXIST;
-
- case 530: // {STARTTLS,Authentication} required
- case 538: // Encryption required for requested authentication mechanism
- case 534: // Authentication mechanism is too weak
- return KIO::ERR_UPGRADE_REQUIRED;
-
- case 432: // A password transition is needed
- return KIO::ERR_COULD_NOT_AUTHENTICATE;
-
- default:
- if (isPositive()) {
- return 0;
- } else {
- return KIO::ERR_UNKNOWN;
- }
- }
-}
-} // namespace KioSMTP
diff --git a/kioslave/src/smtp/response.h b/kioslave/src/smtp/response.h
deleted file mode 100644
index 9f5acd7..0000000
--- a/kioslave/src/smtp/response.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/* -*- c++ -*-
- response.h
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#ifndef __KIOSMTP_RESPONSE_H__
-#define __KIOSMTP_RESPONSE_H__
-
-#include
-#include
-typedef QList QCStringList;
-
-class QString;
-
-namespace KioSMTP {
-class Response
-{
-public:
- Response()
- : mCode(0)
- , mValid(true)
- , mSawLastLine(false)
- , mWellFormed(true)
- {
- }
-
- void parseLine(const char *line)
- {
- parseLine(line, qstrlen(line));
- }
-
- void parseLine(const char *line, int len);
-
- /** Return an internationalized error message according to the
- * response's code. */
- QString errorMessage() const;
- /** Translate the SMTP error code into a KIO one */
- int errorCode() const;
-
- enum Reply {
- UnknownReply =3D -1,
- PositivePreliminary =3D 1,
- PositiveCompletion =3D 2,
- PositiveIntermediate =3D 3,
- TransientNegative =3D 4,
- PermanentNegative =3D 5
- };
-
- enum Category {
- UnknownCategory =3D -1,
- SyntaxError =3D 0,
- Information =3D 1,
- Connections =3D 2,
- MailSystem =3D 5
- };
-
- unsigned int code() const
- {
- return mCode;
- }
-
- unsigned int first() const
- {
- return code() / 100;
- }
-
- unsigned int second() const
- {
- return (code() % 100) / 10;
- }
-
- unsigned int third() const
- {
- return code() % 10;
- }
-
- bool isPositive() const
- {
- return first() <=3D 3 && first() >=3D 1;
- }
-
- bool isNegative() const
- {
- return first() =3D=3D 4 || first() =3D=3D 5;
- }
-
- bool isUnknown() const
- {
- return !isPositive() && !isNegative();
- }
-
- QCStringList lines() const
- {
- return mLines;
- }
-
- bool isValid() const
- {
- return mValid;
- }
-
- bool isComplete() const
- {
- return mSawLastLine;
- }
-
- /** Shortcut method.
- * @return true iff the response is valid, complete and positive
- */
- bool isOk() const
- {
- return isValid() && isComplete() && isPositive();
- }
-
- /** Indicates whether the response was well-formed, meaning it
- * obeyed the syntax of smtp responses. That the response
- * nevertheless is not valid may be caused by e.g. different
- * response codes in a multilie response. A non-well-formed
- * response is never valid.
- */
- bool isWellFormed() const
- {
- return mWellFormed;
- }
-
- void clear()
- {
- *this =3D Response();
- }
-
-#ifdef KIOSMTP_COMPARATORS
- bool operator=3D=3D(const Response &other) const
- {
- return mCode =3D=3D other.mCode
- && mValid =3D=3D other.mValid
- && mSawLastLine =3D=3D other.mSawLastLine
- && mWellFormed =3D=3D other.mWellFormed
- && mLines =3D=3D other.mLines;
- }
-
-#endif
-
-private:
- unsigned int mCode;
- QCStringList mLines;
- bool mValid;
- bool mSawLastLine;
- bool mWellFormed;
-};
-} // namespace KioSMTP
-
-#endif // __KIOSMTP_RESPONSE_H__
diff --git a/kioslave/src/smtp/smtp.cpp b/kioslave/src/smtp/smtp.cpp
deleted file mode 100644
index 180c721..0000000
--- a/kioslave/src/smtp/smtp.cpp
+++ /dev/null
@@ -1,652 +0,0 @@
-/*
- * Copyright (c) 2000, 2001 Alex Zepeda
- * Copyright (c) 2001 Michael H=EF=BF=BDkel
- * Copyright (c) 2002 Aaron J. Seigo
- * Copyright (c) 2003 Marc Mutz
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURP=
OSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENT=
IAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STR=
ICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY W=
AY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#include "smtp.h"
-#include "smtp_debug.h"
-
-extern "C" {
-#include
-}
-
-#include "../common.h"
-#include "request.h"
-#include "response.h"
-#include "transactionstate.h"
-#include "command.h"
-#include "kioslavesession.h"
-using KioSMTP::Capabilities;
-using KioSMTP::Command;
-using KioSMTP::EHLOCommand;
-using KioSMTP::AuthCommand;
-using KioSMTP::MailFromCommand;
-using KioSMTP::RcptToCommand;
-using KioSMTP::DataCommand;
-using KioSMTP::TransferCommand;
-using KioSMTP::Request;
-using KioSMTP::Response;
-using KioSMTP::TransactionState;
-using KioSMTP::SMTPSessionInterface;
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-using std::unique_ptr;
-
-#include
-#include
-#include
-#include
-#include
-#ifdef Q_OS_WIN
-#include
-#else
-#include
-#endif
-
-extern "C" {
-Q_DECL_EXPORT int kdemain(int argc, char **argv);
-}
-
-int kdemain(int argc, char **argv)
-{
- QCoreApplication app(argc, argv);
- app.setApplicationName(QStringLiteral("kio_smtp"));
-
- if (argc !=3D 4) {
- fprintf(stderr,
- "Usage: kio_smtp protocol domain-socket1 domain-socket2\n"=
);
- exit(-1);
- }
-
- if (!initSASL()) {
- exit(-1);
- }
- SMTPProtocol slave(argv[2], argv[3], qstricmp(argv[1], "smtps") =3D=3D=
0);
- slave.dispatchLoop();
- sasl_done();
- return 0;
-}
-
-SMTPProtocol::SMTPProtocol(const QByteArray &pool, const QByteArray &app, =
bool useSSL)
- : TCPSlaveBase(useSSL ? "smtps" : "smtp", pool, app, useSSL)
- , m_sOldPort(0)
- , m_opened(false)
- , m_sessionIface(new KioSMTP::KioSlaveSession(this))
-{
- //qCDebug(SMTP_LOG) << "SMTPProtocol::SMTPProtocol";
-}
-
-SMTPProtocol::~SMTPProtocol()
-{
- //qCDebug(SMTP_LOG) << "SMTPProtocol::~SMTPProtocol";
- smtp_close();
- delete m_sessionIface;
-}
-
-void SMTPProtocol::openConnection()
-{
- // Don't actually call smtp_open() yet. Just pretend that we are conne=
cted.
- // We can't call smtp_open() here, as that does EHLO, and the EHLO com=
mand
- // needs the fake hostname. However, we only get the fake hostname in =
put(), so
- // we call smtp_open() there.
- connected();
-}
-
-void SMTPProtocol::closeConnection()
-{
- smtp_close();
-}
-
-void SMTPProtocol::special(const QByteArray &aData)
-{
- QDataStream s(aData);
- int what;
- s >> what;
- if (what =3D=3D 'c') {
- const QString response =3D m_sessionIface->capabilities().createSp=
ecialResponse(
- (isUsingSsl() && !isAutoSsl())
- || m_sessionIface->haveCapability("STARTTLS"));
- infoMessage(response);
- } else if (what =3D=3D 'N') {
- if (!execute(Command::NOOP)) {
- return;
- }
- } else {
- error(KIO::ERR_INTERNAL, i18n("The application sent an invalid req=
uest."));
- return;
- }
- finished();
-}
-
-// Usage: smtp://smtphost:port/send?to=3Duser@host.com&subject=3Dblah
-// If smtphost is the name of a profile, it'll use the information
-// provided by that profile. If it's not a profile name, it'll use it as
-// nature intended.
-// One can also specify in the query:
-// headers=3D0 (turns off header generation)
-// to=3Demailaddress
-// cc=3Demailaddress
-// bcc=3Demailaddress
-// subject=3Dtext
-// profile=3Dtext (this will override the "host" setting)
-// hostname=3Dtext (used in the HELO)
-// body=3D{7bit,8bit} (default: 7bit; 8bit activates the use of the 8BITMI=
ME SMTP extension)
-void SMTPProtocol::put(const QUrl &url, int permissions, KIO::JobFlags fla=
gs)
-{
- Q_UNUSED(permissions);
- Q_UNUSED(flags);
- Request request =3D Request::fromURL(url); // parse settings from UR=
L's query
-
- KEMailSettings mset;
- QUrl open_url =3D url;
- if (!request.hasProfile()) {
- //qCDebug(SMTP_LOG) << "kio_smtp: Profile is null";
- bool hasProfile =3D mset.profiles().contains(open_url.host());
- if (hasProfile) {
- mset.setProfile(open_url.host());
- open_url.setHost(mset.getSetting(KEMailSettings::OutServer));
- m_sUser =3D mset.getSetting(KEMailSettings::OutServerLogin);
- m_sPass =3D mset.getSetting(KEMailSettings::OutServerPass);
-
- if (m_sUser.isEmpty()) {
- m_sUser.clear();
- }
- if (m_sPass.isEmpty()) {
- m_sPass.clear();
- }
- open_url.setUserName(m_sUser);
- open_url.setPassword(m_sPass);
- m_sServer =3D open_url.host();
- m_port =3D open_url.port();
- } else {
- mset.setProfile(mset.defaultProfileName());
- }
- } else {
- mset.setProfile(request.profileName());
- }
-
- // Check KEMailSettings to see if we've specified an E-Mail address
- // if that worked, check to see if we've specified a real name
- // and then format accordingly (either: emailaddress@host.com or
- // Real Name )
- if (!request.hasFromAddress()) {
- const QString from =3D mset.getSetting(KEMailSettings::EmailAddres=
s);
- if (!from.isNull()) {
- request.setFromAddress(from);
- } else if (request.emitHeaders()) {
- error(KIO::ERR_NO_CONTENT, i18n("The sender address is missing=
."));
- return;
- }
- }
-
- if (!smtp_open(request.heloHostname())) {
- error(KIO::ERR_SERVICE_NOT_AVAILABLE,
- i18n("SMTPProtocol::smtp_open failed (%1)", // ### better er=
ror message?
- open_url.path()));
- return;
- }
-
- if (request.is8BitBody()
- && !m_sessionIface->haveCapability("8BITMIME") && !m_sessionIface-=
>eightBitMimeRequested()) {
- error(KIO::ERR_SERVICE_NOT_AVAILABLE,
- i18n("Your server (%1) does not support sending of 8-bit mes=
sages.\n"
- "Please use base64 or quoted-printable encoding.", m_sS=
erver));
- return;
- }
-
- queueCommand(new MailFromCommand(m_sessionIface, request.fromAddress()=
.toLatin1(),
- request.is8BitBody(), request.size())=
);
-
- // Loop through our To and CC recipients, and send the proper
- // SMTP commands, for the benefit of the server.
- const QStringList recipients =3D request.recipients();
- for (QStringList::const_iterator it =3D recipients.begin(), end(recipi=
ents.end()); it !=3D end; ++it) {
- queueCommand(new RcptToCommand(m_sessionIface, (*it).toLatin1()));
- }
-
- queueCommand(Command::DATA);
- queueCommand(new TransferCommand(m_sessionIface, request.headerFields(=
mset.getSetting(KEMailSettings::RealName))));
-
- TransactionState ts;
- if (!executeQueuedCommands(&ts)) {
- if (ts.errorCode()) {
- error(ts.errorCode(), ts.errorMessage());
- }
- } else {
- finished();
- }
-}
-
-void SMTPProtocol::setHost(const QString &host, quint16 port, const QStrin=
g &user, const QString &pass)
-{
- m_sServer =3D host;
- m_port =3D port;
- m_sUser =3D user;
- m_sPass =3D pass;
-}
-
-bool SMTPProtocol::sendCommandLine(const QByteArray &cmdline)
-{
- //kDebug( cmdline.length() < 4096, 7112) << "C: " << cmdline.data();
- //kDebug( cmdline.length() >=3D 4096, 7112) << "C: <" << cmdline.lengt=
h() << " bytes>";
- if (cmdline.length() < 4096) {
- qCDebug(SMTP_LOG) << "C: >>" << cmdline.trimmed().data() << "<<";
- } else {
- qCDebug(SMTP_LOG) << "C: <" << cmdline.length() << " bytes>";
- }
- ssize_t numWritten, cmdline_len =3D cmdline.length();
- if ((numWritten =3D write(cmdline.data(), cmdline_len)) !=3D cmdline_l=
en) {
- qCDebug(SMTP_LOG) << "Tried to write " << cmdline_len << " bytes, =
but only "
- << numWritten << " were written!" << endl;
- error(KIO::ERR_SLAVE_DEFINED, i18n("Writing to socket failed."));
- return false;
- }
- return true;
-}
-
-Response SMTPProtocol::getResponse(bool *ok)
-{
- if (ok) {
- *ok =3D false;
- }
-
- Response response;
- char buf[2048];
-
- int recv_len =3D 0;
- do {
- // wait for data...
- if (!waitForResponse(600)) {
- error(KIO::ERR_SERVER_TIMEOUT, m_sServer);
- return response;
- }
-
- // ...read data...
- recv_len =3D readLine(buf, sizeof(buf) - 1);
- if (recv_len < 1 && !isConnected()) {
- error(KIO::ERR_CONNECTION_BROKEN, m_sServer);
- return response;
- }
-
- qCDebug(SMTP_LOG) << "S: >>" << QByteArray(buf, recv_len).trimmed(=
).data() << "<<";
- // ...and parse lines...
- response.parseLine(buf, recv_len);
-
- // ...until the response is complete or the parser is so confused
- // that it doesn't think a RSET would help anymore:
- } while (!response.isComplete() && response.isWellFormed());
-
- if (!response.isValid()) {
- error(KIO::ERR_NO_CONTENT, i18n("Invalid SMTP response (%1) receiv=
ed.", response.code()));
- return response;
- }
-
- if (ok) {
- *ok =3D true;
- }
-
- return response;
-}
-
-bool SMTPProtocol::executeQueuedCommands(TransactionState *ts)
-{
- assert(ts);
-
- if (m_sessionIface->canPipelineCommands()) {
- qDebug() << "using pipelining";
- }
-
- while (!mPendingCommandQueue.isEmpty()) {
- QByteArray cmdline =3D collectPipelineCommands(ts);
- if (ts->failedFatally()) {
- smtp_close(false); // _hard_ shutdown
- return false;
- }
- if (ts->failed()) {
- break;
- }
- if (cmdline.isEmpty()) {
- continue;
- }
- if (!sendCommandLine(cmdline)
- || !batchProcessResponses(ts)
- || ts->failedFatally()) {
- smtp_close(false); // _hard_ shutdown
- return false;
- }
- }
-
- if (ts->failed()) {
- if (!execute(Command::RSET)) {
- smtp_close(false);
- }
- return false;
- }
- return true;
-}
-
-QByteArray SMTPProtocol::collectPipelineCommands(TransactionState *ts)
-{
- assert(ts);
-
- QByteArray cmdLine;
- unsigned int cmdLine_len =3D 0;
-
- while (!mPendingCommandQueue.isEmpty()) {
- Command *cmd =3D mPendingCommandQueue.head();
-
- if (cmd->doNotExecute(ts)) {
- delete mPendingCommandQueue.dequeue();
- if (cmdLine_len) {
- break;
- } else {
- continue;
- }
- }
-
- if (cmdLine_len && cmd->mustBeFirstInPipeline()) {
- break;
- }
-
- if (cmdLine_len && !m_sessionIface->canPipelineCommands()) {
- break;
- }
-
- while (!cmd->isComplete() && !cmd->needsResponse()) {
- const QByteArray currentCmdLine =3D cmd->nextCommandLine(ts);
- if (ts->failedFatally()) {
- return cmdLine;
- }
- const unsigned int currentCmdLine_len =3D currentCmdLine.lengt=
h();
-
- cmdLine_len +=3D currentCmdLine_len;
- cmdLine +=3D currentCmdLine;
-
- // If we are executing the transfer command, don't collect the=
whole
- // command line (which may be several MBs) before sending it, =
but instead
- // send the data each time we have collected 32 KB of the comm=
and line.
- //
- // This way, the progress information in clients like KMail wo=
rks correctly,
- // because otherwise, the TransferCommand would read the whole=
data from the
- // job at once, then sending it. The progress update on the cl=
ient however
- // happens when sending data to the job, not when this slave w=
rites the data
- // to the socket. Therefore that progress update is incorrect.
- //
- // 32 KB seems to be a sensible limit. Additionally, a job can=
only transfer
- // 32 KB at once anyway.
- if (dynamic_cast(cmd) !=3D nullptr
- && cmdLine_len >=3D 32 * 1024) {
- return cmdLine;
- }
- }
-
- mSentCommandQueue.enqueue(mPendingCommandQueue.dequeue());
-
- if (cmd->mustBeLastInPipeline()) {
- break;
- }
- }
-
- return cmdLine;
-}
-
-bool SMTPProtocol::batchProcessResponses(TransactionState *ts)
-{
- assert(ts);
-
- while (!mSentCommandQueue.isEmpty()) {
- Command *cmd =3D mSentCommandQueue.head();
- assert(cmd->isComplete());
-
- bool ok =3D false;
- Response r =3D getResponse(&ok);
- if (!ok) {
- return false;
- }
- cmd->processResponse(r, ts);
- if (ts->failedFatally()) {
- return false;
- }
-
- delete mSentCommandQueue.dequeue();
- }
-
- return true;
-}
-
-void SMTPProtocol::queueCommand(int type)
-{
- queueCommand(Command::createSimpleCommand(type, m_sessionIface));
-}
-
-bool SMTPProtocol::execute(int type, TransactionState *ts)
-{
- unique_ptr cmd(Command::createSimpleCommand(type, m_sessionIf=
ace));
- if (!cmd.get()) {
- qCritical() << "Command::createSimpleCommand( " << type << " ) ret=
urned null!";
- }
- return execute(cmd.get(), ts);
-}
-
-// ### fold into pipelining engine? How? (execute() is often called
-// ### when command queues are _not_ empty!)
-bool SMTPProtocol::execute(Command *cmd, TransactionState *ts)
-{
- if (!cmd) {
- qCritical() << "SMTPProtocol::execute() called with no command to =
run!";
- }
-
- if (cmd->doNotExecute(ts)) {
- return true;
- }
-
- do {
- while (!cmd->isComplete() && !cmd->needsResponse()) {
- const QByteArray cmdLine =3D cmd->nextCommandLine(ts);
- if (ts && ts->failedFatally()) {
- smtp_close(false);
- return false;
- }
- if (cmdLine.isEmpty()) {
- continue;
- }
- if (!sendCommandLine(cmdLine)) {
- smtp_close(false);
- return false;
- }
- }
-
- bool ok =3D false;
- Response r =3D getResponse(&ok);
- if (!ok) {
- // Only close without sending QUIT if the responce was incompl=
ete
- // rfc5321 forbidds a client from closing a connection without=
sending
- // QUIT (section 4.1.1.10)
- if (r.isComplete()) {
- smtp_close();
- } else {
- smtp_close(false);
- }
- return false;
- }
- if (!cmd->processResponse(r, ts)) {
- if ((ts && ts->failedFatally())
- || cmd->closeConnectionOnError()
- || !execute(Command::RSET)) {
- smtp_close(false);
- }
- return false;
- }
- } while (!cmd->isComplete());
-
- return true;
-}
-
-bool SMTPProtocol::smtp_open(const QString &fakeHostname)
-{
- if (m_opened
- && m_sOldPort =3D=3D m_port
- && m_sOldServer =3D=3D m_sServer
- && m_sOldUser =3D=3D m_sUser
- && (fakeHostname.isNull() || m_hostname =3D=3D fakeHostname)) {
- return true;
- }
-
- smtp_close();
- if (!connectToHost(isAutoSsl() ? QStringLiteral("smtps") : QStringLite=
ral("smtp"), m_sServer, m_port)) {
- return false; // connectToHost has already send an error messag=
e.
- }
- m_opened =3D true;
-
- bool ok =3D false;
- Response greeting =3D getResponse(&ok);
- if (!ok || !greeting.isOk()) {
- if (ok) {
- error(KIO::ERR_COULD_NOT_LOGIN,
- i18n("The server (%1) did not accept the connection.\n"
- "%2", m_sServer, greeting.errorMessage()));
- }
- smtp_close();
- return false;
- }
-
- if (!fakeHostname.isNull()) {
- m_hostname =3D fakeHostname;
- } else {
- // FIXME: We need a way to find the FQDN again. Also change in ser=
vertest then.
- m_hostname =3D QHostInfo::localHostName();
- if (m_hostname.isEmpty()) {
- m_hostname =3D QStringLiteral("localhost.invalid");
- } else if (!m_hostname.contains(QLatin1Char('.'))) {
- m_hostname +=3D QLatin1String(".localnet");
- }
- }
-
- EHLOCommand ehloCmdPreTLS(m_sessionIface, m_hostname);
- if (!execute(&ehloCmdPreTLS)) {
- smtp_close();
- return false;
- }
-
- if ((m_sessionIface->haveCapability("STARTTLS") /*### && canUseTLS()*/=
&& m_sessionIface->tlsRequested() !=3D SMTPSessionInterface::ForceNoTLS)
- || m_sessionIface->tlsRequested() =3D=3D SMTPSessionInterface::For=
ceTLS) {
- // For now we're gonna force it on.
-
- if (execute(Command::STARTTLS)) {
- // re-issue EHLO to refresh the capability list (could be have
- // been faked before TLS was enabled):
- EHLOCommand ehloCmdPostTLS(m_sessionIface, m_hostname);
- if (!execute(&ehloCmdPostTLS)) {
- smtp_close();
- return false;
- }
- }
- }
- // Now we try and login
- if (!authenticate()) {
- smtp_close();
- return false;
- }
-
- m_sOldPort =3D m_port;
- m_sOldServer =3D m_sServer;
- m_sOldUser =3D m_sUser;
- m_sOldPass =3D m_sPass;
-
- return true;
-}
-
-bool SMTPProtocol::authenticate()
-{
- // return with success if the server doesn't support SMTP-AUTH or an u=
ser
- // name is not specified and metadata doesn't tell us to force it.
- if ((m_sUser.isEmpty() || !m_sessionIface->haveCapability("AUTH"))
- && m_sessionIface->requestedSaslMethod().isEmpty()) {
- return true;
- }
-
- KIO::AuthInfo authInfo;
- authInfo.username =3D m_sUser;
- authInfo.password =3D m_sPass;
- authInfo.prompt =3D i18n("Username and password for your SMTP account:=
");
-
- QStringList strList;
-
- if (!m_sessionIface->requestedSaslMethod().isEmpty()) {
- strList.append(m_sessionIface->requestedSaslMethod());
- } else {
- strList =3D m_sessionIface->capabilities().saslMethodsQSL();
- }
-
- const QByteArray ba =3D strList.join(QLatin1Char(' ')).toLatin1();
- AuthCommand authCmd(m_sessionIface, ba.constData(), m_sServer, authInf=
o);
- bool ret =3D execute(&authCmd);
- m_sUser =3D authInfo.username;
- m_sPass =3D authInfo.password;
- return ret;
-}
-
-void SMTPProtocol::smtp_close(bool nice)
-{
- if (!m_opened) { // We're already closed
- return;
- }
-
- if (nice) {
- execute(Command::QUIT);
- }
- qCDebug(SMTP_LOG) << "closing connection";
- disconnectFromHost();
- m_sOldServer.clear();
- m_sOldUser.clear();
- m_sOldPass.clear();
-
- m_sessionIface->clearCapabilities();
- qDeleteAll(mPendingCommandQueue);
- mPendingCommandQueue.clear();
- qDeleteAll(mSentCommandQueue);
- mSentCommandQueue.clear();
-
- m_opened =3D false;
-}
-
-void SMTPProtocol::stat(const QUrl &url)
-{
- QString path =3D url.path();
- error(KIO::ERR_DOES_NOT_EXIST, url.path());
-}
diff --git a/kioslave/src/smtp/smtp.h b/kioslave/src/smtp/smtp.h
deleted file mode 100644
index 95217d0..0000000
--- a/kioslave/src/smtp/smtp.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- c++ -*-
- * Copyright (c) 2000, 2001 Alex Zepeda
- * Copyright (c) 2001 Michael H=EF=BF=BDkel
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURP=
OSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENT=
IAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STR=
ICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY W=
AY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#ifndef _SMTP_H
-#define _SMTP_H
-
-#include
-
-#include "capabilities.h"
-
-#include
-#include
-
-class QUrl;
-
-namespace KioSMTP {
-class Response;
-class TransactionState;
-class Command;
-class SMTPSessionInterface;
-class KioSlaveSession;
-}
-
-class SMTPProtocol : public KIO::TCPSlaveBase
-{
- friend class KioSMTP::KioSlaveSession;
-public:
- SMTPProtocol(const QByteArray &pool, const QByteArray &app, bool useSS=
L);
- virtual ~SMTPProtocol();
-
- virtual void setHost(const QString &host, quint16 port, const QString =
&user, const QString &pass) override;
-
- void special(const QByteArray &aData) override;
- void put(const QUrl &url, int permissions, KIO::JobFlags flags) overri=
de;
- void stat(const QUrl &url) override;
- void openConnection() override;
- void closeConnection() override;
-
-protected:
-
- bool smtp_open(const QString &fakeHostName);
-
- /** Closes the connection. If @p nice is true (default), then QUIT
- is sent and it's reponse waited for. */
- void smtp_close(bool nice =3D true);
-
- /** Execute command @p cmd */
- bool execute(KioSMTP::Command *cmd, KioSMTP::TransactionState *ts =3D =
nullptr);
- /** Execute a command of type @p type */
- bool execute(int type, KioSMTP::TransactionState *ts =3D nullptr);
- /** Execute the queued commands. If something goes horribly wrong
- (sending command oline fails, getting response fails or some
- command raises the failedFatally() flag in @p ts, shuts down the
- connection with smtp_close( false )
. If The
- transaction fails gracefully (ts->failed()
is
- true), issues a RSET command.
-
- @return true if transaction succeeded, false otherwise.
- **/
- bool executeQueuedCommands(KioSMTP::TransactionState *ts);
-
- /** Parse a single response from the server. Single- vs. multiline
- responses are correctly detected.
-
- @param ok if not 0, returns whether response parsing was
- successful. Don't confuse this with negative responses
- (e.g. 5xx), which you can check for using
- @ref Response::isNegative()
- @return the @ref Response object representing the server response.
- **/
- KioSMTP::Response getResponse(bool *ok);
-
- bool authenticate();
-
- bool sendCommandLine(const QByteArray &cmd);
- QByteArray collectPipelineCommands(KioSMTP::TransactionState *ts);
- bool batchProcessResponses(KioSMTP::TransactionState *ts);
-
- void queueCommand(KioSMTP::Command *command)
- {
- mPendingCommandQueue.enqueue(command);
- }
-
- void queueCommand(int type);
-
- quint16 m_sOldPort;
- quint16 m_port;
- bool m_opened;
- QString m_sServer, m_sOldServer;
- QString m_sUser, m_sOldUser;
- QString m_sPass, m_sOldPass;
- QString m_hostname;
-
- typedef QQueue CommandQueue;
- CommandQueue mPendingCommandQueue;
- CommandQueue mSentCommandQueue;
- KioSMTP::SMTPSessionInterface *m_sessionIface;
-};
-
-#endif // _SMTP_H
diff --git a/kioslave/src/smtp/smtp.protocol b/kioslave/src/smtp/smtp.proto=
col
deleted file mode 100644
index c7bf05b..0000000
--- a/kioslave/src/smtp/smtp.protocol
+++ /dev/null
@@ -1,16 +0,0 @@
-[Protocol]
-exec=3Dkf5/kio/smtp
-protocol=3Dsmtp
-Capabilities=3DSASL
-input=3Dnone
-output=3Dfilesystem
-listing=3DName,Type,Size
-reading=3Dfalse
-writing=3Dtrue
-deleting=3Dfalse
-source=3Dtrue
-makedir=3Dfalse
-linking=3Dfalse
-moving=3Dfalse
-X-DocPath=3Dkioslave5/smtp/index.html
-Icon=3Dmail-folder-outbox
diff --git a/kioslave/src/smtp/smtps.protocol b/kioslave/src/smtp/smtps.pro=
tocol
deleted file mode 100644
index 98222d7..0000000
--- a/kioslave/src/smtp/smtps.protocol
+++ /dev/null
@@ -1,16 +0,0 @@
-[Protocol]
-exec=3Dkf5/kio/smtp
-protocol=3Dsmtps
-Capabilities=3DSASL
-input=3Dnone
-output=3Dfilesystem
-listing=3DName,Type,Size
-reading=3Dfalse
-writing=3Dtrue
-deleting=3Dfalse
-source=3Dtrue
-makedir=3Dfalse
-linking=3Dfalse
-moving=3Dfalse
-X-DocPath=3Dkioslave5/smtp/index.html
-Icon=3Dmail-folder-outbox
diff --git a/kioslave/src/smtp/smtpsessioninterface.cpp b/kioslave/src/smtp=
/smtpsessioninterface.cpp
deleted file mode 100644
index b0e33df..0000000
--- a/kioslave/src/smtp/smtpsessioninterface.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- Copyright (c) 2010 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#include "smtpsessioninterface.h"
-
-using namespace KioSMTP;
-
-SMTPSessionInterface::~SMTPSessionInterface()
-{
-}
-
-void SMTPSessionInterface::parseFeatures(const KioSMTP::Response &ehloResp=
onse)
-{
- m_capabilities =3D Capabilities::fromResponse(ehloResponse);
-}
-
-const Capabilities &KioSMTP::SMTPSessionInterface::capabilities() const
-{
- return m_capabilities;
-}
-
-void SMTPSessionInterface::clearCapabilities()
-{
- m_capabilities.clear();
-}
-
-bool SMTPSessionInterface::haveCapability(const char *cap) const
-{
- return m_capabilities.have(cap);
-}
-
-bool SMTPSessionInterface::canPipelineCommands() const
-{
- return haveCapability("PIPELINING") && pipeliningRequested();
-}
-
-bool KioSMTP::SMTPSessionInterface::eightBitMimeRequested() const
-{
- return false;
-}
-
-bool KioSMTP::SMTPSessionInterface::pipeliningRequested() const
-{
- return true;
-}
diff --git a/kioslave/src/smtp/smtpsessioninterface.h b/kioslave/src/smtp/s=
mtpsessioninterface.h
deleted file mode 100644
index 6f66507..0000000
--- a/kioslave/src/smtp/smtpsessioninterface.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- Copyright (c) 2010 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#ifndef KIOSMTP_SMTPSESSIONINTERFACE_H
-#define KIOSMTP_SMTPSESSIONINTERFACE_H
-
-#include "capabilities.h"
-
-class QByteArray;
-class QString;
-
-namespace KIO {
-class AuthInfo;
-}
-
-namespace KioSMTP {
-class Response;
-
-/** Interface to the SMTP session for command classes.
- * There are sub-classes for the in-process mode, the KIO slave mode and f=
or unit testing.
- * @since 4.6
- */
-class SMTPSessionInterface
-{
-public:
- /** TLS request state. */
- enum TLSRequestState {
- UseTLSIfAvailable,
- ForceTLS,
- ForceNoTLS
- };
-
- virtual ~SMTPSessionInterface();
- virtual bool startSsl() =3D 0;
-
- /** Parse capability response from the server. */
- void parseFeatures(const KioSMTP::Response &ehloResponse);
-
- /** Returns the server reported capabilities. */
- const Capabilities &capabilities() const;
-
- /** Clear the capabilities reported by the server (e.g. when reconnect=
ing the session) */
- void clearCapabilities();
-
- /** This is a pure convenience wrapper around
- * @ref KioSMTP::Capabilities::have()
- */
- virtual bool haveCapability(const char *cap) const;
-
- /** @return true is pipelining is available and allowed by metadata */
- bool canPipelineCommands() const;
-
- virtual void error(int id, const QString &msg) =3D 0;
- /** Show information message box with message @p msg and caption @p ca=
ption. */
- virtual void informationMessageBox(const QString &msg, const QString &=
caption) =3D 0;
- virtual bool openPasswordDialog(KIO::AuthInfo &authInfo) =3D 0;
- virtual void dataReq() =3D 0;
- virtual int readData(QByteArray &ba) =3D 0;
-
- /** SASL method requested for authentication. */
- virtual QString requestedSaslMethod() const =3D 0;
- /** TLS requested for encryption. */
- virtual TLSRequestState tlsRequested() const =3D 0;
- /** LF2CRLF and dot stuffing requested. */
- virtual bool lf2crlfAndDotStuffingRequested() const =3D 0;
- /** 8bit MIME support requested. */
- virtual bool eightBitMimeRequested() const;
- /** Pipelining has been requested. */
- virtual bool pipeliningRequested() const;
-
-private:
- KioSMTP::Capabilities m_capabilities;
-};
-}
-
-#endif
diff --git a/kioslave/src/smtp/tests/CMakeLists.txt b/kioslave/src/smtp/tes=
ts/CMakeLists.txt
deleted file mode 100644
index 38e3d1f..0000000
--- a/kioslave/src/smtp/tests/CMakeLists.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-include(ECMMarkAsTest)
-
-set(QT_REQUIRED_VERSION "5.7.0")
-find_package(Qt5Test ${QT_REQUIRED_VERSION} CONFIG REQUIRED)
-
-set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
-
-########### next target ###############
-
-if (WIN32)
- set(extra_LIB ws2_32)
-endif()
-
-set(test_responseparser_SRCS test_responseparser.cpp )
-
-add_executable( test_responseparser ${test_responseparser_SRCS} )
-add_test( test_responseparser test_responseparser )
-ecm_mark_as_test(smtp-responseparser)
-target_link_libraries(test_responseparser Qt5::Test KF5::I18n KF5::KIOCo=
re ${extra_LIB})
-
-########### next target ###############
-
-set(test_headergeneration_SRCS test_headergeneration.cpp)
-ecm_qt_declare_logging_category(test_headergeneration_SRCS HEADER smtp_deb=
ug.h IDENTIFIER SMTP_LOG CATEGORY_NAME org.kde.pim.smtp)
-
-add_executable( test_headergeneration ${test_headergeneration_SRCS} )
-add_test( test_headergeneration test_headergeneration )
-ecm_mark_as_test(smtp-headergeneration)
-
-target_link_libraries(test_headergeneration Qt5::Test ${extra_LIB})
-
-
-########### next target ###############
-set(test_commands_SRCS test_commands.cpp )
-ecm_qt_declare_logging_category(test_commands_SRCS HEADER smtp_debug.h IDE=
NTIFIER SMTP_LOG CATEGORY_NAME org.kde.pim.smtp)
-
-include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${Sasl2_INCLUDE_DI=
RS} )
-
-add_executable( test_commands ${test_commands_SRCS} )
-add_test( test_commands test_commands )
-ecm_mark_as_test(smtp-commands)
-target_link_libraries(test_commands KF5::KIOCore ${Sasl2_LIBRARIES} Qt5:=
:Test KF5::I18n ${extra_LIB})
-
-
-########### next target ###############
-set(interactivesmtpserver_SRCS interactivesmtpserver.cpp )
-
-add_executable( interactivesmtpserver ${interactivesmtpserver_SRCS} )
-ecm_mark_as_test(smtp-interactivesmtpserver)
-target_link_libraries(interactivesmtpserver Qt5::Test Qt5::Widgets Qt5:=
:Network)
-
-
-########### next target ###############
-set(test_capabilities_SRCS test_capabilities.cpp ../capabilities.cpp )
-
-include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../)
-
-add_executable( test_capabilities ${test_capabilities_SRCS} )
-ecm_mark_as_test(test-capabilities)
-target_link_libraries(test_capabilities KF5::KIOCore)
-
-
-########### next target ###############
-set( test_request_source requesttest.cpp ../request.cpp )
-ecm_qt_declare_logging_category(test_request_source HEADER smtp_debug.h ID=
ENTIFIER SMTP_LOG CATEGORY_NAME org.kde.pim.smtp)
-
-add_executable( requesttest ${test_request_source})
-add_test(requesttest requesttest)
-ecm_mark_as_test(requesttest)
-target_link_libraries( requesttest Qt5::Test Qt5::Gui)
-
diff --git a/kioslave/src/smtp/tests/fakesession.h b/kioslave/src/smtp/test=
s/fakesession.h
deleted file mode 100644
index 3dde50b..0000000
--- a/kioslave/src/smtp/tests/fakesession.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- Copyright (c) 2010 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#ifndef KIOSMTP_FAKESESSION_H
-#define KIOSMTP_FAKESESSION_H
-
-#include "smtpsessioninterface.h"
-
-#include
-#include
-
-namespace KioSMTP {
-class FakeSession : public SMTPSessionInterface
-{
-public:
- FakeSession()
- {
- clear();
- }
-
- //
- // public members to control the API emulation below:
- //
- bool startTLSReturnCode;
- bool usesTLS; // ### unused below, most likely something wrong in the =
tests...
- int lastErrorCode;
- QString lastErrorMessage;
- QString lastMessageBoxText;
- QByteArray nextData;
- int nextDataReturnCode;
- QStringList caps;
-
- bool eightBitMime;
- bool lf2crlfAndDotStuff;
- bool pipelining;
- QString saslMethod;
-
- void clear()
- {
- startTLSReturnCode =3D true;
- usesTLS =3D false;
- lastErrorCode =3D 0;
- lastErrorMessage.clear();
- lastMessageBoxText.clear();
- nextData.resize(0);
- nextDataReturnCode =3D -1;
- caps.clear();
-
- lf2crlfAndDotStuff =3D false;
- saslMethod.clear();
- }
-
- //
- // emulated API:
- //
- bool startSsl() override
- {
- return startTLSReturnCode;
- }
-
- bool haveCapability(const char *cap) const override
- {
- return caps.contains(QLatin1String(cap));
- }
-
- void error(int id, const QString &msg) override
- {
- lastErrorCode =3D id;
- lastErrorMessage =3D msg;
- qWarning() << id << msg;
- }
-
- void informationMessageBox(const QString &msg, const QString &caption)=
override
- {
- Q_UNUSED(caption);
- lastMessageBoxText =3D msg;
- }
-
- bool openPasswordDialog(KIO::AuthInfo &) override
- {
- return true;
- }
-
- void dataReq() override
- {
- /* noop */
- }
-
- int readData(QByteArray &ba) override
- {
- ba =3D nextData;
- return nextDataReturnCode;
- }
-
- bool lf2crlfAndDotStuffingRequested() const override
- {
- return lf2crlfAndDotStuff;
- }
-
- QString requestedSaslMethod() const override
- {
- return saslMethod;
- }
-
- TLSRequestState tlsRequested() const override
- {
- return SMTPSessionInterface::UseTLSIfAvailable;
- }
-};
-}
-
-#include "smtpsessioninterface.cpp"
-#include "capabilities.cpp"
-
-#endif
diff --git a/kioslave/src/smtp/tests/interactivesmtpserver.cpp b/kioslave/s=
rc/smtp/tests/interactivesmtpserver.cpp
deleted file mode 100644
index 7068db1..0000000
--- a/kioslave/src/smtp/tests/interactivesmtpserver.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/* -*- c++ -*-
- interactivesmtpserver.cc
-
- Code based on the serverSocket example by Jesper Pedersen.
-
- This file is part of the testsuite of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2004 Marc Mutz
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This program 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 Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "interactivesmtpserver.h"
-
-static const QHostAddress localhost(0x7f000001); // 127.0.0.1
-
-static QString err2str(QAbstractSocket::SocketError error)
-{
- switch (error) {
- case QAbstractSocket::ConnectionRefusedError:
- return QStringLiteral("Connection refused");
- case QAbstractSocket::HostNotFoundError:
- return QStringLiteral("Host not found");
- default:
- return QStringLiteral("Unknown error");
- }
-}
-
-static QString escape(QString s)
-{
- return s
- .replace(QLatin1Char('&'), QLatin1String("&"))
- .replace(QLatin1Char('>'), QLatin1String(">"))
- .replace(QLatin1Char('<'), QLatin1String("<"))
- .replace(QLatin1Char('"'), QLatin1String("""))
- ;
-}
-
-static QString trim(const QString &s)
-{
- if (s.endsWith(QLatin1String("\r\n"))) {
- return s.left(s.length() - 2);
- }
- if (s.endsWith(QLatin1String("\r")) || s.endsWith(QLatin1String("\n"))=
) {
- return s.left(s.length() - 1);
- }
- return s;
-}
-
-InteractiveSMTPServerWindow::~InteractiveSMTPServerWindow()
-{
- if (mSocket) {
- mSocket->close();
- if (mSocket->state() =3D=3D QAbstractSocket::ClosingState) {
- connect(mSocket, SIGNAL(disconnected()),
- mSocket, SLOT(deleteLater()));
- } else {
- mSocket->deleteLater();
- }
- mSocket =3D nullptr;
- }
-}
-
-void InteractiveSMTPServerWindow::slotSendResponse()
-{
- const QString line =3D mLineEdit->text();
- mLineEdit->clear();
- QTextStream s(mSocket);
- s << line + QLatin1String("\r\n");
- slotDisplayServer(line);
-}
-
-InteractiveSMTPServer::InteractiveSMTPServer(QObject *parent)
- : QTcpServer(parent)
-{
- listen(localhost, 2525);
- setMaxPendingConnections(1);
-
- connect(this, SIGNAL(newConnection()), this, SLOT(newConnectionAvailab=
le()));
-}
-
-void InteractiveSMTPServer::newConnectionAvailable()
-{
- InteractiveSMTPServerWindow *w =3D new InteractiveSMTPServerWindow(nex=
tPendingConnection());
- w->show();
-}
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
-
- InteractiveSMTPServer server;
-
- qDebug("Server should now listen on localhost:2525");
- qDebug("Hit CTRL-C to quit.");
-
- return app.exec();
-}
-
-InteractiveSMTPServerWindow::InteractiveSMTPServerWindow(QTcpSocket *socke=
t, QWidget *parent)
- : QWidget(parent)
- , mSocket(socket)
-{
- QPushButton *but;
- Q_ASSERT(socket);
-
- QVBoxLayout *vlay =3D new QVBoxLayout(this);
-
- mTextEdit =3D new QTextEdit(this);
- vlay->addWidget(mTextEdit, 1);
- QWidget *mLayoutWidget =3D new QWidget;
- vlay->addWidget(mLayoutWidget);
-
- QHBoxLayout *hlay =3D new QHBoxLayout(mLayoutWidget);
-
- mLineEdit =3D new QLineEdit(this);
- mLabel =3D new QLabel(QStringLiteral("&Response:"), this);
- mLabel->setBuddy(mLineEdit);
- but =3D new QPushButton(QStringLiteral("&Send"), this);
- hlay->addWidget(mLabel);
- hlay->addWidget(mLineEdit, 1);
- hlay->addWidget(but);
-
- connect(mLineEdit, SIGNAL(returnPressed()), SLOT(slotSendResponse()));
- connect(but, SIGNAL(clicked()), SLOT(slotSendResponse()));
-
- but =3D new QPushButton(QStringLiteral("&Close Connection"), this);
- vlay->addWidget(but);
-
- connect(but, SIGNAL(clicked()), SLOT(slotConnectionClosed()));
-
- connect(socket, SIGNAL(disconnected()), SLOT(slotConnectionClosed()));
- connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
- SLOT(slotError(QAbstractSocket::SocketError)));
- connect(socket, SIGNAL(readyRead()), SLOT(slotReadyRead()));
-
- mLineEdit->setText(QStringLiteral("220 hi there"));
- mLineEdit->setFocus();
-}
-
-void InteractiveSMTPServerWindow::slotDisplayClient(const QString &s)
-{
- mTextEdit->append(QLatin1String("C:") + escape(s));
-}
-
-void InteractiveSMTPServerWindow::slotDisplayServer(const QString &s)
-{
- mTextEdit->append(QLatin1String("S:") + escape(s));
-}
-
-void InteractiveSMTPServerWindow::slotDisplayMeta(const QString &s)
-{
- mTextEdit->append(QLatin1String("") + escape(s) =
+ QLatin1String(""));
-}
-
-void InteractiveSMTPServerWindow::slotReadyRead()
-{
- while (mSocket->canReadLine()) {
- slotDisplayClient(trim(QString::fromLatin1(mSocket->readLine())));
- }
-}
-
-void InteractiveSMTPServerWindow::slotError(QAbstractSocket::SocketError e=
rror)
-{
- slotDisplayMeta(QString::fromLatin1("E: %1").arg(err2str(error)));
-}
-
-void InteractiveSMTPServerWindow::slotConnectionClosed()
-{
- slotDisplayMeta(QStringLiteral("Connection closed by peer"));
-}
-
-void InteractiveSMTPServerWindow::slotCloseConnection()
-{
- mSocket->close();
-}
diff --git a/kioslave/src/smtp/tests/interactivesmtpserver.h b/kioslave/src=
/smtp/tests/interactivesmtpserver.h
deleted file mode 100644
index 7441776..0000000
--- a/kioslave/src/smtp/tests/interactivesmtpserver.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef INTERACTIVESMTPSERVER_H
-#define INTERACTIVESMTPSERVER_H
-
-/* -*- c++ -*-
- interactivesmtpserver.h
-
- Code based on the serverSocket example by Jesper Pedersen.
-
- This file is part of the testsuite of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2004 Marc Mutz
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This program 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 Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include
-#include
-
-class QLabel;
-class QLineEdit;
-class QTcpServer;
-class QTextEdit;
-
-class InteractiveSMTPServerWindow : public QWidget
-{
- Q_OBJECT
-public:
- InteractiveSMTPServerWindow(QTcpSocket *socket, QWidget *parent =3D nu=
llptr);
- ~InteractiveSMTPServerWindow();
-
-public Q_SLOTS:
- void slotSendResponse();
- void slotDisplayClient(const QString &s);
- void slotDisplayServer(const QString &s);
- void slotDisplayMeta(const QString &s);
- void slotReadyRead();
- void slotError(QAbstractSocket::SocketError error);
- void slotConnectionClosed();
- void slotCloseConnection();
-
-private:
- QTcpSocket *mSocket;
- QTextEdit *mTextEdit;
- QLineEdit *mLineEdit;
- QLabel *mLabel;
-};
-
-class InteractiveSMTPServer : public QTcpServer
-{
- Q_OBJECT
-
-public:
- InteractiveSMTPServer(QObject *parent =3D nullptr);
- ~InteractiveSMTPServer()
- {
- }
-
-private Q_SLOTS:
- void newConnectionAvailable();
-};
-
-#endif
diff --git a/kioslave/src/smtp/tests/requesttest.cpp b/kioslave/src/smtp/te=
sts/requesttest.cpp
deleted file mode 100644
index 65afd51..0000000
--- a/kioslave/src/smtp/tests/requesttest.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- Copyright (c) 2014-2017 Montel Laurent
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at yo=
ur
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WITH=
OUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,=
MA
- 02110-1301, USA.
-*/
-#include "requesttest.h"
-#include "../request.h"
-#include
-#include
-RequestTest::RequestTest(QObject *parent)
- : QObject(parent)
-{
-}
-
-RequestTest::~RequestTest()
-{
-}
-
-void RequestTest::shouldHaveDefaultValue()
-{
- KioSMTP::Request request;
- QVERIFY(request.to().isEmpty());
- QVERIFY(request.cc().isEmpty());
- QVERIFY(request.bcc().isEmpty());
- QVERIFY(request.emitHeaders());
- QVERIFY(!request.is8BitBody());
- QVERIFY(request.profileName().isEmpty());
- QVERIFY(request.fromAddress().isEmpty());
- QVERIFY(request.heloHostname().isEmpty());
- QCOMPARE(request.size(), static_cast(0));
-}
-
-void RequestTest::shouldParseRequest_data()
-{
- QTest::addColumn("smtpurl");
- QTest::addColumn("to");
- QTest::addColumn("from");
- QTest::addColumn("cc");
- QTest::addColumn("bcc");
- QTest::addColumn("emitheaders");
- QTest::addColumn("size");
- QTest::newRow("correct url") << QUrl(QStringLiteral("smtps://smtp.kde=
.org:465/send?headers=3D0&from=3Dfoo%40kde.org&to=3Dfoo%40kde.org&size=3D61=
7"))
- << QStringLiteral("foo@kde.org")
- << QStringLiteral("foo@kde.org")
- << QString()
- << QString()
- << false
- << static_cast(617);
-}
-
-void RequestTest::shouldParseRequest()
-{
- QFETCH(QUrl, smtpurl);
- QFETCH(QString, to);
- QFETCH(QString, from);
- QFETCH(QString, cc);
- QFETCH(QString, bcc);
- QFETCH(bool, emitheaders);
- QFETCH(unsigned int, size);
-
- KioSMTP::Request request =3D KioSMTP::Request::fromURL(smtpurl);
- QCOMPARE(request.to().join(QLatin1Char(',')), to);
- QCOMPARE(request.cc().join(QLatin1Char(',')), cc);
- QCOMPARE(request.fromAddress(), from);
- QCOMPARE(request.bcc().join(QLatin1Char(',')), bcc);
- QCOMPARE(request.size(), size);
- QCOMPARE(request.emitHeaders(), emitheaders);
-}
-
-QTEST_MAIN(RequestTest)
diff --git a/kioslave/src/smtp/tests/requesttest.h b/kioslave/src/smtp/test=
s/requesttest.h
deleted file mode 100644
index 44cf653..0000000
--- a/kioslave/src/smtp/tests/requesttest.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- Copyright (c) 2014-2017 Montel Laurent
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at yo=
ur
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WITH=
OUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,=
MA
- 02110-1301, USA.
-*/
-
-#ifndef REQUESTTEST_H
-#define REQUESTTEST_H
-
-#include
-
-class RequestTest : public QObject
-{
- Q_OBJECT
-public:
- explicit RequestTest(QObject *parent =3D nullptr);
- ~RequestTest();
-private Q_SLOTS:
- void shouldHaveDefaultValue();
- void shouldParseRequest_data();
- void shouldParseRequest();
-};
-
-#endif // REQUESTTEST_H
diff --git a/kioslave/src/smtp/tests/test_capabilities.cpp b/kioslave/src/s=
mtp/tests/test_capabilities.cpp
deleted file mode 100644
index f0a275c..0000000
--- a/kioslave/src/smtp/tests/test_capabilities.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include
-#include "capabilities.h"
-#include
-
-using namespace KioSMTP;
-
-int main()
-{
- Capabilities c;
-
- const QString size_cap =3D QObject::tr("SIZE 12");
- c.add(size_cap);
- // Capability was added
- assert(c.have("SIZE"));
-
- const QString expected_response =3D QObject::tr("SIZE=3D12");
- const QString actual_response =3D c.createSpecialResponse(false);
- // SIZE actually handled
- assert(actual_response =3D=3D expected_response);
-
- const QString auth_cap =3D QObject::tr("AUTH GSSAPI");
- c.add(auth_cap);
- c.add(auth_cap);
- // Duplicate methods was removed
- assert(c.saslMethodsQSL().length() =3D=3D 1);
-}
diff --git a/kioslave/src/smtp/tests/test_commands.cpp b/kioslave/src/smtp/=
tests/test_commands.cpp
deleted file mode 100644
index 416fadc..0000000
--- a/kioslave/src/smtp/tests/test_commands.cpp
+++ /dev/null
@@ -1,676 +0,0 @@
-#include
-#include
-#include
-
-#define KIOSMTP_COMPARATORS // for TransactionState::operator=3D=3D
-#include "fakesession.h"
-#include "command.h"
-#include "response.h"
-#include "transactionstate.h"
-#include "common.h"
-#include "smtp_debug.h"
-#include
-
-using namespace KioSMTP;
-
-static const char *foobarbaz =3D ".Foo bar baz";
-static const unsigned int foobarbaz_len =3D qstrlen(foobarbaz);
-
-static const char *foobarbaz_dotstuffed =3D "..Foo bar baz";
-static const unsigned int foobarbaz_dotstuffed_len =3D qstrlen(foobarbaz_d=
otstuffed);
-
-static const char *foobarbaz_lf =3D ".Foo bar baz\n";
-static const unsigned int foobarbaz_lf_len =3D qstrlen(foobarbaz_lf);
-
-static const char *foobarbaz_crlf =3D "..Foo bar baz\r\n";
-static const unsigned int foobarbaz_crlf_len =3D qstrlen(foobarbaz_crlf);
-
-static void checkSuccessfulTransferCommand(bool, bool, bool, bool, bool);
-
-int main(int, char **)
-{
- if (!initSASL()) {
- exit(-1);
- }
-
- FakeSession smtp;
- Response r;
- TransactionState ts, ts2;
-
- //
- // EHLO / HELO
- //
-
- smtp.clear();
- EHLOCommand ehlo(&smtp, QStringLiteral("mail.example.com"));
- // flags
- assert(ehlo.closeConnectionOnError());
- assert(ehlo.mustBeLastInPipeline());
- assert(!ehlo.mustBeFirstInPipeline());
-
- // initial state
- assert(!ehlo.isComplete());
- assert(!ehlo.doNotExecute(nullptr));
- assert(!ehlo.needsResponse());
-
- // dynamics 1: EHLO succeeds
- assert(ehlo.nextCommandLine(nullptr) =3D=3D "EHLO mail.example.com\r\n=
");
- assert(!ehlo.isComplete()); // EHLO may fail and we then try HELO
- assert(ehlo.needsResponse());
- r.clear();
- r.parseLine("250-mail.example.net\r\n");
- r.parseLine("250-PIPELINING\r\n");
- r.parseLine("250 8BITMIME\r\n");
- assert(ehlo.processResponse(r, nullptr) =3D=3D true);
- assert(ehlo.isComplete());
- assert(!ehlo.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0);
- assert(smtp.lastErrorMessage.isNull());
-
- // dynamics 2: EHLO fails with "unknown command"
- smtp.clear();
- EHLOCommand ehlo2(&smtp, QStringLiteral("mail.example.com"));
- ehlo2.nextCommandLine(nullptr);
- r.clear();
- r.parseLine("500 unknown command\r\n");
- assert(ehlo2.processResponse(r, nullptr) =3D=3D true);
- assert(!ehlo2.isComplete());
- assert(!ehlo2.needsResponse());
- assert(ehlo2.nextCommandLine(nullptr) =3D=3D "HELO mail.example.com\r\=
n");
- assert(ehlo2.isComplete());
- assert(ehlo2.needsResponse());
- r.clear();
- r.parseLine("250 mail.example.net\r\n");
- assert(ehlo2.processResponse(r, nullptr) =3D=3D true);
- assert(!ehlo2.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0);
- assert(smtp.lastErrorMessage.isNull());
-
- // dynamics 3: EHLO fails with unknown response code
- smtp.clear();
- EHLOCommand ehlo3(&smtp, QStringLiteral("mail.example.com"));
- ehlo3.nextCommandLine(nullptr);
- r.clear();
- r.parseLine("545 you don't know me\r\n");
- assert(ehlo3.processResponse(r, nullptr) =3D=3D false);
- assert(ehlo3.isComplete());
- assert(!ehlo3.needsResponse());
- assert(smtp.lastErrorCode =3D=3D KIO::ERR_UNKNOWN);
-
- // dynamics 4: EHLO _and_ HELO fail with "command unknown"
- smtp.clear();
- EHLOCommand ehlo4(&smtp, QStringLiteral("mail.example.com"));
- ehlo4.nextCommandLine(nullptr);
- r.clear();
- r.parseLine("500 unknown command\r\n");
- ehlo4.processResponse(r, nullptr);
- ehlo4.nextCommandLine(nullptr);
- r.clear();
- r.parseLine("500 unknown command\r\n");
- assert(ehlo4.processResponse(r, nullptr) =3D=3D false);
- assert(ehlo4.isComplete());
- assert(!ehlo4.needsResponse());
- assert(smtp.lastErrorCode =3D=3D KIO::ERR_INTERNAL_SERVER);
-
- //
- // STARTTLS
- //
-
- smtp.clear();
- StartTLSCommand tls(&smtp);
- // flags
- assert(tls.closeConnectionOnError());
- assert(tls.mustBeLastInPipeline());
- assert(!tls.mustBeFirstInPipeline());
-
- // initial state
- assert(!tls.isComplete());
- assert(!tls.doNotExecute(nullptr));
- assert(!tls.needsResponse());
-
- // dynamics 1: ok from server, TLS negotiation successful
- ts.clear();
- ts2 =3D ts;
- assert(tls.nextCommandLine(&ts) =3D=3D "STARTTLS\r\n");
- assert(ts =3D=3D ts2);
- assert(tls.isComplete());
- assert(tls.needsResponse());
- r.clear();
- r.parseLine("220 Go ahead");
- smtp.startTLSReturnCode =3D true;
- assert(tls.processResponse(r, &ts) =3D=3D true);
- assert(!tls.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0);
-
- // dynamics 2: NAK from server
- smtp.clear();
- StartTLSCommand tls2(&smtp);
- ts.clear();
- tls2.nextCommandLine(&ts);
- r.clear();
- r.parseLine("454 TLS temporarily disabled");
- smtp.startTLSReturnCode =3D true;
- assert(tls2.processResponse(r, &ts) =3D=3D false);
- assert(!tls2.needsResponse());
- assert(smtp.lastErrorCode =3D=3D KIO::ERR_SERVICE_NOT_AVAILABLE);
-
- // dynamics 3: ok from server, TLS negotiation unsuccessful
- smtp.clear();
- StartTLSCommand tls3(&smtp);
- ts.clear();
- tls3.nextCommandLine(&ts);
- r.clear();
- r.parseLine("220 Go ahead");
- smtp.startTLSReturnCode =3D false;
- assert(tls.processResponse(r, &ts) =3D=3D false);
- assert(!tls.needsResponse());
-
- //
- // AUTH
- //
-
- smtp.clear();
- QStringList mechs;
- mechs.append(QStringLiteral("PLAIN"));
- smtp.saslMethod =3D QStringLiteral("PLAIN");
- KIO::AuthInfo authInfo;
- authInfo.username =3D QStringLiteral("user");
- authInfo.password =3D QStringLiteral("pass");
- AuthCommand auth(&smtp, "PLAIN", QStringLiteral("mail.example.com"), a=
uthInfo);
- // flags
- assert(auth.closeConnectionOnError());
- assert(auth.mustBeLastInPipeline());
- assert(!auth.mustBeFirstInPipeline());
-
- // initial state
- assert(!auth.isComplete());
- assert(!auth.doNotExecute(nullptr));
- assert(!auth.needsResponse());
-
- // dynamics 1: TLS, so AUTH should include initial-response:
- smtp.usesTLS =3D true;
- ts.clear();
- ts2 =3D ts;
- assert(auth.nextCommandLine(&ts) =3D=3D "AUTH PLAIN dXNlcgB1c2VyAHBhc3=
M=3D\r\n");
- assert(auth.isComplete());
- assert(auth.needsResponse());
- assert(ts =3D=3D ts2);
- r.clear();
- r.parseLine("250 OK");
-
- // dynamics 2: No TLS, so AUTH should not include initial-response:
- /* FIXME fails since nothing evaluates useTLS =3D false anywhere...
- smtp.clear();
- smtp.saslMethod =3D "PLAIN";
- smtp.usesTLS =3D false;
- authInfo =3D KIO::AuthInfo();
- authInfo.username =3D "user";
- authInfo.password =3D "pass";
- AuthCommand auth2( &smtp, "PLAIN", "mail.example.com", authInfo );
- ts.clear();
- assert( auth2.nextCommandLine( &ts ) =3D=3D "AUTH PLAIN\r\n" );
- assert( !auth2.isComplete() );
- assert( auth2.needsResponse() );
- r.clear();
- r.parseLine( "334 Go on" );
- assert( auth2.processResponse( r, &ts ) =3D=3D true );
- assert( auth2.nextCommandLine( &ts ) =3D=3D "dXNlcgB1c2VyAHBhc3M=3D\r\=
n" );
- assert( auth2.isComplete() );
- assert( auth2.needsResponse() );*/
-
- // dynamics 3: LOGIN
- smtp.clear();
- smtp.saslMethod =3D QStringLiteral("LOGIN");
- mechs.clear();
- mechs.append(QStringLiteral("LOGIN"));
- authInfo =3D KIO::AuthInfo();
- authInfo.username =3D QStringLiteral("user");
- authInfo.password =3D QStringLiteral("pass");
- AuthCommand auth3(&smtp, "LOGIN", QStringLiteral("mail.example.com"), =
authInfo);
- ts.clear();
- ts2 =3D ts;
- assert(auth3.nextCommandLine(&ts) =3D=3D "AUTH LOGIN\r\n");
- assert(!auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("334 VXNlcm5hbWU6");
- assert(auth3.processResponse(r, &ts) =3D=3D true);
- assert(!auth3.needsResponse());
- assert(auth3.nextCommandLine(&ts) =3D=3D "dXNlcg=3D=3D\r\n");
- assert(!auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("334 go on");
- assert(auth3.processResponse(r, &ts) =3D=3D true);
- assert(!auth3.needsResponse());
- assert(auth3.nextCommandLine(&ts) =3D=3D "cGFzcw=3D=3D\r\n");
- assert(auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("250 OK");
- assert(auth3.processResponse(r, &ts) =3D=3D true);
- assert(!auth3.needsResponse());
- assert(!smtp.lastErrorCode);
- assert(ts =3D=3D ts2);
-
- //
- // MAIL FROM:
- //
-
- smtp.clear();
- MailFromCommand mail(&smtp, "joe@user.org");
- // flags
- assert(!mail.closeConnectionOnError());
- assert(!mail.mustBeLastInPipeline());
- assert(!mail.mustBeFirstInPipeline());
-
- // initial state
- assert(!mail.isComplete());
- assert(!mail.doNotExecute(nullptr));
- assert(!mail.needsResponse());
-
- // dynamics: success, no size, no 8bit
- ts.clear();
- ts2 =3D ts;
- assert(mail.nextCommandLine(&ts) =3D=3D "MAIL FROM:\r\n"=
);
- assert(ts2 =3D=3D ts);
- assert(mail.isComplete());
- assert(mail.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(mail.processResponse(r, &ts) =3D=3D true);
- assert(!mail.needsResponse());
- assert(ts =3D=3D ts2);
- assert(smtp.lastErrorCode =3D=3D 0);
-
- // dynamics: success, size, 8bit, but no SIZE, 8BITMIME caps
- smtp.clear();
- MailFromCommand mail2(&smtp, "joe@user.org", true, 500);
- ts.clear();
- ts2 =3D ts;
- assert(mail2.nextCommandLine(&ts) =3D=3D "MAIL FROM:\r\n=
");
- assert(ts =3D=3D ts2);
-
- // dynamics: success, size, 8bit, SIZE, 8BITMIME caps
- smtp.clear();
- MailFromCommand mail3(&smtp, "joe@user.org", true, 500);
- ts.clear();
- ts2 =3D ts;
- smtp.caps << QStringLiteral("SIZE") << QStringLiteral("8BITMIME");
- assert(mail3.nextCommandLine(&ts) =3D=3D "MAIL FROM: BOD=
Y=3D8BITMIME SIZE=3D500\r\n");
- assert(ts =3D=3D ts2);
-
- // dynamics: failure
- smtp.clear();
- MailFromCommand mail4(&smtp, "joe@user.org");
- ts.clear();
- mail4.nextCommandLine(&ts);
- r.clear();
- r.parseLine("503 Bad sequence of commands");
- assert(mail4.processResponse(r, &ts) =3D=3D false);
- assert(mail4.isComplete());
- assert(!mail4.needsResponse());
- assert(ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode =3D=3D 0);
-
- //
- // RCPT TO:
- //
-
- smtp.clear();
- RcptToCommand rcpt(&smtp, "joe@user.org");
- // flags
- assert(!rcpt.closeConnectionOnError());
- assert(!rcpt.mustBeLastInPipeline());
- assert(!rcpt.mustBeFirstInPipeline());
-
- // initial state
- assert(!rcpt.isComplete());
- assert(!rcpt.doNotExecute(nullptr));
- assert(!rcpt.needsResponse());
-
- // dynamics: success
- ts.clear();
- ts2 =3D ts;
- assert(rcpt.nextCommandLine(&ts) =3D=3D "RCPT TO:\r\n");
- assert(ts =3D=3D ts2);
- assert(rcpt.isComplete());
- assert(rcpt.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(rcpt.processResponse(r, &ts) =3D=3D true);
- assert(!rcpt.needsResponse());
- assert(ts.atLeastOneRecipientWasAccepted());
- assert(!ts.haveRejectedRecipients());
- assert(!ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode =3D=3D 0);
-
- // dynamics: failure
- smtp.clear();
- RcptToCommand rcpt2(&smtp, "joe@user.org");
- ts.clear();
- rcpt2.nextCommandLine(&ts);
- r.clear();
- r.parseLine("530 5.7.1 Relaying not allowed!");
- assert(rcpt2.processResponse(r, &ts) =3D=3D false);
- assert(rcpt2.isComplete());
- assert(!rcpt2.needsResponse());
- assert(!ts.atLeastOneRecipientWasAccepted());
- assert(ts.haveRejectedRecipients());
- assert(ts.rejectedRecipients().count() =3D=3D 1);
- assert(ts.rejectedRecipients().front().recipient =3D=3D QLatin1String(=
"joe@user.org"));
- assert(ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode =3D=3D 0);
-
- // dynamics: success and failure combined
- smtp.clear();
- RcptToCommand rcpt3(&smtp, "info@example.com");
- RcptToCommand rcpt4(&smtp, "halloween@microsoft.com");
- RcptToCommand rcpt5(&smtp, "joe@user.org");
- ts.clear();
- rcpt3.nextCommandLine(&ts);
- r.clear();
- r.parseLine("530 5.7.1 Relaying not allowed!");
- rcpt3.processResponse(r, &ts);
-
- rcpt4.nextCommandLine(&ts);
- r.clear();
- r.parseLine("250 Ok");
- rcpt4.processResponse(r, &ts);
-
- rcpt5.nextCommandLine(&ts);
- r.clear();
- r.parseLine("250 Ok");
- assert(ts.failed());
- assert(!ts.failedFatally());
- assert(ts.haveRejectedRecipients());
- assert(ts.atLeastOneRecipientWasAccepted());
- assert(smtp.lastErrorCode =3D=3D 0);
-
- //
- // DATA (init)
- //
-
- smtp.clear();
- DataCommand data(&smtp);
- // flags
- assert(!data.closeConnectionOnError());
- assert(data.mustBeLastInPipeline());
- assert(!data.mustBeFirstInPipeline());
-
- // initial state
- assert(!data.isComplete());
- assert(!data.doNotExecute(nullptr));
- assert(!data.needsResponse());
-
- // dynamics: success
- ts.clear();
- assert(data.nextCommandLine(&ts) =3D=3D "DATA\r\n");
- assert(data.isComplete());
- assert(data.needsResponse());
- assert(ts.dataCommandIssued());
- assert(!ts.dataCommandSucceeded());
- r.clear();
- r.parseLine("354 Send data, end in .");
- assert(data.processResponse(r, &ts) =3D=3D true);
- assert(!data.needsResponse());
- assert(ts.dataCommandSucceeded());
- assert(ts.dataResponse() =3D=3D r);
- assert(smtp.lastErrorCode =3D=3D 0);
-
- // dynamics: failure
- smtp.clear();
- DataCommand data2(&smtp);
- ts.clear();
- data2.nextCommandLine(&ts);
- r.clear();
- r.parseLine("551 No valid recipients");
- assert(data2.processResponse(r, &ts) =3D=3D false);
- assert(!data2.needsResponse());
- assert(!ts.dataCommandSucceeded());
- assert(ts.dataResponse() =3D=3D r);
- assert(smtp.lastErrorCode =3D=3D 0);
-
- //
- // DATA (transfer)
- //
-
- TransferCommand xfer(&smtp, nullptr);
- // flags
- assert(!xfer.closeConnectionOnError());
- assert(!xfer.mustBeLastInPipeline());
- assert(xfer.mustBeFirstInPipeline());
-
- // initial state
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
-
- // dynamics 1: DATA command failed
- ts.clear();
- r.clear();
- r.parseLine("551 no valid recipients");
- ts.setDataCommandIssued(true);
- ts.setDataCommandSucceeded(false, r);
- assert(xfer.doNotExecute(&ts));
-
- // dynamics 2: some recipients rejected, but not all
- smtp.clear();
- TransferCommand xfer2(&smtp, nullptr);
- ts.clear();
- ts.setRecipientAccepted();
- ts.addRejectedRecipient(QStringLiteral("joe@user.org"), QStringLiteral=
("No relaying allowed"));
- ts.setDataCommandIssued(true);
- r.clear();
- r.parseLine("354 go on");
- ts.setDataCommandSucceeded(true, r);
- // ### will change with allow-partial-delivery option:
- assert(xfer.doNotExecute(&ts));
-
- // successful dynamics with all combinations of:
- enum {
- EndInLF =3D 1,
- PerformDotStuff =3D 2,
- UngetLast =3D 4,
- Preloading =3D 8,
- Error =3D 16,
- EndOfOptions =3D 32
- };
- for (unsigned int i =3D 0; i < EndOfOptions; ++i) {
- checkSuccessfulTransferCommand(i & Error, i & Preloading, i & Unge=
tLast,
- i & PerformDotStuff, i & EndInLF);
- }
-
- //
- // NOOP
- //
-
- smtp.clear();
- NoopCommand noop(&smtp);
- // flags
- assert(!noop.closeConnectionOnError());
- assert(noop.mustBeLastInPipeline());
- assert(!noop.mustBeFirstInPipeline());
-
- // initial state
- assert(!noop.isComplete());
- assert(!noop.doNotExecute(&ts));
- assert(!noop.needsResponse());
-
- // dynamics: success (failure is tested with RSET)
- assert(noop.nextCommandLine(nullptr) =3D=3D "NOOP\r\n");
- assert(noop.isComplete());
- assert(noop.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(noop.processResponse(r, nullptr) =3D=3D true);
- assert(noop.isComplete());
- assert(!noop.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0);
- assert(smtp.lastErrorMessage.isNull());
-
- //
- // RSET
- //
-
- smtp.clear();
- RsetCommand rset(&smtp);
- // flags
- assert(rset.closeConnectionOnError());
- assert(!rset.mustBeLastInPipeline());
- assert(!rset.mustBeFirstInPipeline());
-
- // initial state
- assert(!rset.isComplete());
- assert(!rset.doNotExecute(&ts));
- assert(!rset.needsResponse());
-
- // dynamics: failure (success is tested with NOOP/QUIT)
- assert(rset.nextCommandLine(nullptr) =3D=3D "RSET\r\n");
- assert(rset.isComplete());
- assert(rset.needsResponse());
- r.clear();
- r.parseLine("502 command not implemented");
- assert(rset.processResponse(r, nullptr) =3D=3D false);
- assert(rset.isComplete());
- assert(!rset.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0); // an RSET failure isn't worth =
it, is it?
- assert(smtp.lastErrorMessage.isNull());
-
- //
- // QUIT
- //
-
- smtp.clear();
- QuitCommand quit(&smtp);
- // flags
- assert(quit.closeConnectionOnError());
- assert(quit.mustBeLastInPipeline());
- assert(!quit.mustBeFirstInPipeline());
-
- // initial state
- assert(!quit.isComplete());
- assert(!quit.doNotExecute(nullptr));
- assert(!quit.needsResponse());
-
- // dynamics 1: success
- assert(quit.nextCommandLine(nullptr) =3D=3D "QUIT\r\n");
- assert(quit.isComplete());
- assert(quit.needsResponse());
- r.clear();
- r.parseLine("221 Goodbye");
- assert(quit.processResponse(r, nullptr) =3D=3D true);
- assert(quit.isComplete());
- assert(!quit.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0);
- assert(smtp.lastErrorMessage.isNull());
-
- // dynamics 2: success
- smtp.clear();
- QuitCommand quit2(&smtp);
- quit2.nextCommandLine(nullptr);
- r.clear();
- r.parseLine("500 unknown command");
- assert(quit2.processResponse(r, nullptr) =3D=3D false);
- assert(quit2.isComplete());
- assert(!quit2.needsResponse());
- assert(smtp.lastErrorCode =3D=3D 0); // an QUIT failure isn't worth =
it, is it?
- assert(smtp.lastErrorMessage.isNull());
-
- return 0;
-}
-
-void checkSuccessfulTransferCommand(bool error, bool preload, bool ungetLa=
st, bool slaveDotStuff, bool mailEndsInNewline)
-{
- qDebug() << " =3D=3D=3D=3D=3D checkTransferCommand( "
- << error << ", "
- << preload << ", "
- << ungetLast << ", "
- << slaveDotStuff << ", "
- << mailEndsInNewline << " ) =3D=3D=3D=3D=3D" << endl;
-
- FakeSession smtp;
- if (slaveDotStuff) {
- smtp.lf2crlfAndDotStuff =3D true;
- }
-
- Response r;
-
- const char *s_pre =3D slaveDotStuff
- ? mailEndsInNewline ? foobarbaz_lf : foobarbaz
- :
- mailEndsInNewline ? foobarbaz_crlf : foobarbaz_dot=
stuffed;
- const unsigned int s_pre_len =3D qstrlen(s_pre);
-
- const char *s_post =3D mailEndsInNewline ? foobarbaz_crlf : foobarbaz_=
dotstuffed;
- //const unsigned int s_post_len =3D qstrlen( s_post );
-
- TransferCommand xfer(&smtp, preload ? s_post : nullptr);
-
- TransactionState ts;
- ts.setRecipientAccepted();
- ts.setDataCommandIssued(true);
- r.clear();
- r.parseLine("354 ok");
- ts.setDataCommandSucceeded(true, r);
- assert(!xfer.doNotExecute(&ts));
- if (preload) {
- assert(xfer.nextCommandLine(&ts) =3D=3D s_post);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.failed());
- assert(smtp.lastErrorCode =3D=3D 0);
- }
- smtp.nextData =3D QByteArray(s_pre, s_pre_len);
- smtp.nextDataReturnCode =3D s_pre_len;
- assert(xfer.nextCommandLine(&ts) =3D=3D s_post);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.failed());
- assert(smtp.lastErrorCode =3D=3D 0);
- smtp.nextData.resize(0);
- smtp.nextDataReturnCode =3D 0;
- if (ungetLast) {
- xfer.ungetCommandLine(xfer.nextCommandLine(&ts), &ts);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.complete());
- smtp.nextDataReturnCode =3D -1; // double read -> error
- }
- if (mailEndsInNewline) {
- assert(xfer.nextCommandLine(&ts) =3D=3D ".\r\n");
- } else {
- assert(xfer.nextCommandLine(&ts) =3D=3D "\r\n.\r\n");
- }
- assert(xfer.isComplete());
- assert(xfer.needsResponse());
- assert(!ts.complete());
- assert(!ts.failed());
- assert(smtp.lastErrorCode =3D=3D 0);
- r.clear();
- if (error) {
- r.parseLine("552 Exceeded storage allocation");
- assert(xfer.processResponse(r, &ts) =3D=3D false);
- assert(!xfer.needsResponse());
- assert(ts.complete());
- assert(ts.failed());
- assert(smtp.lastErrorCode =3D=3D KIO::ERR_DISK_FULL);
- } else {
- r.parseLine("250 Message accepted");
- assert(xfer.processResponse(r, &ts) =3D=3D true);
- assert(!xfer.needsResponse());
- assert(ts.complete());
- assert(!ts.failed());
- assert(smtp.lastErrorCode =3D=3D 0);
- }
-}
-
-#ifndef NDEBUG
-# define NDEBUG
-#endif
-
-#include "command.cpp"
-#include "response.cpp"
-#include "transactionstate.cpp"
diff --git a/kioslave/src/smtp/tests/test_headergeneration.cpp b/kioslave/s=
rc/smtp/tests/test_headergeneration.cpp
deleted file mode 100644
index a056b18..0000000
--- a/kioslave/src/smtp/tests/test_headergeneration.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "../request.h"
-
-#include
-
-//using std::cout;
-//using std::endl;
-
-int main(int, char **)
-{
- static QByteArray expected
- =3D "From: mutz@kde.org\r\n"
- "Subject: missing subject\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: Marc Mutz \r\n"
- "Subject: missing subject\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: \"Mutz, Marc\" \r\n"
- "Subject: missing subject\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: =3D?utf-8?b?TWFyYyBNw7Z0eg=3D=3D?=3D \r\n"
- "Subject: missing subject\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: mutz@kde.org\r\n"
- "Subject: =3D?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=3D\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: Marc Mutz \r\n"
- "Subject: =3D?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=3D\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: \"Mutz, Marc\" \r\n"
- "Subject: =3D?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=3D\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n"
- "From: =3D?utf-8?b?TWFyYyBNw7Z0eg=3D=3D?=3D \r\n"
- "Subject: =3D?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=3D\r\n"
- "To: joe@user.org,\r\n"
- "\tvalentine@14th.february.org\r\n"
- "Cc: boss@example.com\r\n"
- "\n";
-
- KioSMTP::Request request;
- QByteArray result;
-
- request.setEmitHeaders(true);
- request.setFromAddress(QStringLiteral("mutz@kde.org"));
- request.addTo(QStringLiteral("joe@user.org"));
- request.addTo(QStringLiteral("valentine@14th.february.org"));
- request.addCc(QStringLiteral("boss@example.com"));
-
- result +=3D request.headerFields() + '\n';
- result +=3D request.headerFields(QStringLiteral("Marc Mutz")) + '\n';
- result +=3D request.headerFields(QStringLiteral("Mutz, Marc")) + '\n';
- result +=3D request.headerFields(QString::fromUtf8("Marc M=C3=B6tz")) =
+ '\n';
-
- request.setSubject(QString::fromUtf8("Bl=C3=B6des Subject"));
-
- result +=3D request.headerFields() + '\n';
- result +=3D request.headerFields(QStringLiteral("Marc Mutz")) + '\n';
- result +=3D request.headerFields(QStringLiteral("Mutz, Marc")) + '\n';
- result +=3D request.headerFields(QString::fromUtf8("Marc M=C3=B6tz")) =
+ '\n';
-
- if (result !=3D expected) {
- std::cout << "Result:\n" << result.data() << std::endl;
- std::cout << "Expected:\n" << expected.data() << std::endl;
- }
-
- return result =3D=3D expected ? 0 : 1;
-}
-
-#include "../request.cpp"
diff --git a/kioslave/src/smtp/tests/test_responseparser.cpp b/kioslave/src=
/smtp/tests/test_responseparser.cpp
deleted file mode 100644
index 18c016b..0000000
--- a/kioslave/src/smtp/tests/test_responseparser.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include "test_responseparser.h"
-#include "../response.h"
-
-#include
-#include
-
-QTEST_GUILESS_MAIN(ResponseParserTest)
-
-static const QByteArray singleLineResponseCRLF =3D "250 OK\r\n";
-static const QByteArray singleLineResponse =3D "250 OK";
-
-static const QByteArray multiLineResponse[] =3D {
- "250-ktown.kde.org\r\n",
- "250-STARTTLS\r\n",
- "250-AUTH PLAIN DIGEST-MD5\r\n",
- "250 PIPELINING\r\n"
-};
-static const unsigned int numMultiLineLines =3D sizeof multiLineResponse /=
sizeof *multiLineResponse;
-
-void ResponseParserTest::testResponseParser()
-{
- KioSMTP::Response r;
- QVERIFY(r.isValid());
- QVERIFY(r.lines().empty());
- QVERIFY(r.isWellFormed());
- QCOMPARE(r.code(), 0u);
- QVERIFY(r.isUnknown());
- QVERIFY(!r.isComplete());
- QVERIFY(!r.isOk());
- r.parseLine(singleLineResponseCRLF.data(), singleLineResponseCRLF.leng=
th());
- QVERIFY(r.isWellFormed());
- QVERIFY(r.isComplete());
- QVERIFY(r.isValid());
- QVERIFY(r.isPositive());
- QVERIFY(r.isOk());
- QCOMPARE(r.code(), 250u);
- QCOMPARE(r.errorCode(), 0);
- QCOMPARE(r.first(), 2u);
- QCOMPARE(r.second(), 5u);
- QCOMPARE(r.third(), 0u);
- QCOMPARE(r.lines().count(), 1);
- QCOMPARE(r.lines().front(), QByteArray("OK"));
- r.parseLine(singleLineResponse.data(), singleLineResponse.length());
- QVERIFY(!r.isValid());
- r.clear();
- QVERIFY(r.isValid());
- QVERIFY(r.lines().empty());
-
- r.parseLine(singleLineResponse.data(), singleLineResponse.length());
- QVERIFY(r.isWellFormed());
- QVERIFY(r.isComplete());
- QVERIFY(r.isValid());
- QVERIFY(r.isPositive());
- QVERIFY(r.isOk());
- QCOMPARE(r.code(), 250u);
- QCOMPARE(r.first(), 2u);
- QCOMPARE(r.second(), 5u);
- QCOMPARE(r.third(), 0u);
- QCOMPARE(r.lines().count(), 1);
- QCOMPARE(r.lines().front(), QByteArray("OK"));
- r.parseLine(singleLineResponse.data(), singleLineResponse.length());
- QVERIFY(!r.isValid());
- r.clear();
- QVERIFY(r.isValid());
-
- for (unsigned int i =3D 0; i < numMultiLineLines; ++i) {
- r.parseLine(multiLineResponse[i].data(), multiLineResponse[i].leng=
th());
- QVERIFY(r.isWellFormed());
- if (i < numMultiLineLines - 1) {
- QVERIFY(!r.isComplete());
- } else {
- QVERIFY(r.isComplete());
- }
- QVERIFY(r.isValid());
- QVERIFY(r.isPositive());
- QCOMPARE(r.code(), 250u);
- QCOMPARE(r.first(), 2u);
- QCOMPARE(r.second(), 5u);
- QCOMPARE(r.third(), 0u);
- QCOMPARE(r.lines().count(), (int)i + 1);
- }
- QCOMPARE(r.lines().back(), QByteArray("PIPELINING"));
-
- r.clear();
- r.parseLine("230", 3);
- QVERIFY(r.isValid());
- QVERIFY(r.isWellFormed()); // even though it isn't ;-)
- QCOMPARE(r.code(), 230u);
- QCOMPARE(r.lines().count(), 1);
- QVERIFY(r.lines().front().isNull());
-
- r.clear();
- r.parseLine("230\r\n", 5);
- QVERIFY(r.isValid());
- QVERIFY(r.isWellFormed()); // even though it isn't ;-)
- QCOMPARE(r.code(), 230u);
- QCOMPARE(r.lines().count(), 1);
- QVERIFY(r.lines().front().isNull());
-
- r.clear();
- r.parseLine(" 23 ok", 6);
- QVERIFY(!r.isValid());
- QVERIFY(!r.isWellFormed());
-}
-
-#include "../response.cpp"
diff --git a/kioslave/src/smtp/tests/test_responseparser.h b/kioslave/src/s=
mtp/tests/test_responseparser.h
deleted file mode 100644
index b637988..0000000
--- a/kioslave/src/smtp/tests/test_responseparser.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- Copyright (c) 2006 Volker Krause
-
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published=
by
- the Free Software Foundation; either version 2 of the License, or (at =
your
- option) any later version.
-
- This library is distributed in the hope that it will be useful, but WI=
THOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
- License for more details.
-
- You should have received a copy of the GNU Library General Public Lice=
nse
- along with this library; see the file COPYING.LIB. If not, write to t=
he
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
- 02110-1301, USA.
-*/
-
-#ifndef RESPONSEPARSER_TEST_H
-#define RESPONSEPARSER_TEST_H
-
-#include
-
-class ResponseParserTest : public QObject
-{
- Q_OBJECT
-private Q_SLOTS:
- void testResponseParser();
-};
-
-#endif
diff --git a/kioslave/src/smtp/transactionstate.cpp b/kioslave/src/smtp/tra=
nsactionstate.cpp
deleted file mode 100644
index 9577988..0000000
--- a/kioslave/src/smtp/transactionstate.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/* -*- c++ -*-
- transactionstate.cc
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#include "transactionstate.h"
-
-#include
-#include
-
-namespace KioSMTP {
-void TransactionState::setFailedFatally(int code, const QString &msg)
-{
- mFailed =3D mFailedFatally =3D true;
- mErrorCode =3D code;
- mErrorMessage =3D msg;
-}
-
-void TransactionState::setMailFromFailed(const QString &addr, const Respon=
se &r)
-{
- setFailed();
- mErrorCode =3D KIO::ERR_NO_CONTENT;
- if (addr.isEmpty()) {
- mErrorMessage =3D i18n("The server did not accept a blank sender a=
ddress.\n"
- "%1", r.errorMessage());
- } else {
- mErrorMessage =3D i18n("The server did not accept the sender addre=
ss \"%1\".\n"
- "%2", addr, r.errorMessage());
- }
-}
-
-void TransactionState::addRejectedRecipient(const RecipientRejection &r)
-{
- mRejectedRecipients.push_back(r);
- if (mRcptToDenyIsFailure) {
- setFailed();
- }
-}
-
-void TransactionState::setDataCommandSucceeded(bool succeeded, const Respo=
nse &r)
-{
- mDataCommandSucceeded =3D succeeded;
- mDataResponse =3D r;
- if (!succeeded) {
- setFailed();
- } else if (failed()) {
- // can happen with pipelining: the server accepts the DATA, but
- // we don't want to send the data, so force a connection
- // shutdown:
- setFailedFatally();
- }
-}
-
-int TransactionState::errorCode() const
-{
- if (!failed()) {
- return 0;
- }
- if (mErrorCode) {
- return mErrorCode;
- }
- if (haveRejectedRecipients() || !dataCommandSucceeded()) {
- return KIO::ERR_NO_CONTENT;
- }
- // ### what else?
- return KIO::ERR_INTERNAL;
-}
-
-QString TransactionState::errorMessage() const
-{
- if (!failed()) {
- return QString();
- }
-
- if (!mErrorMessage.isEmpty()) {
- return mErrorMessage;
- }
-
- if (haveRejectedRecipients()) {
- QStringList recip;
- recip.reserve(mRejectedRecipients.count());
- for (RejectedRecipientList::const_iterator it =3D mRejectedRecipie=
nts.begin(), end(mRejectedRecipients.end());
- it !=3D end; ++it) {
- recip.push_back((*it).recipient + QLatin1String(" (") + (*it).=
reason + QLatin1Char(')'));
- }
- return i18n("Message sending failed since the following recipients=
were rejected by the server:\n"
- "%1", recip.join(QLatin1Char('\n')));
- }
-
- if (!dataCommandSucceeded()) {
- return i18n("The attempt to start sending the message content fail=
ed.\n"
- "%1", mDataResponse.errorMessage());
- }
-
- // ### what else?
- return i18n("Unhandled error condition. Please send a bug report.");
-}
-}
diff --git a/kioslave/src/smtp/transactionstate.h b/kioslave/src/smtp/trans=
actionstate.h
deleted file mode 100644
index 43fda12..0000000
--- a/kioslave/src/smtp/transactionstate.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/* -*- c++ -*-
- transactionstate.h
-
- This file is part of kio_smtp, the KDE SMTP kioslave.
- Copyright (c) 2003 Marc Mutz
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License, version 2, as
- published by the Free Software Foundation.
-
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1=
301 USA
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of this program with any edition of
- the Qt library by Trolltech AS, Norway (or with modified versions
- of Qt that use the same license as Qt), and distribute linked
- combinations including the two. You must obey the GNU General
- Public License in all respects for all of the code used other than
- Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
- you do not wish to do so, delete this exception statement from
- your version.
-*/
-
-#ifndef __KIOSMTP_TRANSACTIONSTATE_H__
-#define __KIOSMTP_TRANSACTIONSTATE_H__
-
-#include "response.h"
-
-#include
-
-namespace KioSMTP {
-/**
- @short A class modelling an SMTP transaction's state
-
- This class models SMTP transaction state, ie. the collective
- result of the MAIL FROM:, RCPT TO: and DATA commands. This is
- needed since e.g. a single failed RCPT TO: command does not
- necessarily fail the whole transaction (servers are free to
- accept delivery for some recipients, but not for others).
-
- The class can operate in two modes, which differ in the way
- failed recipients are handled. If @p rcptToDenyIsFailure is true
- (the default), then any failing RCPT TO: will cause the
- transaction to fail. Since at the point of RCPT TO: failure
- detection, the DATA command may have already been sent
- (pipelining), the only way to cancel the transaction is to take
- down the connection hard (ie. without proper quit).
-
- Since that is not very nice behaviour, a second mode that is more
- to the spirit of SMTP is provided that can cope with partially
- failed RCPT TO: commands.
-*/
-class TransactionState
-{
-public:
- struct RecipientRejection {
- RecipientRejection(const QString &who =3D QString(), const QString=
&why =3D QString())
- : recipient(who)
- , reason(why)
- {
- }
-
- QString recipient;
- QString reason;
-#ifdef KIOSMTP_COMPARATORS
- bool operator=3D=3D(const RecipientRejection &other) const
- {
- return recipient =3D=3D other.recipient && reason =3D=3D other=
.reason;
- }
-
-#endif
- };
- typedef QList RejectedRecipientList;
-
- TransactionState(bool rcptToDenyIsFailure =3D true)
- : mErrorCode(0)
- , mRcptToDenyIsFailure(rcptToDenyIsFailure)
- , mAtLeastOneRecipientWasAccepted(false)
- , mDataCommandIssued(false)
- , mDataCommandSucceeded(false)
- , mFailed(false)
- , mFailedFatally(false)
- , mComplete(false)
- {
- }
-
- /**
- * @return whether the transaction failed (e.g. the server
- * rejected all recipients. Graceful failure is handled after
- * transaction ends.
- */
- bool failed() const
- {
- return mFailed || mFailedFatally;
- }
-
- void setFailed()
- {
- mFailed =3D true;
- }
-
- /**
- * @return whether the failure was so grave that an immediate
- * untidy connection shutdown is in order (ie. @ref
- * smtp_close(false)). Fatal failure is handled immediately
- */
- bool failedFatally() const
- {
- return mFailedFatally;
- }
-
- void setFailedFatally(int code =3D 0, const QString &msg =3D QString()=
);
-
- /** @return whether the transaction was completed successfully */
- bool complete() const
- {
- return mComplete;
- }
-
- void setComplete()
- {
- mComplete =3D true;
- }
-
- /**
- * @return an appropriate KIO error code in case the transaction
- * failed, or 0 otherwise
- */
- int errorCode() const;
-
- /**
- * @return an appropriate error message in case the transaction
- * failed or QString() otherwise
- */
- QString errorMessage() const;
-
- void setMailFromFailed(const QString &addr, const Response &r);
-
- bool dataCommandIssued() const
- {
- return mDataCommandIssued;
- }
-
- void setDataCommandIssued(bool issued)
- {
- mDataCommandIssued =3D issued;
- }
-
- bool dataCommandSucceeded() const
- {
- return mDataCommandIssued && mDataCommandSucceeded;
- }
-
- void setDataCommandSucceeded(bool succeeded, const Response &r);
-
- Response dataResponse() const
- {
- return mDataResponse;
- }
-
- bool atLeastOneRecipientWasAccepted() const
- {
- return mAtLeastOneRecipientWasAccepted;
- }
-
- void setRecipientAccepted()
- {
- mAtLeastOneRecipientWasAccepted =3D true;
- }
-
- bool haveRejectedRecipients() const
- {
- return !mRejectedRecipients.empty();
- }
-
- RejectedRecipientList rejectedRecipients() const
- {
- return mRejectedRecipients;
- }
-
- void addRejectedRecipient(const RecipientRejection &r);
- void addRejectedRecipient(const QString &who, const QString &why)
- {
- addRejectedRecipient(RecipientRejection(who, why));
- }
-
- void clear()
- {
- mRejectedRecipients.clear();
- mDataResponse.clear();
- mAtLeastOneRecipientWasAccepted
- =3D mDataCommandIssued
- =3D mDataCommandSucceeded
- =3D mFailed =3D mFailedFatally
- =3D mComplete =3D false;
- }
-
-#ifdef KIOSMTP_COMPARATORS
- bool operator=3D=3D(const TransactionState &other) const
- {
- return
- mAtLeastOneRecipientWasAccepted =3D=3D other.mAtLeastOneRecipi=
entWasAccepted
- && mDataCommandIssued =3D=3D other.mDataCommandIssued
- && mDataCommandSucceeded =3D=3D other.mDataCommandSucceeded
- && mFailed =3D=3D other.mFailed
- && mFailedFatally =3D=3D other.mFailedFatally
- && mComplete =3D=3D other.mComplete
- && mDataResponse.code() =3D=3D other.mDataResponse.code()
- && mRejectedRecipients =3D=3D other.mRejectedRecipients;
- }
-
-#endif
-
-private:
- RejectedRecipientList mRejectedRecipients;
- Response mDataResponse;
- QString mErrorMessage;
- int mErrorCode;
- bool mRcptToDenyIsFailure;
- bool mAtLeastOneRecipientWasAccepted;
- bool mDataCommandIssued;
- bool mDataCommandSucceeded;
- bool mFailed;
- bool mFailedFatally;
- bool mComplete;
-};
-} // namespace KioSMTP
-
-#endif // __KIOSMTP_TRANSACTIONSTATE_H__
diff --git a/src/kmailtransport/plugins/smtp/CMakeLists.txt b/src/kmailtran=
sport/plugins/smtp/CMakeLists.txt
index bae5343..f378daa 100644
--- a/src/kmailtransport/plugins/smtp/CMakeLists.txt
+++ b/src/kmailtransport/plugins/smtp/CMakeLists.txt
@@ -1,3 +1,7 @@
+if (BUILD_TESTING)
+ add_subdirectory(autotests)
+endif()
+
set(mailtransport_smtpplugin_SRCS
smtpmailtransportplugin.cpp
smtpconfigdialog.cpp
@@ -18,6 +22,7 @@ target_link_libraries(mailtransport_smtpplugin
KF5::MailTransport
KF5::I18n
KF5::ConfigWidgets
- KF5::KIOCore
+ KF5::KIOWidgets
KF5::Completion
+ KPim::SMTP
)
diff --git a/src/kmailtransport/plugins/smtp/autotests/CMakeLists.txt b/src=
/kmailtransport/plugins/smtp/autotests/CMakeLists.txt
new file mode 100644
index 0000000..f85ae2a
--- /dev/null
+++ b/src/kmailtransport/plugins/smtp/autotests/CMakeLists.txt
@@ -0,0 +1,19 @@
+include(ECMAddTests)
+
+find_package(Qt5Test ${QT_REQUIRED_VERSION} REQUIRED)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
+
+ecm_add_test(smtpjobtest.cpp
+ ../smtpjob.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/../mailtransportplugin_smtp_debug=
.cpp
+ fakeserver.cpp
+ LINK_LIBRARIES Qt5::Network
+ Qt5::Test
+ KF5::MailTransport
+ KF5::I18n
+ KF5::ConfigWidgets
+ KF5::KIOWidgets
+ KPim::SMTP
+ TEST_NAME smtpjobtest
+)
diff --git a/src/kmailtransport/plugins/smtp/autotests/fakeserver.cpp b/src=
/kmailtransport/plugins/smtp/autotests/fakeserver.cpp
new file mode 100644
index 0000000..be692bf
--- /dev/null
+++ b/src/kmailtransport/plugins/smtp/autotests/fakeserver.cpp
@@ -0,0 +1,232 @@
+/*
+ Copyright 2010 BetterInbox
+ Author: Christophe Laveault
+ Gregory Schlomoff
+
+ 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see .
+*/
+
+#include "fakeserver.h"
+
+#include
+#include
+#include
+
+FakeServer::FakeServer(QObject *parent) :
+ QThread(parent),
+ m_tcpServer(nullptr)
+{
+ moveToThread(this);
+}
+
+QByteArray FakeServer::greeting()
+{
+ return "S: 220 localhost ESMTP xx777xx";
+}
+
+QList FakeServer::greetingAndEhlo(bool multiline)
+{
+ return QList() << greeting()
+ << "C: EHLO 127.0.0.1"
+ << QByteArray("S: 250") + (multiline ? '-' : ' ') + "Localhost =
ready to roll";
+}
+
+QList FakeServer::bye()
+{
+ return { "C: QUIT",
+ "S: 221 So long, and thanks for all the fish",
+ "X: "
+ };
+}
+
+FakeServer::~FakeServer()
+{
+ quit();
+ wait();
+}
+
+void FakeServer::startAndWait()
+{
+ start();
+ // this will block until the event queue starts
+ QMetaObject::invokeMethod(this, "started", Qt::BlockingQueuedConnectio=
n);
+}
+
+void FakeServer::dataAvailable()
+{
+ QMutexLocker locker(&m_mutex);
+
+ QTcpSocket *socket =3D qobject_cast(sender());
+ Q_ASSERT(socket !=3D nullptr);
+
+ int scenarioNumber =3D m_clientSockets.indexOf(socket);
+
+ QVERIFY(!m_scenarios[scenarioNumber].isEmpty());
+
+ readClientPart(scenarioNumber);
+ writeServerPart(scenarioNumber);
+}
+
+void FakeServer::newConnection()
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_clientSockets << m_tcpServer->nextPendingConnection();
+ connect(m_clientSockets.last(), SIGNAL(readyRead()), this, SLOT(dataAv=
ailable()));
+ //m_clientParsers << new KIMAP::ImapStreamParser( m_clientSockets.last=
(), true );
+
+ QVERIFY(m_clientSockets.size() <=3D m_scenarios.size());
+
+ writeServerPart(m_clientSockets.size() - 1);
+}
+
+void FakeServer::run()
+{
+ m_tcpServer =3D new QTcpServer();
+ if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), 5989))=
{
+ qFatal("Unable to start the server");
+ return;
+ }
+
+ connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection=
()));
+
+ exec();
+
+ qDeleteAll(m_clientSockets);
+
+ delete m_tcpServer;
+}
+
+void FakeServer::started()
+{
+ // do nothing: this is a dummy slot used by startAndWait()
+}
+
+void FakeServer::setScenario(const QList &scenario)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_scenarios.clear();
+ m_scenarios << scenario;
+}
+
+void FakeServer::addScenario(const QList &scenario)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_scenarios << scenario;
+}
+
+void FakeServer::addScenarioFromFile(const QString &fileName)
+{
+ QFile file(fileName);
+ file.open(QFile::ReadOnly);
+
+ QList scenario;
+
+ // When loading from files we never have the authentication phase
+ // force jumping directly to authenticated state.
+ //scenario << preauth();
+
+ while (!file.atEnd()) {
+ scenario << file.readLine().trimmed();
+ }
+
+ file.close();
+
+ addScenario(scenario);
+}
+
+bool FakeServer::isScenarioDone(int scenarioNumber) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (scenarioNumber < m_scenarios.size()) {
+ return m_scenarios[scenarioNumber].isEmpty();
+ } else {
+ return true; // Non existent hence empty, right?
+ }
+}
+
+bool FakeServer::isAllScenarioDone() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ for (const auto &scenario : qAsConst(m_scenarios)) {
+ if (!scenario.isEmpty()) {
+ qDebug() << scenario;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void FakeServer::writeServerPart(int scenarioNumber)
+{
+ QList scenario =3D m_scenarios[scenarioNumber];
+ QTcpSocket *clientSocket =3D m_clientSockets[scenarioNumber];
+
+ while (!scenario.isEmpty()
+ && (scenario.first().startsWith("S: ") || scenario.first().sta=
rtsWith("W: "))) {
+ QByteArray rule =3D scenario.takeFirst();
+
+ if (rule.startsWith("S: ")) {
+ QByteArray payload =3D rule.mid(3);
+ clientSocket->write(payload + "\r\n");
+ } else {
+ int timeout =3D rule.mid(3).toInt();
+ QTest::qWait(timeout);
+ }
+ }
+
+ if (!scenario.isEmpty() && scenario.first().startsWith('X')) {
+ scenario.takeFirst();
+ clientSocket->close();
+ }
+
+ if (!scenario.isEmpty()) {
+ QVERIFY(scenario.first().startsWith("C: "));
+ }
+
+ m_scenarios[scenarioNumber] =3D scenario;
+}
+
+void FakeServer::readClientPart(int scenarioNumber)
+{
+ QList scenario =3D m_scenarios[scenarioNumber];
+ QTcpSocket *clientSocket =3D m_clientSockets[scenarioNumber];
+
+ while (!scenario.isEmpty() && scenario.first().startsWith("C: ")) {
+ QByteArray line =3D clientSocket->readLine();
+ QByteArray received =3D "C: " + line.trimmed();
+ QByteArray expected =3D scenario.takeFirst();
+
+ if (expected =3D=3D "C: SKIP" && !scenario.isEmpty()) {
+ expected =3D scenario.takeFirst();
+ while (received !=3D expected) {
+ received =3D "C: " + clientSocket->readLine().trimmed();
+ }
+ }
+
+ QCOMPARE(QString::fromUtf8(received), QString::fromUtf8(expected));
+ QCOMPARE(received, expected);
+ }
+
+ if (!scenario.isEmpty()) {
+ QVERIFY(scenario.first().startsWith("S: "));
+ }
+
+ m_scenarios[scenarioNumber] =3D scenario;
+}
diff --git a/src/kmailtransport/plugins/smtp/autotests/fakeserver.h b/src/k=
mailtransport/plugins/smtp/autotests/fakeserver.h
new file mode 100644
index 0000000..6e252d6
--- /dev/null
+++ b/src/kmailtransport/plugins/smtp/autotests/fakeserver.h
@@ -0,0 +1,67 @@
+/*
+ Copyright 2010 BetterInbox
+ Author: Christophe Laveault
+ Gregory Schlomoff
+
+ 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see .
+*/
+
+#ifndef KSMTP_FAKESERVER_H
+#define KSMTP_FAKESERVER_H
+
+#include
+#include
+#include
+#include
+#include
+
+Q_DECLARE_METATYPE(QList)
+
+class FakeServer : public QThread
+{
+ Q_OBJECT
+
+public:
+ explicit FakeServer(QObject *parent =3D nullptr);
+ ~FakeServer() override;
+
+ void startAndWait();
+ void run() override;
+
+ static QByteArray greeting();
+ static QList greetingAndEhlo(bool multiline =3D true);
+ static QList bye();
+
+ void setScenario(const QList &scenario);
+ void addScenario(const QList &scenario);
+ void addScenarioFromFile(const QString &fileName);
+ bool isScenarioDone(int scenarioNumber) const;
+ bool isAllScenarioDone() const;
+
+private Q_SLOTS:
+ void newConnection();
+ void dataAvailable();
+ void started();
+
+private:
+ void writeServerPart(int scenarioNumber);
+ void readClientPart(int scenarioNumber);
+
+ QList< QList > m_scenarios;
+ QTcpServer *m_tcpServer;
+ mutable QMutex m_mutex;
+ QList m_clientSockets;
+};
+
+#endif // KSMTP_FAKESERVER_H
diff --git a/src/kmailtransport/plugins/smtp/autotests/smtpjobtest.cpp b/sr=
c/kmailtransport/plugins/smtp/autotests/smtpjobtest.cpp
new file mode 100644
index 0000000..43dd9d1
--- /dev/null
+++ b/src/kmailtransport/plugins/smtp/autotests/smtpjobtest.cpp
@@ -0,0 +1,132 @@
+/*
+ Copyright (c) 2017 Daniel Vr=C3=A1til
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published=
by
+ the Free Software Foundation; either version 2 of the License, or (at =
your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WI=
THOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public Lice=
nse
+ along with this library; see the file COPYING.LIB. If not, write to t=
he
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Bosto=
n, MA
+ 02110-1301, USA.
+*/
+
+#include "../smtpjob.h"
+#include "fakeserver.h"
+
+#include "transportbase.h"
+#include "transportmanager.h"
+
+#include
+#include
+#include
+
+Q_DECLARE_METATYPE(MailTransport::TransportBase::EnumAuthenticationType::t=
ype)
+
+class SmtpJobTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ QStandardPaths::setTestModeEnabled(true);
+ }
+ =
+ void smtpJobTest_data()
+ {
+ QTest::addColumn>("scenario");
+ QTest::addColumn("authType");
+ QTest::addColumn("from");
+ QTest::addColumn("to");
+ QTest::addColumn("cc");
+ QTest::addColumn("data");
+ QTest::addColumn("success");
+
+ QList scenario;
+ scenario << FakeServer::greetingAndEhlo()
+ << "S: 250 AUTH PLAIN LOGIN"
+ << "C: AUTH LOGIN"
+ << "S: 334 VXNlcm5hbWU6"
+ << "C: bG9naW4=3D" // "login".toBase64()
+ << "S: 334 UGFzc3dvcmQ6"
+ << "C: cGFzc3dvcmQ=3D" // "password".toBase64()
+ << "S: 235 Authenticated."
+ << "C: MAIL FROM:"
+ << "S: 250 ok"
+ << "C: RCPT TO:"
+ << "S: 250 ok"
+ << "C: RCPT TO:"
+ << "S: 250 ok"
+ << "C: DATA"
+ << "S: 354 Ok go ahead"
+ << "C: Hi Bob"
+ << "C: "
+ << "C: ."
+ << "S: 250 Ok transfer done"
+ << FakeServer::bye();
+ QTest::newRow("simple") << scenario << MailTransport::TransportBas=
e::EnumAuthenticationType::LOGIN
+ << QStringLiteral("Foo Bar ")
+ << QStringList{}
+ << QStringList{ QStringLiteral("bar@foo.co=
m"), QStringLiteral("") }
+ << QByteArray("Hi Bob")
+ << true;
+ }
+
+ void smtpJobTest()
+ {
+ QFETCH(QList, scenario);
+ QFETCH(MailTransport::TransportBase::EnumAuthenticationType::type,=
authType);
+ QFETCH(QString, from);
+ QFETCH(QStringList, to);
+ QFETCH(QStringList, cc);
+ QFETCH(QByteArray, data);
+ QFETCH(bool, success);
+
+ FakeServer server;
+ server.setScenario(scenario);
+ server.startAndWait();
+
+ auto transport =3D MailTransport::TransportManager::self()->create=
Transport();
+ transport->setHost(QStringLiteral("127.0.0.1"));
+ transport->setPort(5989);
+ transport->setRequiresAuthentication(true);
+ transport->setAuthenticationType(authType);
+ transport->setStorePassword(false);
+ transport->setUserName(QStringLiteral("login"));
+ transport->setPassword(QStringLiteral("password"));
+
+ {
+ MailTransport::SmtpJob smtpJob(transport);
+ smtpJob.setSender(from);
+ smtpJob.setTo(to);
+ smtpJob.setCc(cc);
+ smtpJob.setData(data);
+
+ QVERIFY(smtpJob.exec());
+ if (success) {
+ QCOMPARE(smtpJob.error(), 0);
+ } else {
+ QVERIFY(smtpJob.error() > 0);
+ }
+
+ // Make sure the smtpJob goes out-of-scope here and thus the
+ // internal session pool is destroyed
+ }
+ // KSMTP time to stop the session
+ QTest::qWait(10);
+
+ QVERIFY(server.isAllScenarioDone());
+ server.quit();
+ }
+};
+
+QTEST_MAIN(SmtpJobTest)
+
+#include "smtpjobtest.moc"
diff --git a/src/kmailtransport/plugins/smtp/sessionuiproxy.h b/src/kmailtr=
ansport/plugins/smtp/sessionuiproxy.h
new file mode 100644
index 0000000..0afe15c
--- /dev/null
+++ b/src/kmailtransport/plugins/smtp/sessionuiproxy.h
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2017 Daniel Vr=C3=A1til
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License, version 2, as
+ published by the Free Software Foundation.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef SESSIONUIPROXY_H_
+#define SESSIONUIPROXY_H_
+
+#include
+#include
+
+class SmtpSessionUiProxy : public KSmtp::SessionUiProxy
+{
+public:
+ bool ignoreSslError(const KSslErrorUiData &errorData) override
+ {
+ return KIO::SslUi::askIgnoreSslErrors(errorData, KIO::SslUi::Recal=
lAndStoreRules);
+ }
+};
+
+#endif
diff --git a/src/kmailtransport/plugins/smtp/smtpjob.cpp b/src/kmailtranspo=
rt/plugins/smtp/smtpjob.cpp
index 5f40e53..518cac8 100644
--- a/src/kmailtransport/plugins/smtp/smtpjob.cpp
+++ b/src/kmailtransport/plugins/smtp/smtpjob.cpp
@@ -24,6 +24,7 @@
#include "transport.h"
#include "mailtransport_defs.h"
#include "precommandjob.h"
+#include "sessionuiproxy.h"
#include "mailtransportplugin_smtp_debug.h"
=
#include
@@ -34,36 +35,42 @@
#include
#include
#include "mailtransport_debug.h"
-#include
-#include
#include
=
+#include
+#include
+#include
+
using namespace MailTransport;
=
-class SlavePool
+class SessionPool
{
public:
- SlavePool() : ref(0)
+ SessionPool() : ref(0)
{
}
=
int ref;
- QHash slaves;
+ QHash sessions;
=
- void removeSlave(KIO::Slave *slave, bool disconnect =3D false)
+ void removeSession(KSmtp::Session *session)
{
- qCDebug(MAILTRANSPORT_SMTP_LOG) << "Removing slave" << slave << "f=
rom pool";
- const int slaveKey =3D slaves.key(slave);
- if (slaveKey > 0) {
- slaves.remove(slaveKey);
- if (disconnect) {
- KIO::Scheduler::disconnectSlave(slave);
- }
+ qCDebug(MAILTRANSPORT_SMTP_LOG) << "Removing session" << session <=
< "from the pool";
+ int key =3D sessions.key(session);
+ if (key > 0) {
+ QObject::connect(session, &KSmtp::Session::stateChanged,
+ [session](KSmtp::Session::State state) {
+ if (state =3D=3D KSmtp::Session::Disconnec=
ted) {
+ session->deleteLater();
+ }
+ });
+ session->quit();
+ sessions.remove(key);
}
}
};
=
-Q_GLOBAL_STATIC(SlavePool, s_slavePool)
+Q_GLOBAL_STATIC(SessionPool, s_sessionPool)
=
/**
* Private class that helps to provide binary compatibility between releas=
es.
@@ -77,7 +84,8 @@ public:
}
=
SmtpJob *q;
- KIO::Slave *slave;
+ KSmtp::Session *session;
+ KSmtp::SessionUiProxy::Ptr uiProxy;
enum State {
Idle, Precommand, Smtp
} currentState;
@@ -89,26 +97,23 @@ SmtpJob::SmtpJob(Transport *transport, QObject *parent)
, d(new SmtpJobPrivate(this))
{
d->currentState =3D SmtpJobPrivate::Idle;
- d->slave =3D nullptr;
+ d->session =3D nullptr;
d->finished =3D false;
- if (!s_slavePool.isDestroyed()) {
- s_slavePool->ref++;
+ d->uiProxy =3D KSmtp::SessionUiProxy::Ptr(new SmtpSessionUiProxy);
+ if (!s_sessionPool.isDestroyed()) {
+ s_sessionPool->ref++;
}
- KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *,int,QString)), =
this, SLOT(slaveError(KIO::Slave *,int,QString)));
}
=
SmtpJob::~SmtpJob()
{
- if (!s_slavePool.isDestroyed()) {
- s_slavePool->ref--;
- if (s_slavePool->ref =3D=3D 0) {
- qCDebug(MAILTRANSPORT_SMTP_LOG) << "clearing SMTP slave pool" =
<< s_slavePool->slaves.count();
- foreach (KIO::Slave *slave, s_slavePool->slaves) {
- if (slave) {
- KIO::Scheduler::disconnectSlave(slave);
- }
+ if (!s_sessionPool.isDestroyed()) {
+ s_sessionPool->ref--;
+ if (s_sessionPool->ref =3D=3D 0) {
+ qCDebug(MAILTRANSPORT_SMTP_LOG) << "clearing SMTP session pool=
" << s_sessionPool->sessions.count();
+ while (!s_sessionPool->sessions.isEmpty()) {
+ s_sessionPool->removeSession(*(s_sessionPool->sessions.beg=
in()));
}
- s_slavePool->slaves.clear();
}
}
delete d;
@@ -116,12 +121,12 @@ SmtpJob::~SmtpJob()
=
void SmtpJob::doStart()
{
- if (s_slavePool.isDestroyed()) {
+ if (s_sessionPool.isDestroyed()) {
return;
}
=
- if ((!s_slavePool->slaves.isEmpty()
- && s_slavePool->slaves.contains(transport()->id()))
+ if ((!s_sessionPool->sessions.isEmpty()
+ && s_sessionPool->sessions.contains(transport()->id()))
|| transport()->precommand().isEmpty()) {
d->currentState =3D SmtpJobPrivate::Smtp;
startSmtpJob();
@@ -135,118 +140,161 @@ void SmtpJob::doStart()
=
void SmtpJob::startSmtpJob()
{
- if (s_slavePool.isDestroyed()) {
+ if (s_sessionPool.isDestroyed()) {
return;
}
=
- QUrl destination;
- destination.setScheme((transport()->encryption() =3D=3D Transport::Enu=
mEncryption::SSL)
- ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
- destination.setHost(transport()->host().trimmed());
- destination.setPort(transport()->port());
+ d->session =3D s_sessionPool->sessions.value(transport()->id());
+ if (!d->session) {
+ d->session =3D new KSmtp::Session(transport()->host(), transport()=
->port());
+ d->session->setUiProxy(d->uiProxy);
+ if (transport()->specifyHostname()) {
+ d->session->setCustomHostname(transport()->localHostname());
+ }
+ s_sessionPool->sessions.insert(transport()->id(), d->session);
+ }
=
- QUrlQuery destinationQuery(destination);
- destinationQuery.addQueryItem(QStringLiteral("headers"), QStringLitera=
l("0"));
- destinationQuery.addQueryItem(QStringLiteral("from"), sender());
+ connect(d->session, &KSmtp::Session::stateChanged,
+ this, &SmtpJob::sessionStateChanged, Qt::UniqueConnection);
+ connect(d->session, &KSmtp::Session::connectionError,
+ this, [this](const QString &err) {
+ setError(KJob::UserDefinedError);
+ setErrorText(err);
+ s_sessionPool->removeSession(d->session);
+ emitResult();
+ });
=
- for (const QString &str : to()) {
- destinationQuery.addQueryItem(QStringLiteral("to"), str);
- }
- for (const QString &str : cc()) {
- destinationQuery.addQueryItem(QStringLiteral("cc"), str);
+ if (d->session->state() =3D=3D KSmtp::Session::Disconnected) {
+ d->session->open();
+ } else {
+ if (d->session->state() !=3D KSmtp::Session::Authenticated) {
+ startLoginJob();
+ }
+
+ startSendJob();
}
- for (const QString &str : bcc()) {
- destinationQuery.addQueryItem(QStringLiteral("bcc"), str);
+}
+
+void SmtpJob::sessionStateChanged(KSmtp::Session::State state)
+{
+ if (state =3D=3D KSmtp::Session::Ready) {
+ startLoginJob();
+ } else if (state =3D=3D KSmtp::Session::Authenticated) {
+ startSendJob();
}
+}
=
- if (transport()->specifyHostname()) {
- destinationQuery.addQueryItem(QStringLiteral("hostname"), transpor=
t()->localHostname());
+void SmtpJob::startLoginJob()
+{
+ if (!transport()->requiresAuthentication()) {
+ startSendJob();
+ return;
}
=
- if (transport()->requiresAuthentication()) {
- QString user =3D transport()->userName();
- QString passwd =3D transport()->password();
- if ((user.isEmpty() || passwd.isEmpty())
- && transport()->authenticationType() !=3D Transport::EnumAuthe=
nticationType::GSSAPI) {
- QPointer dlg
- =3D new KPasswordDialog(
- nullptr,
- KPasswordDialog::ShowUsernameLine
- |KPasswordDialog::ShowKeepPassword);
- dlg->setPrompt(i18n("You need to supply a username and a passw=
ord "
- "to use this SMTP server."));
- dlg->setKeepPassword(transport()->storePassword());
- dlg->addCommentLine(QString(), transport()->name());
- dlg->setUsername(user);
- dlg->setPassword(passwd);
-
- bool gotIt =3D false;
- if (dlg->exec()) {
- transport()->setUserName(dlg->username());
- transport()->setPassword(dlg->password());
- transport()->setStorePassword(dlg->keepPassword());
- transport()->save();
- gotIt =3D true;
- }
- delete dlg;
+ auto login =3D new KSmtp::LoginJob(d->session);
+ auto user =3D transport()->userName();
+ auto passwd =3D transport()->password();
+ if ((user.isEmpty() || passwd.isEmpty())
+ && transport()->authenticationType() !=3D Transport::EnumAuthentic=
ationType::GSSAPI) {
+ QPointer dlg
+ =3D new KPasswordDialog(
+ nullptr,
+ KPasswordDialog::ShowUsernameLine
+ |KPasswordDialog::ShowKeepPassword);
+ dlg->setPrompt(i18n("You need to supply a username and a password "
+ "to use this SMTP server."));
+ dlg->setKeepPassword(transport()->storePassword());
+ dlg->addCommentLine(QString(), transport()->name());
+ dlg->setUsername(user);
+ dlg->setPassword(passwd);
+
+ bool gotIt =3D false;
+ if (dlg->exec()) {
+ transport()->setUserName(dlg->username());
+ transport()->setPassword(dlg->password());
+ transport()->setStorePassword(dlg->keepPassword());
+ transport()->save();
+ gotIt =3D true;
+ }
+ delete dlg;
=
- if (!gotIt) {
- setError(KilledJobError);
- emitResult();
- return;
- }
+ if (!gotIt) {
+ setError(KilledJobError);
+ emitResult();
+ return;
}
- destination.setUserName(transport()->userName());
- destination.setPassword(transport()->password());
}
=
- // dotstuffing is now done by the slave (see setting of metadata)
- if (!data().isEmpty()) {
- // allow +5% for subsequent LF->CRLF and dotstuffing (an average
- // over 2G-lines gives an average line length of 42-43):
- destinationQuery.addQueryItem(QStringLiteral("size"),
- QString::number(qRound(data().length=
() * 1.05)));
+ login->setUserName(transport()->userName());
+ login->setPassword(transport()->password());
+ switch (transport()->authenticationType()) {
+ case TransportBase::EnumAuthenticationType::PLAIN:
+ login->setPreferedAuthMode(KSmtp::LoginJob::Plain);
+ break;
+ case TransportBase::EnumAuthenticationType::LOGIN:
+ login->setPreferedAuthMode(KSmtp::LoginJob::Login);
+ break;
+ case TransportBase::EnumAuthenticationType::CRAM_MD5:
+ login->setPreferedAuthMode(KSmtp::LoginJob::CramMD5);
+ break;
+ case TransportBase::EnumAuthenticationType::XOAUTH2:
+ login->setPreferedAuthMode(KSmtp::LoginJob::XOAuth);
+ break;
+ case TransportBase::EnumAuthenticationType::DIGEST_MD5:
+ login->setPreferedAuthMode(KSmtp::LoginJob::DigestMD5);
+ break;
+ case TransportBase::EnumAuthenticationType::NTLM:
+ login->setPreferedAuthMode(KSmtp::LoginJob::NTLM);
+ break;
+ case TransportBase::EnumAuthenticationType::GSSAPI:
+ login->setPreferedAuthMode(KSmtp::LoginJob::GSSAPI);
+ break;
+ default:
+ qCWarning(MAILTRANSPORT_SMTP_LOG) << "Unknown authentication mode"=
<< transport()->authenticationTypeString();
+ break;
}
=
- destination.setPath(QStringLiteral("/send"));
- destination.setQuery(destinationQuery);
-
- d->slave =3D s_slavePool->slaves.value(transport()->id());
- if (!d->slave) {
- KIO::MetaData slaveConfig;
- slaveConfig.insert(QStringLiteral("tls"),
- (transport()->encryption() =3D=3D Transport::En=
umEncryption::TLS)
- ? QStringLiteral("on") : QStringLiteral("off"));
- if (transport()->requiresAuthentication()) {
- slaveConfig.insert(QStringLiteral("sasl"), transport()->authen=
ticationTypeString());
- }
- d->slave =3D KIO::Scheduler::getConnectedSlave(destination, slaveC=
onfig);
- qCDebug(MAILTRANSPORT_SMTP_LOG) << "Created new SMTP slave" << d->=
slave;
- s_slavePool->slaves.insert(transport()->id(), d->slave);
- } else {
- qCDebug(MAILTRANSPORT_SMTP_LOG) << "Re-using existing slave" << d-=
>slave;
- }
+ switch (transport()->encryption()) {
+ case Transport::EnumEncryption::None:
+ login->setEncryptionMode(KSmtp::LoginJob::Unencrypted);
+ break;
+ case Transport::EnumEncryption::TLS:
+ login->setEncryptionMode(KSmtp::LoginJob::TlsV1);
+ break;
+ case Transport::EnumEncryption::SSL:
+ login->setEncryptionMode(KSmtp::LoginJob::AnySslVersion);
+ break;
+ default:
+ qCWarning(MAILTRANSPORT_SMTP_LOG) << "Unknown encryption mode" << =
transport()->encryption();
+ break;
=
- KIO::TransferJob *job =3D KIO::put(destination, -1, KIO::HideProgressI=
nfo);
- if (!d->slave || !job) {
- setError(UserDefinedError);
- setErrorText(i18n("Unable to create SMTP job."));
- emitResult();
- return;
}
=
- job->addMetaData(QStringLiteral("lf2crlf+dotstuff"), QStringLiteral("s=
lave"));
- connect(job, &KIO::TransferJob::dataReq, this, &SmtpJob::dataRequest);
-
- addSubjob(job);
- KIO::Scheduler::assignJobToSlave(d->slave, job);
+ connect(login, &KJob::result, this, &SmtpJob::slotResult);
+ addSubjob(login);
+ login->start();
+ qCDebug(MAILTRANSPORT_SMTP_LOG) << "Login started";
+}
=
- setTotalAmount(KJob::Bytes, data().length());
+void SmtpJob::startSendJob()
+{
+ auto send =3D new KSmtp::SendJob(d->session);
+ send->setFrom(sender());
+ send->setTo(to());
+ send->setCc(cc());
+ send->setBcc(bcc());
+ send->setData(data());
+
+ connect(send, &KJob::result, this, &SmtpJob::slotResult);
+ addSubjob(send);
+ send->start();
+
+ qCDebug(MAILTRANSPORT_SMTP_LOG) << "Send started";
}
=
bool SmtpJob::doKill()
{
- if (s_slavePool.isDestroyed()) {
+ if (s_sessionPool.isDestroyed()) {
return false;
}
=
@@ -256,10 +304,8 @@ bool SmtpJob::doKill()
if (d->currentState =3D=3D SmtpJobPrivate::Precommand) {
return subjobs().first()->kill();
} else if (d->currentState =3D=3D SmtpJobPrivate::Smtp) {
- KIO::SimpleJob *job =3D static_cast(subjobs().fi=
rst());
clearSubjobs();
- KIO::Scheduler::cancelJob(job);
- s_slavePool->removeSlave(d->slave);
+ s_sessionPool->removeSession(d->session);
return true;
}
return false;
@@ -267,7 +313,7 @@ bool SmtpJob::doKill()
=
void SmtpJob::slotResult(KJob *job)
{
- if (s_slavePool.isDestroyed()) {
+ if (s_sessionPool.isDestroyed()) {
return;
}
=
@@ -298,7 +344,7 @@ void SmtpJob::slotResult(KJob *job)
}
=
if (errorCode && d->currentState =3D=3D SmtpJobPrivate::Smtp) {
- s_slavePool->removeSlave(d->slave, errorCode !=3D KIO::ERR_SLAVE_D=
IED);
+ s_sessionPool->removeSession(d->session);
TransportJob::slotResult(job);
return;
}
@@ -309,38 +355,7 @@ void SmtpJob::slotResult(KJob *job)
startSmtpJob();
return;
}
- if (!error()) {
- emitResult();
- }
-}
-
-void SmtpJob::dataRequest(KIO::Job *job, QByteArray &data)
-{
- if (s_slavePool.isDestroyed()) {
- return;
- }
-
- Q_UNUSED(job);
- Q_ASSERT(job);
- if (buffer()->atEnd()) {
- data.clear();
- } else {
- Q_ASSERT(buffer()->isOpen());
- data =3D buffer()->read(32 * 1024);
- }
- setProcessedAmount(KJob::Bytes, buffer()->pos());
-}
-
-void SmtpJob::slaveError(KIO::Slave *slave, int errorCode, const QString &=
errorMsg)
-{
- if (s_slavePool.isDestroyed()) {
- return;
- }
-
- s_slavePool->removeSlave(slave, errorCode !=3D KIO::ERR_SLAVE_DIED);
- if (d->slave =3D=3D slave && !d->finished) {
- setError(errorCode);
- setErrorText(KIO::buildErrorString(errorCode, errorMsg));
+ if (!error() && !hasSubjobs()) {
emitResult();
}
}
diff --git a/src/kmailtransport/plugins/smtp/smtpjob.h b/src/kmailtransport=
/plugins/smtp/smtpjob.h
index b5fbc42..f0ded90 100644
--- a/src/kmailtransport/plugins/smtp/smtpjob.h
+++ b/src/kmailtransport/plugins/smtp/smtpjob.h
@@ -24,6 +24,7 @@
#define MAILTRANSPORT_SMTPJOB_H
=
#include
+#include
=
namespace KIO {
class Job;
@@ -65,13 +66,12 @@ protected:
=
protected Q_SLOTS:
void slotResult(KJob *job) override;
- void slaveError(KIO::Slave *slave, int errorCode, const QString &error=
Msg);
+ void sessionStateChanged(KSmtp::Session::State state);
=
private:
void startSmtpJob();
-
-private Q_SLOTS:
- void dataRequest(KIO::Job *job, QByteArray &data);
+ void startLoginJob();
+ void startSendJob();
=
private:
friend class ::SmtpJobPrivate;