[kde-doc-english] [libksieve] /: Move here

Montel Laurent montel at kde.org
Tue Apr 5 05:02:47 UTC 2016


Git commit 6eef4101131a197be7fd478f3852fdb08501697d by Montel Laurent.
Committed on 05/04/2016 at 05:02.
Pushed by mlaurent into branch 'master'.

Move here

M  +10   -1    CMakeLists.txt
A  +22   -0    cmake/COPYING-CMAKE-SCRIPTS
A  +53   -0    cmake/FindSasl2.cmake
A  +1    -0    kioslave/.krazy
A  +5    -0    kioslave/.reviewboardrc
A  +5    -0    kioslave/CMakeLists.txt
A  +1    -0    kioslave/docs/CMakeLists.txt
A  +3    -0    kioslave/docs/sieve/CMakeLists.txt
A  +30   -0    kioslave/docs/sieve/index.docbook
A  +4    -0    kioslave/src/CMakeLists.txt
A  +50   -0    kioslave/src/common.h     [License: LGPL (v2+)]
A  +20   -0    kioslave/src/sieve/CMakeLists.txt
A  +2    -0    kioslave/src/sieve/Messages.sh
A  +4    -0    kioslave/src/sieve/RFCs
A  +2    -0    kioslave/src/sieve/sieve-config.h.cmake
A  +1313 -0    kioslave/src/sieve/sieve.cpp     [License: GPL (v2)]
A  +145  -0    kioslave/src/sieve/sieve.h     [License: GPL (v2)]
A  +65   -0    kioslave/src/sieve/sieve.protocol
M  +1    -0    libksieve.categories

http://commits.kde.org/libksieve/6eef4101131a197be7fd478f3852fdb08501697d

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ae70009..084a326 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.12)
 project(libksieve)
 
 find_package(ECM 5.19.0 CONFIG REQUIRED)
-set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
+set(CMAKE_MODULE_PATH ${libksieve_SOURCE_DIR}/cmake ${ECM_MODULE_PATH})
 set(LIBRARY_NAMELINK)
 include(GenerateExportHeader)
 include(ECMSetupVersion)
@@ -45,6 +45,14 @@ find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQU
 find_package(KF5MailTransport ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED)
 find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED)
 
+find_package(Sasl2)
+set_package_properties(Sasl2 PROPERTIES
+                             DESCRIPTION "The Cyrus-sasl library"
+                             URL "http://www.cyrussasl.org"
+                             TYPE OPTIONAL
+)
+
+
 option(QTWEBENGINE_SUPPORT_OPTION "Enable support for QtWebEngine. False by default." FALSE)
 
 if (QTWEBENGINE_SUPPORT_OPTION)
