summaryrefslogtreecommitdiffstats
path: root/kioslaves/sieve
diff options
context:
space:
mode:
Diffstat (limited to 'kioslaves/sieve')
-rw-r--r--kioslaves/sieve/Makefile.am15
-rw-r--r--kioslaves/sieve/configure.in.in18
-rw-r--r--kioslaves/sieve/draft-daboo-sieve-include.txt1
-rw-r--r--kioslaves/sieve/draft-daboo-sieve-spamtest.txt1
-rw-r--r--kioslaves/sieve/draft-degener-sieve-body-00.txt1
-rw-r--r--kioslaves/sieve/draft-degener-sieve-copy.txt1
-rw-r--r--kioslaves/sieve/draft-degener-sieve-editheader.txt1
-rw-r--r--kioslaves/sieve/draft-degener-sieve-multiscript.txt1
-rw-r--r--kioslaves/sieve/draft-homme-sieve-variables.txt1
-rw-r--r--kioslaves/sieve/draft-martin-managesieve-04.txt1
-rw-r--r--kioslaves/sieve/draft-martin-sieve-notify-01.txt1
-rw-r--r--kioslaves/sieve/draft-melnikov-sieve-imapflags.txt1
-rw-r--r--kioslaves/sieve/draft-murchison-sieve-regex-06.txt1
-rw-r--r--kioslaves/sieve/draft-murchison-sieve-subaddress-05.txt1
-rw-r--r--kioslaves/sieve/draft-showalter-sieve-vacation-04.txt1
-rw-r--r--kioslaves/sieve/rfc3028.txt1
-rw-r--r--kioslaves/sieve/rfc3431.txt1
-rw-r--r--kioslaves/sieve/sieve.cpp1299
-rw-r--r--kioslaves/sieve/sieve.h132
-rw-r--r--kioslaves/sieve/sieve.protocol54
20 files changed, 1533 insertions, 0 deletions
diff --git a/kioslaves/sieve/Makefile.am b/kioslaves/sieve/Makefile.am
new file mode 100644
index 000000000..830c8258e
--- /dev/null
+++ b/kioslaves/sieve/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= -I$(srcdir)/../.. -I$(srcdir)/.. $(all_includes)
+
+kde_module_LTLIBRARIES = kio_sieve.la
+
+kio_sieve_la_SOURCES = sieve.cpp
+kio_sieve_la_LIBADD = $(LIB_KIO) $(SASL2_LIBS)
+kio_sieve_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = sieve.h
+
+kdelnk_DATA = sieve.protocol
+kdelnkdir = $(kde_servicesdir)
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_sieve.pot
diff --git a/kioslaves/sieve/configure.in.in b/kioslaves/sieve/configure.in.in
new file mode 100644
index 000000000..a722cfebb
--- /dev/null
+++ b/kioslaves/sieve/configure.in.in
@@ -0,0 +1,18 @@
+AC_MSG_CHECKING(if we need to talk to broken timsieved's)
+AC_ARG_ENABLE(broken-timsieved-workaround,
+[AC_HELP_STRING([--enable-broken-timsieved-workaround],
+ [versions <= 1.1.0 of the timsieved (part of the Cyrus
+ IMAP suite) do not interpret the HAVESPACE command of
+ the sieve protocol. This option makes the sieve
+ KIO::Slave simply omit that command.])],
+[case "${enableval}" in
+ yes) AC_MSG_RESULT(yes)
+ need_broken_timsieved_support="yes" ;;
+ no) AC_MSG_RESULT(no) ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-broken-timsieved-workaround]) ;;
+esac],[AC_MSG_RESULT(no)])
+
+if test "$need_broken_timsieved_support" = "yes"; then
+ AC_SUBST(HAVE_BROKEN_TIMSIEVED)
+ AC_DEFINE(HAVE_BROKEN_TIMSIEVED,1,[Define if the sieve KIO::Slave must be able to talk to timsieved <= 1.1.0])
+fi
diff --git a/kioslaves/sieve/draft-daboo-sieve-include.txt b/kioslaves/sieve/draft-daboo-sieve-include.txt
new file mode 100644
index 000000000..83a08dfb8
--- /dev/null
+++ b/kioslaves/sieve/draft-daboo-sieve-include.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-daboo-sieve-include.txt
diff --git a/kioslaves/sieve/draft-daboo-sieve-spamtest.txt b/kioslaves/sieve/draft-daboo-sieve-spamtest.txt
new file mode 100644
index 000000000..8f22e5607
--- /dev/null
+++ b/kioslaves/sieve/draft-daboo-sieve-spamtest.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-daboo-sieve-spamtest.txt
diff --git a/kioslaves/sieve/draft-degener-sieve-body-00.txt b/kioslaves/sieve/draft-degener-sieve-body-00.txt
new file mode 100644
index 000000000..7b1ee844e
--- /dev/null
+++ b/kioslaves/sieve/draft-degener-sieve-body-00.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-degener-sieve-body-00.txt
diff --git a/kioslaves/sieve/draft-degener-sieve-copy.txt b/kioslaves/sieve/draft-degener-sieve-copy.txt
new file mode 100644
index 000000000..8c23855c8
--- /dev/null
+++ b/kioslaves/sieve/draft-degener-sieve-copy.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-degener-sieve-copy.txt
diff --git a/kioslaves/sieve/draft-degener-sieve-editheader.txt b/kioslaves/sieve/draft-degener-sieve-editheader.txt
new file mode 100644
index 000000000..6e9674f9d
--- /dev/null
+++ b/kioslaves/sieve/draft-degener-sieve-editheader.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-degener-sieve-editheader.txt
diff --git a/kioslaves/sieve/draft-degener-sieve-multiscript.txt b/kioslaves/sieve/draft-degener-sieve-multiscript.txt
new file mode 100644
index 000000000..53a838226
--- /dev/null
+++ b/kioslaves/sieve/draft-degener-sieve-multiscript.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-degener-sieve-multiscript.txt
diff --git a/kioslaves/sieve/draft-homme-sieve-variables.txt b/kioslaves/sieve/draft-homme-sieve-variables.txt
new file mode 100644
index 000000000..cccaa59c8
--- /dev/null
+++ b/kioslaves/sieve/draft-homme-sieve-variables.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-homme-sieve-variables.txt
diff --git a/kioslaves/sieve/draft-martin-managesieve-04.txt b/kioslaves/sieve/draft-martin-managesieve-04.txt
new file mode 100644
index 000000000..82684ad57
--- /dev/null
+++ b/kioslaves/sieve/draft-martin-managesieve-04.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-martin-managesieve-04.txt
diff --git a/kioslaves/sieve/draft-martin-sieve-notify-01.txt b/kioslaves/sieve/draft-martin-sieve-notify-01.txt
new file mode 100644
index 000000000..6eb1cac33
--- /dev/null
+++ b/kioslaves/sieve/draft-martin-sieve-notify-01.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-martin-sieve-notify-01.txt
diff --git a/kioslaves/sieve/draft-melnikov-sieve-imapflags.txt b/kioslaves/sieve/draft-melnikov-sieve-imapflags.txt
new file mode 100644
index 000000000..71cfbb3ce
--- /dev/null
+++ b/kioslaves/sieve/draft-melnikov-sieve-imapflags.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-melnikov-sieve-imapflags.txt
diff --git a/kioslaves/sieve/draft-murchison-sieve-regex-06.txt b/kioslaves/sieve/draft-murchison-sieve-regex-06.txt
new file mode 100644
index 000000000..b3e922ee8
--- /dev/null
+++ b/kioslaves/sieve/draft-murchison-sieve-regex-06.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-murchison-sieve-regex-06.txt
diff --git a/kioslaves/sieve/draft-murchison-sieve-subaddress-05.txt b/kioslaves/sieve/draft-murchison-sieve-subaddress-05.txt
new file mode 100644
index 000000000..95cea85a7
--- /dev/null
+++ b/kioslaves/sieve/draft-murchison-sieve-subaddress-05.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-murchison-sieve-subaddress-05.txt
diff --git a/kioslaves/sieve/draft-showalter-sieve-vacation-04.txt b/kioslaves/sieve/draft-showalter-sieve-vacation-04.txt
new file mode 100644
index 000000000..bd2d33593
--- /dev/null
+++ b/kioslaves/sieve/draft-showalter-sieve-vacation-04.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/draft-showalter-sieve-vacation-04.txt
diff --git a/kioslaves/sieve/rfc3028.txt b/kioslaves/sieve/rfc3028.txt
new file mode 100644
index 000000000..b861e2e14
--- /dev/null
+++ b/kioslaves/sieve/rfc3028.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/rfc3028.txt
diff --git a/kioslaves/sieve/rfc3431.txt b/kioslaves/sieve/rfc3431.txt
new file mode 100644
index 000000000..3f11c3259
--- /dev/null
+++ b/kioslaves/sieve/rfc3431.txt
@@ -0,0 +1 @@
+http://ktown.kde.org/~dirk/sieve/rfc3431.txt
diff --git a/kioslaves/sieve/sieve.cpp b/kioslaves/sieve/sieve.cpp
new file mode 100644
index 000000000..012d77d15
--- /dev/null
+++ b/kioslaves/sieve/sieve.cpp
@@ -0,0 +1,1299 @@
+/***************************************************************************
+ sieve.cpp - description
+ -------------------
+ begin : Thu Dec 20 18:47:08 EST 2001
+ copyright : (C) 2001 by Hamish Rodda
+ email : meddie@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@pacbell.net>
+ * Copyright (c) 2001 Michael Häckel <Michael@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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+extern "C" {
+#include <sasl/sasl.h>
+}
+#include "sieve.h"
+
+#include <kdebug.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kmdcodec.h>
+#include <kglobal.h>
+
+#include <qcstring.h>
+#include <qregexp.h>
+
+#include <cstdlib>
+using std::exit;
+#include <sys/stat.h>
+
+#include <kdepimmacros.h>
+
+static const int debugArea = 7122;
+
+static inline
+#ifdef NDEBUG
+ kndbgstream ksDebug() { return kdDebug( debugArea ); }
+ kndbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); }
+#else
+ kdbgstream ksDebug() { return kdDebug( debugArea ); }
+ kdbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); }
+#endif
+
+#define SIEVE_DEFAULT_PORT 2000
+
+static 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"
+{
+ KDE_EXPORT int kdemain(int argc, char **argv)
+ {
+ KInstance instance("kio_sieve" );
+
+ ksDebug() << "*** Starting kio_sieve " << endl;
+
+ if (argc != 4) {
+ ksDebug() << "Usage: kio_sieve protocol domain-socket1 domain-socket2" << endl;
+ exit(-1);
+ }
+
+ if ( sasl_client_init( NULL ) != SASL_OK ) {
+ fprintf(stderr, "SASL library initialization failed!\n");
+ ::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;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const uint kio_sieveResponse::getQuantity() const
+{
+ return quantity;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QCString& kio_sieveResponse::getAction() const
+{
+ return key;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QCString& kio_sieveResponse::getKey() const
+{
+ return key;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QCString& kio_sieveResponse::getVal() const
+{
+ return val;
+}
+
+/* ---------------------------------------------------------------------------------- */
+const QCString& kio_sieveResponse::getExtra() const
+{
+ return extra;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setQuantity(const uint& newQty)
+{
+ rType = QUANTITY;
+ quantity = newQty;
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setAction(const QCString& newAction)
+{
+ rType = ACTION;
+ key = newAction.copy();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setKey(const QCString& newKey)
+{
+ rType = KEY_VAL_PAIR;
+ key = newKey.copy();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setVal(const QCString& newVal)
+{
+ val = newVal.copy();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::setExtra(const QCString& newExtra)
+{
+ extra = newExtra.copy();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveResponse::clear()
+{
+ rType = NONE;
+ extra = key = val = QCString("");
+ quantity = 0;
+}
+
+/* ---------------------------------------------------------------------------------- */
+kio_sieveProtocol::kio_sieveProtocol(const QCString &pool_socket, const QCString &app_socket)
+ : TCPSlaveBase( SIEVE_DEFAULT_PORT, "sieve", pool_socket, app_socket, false)
+ , m_connMode(NORMAL)
+ , m_supportsTLS(false)
+ , m_shouldBeConnected(false)
+{
+}
+
+/* ---------------------------------------------------------------------------------- */
+kio_sieveProtocol::~kio_sieveProtocol()
+{
+ if ( isConnectionValid() )
+ disconnect();
+}
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::setHost (const QString &host, int port, const QString &user, const QString &pass)
+{
+ if ( isConnectionValid() &&
+ ( m_sServer != host ||
+ m_iPort != port ||
+ m_sUser != user ||
+ m_sPass != pass ) ) {
+ disconnect();
+ }
+ m_sServer = host;
+ m_iPort = port ? port : m_iDefaultPort;
+ 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() << k_funcinfo << endl;
+
+ // Setup...
+ bool ret = false;
+
+ if (requestCapabilities) {
+ sendData("CAPABILITY");
+ }
+
+ while (receiveData()) {
+ ksDebug() << "Looping receive" << endl;
+
+ if (r.getType() == kio_sieveResponse::ACTION) {
+ if ( r.getAction().contains("ok", false) != -1 ) {
+ ksDebug() << "Sieve server ready & awaiting authentication." << endl;
+ break;
+ } else
+ ksDebug() << "Unknown action " << r.getAction() << "." << endl;
+
+ } else if (r.getKey() == "IMPLEMENTATION") {
+ if (r.getVal().contains("sieve", false) != -1) {
+ ksDebug() << "Connected to Sieve server: " << r.getVal() << endl;
+ ret = true;
+ setMetaData("implementation", r.getVal());
+ m_implementation = r.getVal();
+ }
+
+ } else if (r.getKey() == "SASL") {
+ // Save list of available SASL methods
+ m_sasl_caps = QStringList::split(' ', r.getVal());
+ ksDebug() << "Server SASL authentication methods: " << m_sasl_caps.join(", ") << endl;
+ setMetaData("saslMethods", r.getVal());
+
+ } else if (r.getKey() == "SIEVE") {
+ // Save script capabilities; report back as meta data:
+ ksDebug() << "Server script capabilities: " << QStringList::split(' ', r.getVal()).join(", ") << endl;
+ setMetaData("sieveExtensions", r.getVal());
+
+ } else if (r.getKey() == "STARTTLS") {
+ // The server supports TLS
+ ksDebug() << "Server supports TLS" << endl;
+ m_supportsTLS = true;
+ setMetaData("tlsSupported", "true");
+
+ } else {
+ ksDebug() << "Unrecognised key " << r.getKey() << endl;
+ }
+ }
+
+ if (!m_supportsTLS) {
+ setMetaData("tlsSupported", "false");
+ }
+
+ return ret;
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+/**
+ * Checks if connection parameters (currently - auth method) have changed.
+ * If it it, close the current connection
+ */
+void kio_sieveProtocol::changeCheck( const KURL &url )
+{
+ QString auth;
+
+ if (!metaData("sasl").isEmpty())
+ auth = metaData("sasl").upper();
+ else {
+ QString query = url.query();
+ if ( query.startsWith("?") ) query.remove( 0, 1 );
+ QStringList q = QStringList::split( ",", query );
+ QStringList::iterator it;
+
+ for ( it = q.begin(); it != q.end(); ++it ) {
+ if ( ( (*it).section('=',0,0) ).lower() == "x-mech" ) {
+ auth = ( (*it).section('=',1) ).upper();
+ break;
+ }
+ }
+ }
+ ksDebug() << "auth: " << auth << " m_sAuth: " << m_sAuth << endl;
+ if ( m_sAuth != auth ) {
+ m_sAuth = auth;
+ if ( isConnectionValid() )
+ disconnect();
+ }
+}
+
+/* ---------------------------------------------------------------------------------- */
+/**
+ * Connects to the server.
+ * returns false and calls error() if an error occurred.
+ */
+bool kio_sieveProtocol::connect(bool useTLSIfAvailable)
+{
+ ksDebug() << k_funcinfo << endl;
+
+ if (isConnectionValid()) return true;
+
+ infoMessage(i18n("Connecting to %1...").arg( m_sServer));
+
+ if (m_connMode == CONNECTION_ORIENTED && m_shouldBeConnected) {
+ error(ERR_CONNECTION_BROKEN, i18n("The connection to the server was lost."));
+ return false;
+ }
+
+ setBlockConnection(true);
+
+ if (!connectToHost(m_sServer, m_iPort, true)) {
+ return false;
+ }
+
+ if (!parseCapabilities()) {
+ closeDescriptor();
+ error(ERR_UNSUPPORTED_PROTOCOL, i18n("Server identification failed."));
+ return false;
+ }
+
+ // Attempt to start TLS
+ // FIXME find a test server and test that this works
+ if (useTLSIfAvailable && m_supportsTLS && canUseTLS()) {
+ sendData("STARTTLS");
+ if (operationSuccessful()) {
+ ksDebug() << "TLS has been accepted. Starting TLS..." << endl
+ << "WARNING this is untested and may fail." << endl;
+ int retval = startTLS();
+ if (retval == 1) {
+ ksDebug() << "TLS enabled successfully." << endl;
+ // reparse capabilities:
+ parseCapabilities( requestCapabilitiesAfterStartTLS() );
+ } else {
+ ksDebug() << "TLS initiation failed, code " << retval << endl;
+ disconnect(true);
+ return connect(false);
+ // error(ERR_INTERNAL, i18n("TLS initiation failed."));
+ }
+ } 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;
+
+ 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");
+
+ // This crashes under certain conditions as described in
+ // http://intevation.de/roundup/kolab/issue2442
+ // Fixing KIO::TCPSlaveBase::atEnd() for !fd would also work but 3.x is on life support.
+ //if (!operationSuccessful())
+ // ksDebug() << "Server did not logout cleanly." << endl;
+ }
+
+ closeDescriptor();
+ m_shouldBeConnected = false;
+}
+
+/* ---------------------------------------------------------------------------------- */
+/*void kio_sieveProtocol::slave_status()
+{
+ slaveStatus(isConnectionValid() ? m_sServer : "", isConnectionValid());
+
+ finished();
+}*/
+
+/* ---------------------------------------------------------------------------------- */
+void kio_sieveProtocol::special(const QByteArray &data)
+{
+ int tmp;
+ QDataStream stream(data, IO_ReadOnly);
+ KURL 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(i18n("Done."));
+
+ finished();
+}
+
+/* ---------------------------------------------------------------------------------- */
+bool kio_sieveProtocol::activate(const KURL& url)
+{
+ changeCheck( url );
+ if (!connect())
+ return false;
+
+ infoMessage(i18n("Activating script..."));
+
+ QString filename = url.fileName(false);
+
+ if (filename.isEmpty()) {
+ error(ERR_DOES_NOT_EXIST, url.prettyURL());
+ return false;
+ }
+
+ if (!sendData("SETACTIVE \"" + filename.utf8() + "\""))
+ 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 KURL& url, int /*permissions*/, bool /*overwrite*/, bool /*resume*/)
+{
+ changeCheck( url );
+ if (!connect())
+ return;
+
+ infoMessage(i18n("Sending data..."));
+
+ QString filename = url.fileName(false);
+
+ if (filename.isEmpty()) {
+ error(ERR_MALFORMED_URL, url.prettyURL());
+ 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)
+#ifndef HAVE_BROKEN_TIMSIEVED
+ // first, check quota (it's a SHOULD in draft std)
+ if (!sendData("HAVESPACE \"" + filename.utf8() + "\" "
+ + QCString().setNum( bufLen )))
+ return;
+
+ if (!operationSuccessful()) {
+ error(ERR_DISK_FULL, i18n("Quota exceeded"));
+ return;
+ }
+#endif
+
+ if (!sendData("PUTSCRIPT \"" + filename.utf8() + "\" {"
+ + QCString().setNum( 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
+ QCString 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();
+
+ QCString errmsg(len + 1);
+
+ 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").arg(errmsg));
+
+ // 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").arg(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(i18n("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 KURL& url)
+{
+ changeCheck( url );
+ if (!connect())
+ return;
+
+ infoMessage(i18n("Retrieving data..."));
+
+ QString filename = url.fileName(false);
+
+ if (filename.isEmpty()) {
+ error(ERR_MALFORMED_URL, url.prettyURL());
+ return;
+ }
+
+ //SlaveBase::mimetype( QString("text/plain") ); // "application/sieve");
+
+ if (!sendData("GETSCRIPT \"" + filename.utf8() + "\""))
+ return;
+
+ if (receiveData() && r.getType() == kio_sieveResponse::QUANTITY) {
+ // determine script size
+ ssize_t total_len = r.getQuantity();
+ totalSize( total_len );
+
+ int 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( kMin( total_len - recv_len, ssize_t(64 * 1024 )) );
+ ssize_t this_recv_len = read( dat.data(), dat.size() );
+
+ if ( this_recv_len < 1 && !isConnectionValid() ) {
+ 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(i18n("Done."));
+ finished();
+}
+
+void kio_sieveProtocol::del(const KURL &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(false);
+
+ if (filename.isEmpty()) {
+ error(ERR_MALFORMED_URL, url.prettyURL());
+ return;
+ }
+
+ if (!sendData("DELETESCRIPT \"" + filename.utf8() + "\""))
+ return;
+
+ if (operationSuccessful())
+ ksDebug() << "Script deletion successful." << endl;
+ else {
+ error(ERR_INTERNAL_SERVER, i18n("The server would not delete the file."));
+ return;
+ }
+
+ infoMessage(i18n("Done."));
+
+ finished();
+}
+
+void kio_sieveProtocol::chmod(const KURL& 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();
+}
+
+#if defined(_AIX) && defined(stat)
+#undef stat
+#endif
+
+void kio_sieveProtocol::stat(const KURL& url)
+{
+ changeCheck( url );
+ if (!connect())
+ return;
+
+ UDSEntry entry;
+
+ QString filename = url.fileName(false);
+
+ if (filename.isEmpty()) {
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = "/";
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = 0700;
+ entry.append(atom);
+
+ statEntry(entry);
+
+ } else {
+ if (!sendData("LISTSCRIPTS"))
+ return;
+
+ while(receiveData()) {
+ if (r.getType() == kio_sieveResponse::ACTION) {
+ if (r.getAction().contains("OK", false) == 1)
+ // Script list completed
+ break;
+
+ } else
+ if (filename == QString::fromUtf8(r.getKey())) {
+ entry.clear();
+
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = QString::fromUtf8(r.getKey());
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ if ( r.getExtra() == "ACTIVE" )
+ atom.m_long = 0700; // mark exec'able
+ else
+ atom.m_long = 0600;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str = "application/sieve";
+ entry.append(atom);
+
+ //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 KURL& url)
+{
+ changeCheck( url );
+ if (!connect())
+ return;
+
+ if (!sendData("LISTSCRIPTS"))
+ return;
+
+ UDSEntry entry;
+
+ while(receiveData()) {
+ if (r.getType() == kio_sieveResponse::ACTION) {
+ if (r.getAction().contains("OK", false) == 1)
+ // Script list completed.
+ break;
+
+ } else {
+ entry.clear();
+
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = QString::fromUtf8(r.getKey());
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ if ( r.getExtra() == "ACTIVE" )
+ atom.m_long = 0700; // mark exec'able
+ else
+ atom.m_long = 0600;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str = "application/sieve";
+ entry.append(atom);
+
+ //asetMetaData("active", (r.getExtra() == "ACTIVE") ? "true" : "false");
+
+ ksDebug() << "Listing script " << r.getKey() << endl;
+ listEntry(entry , false);
+ }
+ }
+
+ listEntry(entry, true);
+
+ 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 (!openPassDlg(ai)) {
+ 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.utf8() );
+ interact->len = strlen( (const char *) interact->result );
+ break;
+ case SASL_CB_PASS:
+ ksDebug() << "SASL_CB_PASS: [hidden] " << endl;
+ interact->result = strdup( m_sPass.utf8() );
+ 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").arg( \
+ 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, tmp;
+
+ /* 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.setProtocol("sieve");
+ ai.url.setHost(m_sServer);
+ ai.url.setPort(m_iPort);
+ 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.latin1(),
+ 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(" ").latin1(), &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 = "AUTHENTICATE \"" + QString::fromLatin1( mechusing ) + "\"";
+ tmp.setRawData( out, outlen );
+ KCodecs::base64Encode( tmp, challenge );
+ tmp.resetRawData( out, outlen );
+ if ( !challenge.isEmpty() ) {
+ firstCommand += " \"";
+ firstCommand += QString::fromLatin1( challenge.data(), challenge.size() );
+ firstCommand += "\"";
+ }
+
+ if (!sendData( firstCommand.latin1() ))
+ return false;
+
+ QCString command;
+
+ do {
+ receiveData();
+
+ if (operationResult() != OTHER)
+ break;
+
+ ksDebug() << "Challenge len " << r.getQuantity() << endl;
+
+ if (r.getType() != kio_sieveResponse::QUANTITY) {
+ sasl_dispose( &conn );
+ error(ERR_SLAVE_DEFINED,
+ i18n("A protocol error occurred during authentication.\n"
+ "Choose a different authentication method to %1.").arg(mechusing));
+ return false;
+ }
+
+ uint 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.").arg(mechusing));
+ return false;
+ }
+
+ tmp.setRawData( r.getAction().data(), qty );
+ KCodecs::base64Decode( tmp, challenge );
+ tmp.resetRawData( r.getAction().data(), qty );
+// ksDebug() << "S: [" << r.getAction() << "]." << endl;
+// ksDebug() << "S-1: [" << QCString(challenge.data(), challenge.size()+1) << "]." << 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;
+ }
+
+ tmp.setRawData( out, outlen );
+ KCodecs::base64Encode( tmp, challenge );
+ tmp.resetRawData( out, outlen );
+ sendData("\"" + QCString( challenge.data(), challenge.size()+1 ) + "\"");
+// ksDebug() << "C: [" << QCString(challenge.data(), challenge.size()+1) << "]." << endl;
+// 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").arg( r.getAction() ) );
+ return false;
+ }
+}
+
+/* --------------------------------------------------------------------------- */
+void kio_sieveProtocol::mimetype(const KURL & url)
+{
+ ksDebug() << "Requesting mimetype for " << url.prettyURL() << endl;
+
+ if (url.fileName(false).isEmpty())
+ mimeType( "inode/directory" );
+ else
+ mimeType( "application/sieve" );
+
+ finished();
+}
+
+
+/* --------------------------------------------------------------------------- */
+bool kio_sieveProtocol::sendData(const QCString &data)
+{
+ QCString 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, QCString *reparse)
+{
+ QCString interpret;
+ int start, end;
+
+ if (!reparse) {
+ if (!waitForData)
+ // is there data waiting?
+ if (atEnd()) return false;
+
+ // read data from the server
+ char buffer[SIEVE_DEFAULT_RECIEVE_BUFFER];
+ readLine(buffer, SIEVE_DEFAULT_RECIEVE_BUFFER - 1);
+ buffer[SIEVE_DEFAULT_RECIEVE_BUFFER-1] = '\0';
+
+ // strip LF/CR
+ interpret = QCString(buffer).left(qstrlen(buffer) - 2);
+
+ } else {
+ interpret = reparse->copy();
+ }
+
+ r.clear();
+
+ //ksDebug() << "S: " << interpret << endl;
+
+ switch(interpret[0]) {
+ case '{':
+ {
+ // expecting {quantity}
+ start = 0;
+ end = interpret.find("+}", start + 1);
+ // some older versions of Cyrus enclose the literal size just in { } instead of { +}
+ if ( end == -1 )
+ end = interpret.find('}', 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.find(34, 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.find(34, 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.find(34, 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(false)) {
+ if (r.getType() == kio_sieveResponse::ACTION) {
+ QCString 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) {
+ QCString 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( "Cyrus\\stimsieved\\sv(\\d+)\\.(\\d+)\\.(\\d+)([-\\w]*)", false );
+ if ( regExp.search( 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 == "-kolab-nocaps") ) {
+ ksDebug() << k_funcinfo << "Enabling compat mode for Cyrus < 2.3.11 or Cyrus marked as \"kolab-nocaps\"" << endl;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/kioslaves/sieve/sieve.h b/kioslaves/sieve/sieve.h
new file mode 100644
index 000000000..1e064bbf6
--- /dev/null
+++ b/kioslaves/sieve/sieve.h
@@ -0,0 +1,132 @@
+/***************************************************************************
+ sieve.h - description
+ -------------------
+ begin : Thu Dec 20 18:47:08 EST 2001
+ copyright : (C) 2001 by Hamish Rodda
+ email : meddie@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 <kio/tcpslavebase.h>
+#include <kio/authinfo.h>
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+
+class KDESasl;
+class KURL;
+
+
+class kio_sieveResponse
+{
+public:
+ enum responses { NONE, KEY_VAL_PAIR, ACTION, QUANTITY };
+
+ kio_sieveResponse();
+
+ const uint& getType() const;
+
+ const QCString& getAction() const;
+ const uint getQuantity() const;
+ const QCString& getKey() const;
+ const QCString& getVal() const;
+ const QCString& getExtra() const;
+
+ void setQuantity(const uint& quantity);
+ void setAction(const QCString& newAction);
+ void setKey(const QCString& newKey);
+ void setVal(const QCString& newVal);
+ void setExtra(const QCString& newExtra);
+
+ void clear();
+
+protected:
+ uint rType;
+ uint quantity;
+ QCString key;
+ QCString val;
+ QCString extra;
+};
+
+class kio_sieveProtocol : public KIO::TCPSlaveBase
+{
+
+public:
+ enum connectionModes { NORMAL, CONNECTION_ORIENTED };
+ enum Results { OK, NO, BYE, OTHER };
+
+ kio_sieveProtocol(const QCString &pool_socket, const QCString &app_socket);
+ virtual ~kio_sieveProtocol();
+
+ virtual void mimetype(const KURL& url);
+ virtual void get(const KURL& url);
+ virtual void put(const KURL& url, int permissions, bool overwrite, bool resume);
+ virtual void del(const KURL &url, bool isfile);
+
+ virtual void listDir(const KURL& url);
+ virtual void chmod(const KURL& url, int permissions);
+ virtual void stat(const KURL& url);
+
+ virtual void setHost(const QString &host, int port, const QString &user, const QString &pass);
+ virtual void openConnection();
+ virtual void closeConnection();
+ //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
+ */
+ virtual void special(const QByteArray &data);
+ bool activate(const KURL& url);
+ bool deactivate();
+
+protected:
+ bool connect(bool useTLSIfAvailable = true);
+ bool authenticate();
+ void disconnect(bool forcibly = false);
+ void changeCheck( const KURL &url );
+
+ bool sendData(const QCString &data);
+ bool receiveData(bool waitForData = true, QCString *reparse = 0);
+ 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;
+
+private:
+ bool requestCapabilitiesAfterStartTLS() const;
+
+ QString m_implementation;
+};
+
+#endif
diff --git a/kioslaves/sieve/sieve.protocol b/kioslaves/sieve/sieve.protocol
new file mode 100644
index 000000000..3bd00e25e
--- /dev/null
+++ b/kioslaves/sieve/sieve.protocol
@@ -0,0 +1,54 @@
+[Protocol]
+exec=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=remote
+Description=An ioslave for the Sieve mail filtering protocol
+Description[af]='n <i>IOSlave</i> vir die Sieve e-pos filter protokol
+Description[ca]=Un ioslave pel protocol de filtrar de correu Sieve
+Description[cs]=ioslave pro protokol filtrování zpráv Sieve
+Description[da]=En ioslave for Sieve mail filtreringsprotokollen
+Description[de]=Ein-/Ausgabemodul für das E-Mail-Filterprotokoll "Sieve"
+Description[el]=Ένας ioslave για το πρωτόκολλο φιλτραρίσματος αλληλογραφίας Sieve
+Description[es]=Un ioslave para el protocolo de filtrado de correo Sieve
+Description[et]=Sieve e-kirjade filtreerimise protokolli IO-moodul
+Description[eu]=Sieve posta iragazketa protokoloarentztko irteerako/sarrerako mendeko bat
+Description[fa]=یک ioslave برای قرارداد پالایش نامۀ Sieve
+Description[fi]=Siirräntätyöskentelijä Sieve-sähköpostiensuodatusyhteyskäytännölle
+Description[fr]=Un module d'entrées / sorties pour le protocole de filtrage de messagerie Sieve
+Description[fy]=In ioslave foar it Sieve-mailfilterprotokol
+Description[gl]=Un esclavo io para o protocolo de filtraxe de correo Sieve
+Description[hu]=KDE-protokoll a Sieve levélszűrő protokollhoz
+Description[is]=Ioslave fyrir Sieve tölvupóstsíu samskiptaregluna
+Description[it]=Un ioslave per il protocollo di filtraggio posta Sieve
+Description[ja]=Sieve メールフィルタプロトコル用 ioslave
+Description[ka]=Sieve ფოსტის ფილტრის შეტანა-გამოტანის განაწესი
+Description[kk]=Sieve поштаны сүзгілеу протоколының ioslave модулі
+Description[km]=ioslave សម្រាប់​ពិធីការ​ត្រង​សំបុត្រ Sieve
+Description[ms]=Hamba io untuk protokol tapisan mel saringan
+Description[nb]=En i/u-slave for e-postfilterprotokollen Sieve
+Description[nds]=In-/Utgaavmoduul för't Nettpostfilter-Protokoll Sieve
+Description[ne]=मेल फिल्टरिङ प्रोटोकल सिभ गर्नका लागि एउटा आइओस्लेभ
+Description[nl]=Een ioslave voor het Sieve-mailfilterprotocol
+Description[nn]=Ein i/u-slave for e-postfilterprotokollen Sieve
+Description[pl]=Wtyczka protokołu filtrowania poczty Sieve
+Description[pt]=Um 'ioslave' para o protocolo de filtragem de correio Sieve
+Description[pt_BR]=Um IO-Slave para o protocolo de filtragem de email Sieve
+Description[ru]=Канал протокола фильтра почты Sieve
+Description[sk]=ioslave pre protokol Sieve filtrovanie pošty
+Description[sl]=ioslave za protokol poštnega filtriranja Sieve
+Description[sr]=IOslave протокола за филтрирање поште Sieve
+Description[sr@Latn]=IOslave protokola za filtriranje pošte Sieve
+Description[sv]=En I/O-slav för brevfiltreringsprotokollet Sieve
+Description[ta]=Sieve அஞ்சல் வடிகட்டும் நெறிமுறைக்கான ஒரு ioslave
+Description[tr]=Sieve e-posta filtreleme protokolü için bir ioslave
+Description[uk]=Підлеглий В/В для протоколу фільтрування пошти Sieve
+Description[zh_CN]=邮件过滤协议仆人