[kmailtransport] /: Replace SMTP KIO Slave with KSMTP library
Daniel Vrátil
null at kde.org
Mon Jul 31 19:39:09 UTC 2017
Git commit b2198b447ab2c4d048035aeacd71a8b05f3c53df by Daniel Vrátil.
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 [License: 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/b2198b447ab2c4d048035aeacd71a8b05f3c53df
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}/KF5MailTransport")
########### 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 DESTINATION ${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/CMakeLists.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.docbook
deleted file mode 100644
index 1939851..0000000
--- a/kioslave/doc/smtp/index.docbook
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" ?>
-<!DOCTYPE article PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN"
-"dtd/kdedbx45.dtd" [
-<!ENTITY % addindex "IGNORE">
-<!ENTITY % English "INCLUDE" > <!-- change language only here -->
-]>
-
-<article lang="&language;" id="smtp">
-<title>smtp</title>
-<articleinfo>
-<authorgroup>
-<author>&Ferdinand.Gassauer; &Ferdinand.Gassauer.mail;</author>
-<!-- TRANS:ROLES_OF_TRANSLATORS -->
-</authorgroup>
-</articleinfo>
-<para>
-A protocol to send mail from the client workstation to the mail server.
-</para>
-
-<para> See : <ulink url="http://cr.yp.to/smtp.html">Simple Mail Transfer Protocol </ulink>.
-</para>
-
-</article>
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ław Staniek <staniek at kde.org>
-
- 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 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 _KIOSLAVE_COMMON_H
-#define _KIOSLAVE_COMMON_H
-
-#include <stdio.h>
-#include <QFile>
-#include <QDir>
-#include <QCoreApplication>
-
-extern "C" {
-#include <sasl/sasl.h>
-}
-
-inline bool initSASL()
-{
-#ifdef Q_OS_WIN //krazy:exclude=cpp
- for (const auto &path : QCoreApplication::libraryPaths()) {
- QDir dir(path);
- if (dir.exists(QStringLiteral("sasl2"))) {
- auto libInstallPath = QFile::encodeName(dir.absoluteFilePath(QStringLiteral("sasl2")));
- if (sasl_set_path(SASL_PATH_TYPE_PLUGIN, libInstallPath.data()) != SASL_OK) {
- fprintf(stderr, "SASL path initialization failed!\n");
- return false;
- }
- break;
- }
- }
-#endif
-
- if (sasl_client_init(NULL) != 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/CMakeLists.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_DIRS})
- 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 IDENTIFIER 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_KSERVICES5DIR} )
-
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/capabilities.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 <mutz at kde.org>
-
- 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
-
- 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 != 25 // ### restrict to 250 only?
- || ehlo.lines().empty()) {
- return c;
- }
-
- QCStringList l = ehlo.lines();
-
- for (QCStringList::const_iterator it = ++l.constBegin(), end(l.constEnd()); it != end; ++it) {
- c.add(QString::fromLatin1(*it));
- }
-
- return c;
-}
-
-void Capabilities::add(const QString &cap, bool replace)
-{
- QStringList tokens = cap.toUpper().split(QLatin1Char(' '));
- if (tokens.empty()) {
- return;
- }
- QString name = tokens.front();
- tokens.pop_front();
- add(name, tokens, replace);
-}
-
-void Capabilities::add(const QString &name, const QStringList &args, bool replace)
-{
- if (replace) {
- mCapabilities[name] = args;
- } else {
- mCapabilities[name] += args;
- }
-}
-
-QString Capabilities::createSpecialResponse(bool tls) const
-{
- QStringList result;
- if (tls) {
- result.push_back(QStringLiteral("STARTTLS"));
- }
- result += saslMethodsQSL();
- if (have("PIPELINING")) {
- result.push_back(QStringLiteral("PIPELINING"));
- }
- if (have("8BITMIME")) {
- result.push_back(QStringLiteral("8BITMIME"));
- }
- if (have("SIZE")) {
- bool ok = false;
- unsigned int size = 0;
- if (!mCapabilities[QStringLiteral("SIZE")].isEmpty()) {
- size = mCapabilities[QStringLiteral("SIZE")].front().toUInt(&ok);
- }
- if (ok && !size) {
- result.push_back(QStringLiteral("SIZE=*")); // any size
- } else if (ok) {
- result.push_back(QStringLiteral("SIZE=%1").arg(size)); // fixed max
- } else {
- result.push_back(QStringLiteral("SIZE")); // indetermined
- }
- }
- return result.join(QLatin1Char(' '));
-}
-
-QStringList Capabilities::saslMethodsQSL() const
-{
- QStringList result;
- for (QMap<QString, QStringList>::const_iterator it = mCapabilities.begin(), end(mCapabilities.end());
- it != end; ++it) {
- if (it.key() == QLatin1String("AUTH")) {
- result += it.value();
- } else if (it.key().startsWith(QLatin1String("AUTH="))) {
- result.push_back(it.key().mid(qstrlen("AUTH=")));
- result += it.value();
- }
- }
- result.removeDuplicates();
- return result;
-}
-} // namespace KioSMTP
diff --git a/kioslave/src/smtp/capabilities.h b/kioslave/src/smtp/capabilities.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 <mutz at kde.org>
-
- 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
-
- 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 <QMap>
-
-#include <QStringList>
-
-namespace KioSMTP {
-class Response;
-
-class Capabilities
-{
-public:
- Capabilities()
- {
- }
-
- static Capabilities fromResponse(const Response &response);
-
- void add(const QString &cap, bool replace = false);
- void add(const QString &name, const QStringList &args, bool replace = false);
- void clear()
- {
- mCapabilities.clear();
- }
-
- bool have(const QString &cap) const
- {
- return mCapabilities.find(cap.toUpper()) != 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<QString, QStringList> 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 <mutz at kde.org>
-
- 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
-
- 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 <KLocalizedString>
-#include <kio/slavebase.h> // for test_commands, where SMTPProtocol is not derived from TCPSlaveBase
-
-#include <QUrl>
-
-#include <assert.h>
-
-namespace KioSMTP {
-static const sasl_callback_t callbacks[] = {
- { 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 = true;
- mNeedResponse = false;
- return r.isOk();
-}
-
-void Command::ungetCommandLine(const QByteArray &cmdLine, TransactionState *ts)
-{
- Q_UNUSED(cmdLine)
- Q_UNUSED(ts)
- mComplete = false;
-}
-
-Command *Command::createSimpleCommand(int which, SMTPSessionInterface *smtp)
-{
- 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 = true;
- mComplete = mEHLONotSupported;
- const char *cmd = mEHLONotSupported ? "HELO " : "EHLO ";
- return cmd + QUrl::toAce(mHostname) + "\r\n"; //krazy:exclude=qclasses
-}
-
-bool EHLOCommand::processResponse(const Response &r, TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse = false;
- // "command not {recognized,implemented}" response:
- if (r.code() == 500 || r.code() == 502) {
- if (mEHLONotSupported) { // HELO failed...
- mSMTP->error(KIO::ERR_INTERNAL_SERVER,
- i18n("The server rejected both EHLO and HELO commands "
- "as unknown or unimplemented.\n"
- "Please contact the server's system administrator."));
- return false;
- }
- mEHLONotSupported = true; // EHLO failed, but that's ok.
- return true;
- }
- mComplete = true;
- if (r.code() / 10 == 25) { // 25x: success
- parseFeatures(r);
- return true;
- }
- mSMTP->error(KIO::ERR_UNKNOWN,
- i18n("Unexpected server response to %1 command.\n%2",
- (mEHLONotSupported ? QStringLiteral("HELO") : QStringLiteral("EHLO")),
- r.errorMessage()));
- return false;
-}
-
-//
-// STARTTLS - rfc 3207
-//
-
-QByteArray StartTLSCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = true;
- return QByteArrayLiteral("STARTTLS\r\n");
-}
-
-bool StartTLSCommand::processResponse(const Response &r, TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse = false;
- if (r.code() != 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 authentication: %1", \
- QString::fromUtf8(sasl_errdetail(conn))));
-
-//
-// AUTH - rfc 2554
-//
-AuthCommand::AuthCommand(SMTPSessionInterface *smtp, const char *mechanisms, const QString &aFQDN, KIO::AuthInfo &ai)
- : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
- , mAi(&ai)
- , mFirstTime(true)
-{
- mMechusing = nullptr;
- int result;
- conn = nullptr;
- client_interact = nullptr;
- mOut = nullptr;
- mOutlen = 0;
- mOneStep = false;
-
- const QByteArray ba = aFQDN.toLatin1();
- result = sasl_client_new("smtp", ba.constData(),
- nullptr, nullptr, callbacks, 0, &conn);
- if (result != SASL_OK) {
- SASLERROR
- return;
- }
- do {
- result = sasl_client_start(conn, mechanisms,
- &client_interact, &mOut, &mOutlen, &mMechusing);
-
- if (result == SASL_INTERACT) {
- if (!saslInteract(client_interact)) {
- return;
- }
- }
- } while (result == SASL_INTERACT);
- if (result != SASL_CONTINUE && result != SASL_OK) {
- SASLERROR
- return;
- }
- if (result == SASL_OK) {
- mOneStep = true;
- }
- qCDebug(SMTP_LOG) << "Mechanism: " << mMechusing << " one step: " << mOneStep;
-}
-
-AuthCommand::~AuthCommand()
-{
- if (conn) {
- qCDebug(SMTP_LOG) << "dispose sasl connection";
- sasl_dispose(&conn);
- conn = nullptr;
- }
-}
-
-bool AuthCommand::saslInteract(void *in)
-{
- qCDebug(SMTP_LOG) << "saslInteract: ";
- sasl_interact_t *interact = (sasl_interact_t *)in;
-
- //some mechanisms do not require username && pass, so don't need a popup
- //window for getting this info
- for (; interact->id != SASL_CB_LIST_END; ++interact) {
- if (interact->id == SASL_CB_AUTHNAME
- || interact->id == 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 = (sasl_interact_t *)in;
- while (interact->id != SASL_CB_LIST_END) {
- switch (interact->id) {
- case SASL_CB_USER:
- case SASL_CB_AUTHNAME:
- {
- qCDebug(SMTP_LOG) << "SASL_CB_[USER|AUTHNAME]: " << mAi->username;
- const QByteArray baUserName = mAi->username.toUtf8();
- interact->result = strdup(baUserName.constData());
- interact->len = strlen((const char *)interact->result);
- break;
- }
- case SASL_CB_PASS:
- {
- qCDebug(SMTP_LOG) << "SASL_CB_PASS: [HIDDEN]";
- const QByteArray baPassword = mAi->password.toUtf8();
- interact->result = strdup(baPassword.constData());
- interact->len = strlen((const char *)interact->result);
- break;
- }
- default:
- interact->result = nullptr;
- interact->len = 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 = s;
- mComplete = false;
-}
-
-QByteArray AuthCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mNeedResponse = true;
- QByteArray cmd;
-
- QByteArray challenge;
- if (!mUngetSASLResponse.isNull()) {
- // implement un-ungetCommandLine
- cmd = mUngetSASLResponse;
- mUngetSASLResponse = nullptr;
- } else if (mFirstTime) {
- QString firstCommand = QLatin1String("AUTH ") + QString::fromLatin1(mMechusing);
-
- challenge = QByteArray::fromRawData(mOut, mOutlen).toBase64();
- if (!challenge.isEmpty()) {
- firstCommand += QLatin1Char(' ');
- firstCommand += QString::fromLatin1(challenge.data(), challenge.size());
- }
- cmd = firstCommand.toLatin1();
-
- if (mOneStep) {
- mComplete = true;
- }
- } else {
-// qCDebug(SMTP_LOG) << "SS: '" << mLastChallenge << "'";
- challenge = QByteArray::fromBase64(mLastChallenge);
- int result;
- do {
- result = sasl_client_step(conn, challenge.isEmpty() ? nullptr : challenge.data(),
- challenge.size(),
- &client_interact,
- &mOut, &mOutlen);
- if (result == SASL_INTERACT) {
- if (!saslInteract(client_interact)) {
- return "";
- }
- }
- } while (result == SASL_INTERACT);
- if (result != SASL_CONTINUE && result != SASL_OK) {
- qCDebug(SMTP_LOG) << "sasl_client_step failed with: " << result;
- SASLERROR
- return "";
- }
- cmd = QByteArray::fromRawData(mOut, mOutlen).toBase64();
-
-// qCDebug(SMTP_LOG) << "CC: '" << cmd << "'";
- mComplete = (result == SASL_OK);
- }
- cmd += 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 authentication 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 (unspecified method)."))
- + QLatin1Char('\n') + chooseADifferentMsg + QLatin1Char('\n') + r.errorMessage());
- } else {
- mSMTP->error(KIO::ERR_COULD_NOT_LOGIN,
- i18n("Your SMTP server does not support authentication.\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 = false;
- mLastChallenge = r.lines().at(0); // ### better join all lines with \n?
- mNeedResponse = false;
- return true;
-}
-
-//
-// MAIL FROM:
-//
-
-QByteArray MailFromCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = true;
- QByteArray cmdLine = QByteArrayLiteral("MAIL FROM:<") + mAddr + '>';
- if (m8Bit && haveCapability("8BITMIME")) {
- cmdLine += QByteArrayLiteral(" BODY=8BITMIME");
- }
- if (mSize && haveCapability("SIZE")) {
- cmdLine += QByteArrayLiteral(" SIZE=") + QByteArray().setNum(mSize);
- }
- return cmdLine + QByteArrayLiteral("\r\n");
-}
-
-bool MailFromCommand::processResponse(const Response &r, TransactionState *ts)
-{
- assert(ts);
- mNeedResponse = false;
-
- if (r.code() == 250) {
- return true;
- }
-
- ts->setMailFromFailed(QString::fromLatin1(mAddr), r);
- return false;
-}
-
-//
-// RCPT TO:
-//
-
-QByteArray RcptToCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = true;
- return QByteArrayLiteral("RCPT TO:<") + mAddr + QByteArrayLiteral(">\r\n");
-}
-
-bool RcptToCommand::processResponse(const Response &r, TransactionState *ts)
-{
- assert(ts);
- mNeedResponse = false;
-
- if (r.code() == 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 = true;
- mNeedResponse = true;
- ts->setDataCommandIssued(true);
- return QByteArrayLiteral("DATA\r\n");
-}
-
-void DataCommand::ungetCommandLine(const QByteArray &cmd, TransactionState *ts)
-{
- Q_UNUSED(cmd)
- assert(ts);
- mComplete = false;
- ts->setDataCommandIssued(false);
-}
-
-bool DataCommand::processResponse(const Response &r, TransactionState *ts)
-{
- assert(ts);
- mNeedResponse = false;
-
- if (r.code() == 354) {
- ts->setDataCommandSucceeded(true, r);
- return true;
- }
-
- ts->setDataCommandSucceeded(false, r);
- return false;
-}
-
-//
-// DATA (data transfer)
-//
-void TransferCommand::ungetCommandLine(const QByteArray &cmd, TransactionState *ts)
-{
- Q_UNUSED(ts)
- if (cmd.isEmpty()) {
- return; // don't change state when we can't detect the unget in
- }
- // the next nextCommandLine !!
- mWasComplete = mComplete;
- mComplete = false;
- mNeedResponse = false;
- mUngetBuffer = 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 = QByteArrayLiteral(".\r\n");
- static const QByteArray CRLFdotCRLF = QByteArrayLiteral("\r\n.\r\n");
-
- if (!mUngetBuffer.isEmpty()) {
- const QByteArray ret = mUngetBuffer;
- mUngetBuffer = nullptr;
- if (mWasComplete) {
- mComplete = true;
- mNeedResponse = true;
- }
- return ret; // don't prepare(), it's slave-generated or already prepare()d
- }
-
- // normal processing:
-
- qCDebug(SMTP_LOG) << "requesting data";
- mSMTP->dataReq();
- QByteArray ba;
- int result = 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 = true;
- mNeedResponse = true;
- return nullptr;
- }
- mComplete = true;
- mNeedResponse = true;
- return mLastChar == '\n' ? dotCRLF : CRLFdotCRLF;
-}
-
-bool TransferCommand::processResponse(const Response &r, TransactionState *ts)
-{
- mNeedResponse = 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 = ba.data();
- const char *const send = ba.data() + ba.size();
- char *d = result.data();
-
- while (s < send) {
- const char ch = *s++;
- if (ch == '\n' && last != '\r') {
- *d++ = '\r'; // lf2crlf
- } else if (ch == '.' && last == '\n') {
- *d++ = '.'; // dotstuff
- }
- last = *d++ = 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 transformation";
- return dotstuff_lf2crlf(ba, mLastChar);
- } else {
- mLastChar = ba[ba.size() - 1];
- return ba;
- }
-}
-
-//
-// NOOP
-//
-
-QByteArray NoopCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = true;
- return QByteArrayLiteral("NOOP\r\n");
-}
-
-//
-// RSET
-//
-
-QByteArray RsetCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = true;
- return QByteArrayLiteral("RSET\r\n");
-}
-
-//
-// QUIT
-//
-
-QByteArray QuitCommand::nextCommandLine(TransactionState *ts)
-{
- Q_UNUSED(ts)
- mComplete = true;
- mNeedResponse = 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 <mutz at kde.org>
-
- 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
-
- 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 <sys/types.h>
-extern "C" {
-#include <sasl/sasl.h>
-}
-
-#include <kio/authinfo.h>
-
-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 = 1,
- OnlyFirstInPipeline = 2,
- CloseConnectionOnError = 4
- };
-
- explicit Command(SMTPSessionInterface *smtp, int flags = 0);
- virtual ~Command();
-
- enum Type {
- STARTTLS,
- DATA,
- NOOP,
- RSET,
- QUIT
- };
-
- static Command *createSimpleCommand(int which, SMTPSessionInterface *smtp);
-
- virtual QByteArray nextCommandLine(TransactionState *ts = nullptr) = 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, TransactionState *ts = 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, TransactionState *ts = 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) override;
-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) override;
-};
-
-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) override;
-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, bool eightBit = false, unsigned int size = 0)
- : Command(smtp)
- , mAddr(addr)
- , m8Bit(eightBit)
- , mSize(size)
- {
- }
-
- QByteArray nextCommandLine(TransactionState *ts) override;
- bool processResponse(const Response &response, TransactionState *ts) override;
-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) override;
-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) override;
- bool processResponse(const Response &response, TransactionState *ts) override;
-};
-
-/** Handles the data transfer following a successful DATA command */
-class TransferCommand : public Command
-{
-public:
- TransferCommand(SMTPSessionInterface *smtp, const QByteArray &initialBuffer)
- : 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) override;
- bool processResponse(const Response &response, TransactionState *ts) override;
-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/compliance.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=192504 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=18679 bytes)
- (Obsoletes RFC2487) (Status: PROPOSED STANDARD)
-
-2554 SMTP Service Extension for Authentication. J. Myers. March 1999.
- (Format: TXT=20534 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=11842 bytes) (Obsoletes RFC1426) (Status: DRAFT STANDARD)
-
-1870 SMTP Service Extension for Message Size Declaration. J. Klensin,
- N. Freed, K. Moore. November 1995. (Format: TXT=18226 bytes)
- (Obsoletes RFC1653) (Also STD0010) (Status: STANDARD)
-
-2920 SMTP Service Extension for Command Pipelining. N. Freed.
- September 2000. (Format: TXT=17065 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/kioslavesession.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 <vkrause at kde.org>
-
- 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 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 "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 QString &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")) == QLatin1String("on");
-}
-
-bool KioSlaveSession::lf2crlfAndDotStuffingRequested() const
-{
- return m_protocol->metaData(QStringLiteral("lf2crlf+dotstuff")) == QLatin1String("slave");
-}
-
-bool KioSlaveSession::pipeliningRequested() const
-{
- return m_protocol->metaData(QStringLiteral("pipelining")) != QLatin1String("off");
-}
-
-QString KioSlaveSession::requestedSaslMethod() const
-{
- return m_protocol->metaData(QStringLiteral("sasl"));
-}
-
-KioSMTP::SMTPSessionInterface::TLSRequestState KioSMTP::KioSlaveSession::tlsRequested() const
-{
- if (m_protocol->metaData(QStringLiteral("tls")) == QLatin1String("off")) {
- return ForceNoTLS;
- }
- if (m_protocol->metaData(QStringLiteral("tls")) == QLatin1String("on")) {
- return ForceTLS;
- }
- return UseTLSIfAvailable;
-}
diff --git a/kioslave/src/smtp/kioslavesession.h b/kioslave/src/smtp/kioslavesession.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 <vkrause at kde.org>
-
- 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 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 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 <mutz at kde.org>
-
- 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
-
- 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 <QUrl>
-#include <QUrl>
-#include <qdebug.h>
-
-#include <assert.h>
-
-namespace KioSMTP {
-Request Request::fromURL(const QUrl &url)
-{
- Request request;
-
- const QStringList query = url.query().split(QLatin1Char('&'));
-#ifndef NDEBUG
- qCDebug(SMTP_LOG) << "Parsing request from query:\n" << query.join(QLatin1Char('\n'));
-#endif
- for (QStringList::const_iterator it = query.begin(), end(query.end()); it != end; ++it) {
- int equalsPos = (*it).indexOf(QLatin1Char('='));
- if (equalsPos <= 0) {
- continue;
- }
-
- const QString key = (*it).left(equalsPos).toLower();
- const QString value = QUrl::fromPercentEncoding((*it).mid(equalsPos + 1).toLatin1()); //krazy:exclude=qclasses
-
- if (key == QLatin1String("to")) {
- request.addTo(value);
- } else if (key == QLatin1String("cc")) {
- request.addCc(value);
- } else if (key == QLatin1String("bcc")) {
- request.addBcc(value);
- } else if (key == QLatin1String("headers")) {
- request.setEmitHeaders(value == QLatin1String("0"));
- request.setEmitHeaders(false); // ### ???
- } else if (key == QLatin1String("subject")) {
- request.setSubject(value);
- } else if (key == QLatin1String("from")) {
- request.setFromAddress(value);
- } else if (key == QLatin1String("profile")) {
- request.setProfileName(value);
- } else if (key == QLatin1String("hostname")) {
- request.setHeloHostname(value);
- } else if (key == QLatin1String("body")) {
- request.set8BitBody(value.toUpper() == QLatin1String("8BIT"));
- } else if (key == QLatin1String("size")) {
- request.setSize(value.toUInt());
- } else {
- qCWarning(SMTP_LOG) << "while parsing query: unknown query item \""
- << key << "\" with value \"" << value << "\"" << endl;
- }
- }
-
- return request;
-}
-
-QByteArray Request::heloHostnameCString() const
-{
- return QUrl::toAce(heloHostname()); //krazy:exclude=qclasses
-}
-
-static bool isUsAscii(const QString &s)
-{
- for (int i = 0; i < s.length(); ++i) {
- if (s[i].unicode() > 127) {
- return false;
- }
- }
- return true;
-}
-
-static inline bool isSpecial(char ch)
-{
- static const QByteArray specials = "()<>[]:;@\\,.\"";
- return specials.indexOf(ch) >= 0;
-}
-
-static inline bool needsQuoting(char ch)
-{
- return ch == '\\' || ch == '"' || ch == '\n';
-}
-
-static inline QByteArray rfc2047Encode(const QString &s)
-{
- QByteArray r = s.trimmed().toUtf8().toBase64();
- return "=?utf-8?b?" + r + "?="; // use base64 since that always gives a valid encoded-word
-}
-
-static QByteArray quote(const QString &s)
-{
- assert(isUsAscii(s));
-
- QByteArray r(s.length() * 2, 0);
- bool needsQuotes = false;
-
- unsigned int j = 0;
- for (int i = 0; i < s.length(); ++i) {
- char ch = s[i].toLatin1();
- if (isSpecial(ch)) {
- if (needsQuoting(ch)) {
- r[j++] = '\\';
- }
- needsQuotes = true;
- }
- r[j++] = ch;
- }
- r.truncate(j);
-
- if (needsQuotes) {
- return '"' + r + '"';
- } else {
- return r;
- }
-}
-
-static QByteArray formatFromAddress(const QString &fromRealName, const QString &fromAddress)
-{
- if (fromRealName.isEmpty()) {
- return fromAddress.toLatin1(); // no real name: return "joe at user.org"
- }
-
- // return "Joe User <joe at user.org>", "\"User, Joe\" <joe at user.org>"
- // or "=?utf-8?q?Joe_User?= <joe at user.org>", depending on real name's nature.
- QByteArray r = isUsAscii(fromRealName) ? quote(fromRealName) : rfc2047Encode(fromRealName);
- return r + " <" + fromAddress.toLatin1() + '>';
-}
-
-static QByteArray formatSubject(QString s)
-{
- if (isUsAscii(s)) {
- return s.remove(QLatin1Char('\n')).toLatin1(); // don't break header 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 = QByteArrayLiteral("From: ") + formatFromAddress(fromRealName, fromAddress()) + QByteArrayLiteral("\r\n");
-
- if (!subject().isEmpty()) {
- result += QByteArrayLiteral("Subject: ") + formatSubject(subject()) + QByteArrayLiteral("\r\n");
- }
- if (!to().empty()) {
- result += QByteArrayLiteral("To: ") + to().join(QStringLiteral(",\r\n\t") /* line folding */).toLatin1() + "\r\n";
- }
- if (!cc().empty()) {
- result += 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 <mutz at kde.org>
-
- 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
-
- 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 <QStringList>
-#include <QByteArray>
-
-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 = profileName;
- }
-
- bool hasProfile() const
- {
- return !profileName().isNull();
- }
-
- QString subject() const
- {
- return mSubject;
- }
-
- void setSubject(const QString &subject)
- {
- mSubject = subject;
- }
-
- QString fromAddress() const
- {
- return mFromAddress;
- }
-
- void setFromAddress(const QString &fromAddress)
- {
- mFromAddress = 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 = hostname;
- }
-
- bool emitHeaders() const
- {
- return mEmitHeaders;
- }
-
- void setEmitHeaders(bool emitHeaders)
- {
- mEmitHeaders = emitHeaders;
- }
-
- bool is8BitBody() const
- {
- return m8Bit;
- }
-
- void set8BitBody(bool a8Bit)
- {
- m8Bit = a8Bit;
- }
-
- unsigned int size() const
- {
- return mSize;
- }
-
- void setSize(unsigned int size)
- {
- mSize = 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 = QString()) const;
-
-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 <mutz at kde.org>
-
- 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
-
- 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 <KLocalizedString>
-#include <kio/global.h>
-
-#include <QByteArray>
-
-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 = false;
- }
-
- if (len > 1 && line[len - 1] == '\n' && line[len - 2] == '\r') {
- len -= 2;
- }
-
- if (len < 3) {
- // can't be valid - too short
- mValid = false;
- mWellFormed = false;
- return;
- }
-
- bool ok = false;
- unsigned int code = QByteArray(line, 3).toUInt(&ok);
- if (!ok || code < 100 || code > 559) {
- // not a number or number out of range
- mValid = false;
- if (!ok || code < 100) {
- mWellFormed = false;
- }
- return;
- }
- if (mCode && code != mCode) {
- // different codes in one response are not allowed.
- mValid = false;
- return;
- }
- mCode = code;
-
- if (len == 3 || line[3] == ' ') {
- mSawLastLine = true;
- } else if (line[3] != '-') {
- // code must be followed by either SP or hyphen (len == 3 is
- // also accepted since broken servers exist); all else is
- // invalid
- mValid = false;
- mWellFormed = false;
- return;
- }
-
- mLines.push_back(len > 4 ? QByteArray(line + 4, len - 4).trimmed() : QByteArray());
-}
-
-// hackishly fixing QCStringList flaws...
-static QByteArray join(char sep, const QCStringList &list)
-{
- if (list.empty()) {
- return QByteArray();
- }
- QByteArray result = list.front();
- for (QCStringList::const_iterator it = ++list.begin(), end(list.end()); it != end; ++it) {
- result += sep + *it;
- }
- return result;
-}
-
-QString Response::errorMessage() const
-{
- QString msg;
- if (lines().count() > 1) {
- msg = i18n("The server responded:\n%1", QString::fromLatin1(join('\n', lines())));
- } else {
- msg = i18n("The server responded: \"%1\"", QString::fromLatin1(lines().at(0)));
- }
- if (first() == 4) {
- msg += QLatin1Char('\n') + i18n("This is a temporary failure. You 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 recipients
- 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 <forward-path>
- 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 <mutz at kde.org>
-
- 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
-
- 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 <QList>
-#include <QString>
-typedef QList<QByteArray> 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 = -1,
- PositivePreliminary = 1,
- PositiveCompletion = 2,
- PositiveIntermediate = 3,
- TransientNegative = 4,
- PermanentNegative = 5
- };
-
- enum Category {
- UnknownCategory = -1,
- SyntaxError = 0,
- Information = 1,
- Connections = 2,
- MailSystem = 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() <= 3 && first() >= 1;
- }
-
- bool isNegative() const
- {
- return first() == 4 || first() == 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 = Response();
- }
-
-#ifdef KIOSMTP_COMPARATORS
- bool operator==(const Response &other) const
- {
- return mCode == other.mCode
- && mValid == other.mValid
- && mSawLastLine == other.mSawLastLine
- && mWellFormed == other.mWellFormed
- && mLines == 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 <zipzippy at sonic.net>
- * Copyright (c) 2001 Michael H�kel <Michael at Haeckel.Net>
- * Copyright (c) 2002 Aaron J. Seigo <aseigo at olympusproject.org>
- * Copyright (c) 2003 Marc Mutz <mutz at kde.org>
- * 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 PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * 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, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * 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 <sasl/sasl.h>
-}
-
-#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 <kemailsettings.h>
-
-#include <qdebug.h>
-#include <kio/slaveinterface.h>
-#include <KLocalizedString>
-#include <QUrl>
-
-#include <QHostInfo>
-#include <QDataStream>
-
-#include <memory>
-using std::unique_ptr;
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#ifdef Q_OS_WIN
-#include <Winsock2.h>
-#else
-#include <netdb.h>
-#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 != 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") == 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 connected.
- // We can't call smtp_open() here, as that does EHLO, and the EHLO command
- // 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 == 'c') {
- const QString response = m_sessionIface->capabilities().createSpecialResponse(
- (isUsingSsl() && !isAutoSsl())
- || m_sessionIface->haveCapability("STARTTLS"));
- infoMessage(response);
- } else if (what == 'N') {
- if (!execute(Command::NOOP)) {
- return;
- }
- } else {
- error(KIO::ERR_INTERNAL, i18n("The application sent an invalid request."));
- return;
- }
- finished();
-}
-
-// Usage: smtp://smtphost:port/send?to=user@host.com&subject=blah
-// 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=0 (turns off header generation)
-// to=emailaddress
-// cc=emailaddress
-// bcc=emailaddress
-// subject=text
-// profile=text (this will override the "host" setting)
-// hostname=text (used in the HELO)
-// body={7bit,8bit} (default: 7bit; 8bit activates the use of the 8BITMIME SMTP extension)
-void SMTPProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags)
-{
- Q_UNUSED(permissions);
- Q_UNUSED(flags);
- Request request = Request::fromURL(url); // parse settings from URL's query
-
- KEMailSettings mset;
- QUrl open_url = url;
- if (!request.hasProfile()) {
- //qCDebug(SMTP_LOG) << "kio_smtp: Profile is null";
- bool hasProfile = mset.profiles().contains(open_url.host());
- if (hasProfile) {
- mset.setProfile(open_url.host());
- open_url.setHost(mset.getSetting(KEMailSettings::OutServer));
- m_sUser = mset.getSetting(KEMailSettings::OutServerLogin);
- m_sPass = 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 = open_url.host();
- m_port = 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 at host.com or
- // Real Name <emailaddress at host.com>)
- if (!request.hasFromAddress()) {
- const QString from = mset.getSetting(KEMailSettings::EmailAddress);
- 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 error 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 messages.\n"
- "Please use base64 or quoted-printable encoding.", m_sServer));
- 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 = request.recipients();
- for (QStringList::const_iterator it = recipients.begin(), end(recipients.end()); it != 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 QString &user, const QString &pass)
-{
- m_sServer = host;
- m_port = port;
- m_sUser = user;
- m_sPass = pass;
-}
-
-bool SMTPProtocol::sendCommandLine(const QByteArray &cmdline)
-{
- //kDebug( cmdline.length() < 4096, 7112) << "C: " << cmdline.data();
- //kDebug( cmdline.length() >= 4096, 7112) << "C: <" << cmdline.length() << " 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 = cmdline.length();
- if ((numWritten = write(cmdline.data(), cmdline_len)) != cmdline_len) {
- 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 = false;
- }
-
- Response response;
- char buf[2048];
-
- int recv_len = 0;
- do {
- // wait for data...
- if (!waitForResponse(600)) {
- error(KIO::ERR_SERVER_TIMEOUT, m_sServer);
- return response;
- }
-
- // ...read data...
- recv_len = 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) received.", response.code()));
- return response;
- }
-
- if (ok) {
- *ok = true;
- }
-
- return response;
-}
-
-bool SMTPProtocol::executeQueuedCommands(TransactionState *ts)
-{
- assert(ts);
-
- if (m_sessionIface->canPipelineCommands()) {
- qDebug() << "using pipelining";
- }
-
- while (!mPendingCommandQueue.isEmpty()) {
- QByteArray cmdline = 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 = 0;
-
- while (!mPendingCommandQueue.isEmpty()) {
- Command *cmd = 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 = cmd->nextCommandLine(ts);
- if (ts->failedFatally()) {
- return cmdLine;
- }
- const unsigned int currentCmdLine_len = currentCmdLine.length();
-
- cmdLine_len += currentCmdLine_len;
- cmdLine += 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 command line.
- //
- // This way, the progress information in clients like KMail works correctly,
- // because otherwise, the TransferCommand would read the whole data from the
- // job at once, then sending it. The progress update on the client however
- // happens when sending data to the job, not when this slave writes 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<TransferCommand *>(cmd) != nullptr
- && cmdLine_len >= 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 = mSentCommandQueue.head();
- assert(cmd->isComplete());
-
- bool ok = false;
- Response r = 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<Command> cmd(Command::createSimpleCommand(type, m_sessionIface));
- if (!cmd.get()) {
- qCritical() << "Command::createSimpleCommand( " << type << " ) returned 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 = 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 = false;
- Response r = getResponse(&ok);
- if (!ok) {
- // Only close without sending QUIT if the responce was incomplete
- // 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 == m_port
- && m_sOldServer == m_sServer
- && m_sOldUser == m_sUser
- && (fakeHostname.isNull() || m_hostname == fakeHostname)) {
- return true;
- }
-
- smtp_close();
- if (!connectToHost(isAutoSsl() ? QStringLiteral("smtps") : QStringLiteral("smtp"), m_sServer, m_port)) {
- return false; // connectToHost has already send an error message.
- }
- m_opened = true;
-
- bool ok = false;
- Response greeting = 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 = fakeHostname;
- } else {
- // FIXME: We need a way to find the FQDN again. Also change in servertest then.
- m_hostname = QHostInfo::localHostName();
- if (m_hostname.isEmpty()) {
- m_hostname = QStringLiteral("localhost.invalid");
- } else if (!m_hostname.contains(QLatin1Char('.'))) {
- m_hostname += QLatin1String(".localnet");
- }
- }
-
- EHLOCommand ehloCmdPreTLS(m_sessionIface, m_hostname);
- if (!execute(&ehloCmdPreTLS)) {
- smtp_close();
- return false;
- }
-
- if ((m_sessionIface->haveCapability("STARTTLS") /*### && canUseTLS()*/ && m_sessionIface->tlsRequested() != SMTPSessionInterface::ForceNoTLS)
- || m_sessionIface->tlsRequested() == SMTPSessionInterface::ForceTLS) {
- // 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 = m_port;
- m_sOldServer = m_sServer;
- m_sOldUser = m_sUser;
- m_sOldPass = m_sPass;
-
- return true;
-}
-
-bool SMTPProtocol::authenticate()
-{
- // return with success if the server doesn't support SMTP-AUTH or an user
- // 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 = m_sUser;
- authInfo.password = m_sPass;
- authInfo.prompt = i18n("Username and password for your SMTP account:");
-
- QStringList strList;
-
- if (!m_sessionIface->requestedSaslMethod().isEmpty()) {
- strList.append(m_sessionIface->requestedSaslMethod());
- } else {
- strList = m_sessionIface->capabilities().saslMethodsQSL();
- }
-
- const QByteArray ba = strList.join(QLatin1Char(' ')).toLatin1();
- AuthCommand authCmd(m_sessionIface, ba.constData(), m_sServer, authInfo);
- bool ret = execute(&authCmd);
- m_sUser = authInfo.username;
- m_sPass = 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 = false;
-}
-
-void SMTPProtocol::stat(const QUrl &url)
-{
- QString path = 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 <zipzippy at sonic.net>
- * Copyright (c) 2001 Michael H�kel <Michael at Haeckel.Net>
- * 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 PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * 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, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#ifndef _SMTP_H
-#define _SMTP_H
-
-#include <kio/tcpslavebase.h>
-
-#include "capabilities.h"
-
-#include <QQueue>
-#include <QByteArray>
-
-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 useSSL);
- 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) override;
- 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 = true);
-
- /** Execute command @p cmd */
- bool execute(KioSMTP::Command *cmd, KioSMTP::TransactionState *ts = nullptr);
- /** Execute a command of type @p type */
- bool execute(int type, KioSMTP::TransactionState *ts = 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 <code>smtp_close( false )</code>. If The
- transaction fails gracefully (<code>ts->failed()</code> 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<KioSMTP::Command *> 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.protocol
deleted file mode 100644
index c7bf05b..0000000
--- a/kioslave/src/smtp/smtp.protocol
+++ /dev/null
@@ -1,16 +0,0 @@
-[Protocol]
-exec=kf5/kio/smtp
-protocol=smtp
-Capabilities=SASL
-input=none
-output=filesystem
-listing=Name,Type,Size
-reading=false
-writing=true
-deleting=false
-source=true
-makedir=false
-linking=false
-moving=false
-X-DocPath=kioslave5/smtp/index.html
-Icon=mail-folder-outbox
diff --git a/kioslave/src/smtp/smtps.protocol b/kioslave/src/smtp/smtps.protocol
deleted file mode 100644
index 98222d7..0000000
--- a/kioslave/src/smtp/smtps.protocol
+++ /dev/null
@@ -1,16 +0,0 @@
-[Protocol]
-exec=kf5/kio/smtp
-protocol=smtps
-Capabilities=SASL
-input=none
-output=filesystem
-listing=Name,Type,Size
-reading=false
-writing=true
-deleting=false
-source=true
-makedir=false
-linking=false
-moving=false
-X-DocPath=kioslave5/smtp/index.html
-Icon=mail-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 <vkrause at kde.org>
-
- 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 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 "smtpsessioninterface.h"
-
-using namespace KioSMTP;
-
-SMTPSessionInterface::~SMTPSessionInterface()
-{
-}
-
-void SMTPSessionInterface::parseFeatures(const KioSMTP::Response &ehloResponse)
-{
- m_capabilities = 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/smtpsessioninterface.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 <vkrause at kde.org>
-
- 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 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 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 for unit testing.
- * @since 4.6
- */
-class SMTPSessionInterface
-{
-public:
- /** TLS request state. */
- enum TLSRequestState {
- UseTLSIfAvailable,
- ForceTLS,
- ForceNoTLS
- };
-
- virtual ~SMTPSessionInterface();
- virtual bool startSsl() = 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 reconnecting 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) = 0;
- /** Show information message box with message @p msg and caption @p caption. */
- virtual void informationMessageBox(const QString &msg, const QString &caption) = 0;
- virtual bool openPasswordDialog(KIO::AuthInfo &authInfo) = 0;
- virtual void dataReq() = 0;
- virtual int readData(QByteArray &ba) = 0;
-
- /** SASL method requested for authentication. */
- virtual QString requestedSaslMethod() const = 0;
- /** TLS requested for encryption. */
- virtual TLSRequestState tlsRequested() const = 0;
- /** LF2CRLF and dot stuffing requested. */
- virtual bool lf2crlfAndDotStuffingRequested() const = 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/tests/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::KIOCore ${extra_LIB})
-
-########### next target ###############
-
-set(test_headergeneration_SRCS test_headergeneration.cpp)
-ecm_qt_declare_logging_category(test_headergeneration_SRCS HEADER smtp_debug.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 IDENTIFIER SMTP_LOG CATEGORY_NAME org.kde.pim.smtp)
-
-include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${Sasl2_INCLUDE_DIRS} )
-
-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 IDENTIFIER 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/tests/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 <vkrause at kde.org>
-
- 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 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 KIOSMTP_FAKESESSION_H
-#define KIOSMTP_FAKESESSION_H
-
-#include "smtpsessioninterface.h"
-
-#include <QStringList>
-#include <kio/slavebase.h>
-
-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 = true;
- usesTLS = false;
- lastErrorCode = 0;
- lastErrorMessage.clear();
- lastMessageBoxText.clear();
- nextData.resize(0);
- nextDataReturnCode = -1;
- caps.clear();
-
- lf2crlfAndDotStuff = 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 = id;
- lastErrorMessage = msg;
- qWarning() << id << msg;
- }
-
- void informationMessageBox(const QString &msg, const QString &caption) override
- {
- Q_UNUSED(caption);
- lastMessageBoxText = msg;
- }
-
- bool openPasswordDialog(KIO::AuthInfo &) override
- {
- return true;
- }
-
- void dataReq() override
- {
- /* noop */
- }
-
- int readData(QByteArray &ba) override
- {
- ba = 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/src/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 <mutz at kde.org>
-
- 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 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.
-
- 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 <QString>
-#include <QTextStream>
-#include <QApplication>
-#include <QLabel>
-#include <QLineEdit>
-#include <QPushButton>
-#include <QTextEdit>
-#include <QWidget>
-#include <QTcpSocket>
-#include <QVBoxLayout>
-#include <QHBoxLayout>
-
-#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() == QAbstractSocket::ClosingState) {
- connect(mSocket, SIGNAL(disconnected()),
- mSocket, SLOT(deleteLater()));
- } else {
- mSocket->deleteLater();
- }
- mSocket = nullptr;
- }
-}
-
-void InteractiveSMTPServerWindow::slotSendResponse()
-{
- const QString line = 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(newConnectionAvailable()));
-}
-
-void InteractiveSMTPServer::newConnectionAvailable()
-{
- InteractiveSMTPServerWindow *w = new InteractiveSMTPServerWindow(nextPendingConnection());
- 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 *socket, QWidget *parent)
- : QWidget(parent)
- , mSocket(socket)
-{
- QPushButton *but;
- Q_ASSERT(socket);
-
- QVBoxLayout *vlay = new QVBoxLayout(this);
-
- mTextEdit = new QTextEdit(this);
- vlay->addWidget(mTextEdit, 1);
- QWidget *mLayoutWidget = new QWidget;
- vlay->addWidget(mLayoutWidget);
-
- QHBoxLayout *hlay = new QHBoxLayout(mLayoutWidget);
-
- mLineEdit = new QLineEdit(this);
- mLabel = new QLabel(QStringLiteral("&Response:"), this);
- mLabel->setBuddy(mLineEdit);
- but = 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 = 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("<font color=\"red\">") + escape(s) + QLatin1String("</font>"));
-}
-
-void InteractiveSMTPServerWindow::slotReadyRead()
-{
- while (mSocket->canReadLine()) {
- slotDisplayClient(trim(QString::fromLatin1(mSocket->readLine())));
- }
-}
-
-void InteractiveSMTPServerWindow::slotError(QAbstractSocket::SocketError error)
-{
- 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 <mutz at kde.org>
-
- 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 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.
-
- 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 <QWidget>
-#include <QTcpServer>
-
-class QLabel;
-class QLineEdit;
-class QTcpServer;
-class QTextEdit;
-
-class InteractiveSMTPServerWindow : public QWidget
-{
- Q_OBJECT
-public:
- InteractiveSMTPServerWindow(QTcpSocket *socket, QWidget *parent = nullptr);
- ~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 = nullptr);
- ~InteractiveSMTPServer()
- {
- }
-
-private Q_SLOTS:
- void newConnectionAvailable();
-};
-
-#endif
diff --git a/kioslave/src/smtp/tests/requesttest.cpp b/kioslave/src/smtp/tests/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 <montel at kde.org>
-
- 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 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 <qtest.h>
-#include <QUrl>
-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<unsigned int>(0));
-}
-
-void RequestTest::shouldParseRequest_data()
-{
- QTest::addColumn<QUrl>("smtpurl");
- QTest::addColumn<QString>("to");
- QTest::addColumn<QString>("from");
- QTest::addColumn<QString>("cc");
- QTest::addColumn<QString>("bcc");
- QTest::addColumn<bool>("emitheaders");
- QTest::addColumn<unsigned int>("size");
- QTest::newRow("correct url") << QUrl(QStringLiteral("smtps://smtp.kde.org:465/send?headers=0&from=foo%40kde.org&to=foo%40kde.org&size=617"))
- << QStringLiteral("foo at kde.org")
- << QStringLiteral("foo at kde.org")
- << QString()
- << QString()
- << false
- << static_cast<unsigned int>(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 = 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/tests/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 <montel at kde.org>
-
- 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 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 <QObject>
-
-class RequestTest : public QObject
-{
- Q_OBJECT
-public:
- explicit RequestTest(QObject *parent = 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/smtp/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 <assert.h>
-#include "capabilities.h"
-#include <QObject>
-
-using namespace KioSMTP;
-
-int main()
-{
- Capabilities c;
-
- const QString size_cap = QObject::tr("SIZE 12");
- c.add(size_cap);
- // Capability was added
- assert(c.have("SIZE"));
-
- const QString expected_response = QObject::tr("SIZE=12");
- const QString actual_response = c.createSpecialResponse(false);
- // SIZE actually handled
- assert(actual_response == expected_response);
-
- const QString auth_cap = QObject::tr("AUTH GSSAPI");
- c.add(auth_cap);
- c.add(auth_cap);
- // Duplicate methods was removed
- assert(c.saslMethodsQSL().length() == 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 <kio/global.h>
-#include <kio/authinfo.h>
-#include <qdebug.h>
-
-#define KIOSMTP_COMPARATORS // for TransactionState::operator==
-#include "fakesession.h"
-#include "command.h"
-#include "response.h"
-#include "transactionstate.h"
-#include "common.h"
-#include "smtp_debug.h"
-#include <assert.h>
-
-using namespace KioSMTP;
-
-static const char *foobarbaz = ".Foo bar baz";
-static const unsigned int foobarbaz_len = qstrlen(foobarbaz);
-
-static const char *foobarbaz_dotstuffed = "..Foo bar baz";
-static const unsigned int foobarbaz_dotstuffed_len = qstrlen(foobarbaz_dotstuffed);
-
-static const char *foobarbaz_lf = ".Foo bar baz\n";
-static const unsigned int foobarbaz_lf_len = qstrlen(foobarbaz_lf);
-
-static const char *foobarbaz_crlf = "..Foo bar baz\r\n";
-static const unsigned int foobarbaz_crlf_len = 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) == "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) == true);
- assert(ehlo.isComplete());
- assert(!ehlo.needsResponse());
- assert(smtp.lastErrorCode == 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) == true);
- assert(!ehlo2.isComplete());
- assert(!ehlo2.needsResponse());
- assert(ehlo2.nextCommandLine(nullptr) == "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) == true);
- assert(!ehlo2.needsResponse());
- assert(smtp.lastErrorCode == 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) == false);
- assert(ehlo3.isComplete());
- assert(!ehlo3.needsResponse());
- assert(smtp.lastErrorCode == 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) == false);
- assert(ehlo4.isComplete());
- assert(!ehlo4.needsResponse());
- assert(smtp.lastErrorCode == 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 = ts;
- assert(tls.nextCommandLine(&ts) == "STARTTLS\r\n");
- assert(ts == ts2);
- assert(tls.isComplete());
- assert(tls.needsResponse());
- r.clear();
- r.parseLine("220 Go ahead");
- smtp.startTLSReturnCode = true;
- assert(tls.processResponse(r, &ts) == true);
- assert(!tls.needsResponse());
- assert(smtp.lastErrorCode == 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 = true;
- assert(tls2.processResponse(r, &ts) == false);
- assert(!tls2.needsResponse());
- assert(smtp.lastErrorCode == 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 = false;
- assert(tls.processResponse(r, &ts) == false);
- assert(!tls.needsResponse());
-
- //
- // AUTH
- //
-
- smtp.clear();
- QStringList mechs;
- mechs.append(QStringLiteral("PLAIN"));
- smtp.saslMethod = QStringLiteral("PLAIN");
- KIO::AuthInfo authInfo;
- authInfo.username = QStringLiteral("user");
- authInfo.password = QStringLiteral("pass");
- AuthCommand auth(&smtp, "PLAIN", QStringLiteral("mail.example.com"), authInfo);
- // 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 = true;
- ts.clear();
- ts2 = ts;
- assert(auth.nextCommandLine(&ts) == "AUTH PLAIN dXNlcgB1c2VyAHBhc3M=\r\n");
- assert(auth.isComplete());
- assert(auth.needsResponse());
- assert(ts == ts2);
- r.clear();
- r.parseLine("250 OK");
-
- // dynamics 2: No TLS, so AUTH should not include initial-response:
- /* FIXME fails since nothing evaluates useTLS = false anywhere...
- smtp.clear();
- smtp.saslMethod = "PLAIN";
- smtp.usesTLS = false;
- authInfo = KIO::AuthInfo();
- authInfo.username = "user";
- authInfo.password = "pass";
- AuthCommand auth2( &smtp, "PLAIN", "mail.example.com", authInfo );
- ts.clear();
- assert( auth2.nextCommandLine( &ts ) == "AUTH PLAIN\r\n" );
- assert( !auth2.isComplete() );
- assert( auth2.needsResponse() );
- r.clear();
- r.parseLine( "334 Go on" );
- assert( auth2.processResponse( r, &ts ) == true );
- assert( auth2.nextCommandLine( &ts ) == "dXNlcgB1c2VyAHBhc3M=\r\n" );
- assert( auth2.isComplete() );
- assert( auth2.needsResponse() );*/
-
- // dynamics 3: LOGIN
- smtp.clear();
- smtp.saslMethod = QStringLiteral("LOGIN");
- mechs.clear();
- mechs.append(QStringLiteral("LOGIN"));
- authInfo = KIO::AuthInfo();
- authInfo.username = QStringLiteral("user");
- authInfo.password = QStringLiteral("pass");
- AuthCommand auth3(&smtp, "LOGIN", QStringLiteral("mail.example.com"), authInfo);
- ts.clear();
- ts2 = ts;
- assert(auth3.nextCommandLine(&ts) == "AUTH LOGIN\r\n");
- assert(!auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("334 VXNlcm5hbWU6");
- assert(auth3.processResponse(r, &ts) == true);
- assert(!auth3.needsResponse());
- assert(auth3.nextCommandLine(&ts) == "dXNlcg==\r\n");
- assert(!auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("334 go on");
- assert(auth3.processResponse(r, &ts) == true);
- assert(!auth3.needsResponse());
- assert(auth3.nextCommandLine(&ts) == "cGFzcw==\r\n");
- assert(auth3.isComplete());
- assert(auth3.needsResponse());
- r.clear();
- r.parseLine("250 OK");
- assert(auth3.processResponse(r, &ts) == true);
- assert(!auth3.needsResponse());
- assert(!smtp.lastErrorCode);
- assert(ts == ts2);
-
- //
- // MAIL FROM:
- //
-
- smtp.clear();
- MailFromCommand mail(&smtp, "joe at 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 = ts;
- assert(mail.nextCommandLine(&ts) == "MAIL FROM:<joe at user.org>\r\n");
- assert(ts2 == ts);
- assert(mail.isComplete());
- assert(mail.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(mail.processResponse(r, &ts) == true);
- assert(!mail.needsResponse());
- assert(ts == ts2);
- assert(smtp.lastErrorCode == 0);
-
- // dynamics: success, size, 8bit, but no SIZE, 8BITMIME caps
- smtp.clear();
- MailFromCommand mail2(&smtp, "joe at user.org", true, 500);
- ts.clear();
- ts2 = ts;
- assert(mail2.nextCommandLine(&ts) == "MAIL FROM:<joe at user.org>\r\n");
- assert(ts == ts2);
-
- // dynamics: success, size, 8bit, SIZE, 8BITMIME caps
- smtp.clear();
- MailFromCommand mail3(&smtp, "joe at user.org", true, 500);
- ts.clear();
- ts2 = ts;
- smtp.caps << QStringLiteral("SIZE") << QStringLiteral("8BITMIME");
- assert(mail3.nextCommandLine(&ts) == "MAIL FROM:<joe at user.org> BODY=8BITMIME SIZE=500\r\n");
- assert(ts == ts2);
-
- // dynamics: failure
- smtp.clear();
- MailFromCommand mail4(&smtp, "joe at user.org");
- ts.clear();
- mail4.nextCommandLine(&ts);
- r.clear();
- r.parseLine("503 Bad sequence of commands");
- assert(mail4.processResponse(r, &ts) == false);
- assert(mail4.isComplete());
- assert(!mail4.needsResponse());
- assert(ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode == 0);
-
- //
- // RCPT TO:
- //
-
- smtp.clear();
- RcptToCommand rcpt(&smtp, "joe at 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 = ts;
- assert(rcpt.nextCommandLine(&ts) == "RCPT TO:<joe at user.org>\r\n");
- assert(ts == ts2);
- assert(rcpt.isComplete());
- assert(rcpt.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(rcpt.processResponse(r, &ts) == true);
- assert(!rcpt.needsResponse());
- assert(ts.atLeastOneRecipientWasAccepted());
- assert(!ts.haveRejectedRecipients());
- assert(!ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode == 0);
-
- // dynamics: failure
- smtp.clear();
- RcptToCommand rcpt2(&smtp, "joe at user.org");
- ts.clear();
- rcpt2.nextCommandLine(&ts);
- r.clear();
- r.parseLine("530 5.7.1 Relaying not allowed!");
- assert(rcpt2.processResponse(r, &ts) == false);
- assert(rcpt2.isComplete());
- assert(!rcpt2.needsResponse());
- assert(!ts.atLeastOneRecipientWasAccepted());
- assert(ts.haveRejectedRecipients());
- assert(ts.rejectedRecipients().count() == 1);
- assert(ts.rejectedRecipients().front().recipient == QLatin1String("joe at user.org"));
- assert(ts.failed());
- assert(!ts.failedFatally());
- assert(smtp.lastErrorCode == 0);
-
- // dynamics: success and failure combined
- smtp.clear();
- RcptToCommand rcpt3(&smtp, "info at example.com");
- RcptToCommand rcpt4(&smtp, "halloween at microsoft.com");
- RcptToCommand rcpt5(&smtp, "joe at 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 == 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) == "DATA\r\n");
- assert(data.isComplete());
- assert(data.needsResponse());
- assert(ts.dataCommandIssued());
- assert(!ts.dataCommandSucceeded());
- r.clear();
- r.parseLine("354 Send data, end in <CR><LF>.<CR><LF>");
- assert(data.processResponse(r, &ts) == true);
- assert(!data.needsResponse());
- assert(ts.dataCommandSucceeded());
- assert(ts.dataResponse() == r);
- assert(smtp.lastErrorCode == 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) == false);
- assert(!data2.needsResponse());
- assert(!ts.dataCommandSucceeded());
- assert(ts.dataResponse() == r);
- assert(smtp.lastErrorCode == 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 at 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 = 1,
- PerformDotStuff = 2,
- UngetLast = 4,
- Preloading = 8,
- Error = 16,
- EndOfOptions = 32
- };
- for (unsigned int i = 0; i < EndOfOptions; ++i) {
- checkSuccessfulTransferCommand(i & Error, i & Preloading, i & UngetLast,
- 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) == "NOOP\r\n");
- assert(noop.isComplete());
- assert(noop.needsResponse());
- r.clear();
- r.parseLine("250 Ok");
- assert(noop.processResponse(r, nullptr) == true);
- assert(noop.isComplete());
- assert(!noop.needsResponse());
- assert(smtp.lastErrorCode == 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) == "RSET\r\n");
- assert(rset.isComplete());
- assert(rset.needsResponse());
- r.clear();
- r.parseLine("502 command not implemented");
- assert(rset.processResponse(r, nullptr) == false);
- assert(rset.isComplete());
- assert(!rset.needsResponse());
- assert(smtp.lastErrorCode == 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) == "QUIT\r\n");
- assert(quit.isComplete());
- assert(quit.needsResponse());
- r.clear();
- r.parseLine("221 Goodbye");
- assert(quit.processResponse(r, nullptr) == true);
- assert(quit.isComplete());
- assert(!quit.needsResponse());
- assert(smtp.lastErrorCode == 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) == false);
- assert(quit2.isComplete());
- assert(!quit2.needsResponse());
- assert(smtp.lastErrorCode == 0); // an QUIT failure isn't worth it, is it?
- assert(smtp.lastErrorMessage.isNull());
-
- return 0;
-}
-
-void checkSuccessfulTransferCommand(bool error, bool preload, bool ungetLast, bool slaveDotStuff, bool mailEndsInNewline)
-{
- qDebug() << " ===== checkTransferCommand( "
- << error << ", "
- << preload << ", "
- << ungetLast << ", "
- << slaveDotStuff << ", "
- << mailEndsInNewline << " ) =====" << endl;
-
- FakeSession smtp;
- if (slaveDotStuff) {
- smtp.lf2crlfAndDotStuff = true;
- }
-
- Response r;
-
- const char *s_pre = slaveDotStuff
- ? mailEndsInNewline ? foobarbaz_lf : foobarbaz
- :
- mailEndsInNewline ? foobarbaz_crlf : foobarbaz_dotstuffed;
- const unsigned int s_pre_len = qstrlen(s_pre);
-
- const char *s_post = mailEndsInNewline ? foobarbaz_crlf : foobarbaz_dotstuffed;
- //const unsigned int s_post_len = 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) == s_post);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.failed());
- assert(smtp.lastErrorCode == 0);
- }
- smtp.nextData = QByteArray(s_pre, s_pre_len);
- smtp.nextDataReturnCode = s_pre_len;
- assert(xfer.nextCommandLine(&ts) == s_post);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.failed());
- assert(smtp.lastErrorCode == 0);
- smtp.nextData.resize(0);
- smtp.nextDataReturnCode = 0;
- if (ungetLast) {
- xfer.ungetCommandLine(xfer.nextCommandLine(&ts), &ts);
- assert(!xfer.isComplete());
- assert(!xfer.needsResponse());
- assert(!ts.complete());
- smtp.nextDataReturnCode = -1; // double read -> error
- }
- if (mailEndsInNewline) {
- assert(xfer.nextCommandLine(&ts) == ".\r\n");
- } else {
- assert(xfer.nextCommandLine(&ts) == "\r\n.\r\n");
- }
- assert(xfer.isComplete());
- assert(xfer.needsResponse());
- assert(!ts.complete());
- assert(!ts.failed());
- assert(smtp.lastErrorCode == 0);
- r.clear();
- if (error) {
- r.parseLine("552 Exceeded storage allocation");
- assert(xfer.processResponse(r, &ts) == false);
- assert(!xfer.needsResponse());
- assert(ts.complete());
- assert(ts.failed());
- assert(smtp.lastErrorCode == KIO::ERR_DISK_FULL);
- } else {
- r.parseLine("250 Message accepted");
- assert(xfer.processResponse(r, &ts) == true);
- assert(!xfer.needsResponse());
- assert(ts.complete());
- assert(!ts.failed());
- assert(smtp.lastErrorCode == 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/src/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 <iostream>
-
-//using std::cout;
-//using std::endl;
-
-int main(int, char **)
-{
- static QByteArray expected
- = "From: mutz at kde.org\r\n"
- "Subject: missing subject\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: Marc Mutz <mutz at kde.org>\r\n"
- "Subject: missing subject\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: \"Mutz, Marc\" <mutz at kde.org>\r\n"
- "Subject: missing subject\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: =?utf-8?b?TWFyYyBNw7Z0eg==?= <mutz at kde.org>\r\n"
- "Subject: missing subject\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: mutz at kde.org\r\n"
- "Subject: =?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: Marc Mutz <mutz at kde.org>\r\n"
- "Subject: =?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: \"Mutz, Marc\" <mutz at kde.org>\r\n"
- "Subject: =?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n"
- "From: =?utf-8?b?TWFyYyBNw7Z0eg==?= <mutz at kde.org>\r\n"
- "Subject: =?utf-8?b?QmzDtmRlcyBTdWJqZWN0?=\r\n"
- "To: joe at user.org,\r\n"
- "\tvalentine at 14th.february.org\r\n"
- "Cc: boss at example.com\r\n"
- "\n";
-
- KioSMTP::Request request;
- QByteArray result;
-
- request.setEmitHeaders(true);
- request.setFromAddress(QStringLiteral("mutz at kde.org"));
- request.addTo(QStringLiteral("joe at user.org"));
- request.addTo(QStringLiteral("valentine at 14th.february.org"));
- request.addCc(QStringLiteral("boss at example.com"));
-
- result += request.headerFields() + '\n';
- result += request.headerFields(QStringLiteral("Marc Mutz")) + '\n';
- result += request.headerFields(QStringLiteral("Mutz, Marc")) + '\n';
- result += request.headerFields(QString::fromUtf8("Marc Mötz")) + '\n';
-
- request.setSubject(QString::fromUtf8("Blödes Subject"));
-
- result += request.headerFields() + '\n';
- result += request.headerFields(QStringLiteral("Marc Mutz")) + '\n';
- result += request.headerFields(QStringLiteral("Mutz, Marc")) + '\n';
- result += request.headerFields(QString::fromUtf8("Marc Mötz")) + '\n';
-
- if (result != expected) {
- std::cout << "Result:\n" << result.data() << std::endl;
- std::cout << "Expected:\n" << expected.data() << std::endl;
- }
-
- return result == 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 <qtest.h>
-#include <assert.h>
-
-QTEST_GUILESS_MAIN(ResponseParserTest)
-
-static const QByteArray singleLineResponseCRLF = "250 OK\r\n";
-static const QByteArray singleLineResponse = "250 OK";
-
-static const QByteArray multiLineResponse[] = {
- "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 = 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.length());
- 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 = 0; i < numMultiLineLines; ++i) {
- r.parseLine(multiLineResponse[i].data(), multiLineResponse[i].length());
- 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/smtp/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 <vkrause at kde.org>
-
- 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 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 RESPONSEPARSER_TEST_H
-#define RESPONSEPARSER_TEST_H
-
-#include <QObject>
-
-class ResponseParserTest : public QObject
-{
- Q_OBJECT
-private Q_SLOTS:
- void testResponseParser();
-};
-
-#endif
diff --git a/kioslave/src/smtp/transactionstate.cpp b/kioslave/src/smtp/transactionstate.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 <mutz at kde.org>
-
- 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
-
- 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 <kio/global.h>
-#include <KLocalizedString>
-
-namespace KioSMTP {
-void TransactionState::setFailedFatally(int code, const QString &msg)
-{
- mFailed = mFailedFatally = true;
- mErrorCode = code;
- mErrorMessage = msg;
-}
-
-void TransactionState::setMailFromFailed(const QString &addr, const Response &r)
-{
- setFailed();
- mErrorCode = KIO::ERR_NO_CONTENT;
- if (addr.isEmpty()) {
- mErrorMessage = i18n("The server did not accept a blank sender address.\n"
- "%1", r.errorMessage());
- } else {
- mErrorMessage = i18n("The server did not accept the sender address \"%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 Response &r)
-{
- mDataCommandSucceeded = succeeded;
- mDataResponse = 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 = mRejectedRecipients.begin(), end(mRejectedRecipients.end());
- it != 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 failed.\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/transactionstate.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 <mutz at kde.org>
-
- 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
-
- 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 <QString>
-
-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 = QString(), const QString &why = QString())
- : recipient(who)
- , reason(why)
- {
- }
-
- QString recipient;
- QString reason;
-#ifdef KIOSMTP_COMPARATORS
- bool operator==(const RecipientRejection &other) const
- {
- return recipient == other.recipient && reason == other.reason;
- }
-
-#endif
- };
- typedef QList<RecipientRejection> RejectedRecipientList;
-
- TransactionState(bool rcptToDenyIsFailure = 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 = 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 = 0, const QString &msg = QString());
-
- /** @return whether the transaction was completed successfully */
- bool complete() const
- {
- return mComplete;
- }
-
- void setComplete()
- {
- mComplete = 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 = 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 = 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
- = mDataCommandIssued
- = mDataCommandSucceeded
- = mFailed = mFailedFatally
- = mComplete = false;
- }
-
-#ifdef KIOSMTP_COMPARATORS
- bool operator==(const TransactionState &other) const
- {
- return
- mAtLeastOneRecipientWasAccepted == other.mAtLeastOneRecipientWasAccepted
- && mDataCommandIssued == other.mDataCommandIssued
- && mDataCommandSucceeded == other.mDataCommandSucceeded
- && mFailed == other.mFailed
- && mFailedFatally == other.mFailedFatally
- && mComplete == other.mComplete
- && mDataResponse.code() == other.mDataResponse.code()
- && mRejectedRecipients == 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/kmailtransport/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 <contact at betterinbox.com>
+ Author: Christophe Laveault <christophe at betterinbox.com>
+ Gregory Schlomoff <gregory.schlomoff at gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "fakeserver.h"
+
+#include <QTest>
+#include <QFile>
+#include <QDebug>
+
+FakeServer::FakeServer(QObject *parent) :
+ QThread(parent),
+ m_tcpServer(nullptr)
+{
+ moveToThread(this);
+}
+
+QByteArray FakeServer::greeting()
+{
+ return "S: 220 localhost ESMTP xx777xx";
+}
+
+QList<QByteArray> FakeServer::greetingAndEhlo(bool multiline)
+{
+ return QList<QByteArray>() << greeting()
+ << "C: EHLO 127.0.0.1"
+ << QByteArray("S: 250") + (multiline ? '-' : ' ') + "Localhost ready to roll";
+}
+
+QList<QByteArray> 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::BlockingQueuedConnection);
+}
+
+void FakeServer::dataAvailable()
+{
+ QMutexLocker locker(&m_mutex);
+
+ QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(socket != nullptr);
+
+ int scenarioNumber = 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(dataAvailable()));
+ //m_clientParsers << new KIMAP::ImapStreamParser( m_clientSockets.last(), true );
+
+ QVERIFY(m_clientSockets.size() <= m_scenarios.size());
+
+ writeServerPart(m_clientSockets.size() - 1);
+}
+
+void FakeServer::run()
+{
+ m_tcpServer = 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<QByteArray> &scenario)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_scenarios.clear();
+ m_scenarios << scenario;
+}
+
+void FakeServer::addScenario(const QList<QByteArray> &scenario)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_scenarios << scenario;
+}
+
+void FakeServer::addScenarioFromFile(const QString &fileName)
+{
+ QFile file(fileName);
+ file.open(QFile::ReadOnly);
+
+ QList<QByteArray> 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<QByteArray> scenario = m_scenarios[scenarioNumber];
+ QTcpSocket *clientSocket = m_clientSockets[scenarioNumber];
+
+ while (!scenario.isEmpty()
+ && (scenario.first().startsWith("S: ") || scenario.first().startsWith("W: "))) {
+ QByteArray rule = scenario.takeFirst();
+
+ if (rule.startsWith("S: ")) {
+ QByteArray payload = rule.mid(3);
+ clientSocket->write(payload + "\r\n");
+ } else {
+ int timeout = 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] = scenario;
+}
+
+void FakeServer::readClientPart(int scenarioNumber)
+{
+ QList<QByteArray> scenario = m_scenarios[scenarioNumber];
+ QTcpSocket *clientSocket = m_clientSockets[scenarioNumber];
+
+ while (!scenario.isEmpty() && scenario.first().startsWith("C: ")) {
+ QByteArray line = clientSocket->readLine();
+ QByteArray received = "C: " + line.trimmed();
+ QByteArray expected = scenario.takeFirst();
+
+ if (expected == "C: SKIP" && !scenario.isEmpty()) {
+ expected = scenario.takeFirst();
+ while (received != expected) {
+ received = "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] = scenario;
+}
diff --git a/src/kmailtransport/plugins/smtp/autotests/fakeserver.h b/src/kmailtransport/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 <contact at betterinbox.com>
+ Author: Christophe Laveault <christophe at betterinbox.com>
+ Gregory Schlomoff <gregory.schlomoff at gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef KSMTP_FAKESERVER_H
+#define KSMTP_FAKESERVER_H
+
+#include <QThread>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QMutex>
+#include <QStringList>
+
+Q_DECLARE_METATYPE(QList<QByteArray>)
+
+class FakeServer : public QThread
+{
+ Q_OBJECT
+
+public:
+ explicit FakeServer(QObject *parent = nullptr);
+ ~FakeServer() override;
+
+ void startAndWait();
+ void run() override;
+
+ static QByteArray greeting();
+ static QList<QByteArray> greetingAndEhlo(bool multiline = true);
+ static QList<QByteArray> bye();
+
+ void setScenario(const QList<QByteArray> &scenario);
+ void addScenario(const QList<QByteArray> &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<QByteArray> > m_scenarios;
+ QTcpServer *m_tcpServer;
+ mutable QMutex m_mutex;
+ QList<QTcpSocket *> m_clientSockets;
+};
+
+#endif // KSMTP_FAKESERVER_H
diff --git a/src/kmailtransport/plugins/smtp/autotests/smtpjobtest.cpp b/src/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átil <dvratil at kde.org>
+
+ 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 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 "../smtpjob.h"
+#include "fakeserver.h"
+
+#include "transportbase.h"
+#include "transportmanager.h"
+
+#include <QObject>
+#include <QTest>
+#include <QStandardPaths>
+
+Q_DECLARE_METATYPE(MailTransport::TransportBase::EnumAuthenticationType::type)
+
+class SmtpJobTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ QStandardPaths::setTestModeEnabled(true);
+ }
+
+ void smtpJobTest_data()
+ {
+ QTest::addColumn<QList<QByteArray>>("scenario");
+ QTest::addColumn<MailTransport::TransportBase::EnumAuthenticationType::type>("authType");
+ QTest::addColumn<QString>("from");
+ QTest::addColumn<QStringList>("to");
+ QTest::addColumn<QStringList>("cc");
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<bool>("success");
+
+ QList<QByteArray> scenario;
+ scenario << FakeServer::greetingAndEhlo()
+ << "S: 250 AUTH PLAIN LOGIN"
+ << "C: AUTH LOGIN"
+ << "S: 334 VXNlcm5hbWU6"
+ << "C: bG9naW4=" // "login".toBase64()
+ << "S: 334 UGFzc3dvcmQ6"
+ << "C: cGFzc3dvcmQ=" // "password".toBase64()
+ << "S: 235 Authenticated."
+ << "C: MAIL FROM:<foo at bar.com>"
+ << "S: 250 ok"
+ << "C: RCPT TO:<bar at foo.com>"
+ << "S: 250 ok"
+ << "C: RCPT TO:<bar at bar.foo>"
+ << "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::TransportBase::EnumAuthenticationType::LOGIN
+ << QStringLiteral("Foo Bar <foo at bar.com>")
+ << QStringList{}
+ << QStringList{ QStringLiteral("bar at foo.com"), QStringLiteral("<bar at bar.foo>") }
+ << QByteArray("Hi Bob")
+ << true;
+ }
+
+ void smtpJobTest()
+ {
+ QFETCH(QList<QByteArray>, 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 = MailTransport::TransportManager::self()->createTransport();
+ 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/kmailtransport/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átil <dvratil at kde.org>
+
+ 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 <KSMTP/SessionUiProxy>
+#include <kio/sslui.h>
+
+class SmtpSessionUiProxy : public KSmtp::SessionUiProxy
+{
+public:
+ bool ignoreSslError(const KSslErrorUiData &errorData) override
+ {
+ return KIO::SslUi::askIgnoreSslErrors(errorData, KIO::SslUi::RecallAndStoreRules);
+ }
+};
+
+#endif
diff --git a/src/kmailtransport/plugins/smtp/smtpjob.cpp b/src/kmailtransport/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 <QBuffer>
@@ -34,36 +35,42 @@
#include <QUrl>
#include <QUrlQuery>
#include "mailtransport_debug.h"
-#include <KIO/Job>
-#include <KIO/Scheduler>
#include <KPasswordDialog>
+#include <KSMTP/Session>
+#include <KSMTP/LoginJob>
+#include <KSMTP/SendJob>
+
using namespace MailTransport;
-class SlavePool
+class SessionPool
{
public:
- SlavePool() : ref(0)
+ SessionPool() : ref(0)
{
}
int ref;
- QHash<int, KIO::Slave *> slaves;
+ QHash<int, KSmtp::Session*> sessions;
- void removeSlave(KIO::Slave *slave, bool disconnect = false)
+ void removeSession(KSmtp::Session *session)
{
- qCDebug(MAILTRANSPORT_SMTP_LOG) << "Removing slave" << slave << "from pool";
- const int slaveKey = 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 = sessions.key(session);
+ if (key > 0) {
+ QObject::connect(session, &KSmtp::Session::stateChanged,
+ [session](KSmtp::Session::State state) {
+ if (state == KSmtp::Session::Disconnected) {
+ 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 releases.
@@ -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 = SmtpJobPrivate::Idle;
- d->slave = nullptr;
+ d->session = nullptr;
d->finished = false;
- if (!s_slavePool.isDestroyed()) {
- s_slavePool->ref++;
+ d->uiProxy = 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 == 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 == 0) {
+ qCDebug(MAILTRANSPORT_SMTP_LOG) << "clearing SMTP session pool" << s_sessionPool->sessions.count();
+ while (!s_sessionPool->sessions.isEmpty()) {
+ s_sessionPool->removeSession(*(s_sessionPool->sessions.begin()));
}
- 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 = 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() == Transport::EnumEncryption::SSL)
- ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
- destination.setHost(transport()->host().trimmed());
- destination.setPort(transport()->port());
+ d->session = s_sessionPool->sessions.value(transport()->id());
+ if (!d->session) {
+ d->session = 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"), QStringLiteral("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() == KSmtp::Session::Disconnected) {
+ d->session->open();
+ } else {
+ if (d->session->state() != KSmtp::Session::Authenticated) {
+ startLoginJob();
+ }
+
+ startSendJob();
}
- for (const QString &str : bcc()) {
- destinationQuery.addQueryItem(QStringLiteral("bcc"), str);
+}
+
+void SmtpJob::sessionStateChanged(KSmtp::Session::State state)
+{
+ if (state == KSmtp::Session::Ready) {
+ startLoginJob();
+ } else if (state == KSmtp::Session::Authenticated) {
+ startSendJob();
}
+}
- if (transport()->specifyHostname()) {
- destinationQuery.addQueryItem(QStringLiteral("hostname"), transport()->localHostname());
+void SmtpJob::startLoginJob()
+{
+ if (!transport()->requiresAuthentication()) {
+ startSendJob();
+ return;
}
- if (transport()->requiresAuthentication()) {
- QString user = transport()->userName();
- QString passwd = transport()->password();
- if ((user.isEmpty() || passwd.isEmpty())
- && transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI) {
- QPointer<KPasswordDialog> dlg
- = 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 = false;
- if (dlg->exec()) {
- transport()->setUserName(dlg->username());
- transport()->setPassword(dlg->password());
- transport()->setStorePassword(dlg->keepPassword());
- transport()->save();
- gotIt = true;
- }
- delete dlg;
+ auto login = new KSmtp::LoginJob(d->session);
+ auto user = transport()->userName();
+ auto passwd = transport()->password();
+ if ((user.isEmpty() || passwd.isEmpty())
+ && transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI) {
+ QPointer<KPasswordDialog> dlg
+ = 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 = false;
+ if (dlg->exec()) {
+ transport()->setUserName(dlg->username());
+ transport()->setPassword(dlg->password());
+ transport()->setStorePassword(dlg->keepPassword());
+ transport()->save();
+ gotIt = 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 = s_slavePool->slaves.value(transport()->id());
- if (!d->slave) {
- KIO::MetaData slaveConfig;
- slaveConfig.insert(QStringLiteral("tls"),
- (transport()->encryption() == Transport::EnumEncryption::TLS)
- ? QStringLiteral("on") : QStringLiteral("off"));
- if (transport()->requiresAuthentication()) {
- slaveConfig.insert(QStringLiteral("sasl"), transport()->authenticationTypeString());
- }
- d->slave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
- 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 = KIO::put(destination, -1, KIO::HideProgressInfo);
- if (!d->slave || !job) {
- setError(UserDefinedError);
- setErrorText(i18n("Unable to create SMTP job."));
- emitResult();
- return;
}
- job->addMetaData(QStringLiteral("lf2crlf+dotstuff"), QStringLiteral("slave"));
- 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 = 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 == SmtpJobPrivate::Precommand) {
return subjobs().first()->kill();
} else if (d->currentState == SmtpJobPrivate::Smtp) {
- KIO::SimpleJob *job = static_cast<KIO::SimpleJob *>(subjobs().first());
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 == SmtpJobPrivate::Smtp) {
- s_slavePool->removeSlave(d->slave, errorCode != KIO::ERR_SLAVE_DIED);
+ 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 = 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 != KIO::ERR_SLAVE_DIED);
- if (d->slave == 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 <transportjob.h>
+#include <KSMTP/Session>
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 &errorMsg);
+ 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;
More information about the kde-doc-english
mailing list