@@ -89,6 +97,7 @@ install(FILES
 
 
 add_subdirectory(src)
+add_subdirectory(kioslave)
 if(BUILD_TESTING)
     add_subdirectory(autotests)
 endif()
diff --git a/cmake/COPYING-CMAKE-SCRIPTS b/cmake/COPYING-CMAKE-SCRIPTS
new file mode 100644
index 0000000..4b41776
--- /dev/null
+++ b/cmake/COPYING-CMAKE-SCRIPTS
@@ -0,0 +1,22 @@
+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 copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products 
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
diff --git a/cmake/FindSasl2.cmake b/cmake/FindSasl2.cmake
new file mode 100644
index 0000000..ec3bb81
--- /dev/null
+++ b/cmake/FindSasl2.cmake
@@ -0,0 +1,53 @@
+#
+# - Try to find the sasl2 directory library
+# Once done this will define
+#
+#  Sasl2_FOUND - system has SASL2
+#  Sasl2_INCLUDE_DIRS - the SASL2 include directory
+#  Sasl2_LIBRARIES - The libraries needed to use SASL2
+
+# Copyright (c) 2006, 2007 Laurent Montel, <montel at kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+# Note: libsasl2.pc doesn't export the include dir.
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_Sasl2 libsasl2)
+
+find_path(Sasl2_INCLUDE_DIRS sasl/sasl.h
+)
+
+# libsasl2 add for windows, because the windows package of cyrus-sasl2
+# contains a libsasl2 also for msvc which is not standard conform
+find_library(Sasl2_LIBRARIES
+  NAMES sasl2 libsasl2
+  HINTS ${PC_Sasl2_LIBRARY_DIRS}
+)
+
+set(Sasl2_VERSION ${PC_Sasl2_VERSION})
+
+if(NOT Sasl2_VERSION)
+  if(EXISTS ${Sasl2_INCLUDE_DIRS}/sasl/sasl.h)
+    file(READ ${Sasl2_INCLUDE_DIRS}/sasl/sasl.h SASL2_H_CONTENT)
+    string(REGEX MATCH "#define SASL_VERSION_MAJOR[ ]+[0-9]+" SASL2_VERSION_MAJOR_MATCH ${SASL2_H_CONTENT})
+    string(REGEX MATCH "#define SASL_VERSION_MINOR[ ]+[0-9]+" SASL2_VERSION_MINOR_MATCH ${SASL2_H_CONTENT})
+    string(REGEX MATCH "#define SASL_VERSION_STEP[ ]+[0-9]+" SASL2_VERSION_STEP_MATCH ${SASL2_H_CONTENT})
+
+    string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" SASL2_VERSION_MAJOR ${SASL2_VERSION_MAJOR_MATCH})
+    string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" SASL2_VERSION_MINOR ${SASL2_VERSION_MINOR_MATCH})
+    string(REGEX REPLACE ".*_STEP[ ]+(.*)" "\\1"  SASL2_VERSION_STEP  ${SASL2_VERSION_STEP_MATCH})
+
+    set(Sasl2_VERSION "${SASL2_VERSION_MAJOR}.${SASL2_VERSION_MINOR}.${SASL2_VERSION_STEP}")
+  endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(Sasl2
+    FOUND_VAR Sasl2_FOUND
+    REQUIRED_VARS Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS
+    VERSION_VAR Sasl2_VERSION
+)
+
+mark_as_advanced(Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS Sasl2_VERSION)
diff --git a/kioslave/.krazy b/kioslave/.krazy
new file mode 100644
index 0000000..0b16e7f
--- /dev/null
+++ b/kioslave/.krazy
@@ -0,0 +1 @@
+SKIP /tests/
diff --git a/kioslave/.reviewboardrc b/kioslave/.reviewboardrc
new file mode 100644
index 0000000..5c1c502
--- /dev/null
+++ b/kioslave/.reviewboardrc
@@ -0,0 +1,5 @@
+REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
+#REPOSITORY = "git://anongit.kde.org/kioslave"
+BRANCH = "master"
+TARGET_GROUPS = "kdepimlibs"
+TARGET_PEOPLE = "mlaurent"
diff --git a/kioslave/CMakeLists.txt b/kioslave/CMakeLists.txt
new file mode 100644
index 0000000..a29ebc0
--- /dev/null
+++ b/kioslave/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII")
+
+add_subdirectory(src)
+add_subdirectory(docs)
+
diff --git a/kioslave/docs/CMakeLists.txt b/kioslave/docs/CMakeLists.txt
new file mode 100644
index 0000000..44813ac
--- /dev/null
+++ b/kioslave/docs/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(sieve)
diff --git a/kioslave/docs/sieve/CMakeLists.txt b/kioslave/docs/sieve/CMakeLists.txt
new file mode 100644
index 0000000..98fdda4
--- /dev/null
+++ b/kioslave/docs/sieve/CMakeLists.txt
@@ -0,0 +1,3 @@
+########### install files ###############
+kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR kioslave5/sieve)
+
diff --git a/kioslave/docs/sieve/index.docbook b/kioslave/docs/sieve/index.docbook
new file mode 100644
index 0000000..d269a68
--- /dev/null
+++ b/kioslave/docs/sieve/index.docbook
@@ -0,0 +1,30 @@
+<?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="sieve">
+<title>sieve</title>
+<articleinfo>
+
+<authorgroup>
+<author><personname><firstname>Daniel</firstname><surname>Black</surname></personname>
+<email>daniel.subs at internode.on.net</email></author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+ 
+<date>2009-11-05</date>
+
+ 
+</articleinfo>
+
+<para>Sieve is a protocol that is used to manage filters for email.</para> 
+
+<para>The filters are stored and run on the email server.</para>
+
+<para><ulink url="http://www.ietf.org/rfc/rfc5228.txt">IETF
+RF5228</ulink> provides more information.</para>
+
+</article>
diff --git a/kioslave/src/CMakeLists.txt b/kioslave/src/CMakeLists.txt
new file mode 100644
index 0000000..0ee108d
--- /dev/null
+++ b/kioslave/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+#remove it
+remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY)
+add_subdirectory(sieve)
+
diff --git a/kioslave/src/common.h b/kioslave/src/common.h
new file mode 100644
index 0000000..acdeb17
--- /dev/null
+++ b/kioslave/src/common.h
@@ -0,0 +1,50 @@
+/*  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>
+
+extern "C" {
+#include <sasl/sasl.h>
+}
+
+inline bool initSASL()
+{
+#ifdef Q_OS_WIN32  //krazy:exclude=cpp
+    QByteArray libInstallPath(QFile::encodeName(QDir::toNativeSeparators(KGlobal::dirs()->installPath("lib") + QLatin1String("sasl2"))));
+    QByteArray configPath(QFile::encodeName(QDir::toNativeSeparators(KGlobal::dirs()->installPath("config") + QLatin1String("sasl2"))));
+    if (sasl_set_path(SASL_PATH_TYPE_PLUGIN, libInstallPath.data()) != SASL_OK ||
+            sasl_set_path(SASL_PATH_TYPE_CONFIG, configPath.data()) != SASL_OK) {
+        fprintf(stderr, "SASL path initialization failed!\n");
+        return false;
+    }
+#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/sieve/CMakeLists.txt b/kioslave/src/sieve/CMakeLists.txt
new file mode 100644
index 0000000..5a6efeb
--- /dev/null
+++ b/kioslave/src/sieve/CMakeLists.txt
@@ -0,0 +1,20 @@
+configure_file (sieve-config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/sieve-config.h )
+########### next target ###############
+
+set(kio_sieve_PART_SRCS sieve.cpp )
+
+ecm_qt_declare_logging_category(kio_sieve_PART_SRCS HEADER sieve_debug.h IDENTIFIER SIEVE_LOG CATEGORY_NAME log_sieve)
+
+add_library(kio_sieve MODULE ${kio_sieve_PART_SRCS})
+
+
+
+target_link_libraries(kio_sieve KF5::KIOCore KF5::I18n Qt5::Network KF5::WidgetsAddons ${Sasl2_LIBRARIES})
+set_target_properties(kio_sieve PROPERTIES OUTPUT_NAME "sieve")
+
+install(TARGETS kio_sieve  DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kio/)
+
+
+########### install files ###############
+
+install( FILES sieve.protocol  DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
diff --git a/kioslave/src/sieve/Messages.sh b/kioslave/src/sieve/Messages.sh
new file mode 100644
index 0000000..d5b8d00
--- /dev/null
+++ b/kioslave/src/sieve/Messages.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+$XGETTEXT *.cpp -o $podir/kio_sieve.pot
diff --git a/kioslave/src/sieve/RFCs b/kioslave/src/sieve/RFCs
new file mode 100644
index 0000000..9f87c4f
--- /dev/null
+++ b/kioslave/src/sieve/RFCs
@@ -0,0 +1,4 @@
+3028: Sieve: A Mail Filtering Language
+3431: Sieve Extension: Relational Tests
+
+Documents at http://ktown.kde.org/~dirk/sieve/
diff --git a/kioslave/src/sieve/sieve-config.h.cmake b/kioslave/src/sieve/sieve-config.h.cmake
new file mode 100644
index 0000000..e11fa9f
--- /dev/null
+++ b/kioslave/src/sieve/sieve-config.h.cmake
@@ -0,0 +1,2 @@
+/* Define if you have cyrus-sasl2 libraries */
+#cmakedefine HAVE_LIBSASL2 1
diff --git a/kioslave/src/sieve/sieve.cpp b/kioslave/src/sieve/sieve.cpp
new file mode 100644
index 0000000..3b8760b
--- /dev/null
+++ b/kioslave/src/sieve/sieve.cpp
@@ -0,0 +1,1313 @@
+/***************************************************************************
+                          sieve.cpp  -  description
+                             -------------------
+    begin                : Thu Dec 20 18:47:08 EST 2001
+    copyright            : (C) 2001 by Hamish Rodda
+    email                : meddie at yoyo.cc.monash.edu.au
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                            *
+ *                                                                         *
+ ***************************************************************************/
+
+/**
+ * Portions adapted from the SMTP ioslave.
+ * Copyright (c) 2000, 2001 Alex Zepeda <jazepeda at pacbell.net>
+ * Copyright (c) 2001 Michael Häckel <Michael at Haeckel.Net>
+ * All rights reserved.
+ *
+ * Policy: the function where the error occurs calls error(). A result of
+ * false, where it signifies an error, thus doesn't need to call error() itself.
+ */
+
+#include "sieve.h"
+#include "../common.h"
+#include "sieve_debug.h"
+
+extern "C" {
+#include <sasl/sasl.h>
+}
+
+#include <qregexp.h>
+#include <QSslSocket>
+#include <QUrlQuery>
+
+#include <KLocalizedString>
+#include <QUrl>
+#include <KMessageBox>
+#include <QApplication>
+#include <sys/stat.h>
+#include <cassert>
+
+#define ksDebug qCDebug( SIEVE_LOG )
+
+#define SIEVE_DEFAULT_PORT 2000
+
+static const sasl_callback_t callbacks[] = {
+    { SASL_CB_ECHOPROMPT, NULL, NULL },
+    { SASL_CB_NOECHOPROMPT, NULL, NULL },
+    { SASL_CB_GETREALM, NULL, NULL },
+    { SASL_CB_USER, NULL, NULL },
+    { SASL_CB_AUTHNAME, NULL, NULL },
+    { SASL_CB_PASS, NULL, NULL },
+    { SASL_CB_CANON_USER, NULL, NULL },
+    { SASL_CB_LIST_END, NULL, NULL }
+};
+
+static const unsigned int SIEVE_DEFAULT_RECIEVE_BUFFER = 512;
+
+using namespace KIO;
+extern "C"
+{
+    Q_DECL_EXPORT int kdemain(int argc, char **argv)
+    {
+        QApplication app(argc, argv);
+        app.setApplicationName(QStringLiteral("kio_sieve"));
+
+        ksDebug << "*** Starting kio_sieve " << endl;
+
+        if (argc != 4) {
+            ksDebug << "Usage: kio_sieve protocol domain-socket1 domain-socket2" << endl;
+            return -1;
+        }
+
+        if (!initSASL()) {
+            ::exit(-1);
+        }
+
+        kio_sieveProtocol slave(argv[2], argv[3]);
+        slave.dispatchLoop();
+
+        sasl_done();
+
+        ksDebug << "*** kio_sieve Done" << endl;
+        return 0;
+    }
+}
+
+/* ---------------------------------------------------------------------------------- */
+kio_sieveResponse::kio_sieveResponse()
+{
+    clear();
+}
+
+/* ---------------------------------------------------------------------------------- */
+const uint &kio_sieveResponse::getType() const
+{
+    return rType;
+}
+
+/* ---------------------------------------------------------------------------------- */
+uint kio_sieveResponse::getQuantity() const
+{
+    return quantity;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QByteArray &kio_sieveResponse::getAction() const
+{
+    return key;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QByteArray &kio_sieveResponse::getKey() const
+{
+    return key;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QByteArray &kio_sieveResponse::getVal() const
+{
+    return val;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QByteArray &kio_sieveResponse::getExtra() const
+{
+    return extra;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setQuantity(const uint &newQty)
+{
+    rType = QUANTITY;
+    quantity = newQty;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setAction(const QByteArray &newAction)
+{
+    rType = ACTION;
+    key = newAction;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setKey(const QByteArray &newKey)
+{
+    rType = KEY_VAL_PAIR;
+    key = newKey;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setVal(const QByteArray &newVal)
+{
+    val = newVal;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setExtra(const QByteArray &newExtra)
+{
+    extra = newExtra;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::clear()
+{
+    rType = NONE;
+    extra = key = val = QByteArray();
+    quantity = 0;
+}
+
+/* ---------------------------------------------------------------------------------- */
+kio_sieveProtocol::kio_sieveProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
+    : TCPSlaveBase("sieve", pool_socket, app_socket, false)
+    , m_connMode(NORMAL)
+    , m_supportsTLS(false)
+    , m_shouldBeConnected(false)
+    , m_allowUnencrypted(false)
+    , m_port(SIEVE_DEFAULT_PORT)
+{
+}
+
+/* ---------------------------------------------------------------------------------- */
+kio_sieveProtocol::~kio_sieveProtocol()
+{
+    if (isConnected()) {
+        disconnect();
+    }
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::setHost(const QString &host, quint16 port, const QString &user, const QString &pass)
+{
+    if (isConnected() &&
+            (m_sServer != host ||
+             m_port != port ||
+             m_sUser != user ||
+             m_sPass != pass)) {
+        disconnect();
+    }
+    m_sServer = host;
+    m_port = port ? port : SIEVE_DEFAULT_PORT;
+    m_sUser = user;
+    m_sPass = pass;
+    m_supportsTLS = false;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::openConnection()
+{
+    m_connMode = CONNECTION_ORIENTED;
+    connect();
+}
+
+bool kio_sieveProtocol::parseCapabilities(bool requestCapabilities/* = false*/)
+{
+    ksDebug << endl;
+
+    // Setup...
+    bool ret = false;
+
+    if (requestCapabilities) {
+        sendData("CAPABILITY");
+    }
+
+    while (receiveData()) {
+        ksDebug << "Looping receive" << endl;
+
+        if (r.getType() == kio_sieveResponse::ACTION) {
+            if (r.getAction().toLower().contains("ok")) {
+                ksDebug << "Sieve server ready & awaiting authentication." << endl;
+                break;
+            } else {
+                ksDebug << "Unknown action " << r.getAction() << "." << endl;
+            }
+
+        } else if (r.getKey() == "IMPLEMENTATION") {
+            ksDebug << "Connected to Sieve server: " << r.getVal() << endl;
+            ret = true;
+            setMetaData(QStringLiteral("implementation"), QLatin1String(r.getVal()));
+            m_implementation = QLatin1String(r.getVal());
+
+        } else if (r.getKey() == "SASL") {
+            // Save list of available SASL methods
+            const QString val = QLatin1String(r.getVal());
+            m_sasl_caps = val.split(QLatin1Char(' '));
+            ksDebug << "Server SASL authentication methods: " << m_sasl_caps.join(QStringLiteral(", ")) << endl;
+            setMetaData(QStringLiteral("saslMethods"), QLatin1String(r.getVal()));
+
+        } else if (r.getKey() == "SIEVE") {
+            // Save script capabilities; report back as meta data:
+            const QString val = QLatin1String(r.getVal());
+            ksDebug << "Server script capabilities: " << val.split(QLatin1Char(' ')).join(QStringLiteral(", ")) << endl;
+            setMetaData(QStringLiteral("sieveExtensions"), QLatin1String(r.getVal()));
+
+        } else if (r.getKey() == "STARTTLS") {
+            // The server supports TLS
+            ksDebug << "Server supports TLS" << endl;
+            m_supportsTLS = true;
+            setMetaData(QStringLiteral("tlsSupported"), QStringLiteral("true"));
+
+        } else {
+            ksDebug << "Unrecognised key " << r.getKey() << endl;
+        }
+    }
+
+    if (!m_supportsTLS) {
+        setMetaData(QStringLiteral("tlsSupported"), QStringLiteral("false"));
+    }
+
+    return ret;
+}
+
+/* ---------------------------------------------------------------------------------- */
+/**
+ * Checks if connection parameters have changed.
+ * If it it, close the current connection
+ */
+void kio_sieveProtocol::changeCheck(const QUrl &url)
+{
+    QString auth;
+
+    // Check the SASL auth mechanism in the 'sasl' metadata...
+    if (!metaData(QStringLiteral("sasl")).isEmpty()) {
+        auth = metaData(QStringLiteral("sasl")).toUpper();
+    } else {
+        // ... and if not found, check the x-mech=AUTH query part of the url.
+        QString query = url.query();
+        if (query.startsWith(QLatin1Char('?'))) {
+            query.remove(0, 1);
+        }
+        QStringList q = query.split(QLatin1Char(','));
+        QStringList::iterator it;
+
+        for (it = q.begin(); it != q.end(); ++it) {
+            if (((*it).section(QLatin1Char('='), 0, 0)).toLower() == QLatin1String("x-mech")) {
+                auth = ((*it).section(QLatin1Char('='), 1)).toUpper();
+                break;
+            }
+        }
+    }
+    ksDebug << "auth: " << auth << " m_sAuth: " << m_sAuth << endl;
+    if (m_sAuth != auth) {
+        m_sAuth = auth;
+        if (isConnected()) {
+            disconnect();
+        }
+    }
+    // For TLS, only disconnect if we are unencrypted and are
+    // no longer allowed (otherwise, it's still fine):
+    const bool allowUnencryptedNow = QUrlQuery(url).queryItemValue(QStringLiteral("x-allow-unencrypted")) == QLatin1String("true");
+    if (m_allowUnencrypted && !allowUnencryptedNow) {
+        if (isConnected()) {
+            disconnect();
+        }
+    }
+    m_allowUnencrypted = allowUnencryptedNow;
+}
+
+/* ---------------------------------------------------------------------------------- */
+/**
+ * Connects to the server.
+ * returns false and calls error() if an error occurred.
+ */
+bool kio_sieveProtocol::connect(bool useTLSIfAvailable)
+{
+    ksDebug << endl;
+
+    if (isConnected()) {
+        return true;
+    }
+
+    infoMessage(i18n("Connecting to %1...", m_sServer));
+
+    if (m_connMode == CONNECTION_ORIENTED && m_shouldBeConnected) {
+        error(ERR_CONNECTION_BROKEN, i18n("The connection to the server was lost."));
+        return false;
+    }
+
+    setBlocking(true);
+
+    if (!connectToHost(QStringLiteral("sieve"), m_sServer, m_port)) {
+        return false;
+    }
+
+    if (!parseCapabilities()) {
+        disconnectFromHost();
+        error(ERR_UNSUPPORTED_PROTOCOL, i18n("Server identification failed."));
+        return false;
+    }
+
+    // Attempt to start TLS
+    if (!m_allowUnencrypted && !QSslSocket::supportsSsl()) {
+        error(ERR_SLAVE_DEFINED, i18n("Can not use TLS since the underlying Qt library does not support it."));
+        disconnect();
+        return false;
+    }
+
+    if (!m_allowUnencrypted && useTLSIfAvailable && QSslSocket::supportsSsl() && !m_supportsTLS &&
+            messageBox(WarningContinueCancel,
+                       i18n("TLS encryption was requested, but your Sieve server does not advertise TLS in its capabilities.\n"
+                            "You can choose to try to initiate TLS negotiations nonetheless, or cancel the operation."),
+                       i18n("Server Does Not Advertise TLS"), i18n("&Start TLS nonetheless"), i18n("&Cancel")) != KMessageBox::Continue) {
+        error(ERR_USER_CANCELED, i18n("TLS encryption requested, but not supported by server."));
+        disconnect();
+        return false;
+    }
+
+    // FIXME find a test server and test that this works
+    if (useTLSIfAvailable && m_supportsTLS && QSslSocket::supportsSsl()) {
+        sendData("STARTTLS");
+        if (operationSuccessful()) {
+            ksDebug << "TLS has been accepted. Starting TLS..." << endl
+                    << "WARNING this is untested and may fail.";
+            if (startSsl()) {
+                ksDebug << "TLS enabled successfully." << endl;
+                // reparse capabilities:
+                parseCapabilities(requestCapabilitiesAfterStartTLS());
+            } else {
+                ksDebug << "TLS initiation failed.";
+                if (m_allowUnencrypted) {
+                    disconnect(true);
+                    return connect(false);
+                }
+                messageBox(Information, i18n("Your Sieve server claims to support TLS, "
+                                             "but negotiation was unsuccessful."),
+                           i18n("Connection Failed"));
+                disconnect(true);
+                return false;
+            }
+        } else if (!m_allowUnencrypted) {
+            ksDebug << "Server incapable of TLS.";
+            disconnect();
+            error(ERR_SLAVE_DEFINED, i18n("The server does not seem to support TLS. "
+                                          "Disable TLS if you want to connect without encryption."));
+            return false;
+        } else {
+            ksDebug << "Server incapable of TLS. Transmitted documents will be unencrypted." << endl;
+        }
+    } else {
+        ksDebug << "We are incapable of TLS. Transmitted documents will be unencrypted." << endl;
+    }
+
+    assert(m_allowUnencrypted || isUsingSsl());
+
+    infoMessage(i18n("Authenticating user..."));
+    if (!authenticate()) {
+        disconnect();
+        error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed."));
+        return false;
+    }
+
+    m_shouldBeConnected = true;
+    return true;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::closeConnection()
+{
+    m_connMode = CONNECTION_ORIENTED;
+    disconnect();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::disconnect(bool forcibly)
+{
+    if (!forcibly) {
+        sendData("LOGOUT");
+
+        if (!operationSuccessful()) {
+            ksDebug << "Server did not logout cleanly." << endl;
+        }
+    }
+
+    disconnectFromHost();
+    m_shouldBeConnected = false;
+}
+
+/* ---------------------------------------------------------------------------------- */
+/*void kio_sieveProtocol::slave_status()
+{
+    slaveStatus(isConnected() ? m_sServer : "", isConnected());
+
+    finished();
+}*/
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::special(const QByteArray &data)
+{
+    int tmp;
+    QDataStream stream(data);
+    QUrl url;
+
+    stream >> tmp;
+
+    switch (tmp) {
+    case 1:
+        stream >> url;
+        if (!activate(url)) {
+            return;
+        }
+        break;
+    case 2:
+        if (!deactivate()) {
+            return;
+        }
+        break;
+    case 3:
+        parseCapabilities(true);
+        break;
+    }
+
+    infoMessage(i18nc("special command completed", "Done."));
+
+    finished();
+}
+
+/* ---------------------------------------------------------------------------------- */
+bool kio_sieveProtocol::activate(const QUrl &url)
+{
+    changeCheck(url);
+    if (!connect()) {
+        return false;
+    }
+
+    infoMessage(i18n("Activating script..."));
+
+    QString filename = url.fileName();
+
+    if (filename.isEmpty()) {
+        error(ERR_DOES_NOT_EXIST, url.toDisplayString());
+        return false;
+    }
+
+    if (!sendData("SETACTIVE \"" + filename.toUtf8() + "\"")) {
+        return false;
+    }
+
+    if (operationSuccessful()) {
+        ksDebug << "Script activation complete." << endl;
+        return true;
+    } else {
+        error(ERR_INTERNAL_SERVER, i18n("There was an error activating the script."));
+        return false;
+    }
+}
+
+/* ---------------------------------------------------------------------------------- */
+bool kio_sieveProtocol::deactivate()
+{
+    if (!connect()) {
+        return false;
+    }
+
+    if (!sendData("SETACTIVE \"\"")) {
+        return false;
+    }
+
+    if (operationSuccessful()) {
+        ksDebug << "Script deactivation complete." << endl;
+        return true;
+    } else {
+        error(ERR_INTERNAL_SERVER, i18n("There was an error deactivating the script."));
+        return false;
+    }
+}
+
+static void append_lf2crlf(QByteArray &out, const QByteArray &in)
+{
+    if (in.isEmpty()) {
+        return;
+    }
+    const unsigned int oldOutSize = out.size();
+    out.resize(oldOutSize + 2 * in.size());
+    const char *s = in.begin();
+    const char *const end = in.end();
+    char *d = out.begin() + oldOutSize;
+    char last = '\0';
+    while (s < end) {
+        if (*s == '\n' && last != '\r') {
+            *d++ = '\r';
+        }
+        *d++ = last = *s++;
+    }
+    out.resize(d - out.begin());
+}
+
+void kio_sieveProtocol::put(const QUrl &url, int /*permissions*/, KIO::JobFlags)
+{
+    changeCheck(url);
+    if (!connect()) {
+        return;
+    }
+
+    infoMessage(i18n("Sending data..."));
+
+    QString filename = url.fileName();
+
+    if (filename.isEmpty()) {
+        error(ERR_MALFORMED_URL, url.toDisplayString());
+        return;
+    }
+
+    QByteArray data;
+    for (;;) {
+        dataReq();
+        QByteArray buffer;
+        const int newSize = readData(buffer);
+        append_lf2crlf(data, buffer);
+        if (newSize < 0) {
+            // read error: network in unknown state so disconnect
+            error(ERR_COULD_NOT_READ, i18n("KIO data supply error."));
+            return;
+        }
+        if (newSize == 0) {
+            break;
+        }
+    }
+
+    // script size
+    int bufLen = (int)data.size();
+    totalSize(bufLen);
+
+    // timsieved 1.1.0:
+    // C: HAVESPACE "rejected" 74
+    // S: NO "Number expected"
+    // C: HAVESPACE 74
+    // S: NO "Missing script name"
+    // S: HAVESPACE "rejected" "74"
+    // C: NO "Number expected"
+    // => broken, we can't use it :-(
+    // (will be fixed in Cyrus 2.1.10)
+
+    if (!sendData("PUTSCRIPT \"" + filename.toUtf8() + "\" {"
+                  + QByteArray::number(bufLen) + "+}")) {
+        return;
+    }
+
+    // atEnd() lies so the code below doesn't work.
+    /*if (!atEnd()) {
+        // We are not expecting any data here, so if the server has responded
+        // with anything but OK we treat it as an error.
+        char * buf = new char[2];
+        while (!atEnd()) {
+                ksDebug << "Reading..." << endl;
+                read(buf, 1);
+                ksDebug << "Trailing [" << buf[0] << "]" << endl;
+        }
+        ksDebug << "End of data." << endl;
+        delete[] buf;
+
+        if (!operationSuccessful()) {
+                error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred "
+                                        "while trying to negotiate script uploading.\n"
+                                        "The server responded:\n%1")
+                                                .arg(r.getAction().right(r.getAction().length() - 3)));
+                return;
+        }
+    }*/
+
+    // upload data to the server
+    if (write(data, bufLen) != bufLen) {
+        error(ERR_COULD_NOT_WRITE, i18n("Network error."));
+        disconnect(true);
+        return;
+    }
+
+    // finishing CR/LF
+    if (!sendData("")) {
+        return;
+    }
+
+    processedSize(bufLen);
+
+    infoMessage(i18n("Verifying upload completion..."));
+
+    if (operationSuccessful()) {
+        ksDebug << "Script upload complete." << endl;
+    } else {
+        /* The managesieve server parses received scripts and rejects
+         * scripts which are not syntactically correct. Here we expect
+         * to receive a message detailing the error (only the first
+         * error is reported. */
+        if (r.getAction().length() > 3) {
+            // make a copy of the extra info
+            QByteArray extra = r.getAction().right(r.getAction().length() - 3);
+
+            // send the extra message off for re-processing
+            receiveData(false, extra);
+
+            if (r.getType() == kio_sieveResponse::QUANTITY) {
+                // length of the error message
+                uint len = r.getQuantity();
+
+                QByteArray errmsg(len, 0);
+
+                read(errmsg.data(), len);
+
+                error(ERR_INTERNAL_SERVER, i18n("The script did not upload successfully.\n"
+                                                "This is probably due to errors in the script.\n"
+                                                "The server responded:\n%1", QString::fromLatin1(errmsg.data(), errmsg.size())));
+
+                // clear the rest of the incoming data
+                receiveData();
+            } else if (r.getType() == kio_sieveResponse::KEY_VAL_PAIR) {
+                error(ERR_INTERNAL_SERVER, i18n("The script did not upload successfully.\n"
+                                                "This is probably due to errors in the script.\n"
+                                                "The server responded:\n%1", QString::fromUtf8(r.getKey())));
+            } else {
+                error(ERR_INTERNAL_SERVER, i18n("The script did not upload successfully.\n"
+                                                "The script may contain errors."));
+            }
+        } else {
+            error(ERR_INTERNAL_SERVER, i18n("The script did not upload successfully.\n"
+                                            "The script may contain errors."));
+        }
+    }
+
+    //if ( permissions != -1 )
+    //        chmod( url, permissions );
+
+    infoMessage(i18nc("data upload complete", "Done."));
+
+    finished();
+}
+
+static void inplace_crlf2lf(QByteArray &in)
+{
+    if (in.isEmpty()) {
+        return;
+    }
+    QByteArray &out = in;  // inplace
+    const char *s = in.begin();
+    const char *const end = in.end();
+    char *d = out.begin();
+    char last = '\0';
+    while (s < end) {
+        if (*s == '\n' && last == '\r') {
+            --d;
+        }
+        *d++ = last = *s++;
+    }
+    out.resize(d - out.begin());
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::get(const QUrl &url)
+{
+    changeCheck(url);
+    if (!connect()) {
+        return;
+    }
+
+    infoMessage(i18n("Retrieving data..."));
+
+    QString filename = url.fileName();
+
+    if (filename.isEmpty()) {
+        error(ERR_MALFORMED_URL, url.toDisplayString());
+        return;
+    }
+
+    //SlaveBase::mimetype( QString("text/plain") ); // "application/sieve");
+
+    if (!sendData("GETSCRIPT \"" + filename.toUtf8() + "\"")) {
+        return;
+    }
+
+    if (receiveData() && r.getType() == kio_sieveResponse::QUANTITY) {
+        // determine script size
+        ssize_t total_len = r.getQuantity();
+        totalSize(total_len);
+
+        ssize_t recv_len = 0;
+        do {
+            // wait for data...
+            if (!waitForResponse(600)) {
+                error(KIO::ERR_SERVER_TIMEOUT, m_sServer);
+                disconnect(true);
+                return;
+            }
+
+            // ...read data...
+            // Only read as much as we need, otherwise we slurp in the OK that
+            // operationSuccessful() is expecting below.
+            QByteArray dat(qMin(total_len - recv_len, ssize_t(64 * 1024)), '\0');
+            ssize_t this_recv_len = read(dat.data(), dat.size());
+
+            if (this_recv_len < 1 && !isConnected()) {
+                error(KIO::ERR_CONNECTION_BROKEN, m_sServer);
+                disconnect(true);
+                return;
+            }
+
+            dat.resize(this_recv_len);
+            inplace_crlf2lf(dat);
+            // send data to slaveinterface
+            data(dat);
+
+            recv_len += this_recv_len;
+            processedSize(recv_len);
+        } while (recv_len < total_len);
+
+        infoMessage(i18n("Finishing up..."));
+        data(QByteArray());
+
+        if (operationSuccessful()) {
+            ksDebug << "Script retrieval complete." << endl;
+        } else {
+            ksDebug << "Script retrieval failed." << endl;
+        }
+    } else {
+        error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred "
+                                             "while trying to negotiate script downloading."));
+        return;
+    }
+
+    infoMessage(i18nc("data retrival complete", "Done."));
+    finished();
+}
+
+void kio_sieveProtocol::del(const QUrl &url, bool isfile)
+{
+    if (!isfile) {
+        error(ERR_INTERNAL, i18n("Folders are not supported."));
+        return;
+    }
+
+    changeCheck(url);
+    if (!connect()) {
+        return;
+    }
+
+    infoMessage(i18n("Deleting file..."));
+
+    QString filename = url.fileName();
+
+    if (filename.isEmpty()) {
+        error(ERR_MALFORMED_URL, url.toDisplayString());
+        return;
+    }
+
+    if (!sendData("DELETESCRIPT \"" + filename.toUtf8() + "\"")) {
+        return;
+    }
+
+    if (operationSuccessful()) {
+        ksDebug << "Script deletion successful." << endl;
+    } else {
+        error(ERR_INTERNAL_SERVER, i18n("The server would not delete the file."));
+        return;
+    }
+
+    infoMessage(i18nc("file removal complete", "Done."));
+
+    finished();
+}
+
+void kio_sieveProtocol::chmod(const QUrl &url, int permissions)
+{
+    switch (permissions) {
+    case 0700: // activate
+        activate(url);
+        break;
+    case 0600: // deactivate
+        deactivate();
+        break;
+    default: // unsupported
+        error(ERR_CANNOT_CHMOD, i18n("Cannot chmod to anything but 0700 (active) or 0600 (inactive script)."));
+        return;
+    }
+
+    finished();
+}
+
+void kio_sieveProtocol::urlStat(const QUrl &url)
+{
+    changeCheck(url);
+    if (!connect()) {
+        return;
+    }
+
+    UDSEntry entry;
+
+    QString filename = url.fileName();
+
+    if (filename.isEmpty()) {
+        entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral("/"));
+
+        entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
+
+        entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);
+
+        statEntry(entry);
+
+    } else {
+        if (!sendData("LISTSCRIPTS")) {
+            return;
+        }
+
+        while (receiveData()) {
+            if (r.getType() == kio_sieveResponse::ACTION) {
+                if (r.getAction().toLower().count("ok") == 1) {
+                    // Script list completed
+                    break;
+                }
+
+            } else {
+                if (filename == QString::fromUtf8(r.getKey())) {
+                    entry.clear();
+
+                    entry.insert(KIO::UDSEntry::UDS_NAME, QString::fromUtf8(r.getKey()));
+
+                    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+
+                    if (r.getExtra() == "ACTIVE") {
+                        entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);
+                    } else {
+                        entry.insert(KIO::UDSEntry::UDS_ACCESS, 0600);
+                    }
+
+                    entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/sieve"));
+
+                    //setMetaData("active", (r.getExtra() == "ACTIVE") ? "yes" : "no");
+
+                    statEntry(entry);
+                    // cannot break here because we need to clear
+                    // the rest of the incoming data.
+                }
+            }
+        }
+    }
+
+    finished();
+}
+
+void kio_sieveProtocol::listDir(const QUrl &url)
+{
+    changeCheck(url);
+    if (!connect()) {
+        return;
+    }
+
+    if (!sendData("LISTSCRIPTS")) {
+        return;
+    }
+
+    UDSEntry entry;
+
+    while (receiveData()) {
+        if (r.getType() == kio_sieveResponse::ACTION) {
+            if (r.getAction().toLower().count("ok") == 1) {
+                // Script list completed.
+                break;
+            }
+
+        } else {
+            entry.clear();
+            entry.insert(KIO::UDSEntry::UDS_NAME, QString::fromUtf8(r.getKey()));
+
+            entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+
+            if (r.getExtra() == "ACTIVE") {
+                entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);// mark exec'able
+            } else {
+                entry.insert(KIO::UDSEntry::UDS_ACCESS, 0600);
+            }
+
+            entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/sieve"));
+
+            //asetMetaData("active", (r.getExtra() == "ACTIVE") ? "true" : "false");
+
+            ksDebug << "Listing script " << r.getKey() << endl;
+            listEntry(entry);
+        }
+    }
+
+    finished();
+}
+
+/* ---------------------------------------------------------------------------------- */
+bool kio_sieveProtocol::saslInteract(void *in, AuthInfo &ai)
+{
+    ksDebug << "sasl_interact" << endl;
+    sasl_interact_t *interact = (sasl_interact_t *) in;
+
+    //some mechanisms do not require username && pass, so it doesn'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 (m_sUser.isEmpty() || m_sPass.isEmpty()) {
+                if (!openPasswordDialog(ai)) {
+                    // calling error() below is wrong for two reasons:
+                    // - ERR_ABORTED is too harsh
+                    // - higher layers already call error() and that can't happen twice.
+                    //error(ERR_ABORTED, i18n("No authentication details supplied."));
+                    return false;
+                }
+                m_sUser = ai.username;
+                m_sPass = ai.password;
+            }
+            break;
+        }
+    }
+
+    interact = (sasl_interact_t *) in;
+    while (interact->id != SASL_CB_LIST_END) {
+        ksDebug << "SASL_INTERACT id: " << interact->id << endl;
+        switch (interact->id) {
+        case SASL_CB_USER:
+        case SASL_CB_AUTHNAME:
+            ksDebug << "SASL_CB_[AUTHNAME|USER]: '" << m_sUser << "'" << endl;
+            interact->result = strdup(m_sUser.toUtf8());
+            interact->len = strlen((const char *) interact->result);
+            break;
+        case SASL_CB_PASS:
+            ksDebug << "SASL_CB_PASS: [hidden] " << endl;
+            interact->result = strdup(m_sPass.toUtf8());
+            interact->len = strlen((const char *) interact->result);
+            break;
+        default:
+            interact->result = NULL;
+            interact->len = 0;
+            break;
+        }
+        interact++;
+    }
+    return true;
+}
+
+#define SASLERROR  error(ERR_COULD_NOT_AUTHENTICATE, i18n("An error occurred during authentication: %1", \
+                         QString::fromUtf8( sasl_errdetail( conn ) )));
+
+bool kio_sieveProtocol::authenticate()
+{
+    int result;
+    sasl_conn_t *conn = NULL;
+    sasl_interact_t *client_interact = NULL;
+    const char *out = NULL;
+    uint outlen;
+    const char *mechusing = NULL;
+    QByteArray challenge;
+
+    /* Retrieve authentication details from user.
+     * Note: should this require realm as well as user & pass details
+     * before it automatically skips the prompt?
+     * Note2: encoding issues with PLAIN login? */
+    AuthInfo ai;
+    ai.url.setScheme(QStringLiteral("sieve"));
+    ai.url.setHost(m_sServer);
+    ai.url.setPort(m_port);
+    ai.username = m_sUser;
+    ai.password = m_sPass;
+    ai.keepPassword = true;
+    ai.caption = i18n("Sieve Authentication Details");
+    ai.comment = i18n("Please enter your authentication details for your sieve account "
+                      "(usually the same as your email password):");
+
+    result = sasl_client_new("sieve", m_sServer.toLatin1(), 0, 0, callbacks, 0, &conn);
+    if (result != SASL_OK) {
+        ksDebug << "sasl_client_new failed with: " << result << endl;
+        SASLERROR
+        return false;
+    }
+
+    QStringList strList;
+//    strList.append("NTLM");
+
+    if (!m_sAuth.isEmpty()) {
+        strList.append(m_sAuth);
+    } else {
+        strList = m_sasl_caps;
+    }
+
+    do {
+        result = sasl_client_start(conn, strList.join(QStringLiteral(" ")).toLatin1(),
+                                   &client_interact, &out, &outlen, &mechusing);
+
+        if (result == SASL_INTERACT) {
+            if (!saslInteract(client_interact, ai)) {
+                sasl_dispose(&conn);
+                return false;
+            };
+        }
+    } while (result == SASL_INTERACT);
+
+    if (result != SASL_CONTINUE && result != SASL_OK) {
+        ksDebug << "sasl_client_start failed with: " << result << endl;
+        SASLERROR
+        sasl_dispose(&conn);
+        return false;
+    }
+
+    ksDebug << "Preferred authentication method is " << mechusing << "." << endl;
+
+    QString firstCommand = QLatin1String("AUTHENTICATE \"") + QString::fromLatin1(mechusing) + QLatin1String("\"");
+    challenge = QByteArray::fromRawData(out, outlen).toBase64();
+    if (!challenge.isEmpty()) {
+        firstCommand += QLatin1String(" \"");
+        firstCommand += QString::fromLatin1(challenge.data(), challenge.size());
+        firstCommand += QLatin1Char('\"');
+    }
+
+    if (!sendData(firstCommand.toLatin1())) {
+        return false;
+    }
+
+    do {
+        receiveData();
+
+        if (operationResult() != OTHER) {
+            break;
+        }
+
+        ksDebug << "Challenge len  " << r.getQuantity() << endl;
+
+        if (r.getType() != kio_sieveResponse::QUANTITY) {
+            sasl_dispose(&conn);
+            error(ERR_UNSUPPORTED_PROTOCOL, QString::fromLatin1(mechusing));
+            return false;
+        }
+
+        int qty = r.getQuantity();
+
+        receiveData();
+
+        if (r.getType() != kio_sieveResponse::ACTION && r.getAction().length() != qty) {
+            sasl_dispose(&conn);
+            error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred during authentication.\n"
+                                                 "Choose a different authentication method to %1.", QLatin1String(mechusing)));
+            return false;
+        }
+        challenge = QByteArray::fromBase64(QByteArray::fromRawData(r.getAction().data(), qty));
+//        ksDebug << "S:  [" << r.getAction() << "]." << endl;
+
+        do {
+            result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
+                                      challenge.size(), &client_interact, &out, &outlen);
+
+            if (result == SASL_INTERACT) {
+                if (!saslInteract(client_interact, ai)) {
+                    sasl_dispose(&conn);
+                    return false;
+                };
+            }
+        } while (result == SASL_INTERACT);
+
+        ksDebug << "sasl_client_step: " << result << endl;
+        if (result != SASL_CONTINUE && result != SASL_OK) {
+            ksDebug << "sasl_client_step failed with: " << result << endl;
+            SASLERROR
+            sasl_dispose(&conn);
+            return false;
+        }
+
+        sendData('\"' + QByteArray::fromRawData(out, outlen).toBase64() + '\"');
+//    ksDebug << "C-1:  [" << out << "]." << endl;
+    } while (true);
+
+    ksDebug << "Challenges finished." << endl;
+    sasl_dispose(&conn);
+
+    if (operationResult() == OK) {
+        // Authentication succeeded.
+        return true;
+    } else {
+        // Authentication failed.
+        error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed.\nMost likely the password is wrong.\nThe server responded:\n%1",
+                                               QString::fromLatin1(r.getAction())));
+        return false;
+    }
+}
+
+/* --------------------------------------------------------------------------- */
+void kio_sieveProtocol::mimetype(const QUrl &url)
+{
+    ksDebug << "Requesting mimetype for " << url.toDisplayString() << endl;
+
+    if (url.fileName().isEmpty()) {
+        mimeType(QStringLiteral("inode/directory"));
+    } else {
+        mimeType(QStringLiteral("application/sieve"));
+    }
+
+    finished();
+}
+
+/* --------------------------------------------------------------------------- */
+bool kio_sieveProtocol::sendData(const QByteArray &data)
+{
+    QByteArray write_buf = data + "\r\n";
+
+    //ksDebug << "C: " << data << endl;
+
+    // Write the command
+    ssize_t write_buf_len = write_buf.length();
+    if (write(write_buf.data(), write_buf_len) != write_buf_len) {
+        error(ERR_COULD_NOT_WRITE, i18n("Network error."));
+        disconnect(true);
+        return false;
+    }
+
+    return true;
+}
+
+/* --------------------------------------------------------------------------- */
+bool kio_sieveProtocol::receiveData(bool waitForData, const QByteArray &reparse)
+{
+    QByteArray interpret;
+    int start, end;
+
+    if (reparse.isEmpty()) {
+        if (!waitForData) {
+            // is there data waiting?
+            if (atEnd()) {
+                return false;
+            }
+        }
+
+        // read data from the server
+        char buffer[SIEVE_DEFAULT_RECIEVE_BUFFER];
+        const ssize_t numRead = readLine(buffer, SIEVE_DEFAULT_RECIEVE_BUFFER - 1);
+        if (numRead < 0) {
+            return false;
+        }
+        buffer[SIEVE_DEFAULT_RECIEVE_BUFFER - 1] = '\0';
+
+        // strip LF/CR
+        interpret = QByteArray(buffer, qstrlen(buffer) - 2);
+
+    } else {
+        interpret = reparse;
+    }
+
+    r.clear();
+
+    //ksDebug << "S: " << interpret << endl;
+
+    switch (interpret[0]) {
+    case '{': {
+        // expecting {quantity}
+        start = 0;
+        end = interpret.indexOf("+}", start + 1);
+        // some older versions of Cyrus enclose the literal size just in { } instead of { +}
+        if (end == -1) {
+            end = interpret.indexOf('}', start + 1);
+        }
+
+        bool ok = false;
+        r.setQuantity(interpret.mid(start + 1, end - start - 1).toUInt(&ok));
+        if (!ok) {
+            disconnect();
+            error(ERR_INTERNAL_SERVER, i18n("A protocol error occurred."));
+            return false;
+        }
+
+        return true;
+    }
+    case '"':
+        // expecting "key" "value" pairs
+        break;
+    default:
+        // expecting single string
+        r.setAction(interpret);
+        return true;
+    }
+
+    start = 0;
+
+    end = interpret.indexOf('"', start + 1);
+    if (end == -1) {
+        ksDebug << "Possible insufficient buffer size." << endl;
+        r.setKey(interpret.right(interpret.length() - start));
+        return true;
+    }
+
+    r.setKey(interpret.mid(start + 1, end - start - 1));
+
+    start = interpret.indexOf('"', end + 1);
+    if (start == -1) {
+        if ((int)interpret.length() > end) {
+            // skip " and space
+            r.setExtra(interpret.right(interpret.length() - end - 2));
+        }
+
+        return true;
+    }
+
+    end = interpret.indexOf('"', start + 1);
+    if (end == -1) {
+        ksDebug << "Possible insufficient buffer size." << endl;
+        r.setVal(interpret.right(interpret.length() - start));
+        return true;
+    }
+
+    r.setVal(interpret.mid(start + 1, end - start - 1));
+    return true;
+}
+
+bool kio_sieveProtocol::operationSuccessful()
+{
+    while (receiveData(true)) {
+        if (r.getType() == kio_sieveResponse::ACTION) {
+            QByteArray response = r.getAction().left(2);
+            if (response == "OK") {
+                return true;
+            } else if (response == "NO") {
+                return false;
+            }
+        }
+    }
+    return false;
+}
+
+int kio_sieveProtocol::operationResult()
+{
+    if (r.getType() == kio_sieveResponse::ACTION) {
+        QByteArray response = r.getAction().left(2);
+        if (response == "OK") {
+            return OK;
+        } else if (response == "NO") {
+            return NO;
+        } else if (response == "BY"/*E*/) {
+            return BYE;
+        }
+    }
+
+    return OTHER;
+}
+
+bool kio_sieveProtocol::requestCapabilitiesAfterStartTLS() const
+{
+    // Cyrus didn't send CAPABILITIES after STARTTLS until 2.3.11, which is
+    // not standard conform, but we need to support that anyway.
+    // m_implementation looks like this 'Cyrus timsieved v2.2.12' for Cyrus btw.
+    QRegExp regExp(QStringLiteral("Cyrus\\stimsieved\\sv(\\d+)\\.(\\d+)\\.(\\d+)([-\\w]*)"), Qt::CaseInsensitive);
+    if (regExp.indexIn(m_implementation) >= 0) {
+        const int major = regExp.cap(1).toInt();
+        const int minor = regExp.cap(2).toInt();
+        const int patch = regExp.cap(3).toInt();
+        const QString vendor = regExp.cap(4);
+        if (major < 2 || (major == 2 && (minor < 3 || (minor == 3 && patch < 11))) || (vendor == QLatin1String("-kolab-nocaps"))) {
+            ksDebug << " kio_sieveProtocol::requestCapabilitiesAfterStartTLS : Enabling compat mode for Cyrus < 2.3.11 or Cyrus marked as \"kolab-nocaps\"" << endl;
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/kioslave/src/sieve/sieve.h b/kioslave/src/sieve/sieve.h
new file mode 100644
index 0000000..6ed5d65
--- /dev/null
+++ b/kioslave/src/sieve/sieve.h
@@ -0,0 +1,145 @@
+/***************************************************************************
+                          sieve.h  -  description
+                             -------------------
+    begin                : Thu Dec 20 18:47:08 EST 2001
+    copyright            : (C) 2001 by Hamish Rodda
+    email                : meddie at yoyo.cc.monash.edu.au
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                            *
+ *                                                                         *
+ ***************************************************************************/
+#ifndef __sieve_h__
+#define __sieve_h__
+
+#include "sieve-config.h"
+
+#include <kio/tcpslavebase.h>
+#include <kio/authinfo.h>
+
+#include <QString>
+#include <QByteArray>
+#include <QStringList>
+
+class kio_sieveResponse
+{
+public:
+    enum responses {
+        NONE,
+        KEY_VAL_PAIR,
+        ACTION,
+        QUANTITY
+    };
+
+    kio_sieveResponse();
+
+    const uint &getType() const;
+
+    const QByteArray &getAction() const;
+    uint getQuantity() const;
+    const QByteArray &getKey() const;
+    const QByteArray &getVal() const;
+    const QByteArray &getExtra() const;
+
+    void setQuantity(const uint &quantity);
+    void setAction(const QByteArray &newAction);
+    void setKey(const QByteArray &newKey);
+    void setVal(const QByteArray &newVal);
+    void setExtra(const QByteArray &newExtra);
+
+    void clear();
+
+protected:
+    uint       rType;
+    uint       quantity;
+    QByteArray key;
+    QByteArray val;
+    QByteArray extra;
+};
+
+class kio_sieveProtocol : public KIO::TCPSlaveBase
+{
+
+public:
+    enum connectionModes {
+        NORMAL,
+        CONNECTION_ORIENTED
+    };
+    enum Results {
+        OK,
+        NO,
+        BYE,
+        OTHER
+    };
+
+    kio_sieveProtocol(const QByteArray &pool_socket, const QByteArray &app_socket);
+    virtual ~kio_sieveProtocol();
+
+    void mimetype(const QUrl &url) Q_DECL_OVERRIDE;
+    void get(const QUrl &url) Q_DECL_OVERRIDE;
+    void put(const QUrl &url, int permissions, KIO::JobFlags flags) Q_DECL_OVERRIDE;
+    void del(const QUrl &url, bool isfile) Q_DECL_OVERRIDE;
+
+    void listDir(const QUrl &url) Q_DECL_OVERRIDE;
+    void chmod(const QUrl &url, int permissions) Q_DECL_OVERRIDE;
+    virtual void urlStat(const QUrl &url);
+
+    void setHost(const QString &host, quint16 port, const QString &user, const QString &pass) Q_DECL_OVERRIDE;
+    void openConnection() Q_DECL_OVERRIDE;
+    void closeConnection() Q_DECL_OVERRIDE;
+    //virtual void slave_status();
+
+    /**
+     * Special commands supported by this slave:
+     * 1 - activate script
+     * 2 - deactivate (all - only one active at any one time) scripts
+     * 3 - request capabilities, returned as metadata
+     */
+    void special(const QByteArray &data) Q_DECL_OVERRIDE;
+    bool activate(const QUrl &url);
+    bool deactivate();
+
+protected:
+    bool connect(bool useTLSIfAvailable = true);
+    bool authenticate();
+    void disconnect(bool forcibly = false);
+    void changeCheck(const QUrl &url);
+
+    bool sendData(const QByteArray &data);
+    bool receiveData(bool waitForData = true, const QByteArray &reparse = QByteArray());
+    bool operationSuccessful();
+    int operationResult();
+
+    bool parseCapabilities(bool requestCapabilities = false);
+    bool saslInteract(void *in, KIO::AuthInfo &ai);
+
+    // IOSlave global data
+    uint              m_connMode;
+
+    // Host-specific data
+    QStringList       m_sasl_caps;
+    bool              m_supportsTLS;
+
+    // Global server respose class
+    kio_sieveResponse r;
+
+    // connection details
+    QString           m_sServer;
+    QString           m_sUser;
+    QString           m_sPass;
+    QString           m_sAuth;
+    bool              m_shouldBeConnected;
+    bool              m_allowUnencrypted;
+    quint16           m_port;
+
+private:
+    bool requestCapabilitiesAfterStartTLS() const;
+
+    QString m_implementation;
+};
+
+#endif
diff --git a/kioslave/src/sieve/sieve.protocol b/kioslave/src/sieve/sieve.protocol
new file mode 100644
index 0000000..0bc4a7e
--- /dev/null
+++ b/kioslave/src/sieve/sieve.protocol
@@ -0,0 +1,65 @@
+[Protocol]
+exec=kf5/kio/sieve
+protocol=sieve
+input=none
+output=filesystem
+listing=Name,Access,Type,MimeType,
+reading=true
+writing=true
+makedir=false
+deleting=true
+linking=false
+moving=false
+Icon=view-filter
+X-DocPath=kioslave5/sieve/index.html
+Description=An ioslave for the Sieve mail filtering protocol
+Description[ast]=Un esclavu ES pal protocolu de peñera de corréu Sieve
+Description[bs]=U/I program za Sieve protokol filtriranja e-pošte
+Description[ca]=Un «ioslave» pel protocol de filtratge de correu «Sieve»
+Description[ca at valencia]=Un «ioslave» pel protocol de filtratge de correu «Sieve»
+Description[cs]=Protokol pro filtrování pošty Sieve
+Description[da]=En ioslave til mail-filtreringsprotokollen Sieve
+Description[de]=Ein Ein-/Ausgabemodul für das Mailfilter-Protokoll Sieve
+Description[el]=Ένα ioslave για το πρωτόκολλο φιλτραρίσματος αλληλογραφίας Sieve
+Description[en_GB]=An ioslave for the Sieve mail filtering protocol
+Description[es]=Un esclavo de E/S para el protocolo de filtrado de correo Sieve
+Description[et]=Sieve kirjade filtreerimise protokolli IO-moodul
+Description[fi]=I/O-asiakas Sieve-postinsuodatusyhteyskäytännölle
+Description[fr]=Un module d'entrées / sorties pour le protocole de filtrage de messagerie « Sieve »
+Description[ga]=Sclábhaí I/A le haghaidh prótacail scagtha ríomhphoist Sieve
+Description[gl]=Un ioslave para o protocolo de filtrado de correo Sieve.
+Description[he]=תוסף ioslave עבור פרוטוקול מסנני דואר של Sieve
+Description[hne]=सीव मेल फिल्टरिंग प्रोटोकाल बर आईओस्लेव
+Description[hu]=KDE-protokoll a Sieve levélszűrő protokollhoz
+Description[ia]=Un ioslave (sclavo de i/e) per le protocollo per filtrar posta Sieve
+Description[it]=Un IOSlave per il protocollo di filtraggio della posta Sieve
+Description[ja]=Sieve メールフィルタリングプロトコルのための ioslave
+Description[kk]=Sieve пошта сүзгілеу протоколының ioslav-модулі
+Description[km]=ioslave សម្រាប់​ពិធីការ​តម្រង​សំបុត្រ​របស់ Sieve
+Description[ko]=Sieve 메일 필터링 프로토콜을 위한 IO 슬레이브
+Description[lt]=Sieve pašto filtravimo protokolo palaikymas
+Description[lv]=IO apstrādātājs Sieve pasta filtrēšanas protokolam
+Description[mr]=सीव्ह मेल गाळणी शिष्टाचार करिता आयओस्लेव्ह
+Description[nb]=En ioslave for Sieve e-postfiltrering
+Description[nds]=In-/Utgaavmoduul för't Nettpostfilter-Protokoll Sieve
+Description[nl]=Een KIO-slave voor het Sieve-mail filterprotocol
+Description[nn]=Ein IO-slave for e-postfiltreringsprotokollen Sieve
+Description[pl]=Moduł ioslave dla protokołu filtrowania poczty Sieve
+Description[pt]=Um 'ioslave' para o protocolo de filtragem de correio Sieve
+Description[pt_BR]=Um ioslave para o protocolo de filtragem de e-mail Sieve
+Description[ro]=Un „ioslave” pentru protocolul de filtrare a poștei Sieve
+Description[ru]=Поддержка протокола фильтрации почты Sieve
+Description[se]=SO-šláva Sieve e-boastasillenprotokolla várás
+Description[sk]=Ioslave pre Sieve mail filtrovací protokol
+Description[sl]=Ioslave za protokol poštnega filtriranja Sieve
+Description[sr]=У/И захват за сито, протокол филтрирања поште
+Description[sr at ijekavian]=У/И захват за сито, протокол филтрирања поште
+Description[sr at ijekavianlatin]=U/I zahvat za sito, protokol filtriranja pošte
+Description[sr at latin]=U/I zahvat za sito, protokol filtriranja pošte
+Description[sv]=En I/O-slav för brevfiltreringsprotokollet Sieve
+Description[th]=ส่วนเสริม ioslave สำหรับโพรโทคอลการกรองจดหมาย Sieve
+Description[tr]=Sieve e-posta filtrelemesi için bir kioslave
+Description[uk]=Підлеглий В/В для протоколу фільтрування пошти Sieve
+Description[x-test]=xxAn ioslave for the Sieve mail filtering protocolxx
+Description[zh_CN]=Sieve 邮件过滤协议的 ioslave
+Description[zh_TW]=Sieve 郵件過濾協定的 ioslave
diff --git a/libksieve.categories b/libksieve.categories
index e0874a2..8b3030b 100644
--- a/libksieve.categories
+++ b/libksieve.categories
@@ -1,2 +1,3 @@
 log_libksieve kdepim (libksieve)
 log_kmanagersieve kdepim (libksieve manager)
+log_sieve kioslave (sieve)


More information about the kde-doc-english mailing list