summaryrefslogtreecommitdiffstats
path: root/kioslaves
diff options
context:
space:
mode:
Diffstat (limited to 'kioslaves')
-rw-r--r--kioslaves/Mainpage.dox9
-rw-r--r--kioslaves/Makefile.am11
-rw-r--r--kioslaves/configure.in.bot6
-rw-r--r--kioslaves/configure.in.in27
-rw-r--r--kioslaves/imap4/Makefile.am26
-rw-r--r--kioslaves/imap4/PATCHING7
-rw-r--r--kioslaves/imap4/README48
-rw-r--r--kioslaves/imap4/configure.in.in1
-rw-r--r--kioslaves/imap4/imap4.cc2721
-rw-r--r--kioslaves/imap4/imap4.h205
-rw-r--r--kioslaves/imap4/imap4.protocol29
-rw-r--r--kioslaves/imap4/imapcommand.cc408
-rw-r--r--kioslaves/imap4/imapcommand.h394
-rw-r--r--kioslaves/imap4/imapinfo.cc236
-rw-r--r--kioslaves/imap4/imapinfo.h232
-rw-r--r--kioslaves/imap4/imaplist.cc135
-rw-r--r--kioslaves/imap4/imaplist.h137
-rw-r--r--kioslaves/imap4/imapparser.cc2085
-rw-r--r--kioslaves/imap4/imapparser.h500
-rw-r--r--kioslaves/imap4/imaps.protocol30
-rw-r--r--kioslaves/imap4/mailaddress.cc323
-rw-r--r--kioslaves/imap4/mailaddress.h81
-rw-r--r--kioslaves/imap4/mailheader.cc203
-rw-r--r--kioslaves/imap4/mailheader.h190
-rw-r--r--kioslaves/imap4/mimehdrline.cc521
-rw-r--r--kioslaves/imap4/mimehdrline.h67
-rw-r--r--kioslaves/imap4/mimeheader.cc745
-rw-r--r--kioslaves/imap4/mimeheader.h337
-rw-r--r--kioslaves/imap4/mimeio.cc188
-rw-r--r--kioslaves/imap4/mimeio.h79
-rw-r--r--kioslaves/imap4/rfcdecoder.cc668
-rw-r--r--kioslaves/imap4/rfcdecoder.h89
-rw-r--r--kioslaves/imap4/selectinfo.h202
-rw-r--r--kioslaves/mbox/AUTHORS1
-rw-r--r--kioslaves/mbox/Makefile.am30
-rw-r--r--kioslaves/mbox/README7
-rw-r--r--kioslaves/mbox/mbox.cc168
-rw-r--r--kioslaves/mbox/mbox.h81
-rw-r--r--kioslaves/mbox/mbox.protocol14
-rw-r--r--kioslaves/mbox/mboxfile.cc45
-rw-r--r--kioslaves/mbox/mboxfile.h68
-rw-r--r--kioslaves/mbox/readmbox.cc204
-rw-r--r--kioslaves/mbox/readmbox.h132
-rw-r--r--kioslaves/mbox/stat.cc115
-rw-r--r--kioslaves/mbox/stat.h82
-rw-r--r--kioslaves/mbox/urlinfo.cc133
-rw-r--r--kioslaves/mbox/urlinfo.h82
-rw-r--r--kioslaves/opengroupware/Makefile.am17
-rw-r--r--kioslaves/opengroupware/opengroupware.cpp250
-rw-r--r--kioslaves/opengroupware/opengroupware.h56
-rw-r--r--kioslaves/opengroupware/opengroupware.protocol7
-rw-r--r--kioslaves/opengroupware/opengroupwares.protocol7
-rw-r--r--kioslaves/opengroupware/webdavhandler.cpp81
-rw-r--r--kioslaves/opengroupware/webdavhandler.h44
-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
74 files changed, 14097 insertions, 0 deletions
diff --git a/kioslaves/Mainpage.dox b/kioslaves/Mainpage.dox
new file mode 100644
index 000000000..24e1ca685
--- /dev/null
+++ b/kioslaves/Mainpage.dox
@@ -0,0 +1,9 @@
+/** @mainpage
+*
+* IO Slaves.
+*
+* KDE PIM contains a few KIOSlaves. The most important
+* of these is the IMAP4Protocol slave which is used for IMAP operations.
+* Similarly, the MBoxProtocol slave is used to access mbox files.
+*
+*/
diff --git a/kioslaves/Makefile.am b/kioslaves/Makefile.am
new file mode 100644
index 000000000..8258295ef
--- /dev/null
+++ b/kioslaves/Makefile.am
@@ -0,0 +1,11 @@
+## $Id$
+
+if compile_kio_sieve
+ SIEVE_SUBDIR = sieve
+endif
+
+SUBDIRS = imap4 mbox $(SIEVE_SUBDIR)
+
+DOXYGEN_REFERENCES = kioslaves/imap4 kioslaves/mbox
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/kioslaves/configure.in.bot b/kioslaves/configure.in.bot
new file mode 100644
index 000000000..fdad4b633
--- /dev/null
+++ b/kioslaves/configure.in.bot
@@ -0,0 +1,6 @@
+if test "x$with_sasl" = xcheck && test -z "$SASL2_LIBS"; then
+ echo ""
+ echo "cyrus-sasl 2 library is missing. The sieve ioslave will not be built, and imap4 will lack of a lot of authentication methods."
+ echo ""
+ all_tests=bad
+fi
diff --git a/kioslaves/configure.in.in b/kioslaves/configure.in.in
new file mode 100644
index 000000000..47b98682b
--- /dev/null
+++ b/kioslaves/configure.in.in
@@ -0,0 +1,27 @@
+KDE_CHECK_SSL
+
+AC_ARG_WITH(sasl,
+ [AC_HELP_STRING(--with-sasl,
+ [enable support for authentication through cyrus-sasl @<:@default=check@:>@])],
+ [], with_sasl=check)
+
+sasl2_header="no"
+SASL2_LIBS=""
+if test "x$with_sasl" != xno; then
+ KDE_CHECK_HEADERS(sasl/sasl.h, sasl2_header="yes")
+ if test "$sasl2_header" = "yes" ; then
+ KDE_CHECK_LIB(sasl2, sasl_client_init, SASL2_LIBS="-lsasl2")
+ fi
+
+ if test "x$SASL2_LIBS" != "x" ; then
+ AC_DEFINE_UNQUOTED(HAVE_LIBSASL2, 1, [Define if you have cyrus-sasl2 libraries])
+ fi
+
+ if test "x$with_sasl" != xcheck && test -z "$SASL2_LIBS"; then
+ AC_MSG_ERROR([--with-sasl was given, but test for cyrus-sasl failed])
+ fi
+fi
+
+AC_SUBST(SASL2_LIBS)
+
+AM_CONDITIONAL(compile_kio_sieve, test -n "$SASL2_LIBS")
diff --git a/kioslaves/imap4/Makefile.am b/kioslaves/imap4/Makefile.am
new file mode 100644
index 000000000..d71987279
--- /dev/null
+++ b/kioslaves/imap4/Makefile.am
@@ -0,0 +1,26 @@
+INCLUDES= -I$(top_srcdir)/libkmime \
+ -I$(srcdir)/.. $(SSL_INCLUDES) \
+ -I$(top_srcdir)/libemailfunctions \
+ $(all_includes)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_imap4.la
+
+kio_imap4_la_SOURCES = imapcommand.cc imaplist.cc mailaddress.cc \
+ mimeheader.cc rfcdecoder.cc imap4.cc imapinfo.cc imapparser.cc mailheader.cc \
+ mimehdrline.cc mimeio.cc
+kio_imap4_la_LIBADD = $(LIB_KIO) $(SASL2_LIBS) ../../libkmime/libkmime.la \
+ ../../libemailfunctions/libemailfunctions.la
+kio_imap4_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = imap4.h
+EXTRA_DIST = README
+
+kdelnk_DATA = imap4.protocol imaps.protocol
+kdelnkdir = $(kde_servicesdir)
+
+messages:
+ $(XGETTEXT) *.cc -o $(podir)/kio_imap4.pot
+
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/kioslaves/imap4/PATCHING b/kioslaves/imap4/PATCHING
new file mode 100644
index 000000000..39bcdce95
--- /dev/null
+++ b/kioslaves/imap4/PATCHING
@@ -0,0 +1,7 @@
+If you are patching this code, please be very sensitive to performance issues.
+The parser is already very slow and resource intensive. Be careful not to add
+any extra string iterations (copies, QCString.length(), etc), mallocs, or
+implicit object creation/copies. Use calltree before and after your patch to
+verify that it is not too expensive, along with cpu usage timing and even
+wall clock time.
+
diff --git a/kioslaves/imap4/README b/kioslaves/imap4/README
new file mode 100644
index 000000000..bc05146ac
--- /dev/null
+++ b/kioslaves/imap4/README
@@ -0,0 +1,48 @@
+This is s.carstens@gmx.de release of KDE 2.0 kioslave
+for the IMAP protocol.
+
+It supports LOGIN, AUTHENTICATE LOGIN, AUTHENTICATE ANONYMOUS and
+AUTHENTICATE CRAM-MD5.
+It supports the rfc2192 URL naming convention.
+
+- UIDVALIDITY check is conditional
+- put will check if the mailbox exists and create it
+ or will append the data to that mailbox
+ (no append after create)
+ use edit->new->textfile from konqueror
+- move will try to guess the correct destination
+ as konqueror appends the source mailbox name to
+ the destination
+- del will currently delete empty directories,
+ mark messages for deletion.
+ If deleting a directory konqueror does the following:
+ - list the box
+ - take the box url + file name and try to delete it
+ - delete the box
+ As the konqueror created urls are invalid we ignore them
+ at the moment.
+- relative URL's are not supported because
+ konqueror will not handle them
+- there are 2 additional section keywords
+ ENVELOPE will do a FETCH ENVELOPE
+ STRUCTURE will do a FETCH BODYSTRUCTURE
+ normal behaviour is FETCH BODY.PEEK[section]
+
+- the mime types delivered are not really consistent
+ with the returned data
+ - it will return inode/directory on list entries
+ which contain inferiors
+ - it will return message/digest on selectable mailboxes
+ with file type S_IFDIR
+ - type message/rfc822-imap on selected messages
+ and type S_IFREG
+
+In Konqueror set the mimetype message/rfc822 to use
+the inline viewer.
+
+Try it: imap://user@host/
+ imap://user;AUTH=*@host/
+ imap://user;AUTH=LOGIN@host/
+ imap://user;AUTH=CRAM-MD5@host/
+
+comments to s.carstens@gmx.de
diff --git a/kioslaves/imap4/configure.in.in b/kioslaves/imap4/configure.in.in
new file mode 100644
index 000000000..680e26e0a
--- /dev/null
+++ b/kioslaves/imap4/configure.in.in
@@ -0,0 +1 @@
+KDE_CHECK_SSL
diff --git a/kioslaves/imap4/imap4.cc b/kioslaves/imap4/imap4.cc
new file mode 100644
index 000000000..eeef10fd4
--- /dev/null
+++ b/kioslaves/imap4/imap4.cc
@@ -0,0 +1,2721 @@
+/**********************************************************************
+ *
+ * imap4.cc - IMAP4rev1 KIOSlave
+ * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
+ * Copyright (C) 1999 John Corey
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to jcorey@fruity.ath.cx
+ *
+ *********************************************************************/
+
+/**
+ * @class IMAP4Protocol
+ * @note References:
+ * - RFC 2060 - Internet Message Access Protocol - Version 4rev1 - December 1996
+ * - RFC 2192 - IMAP URL Scheme - September 1997
+ * - RFC 1731 - IMAP Authentication Mechanisms - December 1994
+ * (Discusses KERBEROSv4, GSSAPI, and S/Key)
+ * - RFC 2195 - IMAP/POP AUTHorize Extension for Simple Challenge/Response
+ * - September 1997 (CRAM-MD5 authentication method)
+ * - RFC 2104 - HMAC: Keyed-Hashing for Message Authentication - February 1997
+ * - RFC 2086 - IMAP4 ACL extension - January 1997
+ * - http://www.ietf.org/internet-drafts/draft-daboo-imap-annotatemore-05.txt
+ * IMAP ANNOTATEMORE draft - April 2004.
+ *
+ *
+ * Supported URLs:
+ * \verbatim
+imap://server/
+imap://user:pass@server/
+imap://user;AUTH=method:pass@server/
+imap://server/folder/
+ * \endverbatim
+ * These URLs cause the following actions (in order):
+ * - Prompt for user/pass, list all folders in home directory
+ * - Uses LOGIN to log in
+ * - Uses AUTHENTICATE to log in
+ * - List messages in folder
+ *
+ * @note API notes:
+ * Not receiving the required write access for a folder means
+ * ERR_CANNOT_OPEN_FOR_WRITING.
+ * ERR_DOES_NOT_EXIST is reserved for folders.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "imap4.h"
+
+#include "rfcdecoder.h"
+
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_LIBSASL2
+extern "C" {
+#include <sasl/sasl.h>
+}
+#endif
+
+#include <qbuffer.h>
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <kprotocolmanager.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kio/connection.h>
+#include <kio/slaveinterface.h>
+#include <kio/passdlg.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kmdcodec.h>
+
+#include "kdepimmacros.h"
+
+#define IMAP_PROTOCOL "imap"
+#define IMAP_SSL_PROTOCOL "imaps"
+
+using namespace KIO;
+
+extern "C"
+{
+ void sigalrm_handler (int);
+ KDE_EXPORT int kdemain (int argc, char **argv);
+}
+
+int
+kdemain (int argc, char **argv)
+{
+ kdDebug(7116) << "IMAP4::kdemain" << endl;
+
+ KInstance instance ("kio_imap4");
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_imap4 protocol domain-socket1 domain-socket2\n");
+ ::exit (-1);
+ }
+
+#ifdef HAVE_LIBSASL2
+ if ( sasl_client_init( NULL ) != SASL_OK ) {
+ fprintf(stderr, "SASL library initialization failed!\n");
+ ::exit (-1);
+ }
+#endif
+
+ //set debug handler
+
+ IMAP4Protocol *slave;
+ if (strcasecmp (argv[1], IMAP_SSL_PROTOCOL) == 0)
+ slave = new IMAP4Protocol (argv[2], argv[3], true);
+ else if (strcasecmp (argv[1], IMAP_PROTOCOL) == 0)
+ slave = new IMAP4Protocol (argv[2], argv[3], false);
+ else
+ abort ();
+ slave->dispatchLoop ();
+ delete slave;
+
+#ifdef HAVE_LIBSASL2
+ sasl_done();
+#endif
+
+ return 0;
+}
+
+void
+sigchld_handler (int signo)
+{
+ int pid, status;
+
+ while (true && signo == SIGCHLD)
+ {
+ pid = waitpid (-1, &status, WNOHANG);
+ if (pid <= 0)
+ {
+ // Reinstall signal handler, since Linux resets to default after
+ // the signal occurred ( BSD handles it different, but it should do
+ // no harm ).
+ signal (SIGCHLD, sigchld_handler);
+ return;
+ }
+ }
+}
+
+IMAP4Protocol::IMAP4Protocol (const QCString & pool, const QCString & app, bool isSSL):TCPSlaveBase ((isSSL ? 993 : 143),
+ (isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool,
+ app, isSSL), imapParser (), mimeIO (), outputBuffer(outputCache)
+{
+ outputBufferIndex = 0;
+ mySSL = isSSL;
+ readBuffer[0] = 0x00;
+ relayEnabled = false;
+ readBufferLen = 0;
+ cacheOutput = false;
+ decodeContent = false;
+ mTimeOfLastNoop = QDateTime();
+}
+
+IMAP4Protocol::~IMAP4Protocol ()
+{
+ closeDescriptor();
+ kdDebug(7116) << "IMAP4: Finishing" << endl;
+}
+
+void
+IMAP4Protocol::get (const KURL & _url)
+{
+ if (!makeLogin()) return;
+ kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl;
+ QString aBox, aSequence, aType, aSection, aValidity, aDelimiter, aInfo;
+ enum IMAP_TYPE aEnum =
+ parseURL (_url, aBox, aSection, aType, aSequence, aValidity, aDelimiter, aInfo);
+ if (aEnum != ITYPE_ATTACH)
+ mimeType (getMimeType(aEnum));
+ if (aInfo == "DECODE")
+ decodeContent = true;
+
+ if (aSequence == "0:0" && getState() == ISTATE_SELECT)
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientNoop());
+ completeQueue.removeRef(cmd);
+ }
+
+ if (aSequence.isEmpty ())
+ {
+ aSequence = "1:*";
+ }
+
+ mProcessedSize = 0;
+ imapCommand *cmd = NULL;
+ if (!assureBox (aBox, true)) return;
+
+#ifdef USE_VALIDITY
+ if (selectInfo.uidValidityAvailable () && !aValidity.isEmpty ()
+ && selectInfo.uidValidity () != aValidity.toULong ())
+ {
+ // this url is stale
+ error (ERR_COULD_NOT_READ, _url.prettyURL());
+ return;
+ }
+ else
+#endif
+ {
+ // The "section" specified by the application can be:
+ // * empty (which means body, size and flags)
+ // * a known keyword, like STRUCTURE, ENVELOPE, HEADER, BODY.PEEK[...]
+ // (in which case the slave has some logic to add the necessary items)
+ // * Otherwise, it specifies the exact data items to request. In this case, all
+ // the logic is in the app.
+
+ QString aUpper = aSection.upper();
+ if (aUpper.find ("STRUCTURE") != -1)
+ {
+ aSection = "BODYSTRUCTURE";
+ }
+ else if (aUpper.find ("ENVELOPE") != -1)
+ {
+ aSection = "UID RFC822.SIZE FLAGS ENVELOPE";
+ if (hasCapability("IMAP4rev1")) {
+ aSection += " BODY.PEEK[HEADER.FIELDS (REFERENCES)]";
+ } else {
+ // imap4 does not know HEADER.FIELDS
+ aSection += " RFC822.HEADER.LINES (REFERENCES)";
+ }
+ }
+ else if (aUpper == "HEADER")
+ {
+ aSection = "UID RFC822.HEADER RFC822.SIZE FLAGS";
+ }
+ else if (aUpper.find ("BODY.PEEK[") != -1)
+ {
+ if (aUpper.find ("BODY.PEEK[]") != -1)
+ {
+ if (!hasCapability("IMAP4rev1")) // imap4 does not know BODY.PEEK[]
+ aSection.replace("BODY.PEEK[]", "RFC822.PEEK");
+ }
+ aSection.prepend("UID RFC822.SIZE FLAGS ");
+ }
+ else if (aSection.isEmpty())
+ {
+ aSection = "UID BODY[] RFC822.SIZE FLAGS";
+ }
+ if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
+ {
+ // write the digest header
+ cacheOutput = true;
+ outputLine
+ ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55);
+ if (selectInfo.recentAvailable ())
+ outputLineStr ("X-Recent: " +
+ QString::number(selectInfo.recent ()) + "\r\n");
+ if (selectInfo.countAvailable ())
+ outputLineStr ("X-Count: " + QString::number(selectInfo.count ()) +
+ "\r\n");
+ if (selectInfo.unseenAvailable ())
+ outputLineStr ("X-Unseen: " +
+ QString::number(selectInfo.unseen ()) + "\r\n");
+ if (selectInfo.uidValidityAvailable ())
+ outputLineStr ("X-uidValidity: " +
+ QString::number(selectInfo.uidValidity ()) +
+ "\r\n");
+ if (selectInfo.uidNextAvailable ())
+ outputLineStr ("X-UidNext: " +
+ QString::number(selectInfo.uidNext ()) + "\r\n");
+ if (selectInfo.flagsAvailable ())
+ outputLineStr ("X-Flags: " + QString::number(selectInfo.flags ()) +
+ "\r\n");
+ if (selectInfo.permanentFlagsAvailable ())
+ outputLineStr ("X-PermanentFlags: " +
+ QString::number(selectInfo.permanentFlags ()) + "\r\n");
+ if (selectInfo.readWriteAvailable ()) {
+ if (selectInfo.readWrite()) {
+ outputLine ("X-Access: Read/Write\r\n", 22);
+ } else {
+ outputLine ("X-Access: Read only\r\n", 21);
+ }
+ }
+ outputLine ("\r\n", 2);
+ flushOutput(QString::null);
+ cacheOutput = false;
+ }
+
+ if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent))
+ relayEnabled = true; // normal mode, relay data
+
+ if (aSequence != "0:0")
+ {
+ QString contentEncoding;
+ if (aEnum == ITYPE_ATTACH && decodeContent)
+ {
+ // get the MIME header and fill getLastHandled()
+ QString mySection = aSection;
+ mySection.replace("]", ".MIME]");
+ cmd = sendCommand (imapCommand::clientFetch (aSequence, mySection));
+ do
+ {
+ while (!parseLoop ()) ;
+ }
+ while (!cmd->isComplete ());
+ completeQueue.removeRef (cmd);
+ // get the content encoding now because getLastHandled will be cleared
+ if (getLastHandled() && getLastHandled()->getHeader())
+ contentEncoding = getLastHandled()->getHeader()->getEncoding();
+
+ // from here on collect the data
+ // it is send to the client in flushOutput in one go
+ // needed to decode the content
+ cacheOutput = true;
+ }
+
+ cmd = sendCommand (imapCommand::clientFetch (aSequence, aSection));
+ int res;
+ aUpper = aSection.upper();
+ do
+ {
+ while (!(res = parseLoop())) ;
+ if (res == -1) break;
+
+ mailHeader *lastone = 0;
+ imapCache *cache = getLastHandled ();
+ if (cache)
+ lastone = cache->getHeader ();
+
+ if (cmd && !cmd->isComplete ())
+ {
+ if ((aUpper.find ("BODYSTRUCTURE") != -1)
+ || (aUpper.find ("FLAGS") != -1)
+ || (aUpper.find ("UID") != -1)
+ || (aUpper.find ("ENVELOPE") != -1)
+ || (aUpper.find ("BODY.PEEK[0]") != -1
+ && (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)))
+ {
+ if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
+ {
+ // write the mime header (default is here message/rfc822)
+ outputLine ("--IMAPDIGEST\r\n", 14);
+ cacheOutput = true;
+ if (cache && cache->getUid () != 0)
+ outputLineStr ("X-UID: " +
+ QString::number(cache->getUid ()) + "\r\n");
+ if (cache && cache->getSize () != 0)
+ outputLineStr ("X-Length: " +
+ QString::number(cache->getSize ()) + "\r\n");
+ if (cache && !cache->getDate ().isEmpty())
+ outputLineStr ("X-Date: " + cache->getDate () + "\r\n");
+ if (cache && cache->getFlags () != 0)
+ outputLineStr ("X-Flags: " +
+ QString::number(cache->getFlags ()) + "\r\n");
+ } else cacheOutput = true;
+ if ( lastone && !decodeContent )
+ lastone->outputPart (*this);
+ cacheOutput = false;
+ flushOutput(contentEncoding);
+ }
+ } // if not complete
+ }
+ while (cmd && !cmd->isComplete ());
+ if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
+ {
+ // write the end boundary
+ outputLine ("--IMAPDIGEST--\r\n", 16);
+ }
+
+ completeQueue.removeRef (cmd);
+ }
+ }
+
+ // just to keep everybody happy when no data arrived
+ data (QByteArray ());
+
+ finished ();
+ relayEnabled = false;
+ cacheOutput = false;
+ kdDebug(7116) << "IMAP4::get - finished" << endl;
+}
+
+void
+IMAP4Protocol::listDir (const KURL & _url)
+{
+ kdDebug(7116) << " IMAP4::listDir - " << _url.prettyURL() << endl;
+
+ if (_url.path().isEmpty())
+ {
+ KURL url = _url;
+ url.setPath("/");
+ redirection( url );
+ finished();
+ return;
+ }
+
+ QString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
+ // parseURL with caching
+ enum IMAP_TYPE myType =
+ parseURL (_url, myBox, mySection, myLType, mySequence, myValidity,
+ myDelimiter, myInfo, true);
+
+ if (!makeLogin()) return;
+
+ if (myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX)
+ {
+ QString listStr = myBox;
+ imapCommand *cmd;
+
+ if (!listStr.isEmpty () && !listStr.endsWith(myDelimiter) &&
+ mySection != "FOLDERONLY")
+ listStr += myDelimiter;
+
+ if (mySection.isEmpty())
+ {
+ listStr += "%";
+ } else if (mySection == "COMPLETE") {
+ listStr += "*";
+ }
+ kdDebug(7116) << "IMAP4Protocol::listDir - listStr=" << listStr << endl;
+ cmd =
+ doCommand (imapCommand::clientList ("", listStr,
+ (myLType == "LSUB" || myLType == "LSUBNOCHECK")));
+ if (cmd->result () == "OK")
+ {
+ QString mailboxName;
+ UDSEntry entry;
+ UDSAtom atom;
+ KURL aURL = _url;
+ if (aURL.path().find(';') != -1)
+ aURL.setPath(aURL.path().left(aURL.path().find(';')));
+
+ kdDebug(7116) << "IMAP4Protocol::listDir - got " << listResponses.count () << endl;
+
+ if (myLType == "LSUB")
+ {
+ // fire the same command as LIST to check if the box really exists
+ QValueList<imapList> listResponsesSave = listResponses;
+ doCommand (imapCommand::clientList ("", listStr, false));
+ for (QValueListIterator < imapList > it = listResponsesSave.begin ();
+ it != listResponsesSave.end (); ++it)
+ {
+ bool boxOk = false;
+ for (QValueListIterator < imapList > it2 = listResponses.begin ();
+ it2 != listResponses.end (); ++it2)
+ {
+ if ((*it2).name() == (*it).name())
+ {
+ boxOk = true;
+ // copy the flags from the LIST-command
+ (*it) = (*it2);
+ break;
+ }
+ }
+ if (boxOk)
+ doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
+ else // this folder is dead
+ kdDebug(7116) << "IMAP4Protocol::listDir - suppress " << (*it).name() << endl;
+ }
+ listResponses = listResponsesSave;
+ }
+ else // LIST or LSUBNOCHECK
+ {
+ for (QValueListIterator < imapList > it = listResponses.begin ();
+ it != listResponses.end (); ++it)
+ {
+ doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
+ }
+ }
+ entry.clear ();
+ listEntry (entry, true);
+ }
+ else
+ {
+ error (ERR_CANNOT_ENTER_DIRECTORY, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ if ((myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX)
+ && myLType != "LIST" && myLType != "LSUB" && myLType != "LSUBNOCHECK")
+ {
+ KURL aURL = _url;
+ aURL.setQuery (QString::null);
+ const QString encodedUrl = aURL.url(0, 106); // utf-8
+
+ if (!_url.query ().isEmpty ())
+ {
+ QString query = KURL::decode_string (_url.query ());
+ query = query.right (query.length () - 1);
+ if (!query.isEmpty())
+ {
+ imapCommand *cmd = NULL;
+
+ if (!assureBox (myBox, true)) return;
+
+ if (!selectInfo.countAvailable() || selectInfo.count())
+ {
+ cmd = doCommand (imapCommand::clientSearch (query));
+ if (cmd->result() != "OK")
+ {
+ error(ERR_UNSUPPORTED_ACTION, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+
+ QStringList list = getResults ();
+ int stretch = 0;
+
+ if (selectInfo.uidNextAvailable ())
+ stretch = QString::number(selectInfo.uidNext ()).length ();
+ UDSEntry entry;
+ imapCache fake;
+
+ for (QStringList::ConstIterator it = list.begin(); it != list.end();
+ ++it)
+ {
+ fake.setUid((*it).toULong());
+ doListEntry (encodedUrl, stretch, &fake);
+ }
+ entry.clear ();
+ listEntry (entry, true);
+ }
+ }
+ }
+ else
+ {
+ if (!assureBox (myBox, true)) return;
+
+ kdDebug(7116) << "IMAP4: select returned:" << endl;
+ if (selectInfo.recentAvailable ())
+ kdDebug(7116) << "Recent: " << selectInfo.recent () << "d" << endl;
+ if (selectInfo.countAvailable ())
+ kdDebug(7116) << "Count: " << selectInfo.count () << "d" << endl;
+ if (selectInfo.unseenAvailable ())
+ kdDebug(7116) << "Unseen: " << selectInfo.unseen () << "d" << endl;
+ if (selectInfo.uidValidityAvailable ())
+ kdDebug(7116) << "uidValidity: " << selectInfo.uidValidity () << "d" << endl;
+ if (selectInfo.flagsAvailable ())
+ kdDebug(7116) << "Flags: " << selectInfo.flags () << "d" << endl;
+ if (selectInfo.permanentFlagsAvailable ())
+ kdDebug(7116) << "PermanentFlags: " << selectInfo.permanentFlags () << "d" << endl;
+ if (selectInfo.readWriteAvailable ())
+ kdDebug(7116) << "Access: " << (selectInfo.readWrite ()? "Read/Write" : "Read only") << endl;
+
+#ifdef USE_VALIDITY
+ if (selectInfo.uidValidityAvailable ()
+ && selectInfo.uidValidity () != myValidity.toULong ())
+ {
+ //redirect
+ KURL newUrl = _url;
+
+ newUrl.setPath ("/" + myBox + ";UIDVALIDITY=" +
+ QString::number(selectInfo.uidValidity ()));
+ kdDebug(7116) << "IMAP4::listDir - redirecting to " << newUrl.prettyURL() << endl;
+ redirection (newUrl);
+
+
+ }
+ else
+#endif
+ if (selectInfo.count () > 0)
+ {
+ int stretch = 0;
+
+ if (selectInfo.uidNextAvailable ())
+ stretch = QString::number(selectInfo.uidNext ()).length ();
+ // kdDebug(7116) << selectInfo.uidNext() << "d used to stretch " << stretch << endl;
+ UDSEntry entry;
+
+ if (mySequence.isEmpty()) mySequence = "1:*";
+
+ bool withSubject = mySection.isEmpty();
+ if (mySection.isEmpty()) mySection = "UID RFC822.SIZE ENVELOPE";
+
+ bool withFlags = mySection.upper().find("FLAGS") != -1;
+ imapCommand *fetch =
+ sendCommand (imapCommand::
+ clientFetch (mySequence, mySection));
+ imapCache *cache;
+ do
+ {
+ while (!parseLoop ()) ;
+
+ cache = getLastHandled ();
+
+ if (cache && !fetch->isComplete())
+ doListEntry (encodedUrl, stretch, cache, withFlags, withSubject);
+ }
+ while (!fetch->isComplete ());
+ entry.clear ();
+ listEntry (entry, true);
+ }
+ }
+ }
+ if ( !selectInfo.alert().isNull() ) {
+ if ( !myBox.isEmpty() ) {
+ warning( i18n( "Message from %1 while processing '%2': %3" ).arg( myHost, myBox, selectInfo.alert() ) );
+ } else {
+ warning( i18n( "Message from %1: %2" ).arg( myHost, selectInfo.alert() ) );
+ }
+ selectInfo.setAlert( 0 );
+ }
+
+ kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl;
+ finished ();
+}
+
+void
+IMAP4Protocol::setHost (const QString & _host, int _port,
+ const QString & _user, const QString & _pass)
+{
+ if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass)
+ { // what's the point of doing 4 string compares to avoid 4 string copies?
+ // DF: I guess to avoid calling closeConnection() unnecessarily.
+ if (!myHost.isEmpty ())
+ closeConnection ();
+ myHost = _host;
+ myPort = _port;
+ myUser = _user;
+ myPass = _pass;
+ }
+}
+
+void
+IMAP4Protocol::parseRelay (const QByteArray & buffer)
+{
+ if (relayEnabled) {
+ // relay data immediately
+ data( buffer );
+ mProcessedSize += buffer.size();
+ processedSize( mProcessedSize );
+ } else if (cacheOutput)
+ {
+ // collect data
+ if ( !outputBuffer.isOpen() ) {
+ outputBuffer.open(IO_WriteOnly);
+ }
+ outputBuffer.at(outputBufferIndex);
+ outputBuffer.writeBlock(buffer, buffer.size());
+ outputBufferIndex += buffer.size();
+ }
+}
+
+void
+IMAP4Protocol::parseRelay (ulong len)
+{
+ if (relayEnabled)
+ totalSize (len);
+}
+
+
+bool IMAP4Protocol::parseRead(QByteArray & buffer, ulong len, ulong relay)
+{
+ char buf[8192];
+ while (buffer.size() < len)
+ {
+ ssize_t readLen = myRead(buf, QMIN(len - buffer.size(), sizeof(buf) - 1));
+ if (readLen == 0)
+ {
+ kdDebug(7116) << "parseRead: readLen == 0 - connection broken" << endl;
+ error (ERR_CONNECTION_BROKEN, myHost);
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return FALSE;
+ }
+ if (relay > buffer.size())
+ {
+ QByteArray relayData;
+ ssize_t relbuf = relay - buffer.size();
+ int currentRelay = QMIN(relbuf, readLen);
+ relayData.setRawData(buf, currentRelay);
+ parseRelay(relayData);
+ relayData.resetRawData(buf, currentRelay);
+ }
+ {
+ QBuffer stream (buffer);
+ stream.open (IO_WriteOnly);
+ stream.at (buffer.size ());
+ stream.writeBlock (buf, readLen);
+ stream.close ();
+ }
+ }
+ return (buffer.size() == len);
+}
+
+
+bool IMAP4Protocol::parseReadLine (QByteArray & buffer, ulong relay)
+{
+ if (myHost.isEmpty()) return FALSE;
+
+ while (true) {
+ ssize_t copyLen = 0;
+ if (readBufferLen > 0)
+ {
+ while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') copyLen++;
+ if (copyLen < readBufferLen) copyLen++;
+ if (relay > 0)
+ {
+ QByteArray relayData;
+
+ if (copyLen < (ssize_t) relay)
+ relay = copyLen;
+ relayData.setRawData (readBuffer, relay);
+ parseRelay (relayData);
+ relayData.resetRawData (readBuffer, relay);
+// kdDebug(7116) << "relayed : " << relay << "d" << endl;
+ }
+ // append to buffer
+ {
+ QBuffer stream (buffer);
+
+ stream.open (IO_WriteOnly);
+ stream.at (buffer.size ());
+ stream.writeBlock (readBuffer, copyLen);
+ stream.close ();
+// kdDebug(7116) << "appended " << copyLen << "d got now " << buffer.size() << endl;
+ }
+
+ readBufferLen -= copyLen;
+ if (readBufferLen)
+ memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
+ if (buffer[buffer.size() - 1] == '\n') return TRUE;
+ }
+ if (!isConnectionValid())
+ {
+ kdDebug(7116) << "parseReadLine - connection broken" << endl;
+ error (ERR_CONNECTION_BROKEN, myHost);
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return FALSE;
+ }
+ if (!waitForResponse( responseTimeout() ))
+ {
+ error(ERR_SERVER_TIMEOUT, myHost);
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return FALSE;
+ }
+ readBufferLen = read(readBuffer, IMAP_BUFFER - 1);
+ if (readBufferLen == 0)
+ {
+ kdDebug(7116) << "parseReadLine: readBufferLen == 0 - connection broken" << endl;
+ error (ERR_CONNECTION_BROKEN, myHost);
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return FALSE;
+ }
+ }
+}
+
+void
+IMAP4Protocol::setSubURL (const KURL & _url)
+{
+ kdDebug(7116) << "IMAP4::setSubURL - " << _url.prettyURL() << endl;
+ KIO::TCPSlaveBase::setSubURL (_url);
+}
+
+void
+IMAP4Protocol::put (const KURL & _url, int, bool, bool)
+{
+ kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl;
+// KIO::TCPSlaveBase::put(_url,permissions,overwrite,resume);
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ enum IMAP_TYPE aType =
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+
+ // see if it is a box
+ if (aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX)
+ {
+ if (aBox[aBox.length () - 1] == '/')
+ aBox = aBox.right (aBox.length () - 1);
+ imapCommand *cmd = doCommand (imapCommand::clientCreate (aBox));
+
+ if (cmd->result () != "OK") {
+ error (ERR_COULD_NOT_WRITE, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ else
+ {
+ QPtrList < QByteArray > bufferList;
+ int length = 0;
+
+ int result;
+ // Loop until we got 'dataEnd'
+ do
+ {
+ QByteArray *buffer = new QByteArray ();
+ dataReq (); // Request for data
+ result = readData (*buffer);
+ if (result > 0)
+ {
+ bufferList.append (buffer);
+ length += result;
+ } else {
+ delete buffer;
+ }
+ }
+ while (result > 0);
+
+ if (result != 0)
+ {
+ error (ERR_ABORTED, _url.prettyURL());
+ return;
+ }
+
+ imapCommand *cmd =
+ sendCommand (imapCommand::clientAppend (aBox, aSection, length));
+ while (!parseLoop ()) ;
+
+ // see if server is waiting
+ if (!cmd->isComplete () && !getContinuation ().isEmpty ())
+ {
+ bool sendOk = true;
+ ulong wrote = 0;
+
+ QByteArray *buffer;
+ // send data to server
+ while (!bufferList.isEmpty () && sendOk)
+ {
+ buffer = bufferList.take (0);
+
+ sendOk =
+ (write (buffer->data (), buffer->size ()) ==
+ (ssize_t) buffer->size ());
+ wrote += buffer->size ();
+ processedSize(wrote);
+ delete buffer;
+ if (!sendOk)
+ {
+ error (ERR_CONNECTION_BROKEN, myHost);
+ completeQueue.removeRef (cmd);
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return;
+ }
+ }
+ parseWriteLine ("");
+ // Wait until cmd is complete, or connection breaks.
+ while (!cmd->isComplete () && getState() != ISTATE_NO)
+ parseLoop ();
+ if ( getState() == ISTATE_NO ) {
+ // TODO KDE4: pass cmd->resultInfo() as third argument.
+ // ERR_CONNECTION_BROKEN expects a host, no way to pass details about the problem.
+ error( ERR_CONNECTION_BROKEN, myHost );
+ completeQueue.removeRef (cmd);
+ closeConnection();
+ return;
+ }
+ else if (cmd->result () != "OK") {
+ error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ else
+ {
+ if (hasCapability("UIDPLUS"))
+ {
+ QString uid = cmd->resultInfo();
+ if (uid.find("APPENDUID") != -1)
+ {
+ uid = uid.section(" ", 2, 2);
+ uid.truncate(uid.length()-1);
+ infoMessage("UID "+uid);
+ }
+ }
+ // MUST reselect to get the new message
+ else if (aBox == getCurrentBox ())
+ {
+ cmd =
+ doCommand (imapCommand::
+ clientSelect (aBox, !selectInfo.readWrite ()));
+ completeQueue.removeRef (cmd);
+ }
+ }
+ }
+ else
+ {
+ //error (ERR_COULD_NOT_WRITE, myHost);
+ // Better ship the error message, e.g. "Over Quota"
+ error (ERR_SLAVE_DEFINED, cmd->resultInfo());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+
+ completeQueue.removeRef (cmd);
+ }
+
+ finished ();
+}
+
+void
+IMAP4Protocol::mkdir (const KURL & _url, int)
+{
+ kdDebug(7116) << "IMAP4::mkdir - " << _url.prettyURL() << endl;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ kdDebug(7116) << "IMAP4::mkdir - create " << aBox << endl;
+ imapCommand *cmd = doCommand (imapCommand::clientCreate(aBox));
+
+ if (cmd->result () != "OK")
+ {
+ kdDebug(7116) << "IMAP4::mkdir - " << cmd->resultInfo() << endl;
+ error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+
+ // start a new listing to find the type of the folder
+ enum IMAP_TYPE type =
+ parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ if (type == ITYPE_BOX)
+ {
+ bool ask = ( aInfo.find( "ASKUSER" ) != -1 );
+ if ( ask &&
+ messageBox(QuestionYesNo,
+ i18n("The following folder will be created on the server: %1 "
+ "What do you want to store in this folder?").arg( aBox ),
+ i18n("Create Folder"),
+ i18n("&Messages"), i18n("&Subfolders")) == KMessageBox::No )
+ {
+ cmd = doCommand(imapCommand::clientDelete(aBox));
+ completeQueue.removeRef (cmd);
+ cmd = doCommand(imapCommand::clientCreate(aBox + aDelimiter));
+ if (cmd->result () != "OK")
+ {
+ error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ }
+
+ cmd = doCommand(imapCommand::clientSubscribe(aBox));
+ completeQueue.removeRef(cmd);
+
+ finished ();
+}
+
+void
+IMAP4Protocol::copy (const KURL & src, const KURL & dest, int, bool overwrite)
+{
+ kdDebug(7116) << "IMAP4::copy - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
+ QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
+ QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
+ enum IMAP_TYPE sType =
+ parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo);
+ enum IMAP_TYPE dType =
+ parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo);
+
+ // see if we have to create anything
+ if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
+ {
+ // this might be konqueror
+ int sub = dBox.find (sBox);
+
+ // might be moving to upper folder
+ if (sub > 0)
+ {
+ KURL testDir = dest;
+
+ QString subDir = dBox.right (dBox.length () - dBox.findRev ('/'));
+ QString topDir = dBox.left (sub);
+ testDir.setPath ("/" + topDir);
+ dType =
+ parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
+ dDelimiter, dInfo);
+
+ kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
+ // see if this is what the user wants
+ if (dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX)
+ {
+ kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
+ dBox = topDir;
+ }
+ else
+ {
+
+ // maybe if we create a new mailbox
+ topDir = "/" + topDir + subDir;
+ testDir.setPath (topDir);
+ kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
+ dType =
+ parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
+ dDelimiter, dInfo);
+ if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
+ {
+ // ok then we'll create a mailbox
+ imapCommand *cmd = doCommand (imapCommand::clientCreate (topDir));
+
+ // on success we'll use it, else we'll just try to create the given dir
+ if (cmd->result () == "OK")
+ {
+ kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
+ dType = ITYPE_BOX;
+ dBox = topDir;
+ }
+ else
+ {
+ completeQueue.removeRef (cmd);
+ cmd = doCommand (imapCommand::clientCreate (dBox));
+ if (cmd->result () == "OK")
+ dType = ITYPE_BOX;
+ else
+ error (ERR_COULD_NOT_WRITE, dest.prettyURL());
+ }
+ completeQueue.removeRef (cmd);
+ }
+ }
+
+ }
+ }
+ if (sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX)
+ {
+ //select the source box
+ if (!assureBox(sBox, true)) return;
+ kdDebug(7116) << "IMAP4::copy - " << sBox << " -> " << dBox << endl;
+
+ //issue copy command
+ imapCommand *cmd =
+ doCommand (imapCommand::clientCopy (dBox, sSequence));
+ if (cmd->result () != "OK")
+ {
+ kdError(5006) << "IMAP4::copy - " << cmd->resultInfo() << endl;
+ error (ERR_COULD_NOT_WRITE, dest.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ } else {
+ if (hasCapability("UIDPLUS"))
+ {
+ QString uid = cmd->resultInfo();
+ if (uid.find("COPYUID") != -1)
+ {
+ uid = uid.section(" ", 2, 3);
+ uid.truncate(uid.length()-1);
+ infoMessage("UID "+uid);
+ }
+ }
+ }
+ completeQueue.removeRef (cmd);
+ }
+ else
+ {
+ error (ERR_ACCESS_DENIED, src.prettyURL());
+ return;
+ }
+ finished ();
+}
+
+void
+IMAP4Protocol::del (const KURL & _url, bool isFile)
+{
+ kdDebug(7116) << "IMAP4::del - [" << (isFile ? "File" : "NoFile") << "] " << _url.prettyURL() << endl;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ enum IMAP_TYPE aType =
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+
+ switch (aType)
+ {
+ case ITYPE_BOX:
+ case ITYPE_DIR_AND_BOX:
+ if (!aSequence.isEmpty ())
+ {
+ if (aSequence == "*")
+ {
+ if (!assureBox (aBox, false)) return;
+ imapCommand *cmd = doCommand (imapCommand::clientExpunge ());
+ if (cmd->result () != "OK") {
+ error (ERR_CANNOT_DELETE, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ else
+ {
+ // if open for read/write
+ if (!assureBox (aBox, false)) return;
+ imapCommand *cmd =
+ doCommand (imapCommand::
+ clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
+ if (cmd->result () != "OK") {
+ error (ERR_CANNOT_DELETE, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ }
+ else
+ {
+ if (getCurrentBox() == aBox)
+ {
+ imapCommand *cmd = doCommand(imapCommand::clientClose());
+ completeQueue.removeRef(cmd);
+ setState(ISTATE_LOGIN);
+ }
+ // We unsubscribe, otherwise we get ghost folders on UW-IMAP
+ imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
+ completeQueue.removeRef(cmd);
+ cmd = doCommand(imapCommand::clientDelete (aBox));
+ // If this doesn't work, we try to empty the mailbox first
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef(cmd);
+ if (!assureBox(aBox, false)) return;
+ bool stillOk = true;
+ if (stillOk)
+ {
+ imapCommand *cmd = doCommand(
+ imapCommand::clientStore("1:*", "+FLAGS.SILENT", "\\DELETED"));
+ if (cmd->result () != "OK") stillOk = false;
+ completeQueue.removeRef(cmd);
+ }
+ if (stillOk)
+ {
+ imapCommand *cmd = doCommand(imapCommand::clientClose());
+ if (cmd->result () != "OK") stillOk = false;
+ completeQueue.removeRef(cmd);
+ setState(ISTATE_LOGIN);
+ }
+ if (stillOk)
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientDelete(aBox));
+ if (cmd->result () != "OK") stillOk = false;
+ completeQueue.removeRef(cmd);
+ }
+ if (!stillOk)
+ {
+ error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
+ return;
+ }
+ } else {
+ completeQueue.removeRef (cmd);
+ }
+ }
+ break;
+
+ case ITYPE_DIR:
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientDelete (aBox));
+ if (cmd->result () != "OK") {
+ error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ break;
+
+ case ITYPE_MSG:
+ {
+ // if open for read/write
+ if (!assureBox (aBox, false)) return;
+ imapCommand *cmd =
+ doCommand (imapCommand::
+ clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
+ if (cmd->result () != "OK") {
+ error (ERR_CANNOT_DELETE, _url.prettyURL());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ break;
+
+ case ITYPE_UNKNOWN:
+ case ITYPE_ATTACH:
+ error (ERR_CANNOT_DELETE, _url.prettyURL());
+ break;
+ }
+ finished ();
+}
+
+/*
+ * Copy a mail: data = 'C' + srcURL (KURL) + destURL (KURL)
+ * Capabilities: data = 'c'. Result shipped in infoMessage() signal
+ * No-op: data = 'N'
+ * Namespace: data = 'n'. Result shipped in infoMessage() signal
+ * The format is: section=namespace=delimiter
+ * Note that the namespace can be empty
+ * Unsubscribe: data = 'U' + URL (KURL)
+ * Subscribe: data = 'u' + URL (KURL)
+ * Change the status: data = 'S' + URL (KURL) + Flags (QCString)
+ * ACL commands: data = 'A' + command + URL (KURL) + command-dependent args
+ * AnnotateMore commands: data = 'M' + 'G'et/'S'et + URL + entry + command-dependent args
+ * Search: data = 'E' + URL (KURL)
+ * Quota commands: data = 'Q' + 'R'oot/'G'et/'S'et + URL + entry + command-dependent args
+ * Custom command: data = 'X' + 'N'ormal/'E'xtended + command + command-dependent args
+ */
+void
+IMAP4Protocol::special (const QByteArray & aData)
+{
+ kdDebug(7116) << "IMAP4Protocol::special" << endl;
+ if (!makeLogin()) return;
+
+ QDataStream stream(aData, IO_ReadOnly);
+
+ int tmp;
+ stream >> tmp;
+
+ switch (tmp) {
+ case 'C':
+ {
+ // copy
+ KURL src;
+ KURL dest;
+ stream >> src >> dest;
+ copy(src, dest, 0, FALSE);
+ break;
+ }
+ case 'c':
+ {
+ // capabilities
+ infoMessage(imapCapabilities.join(" "));
+ finished();
+ break;
+ }
+ case 'N':
+ {
+ // NOOP
+ imapCommand *cmd = doCommand(imapCommand::clientNoop());
+ if (cmd->result () != "OK")
+ {
+ kdDebug(7116) << "NOOP did not succeed - connection broken" << endl;
+ completeQueue.removeRef (cmd);
+ error (ERR_CONNECTION_BROKEN, myHost);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'n':
+ {
+ // namespace in the form "section=namespace=delimiter"
+ // entries are separated by ,
+ infoMessage( imapNamespaces.join(",") );
+ finished();
+ break;
+ }
+ case 'U':
+ {
+ // unsubscribe
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef (cmd);
+ error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
+ "failed. The server returned: %2")
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'u':
+ {
+ // subscribe
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox));
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef (cmd);
+ error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
+ "failed. The server returned: %2")
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'A':
+ {
+ // acl
+ int cmd;
+ stream >> cmd;
+ if ( hasCapability( "ACL" ) ) {
+ specialACLCommand( cmd, stream );
+ } else {
+ error( ERR_UNSUPPORTED_ACTION, "ACL" );
+ }
+ break;
+ }
+ case 'M':
+ {
+ // annotatemore
+ int cmd;
+ stream >> cmd;
+ if ( hasCapability( "ANNOTATEMORE" ) ) {
+ specialAnnotateMoreCommand( cmd, stream );
+ } else {
+ error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
+ }
+ break;
+ }
+ case 'Q':
+ {
+ // quota
+ int cmd;
+ stream >> cmd;
+ if ( hasCapability( "QUOTA" ) ) {
+ specialQuotaCommand( cmd, stream );
+ } else {
+ error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
+ }
+ break;
+ }
+ case 'S':
+ {
+ // status
+ KURL _url;
+ QCString newFlags;
+ stream >> _url >> newFlags;
+
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ if (!assureBox(aBox, false)) return;
+
+ // make sure we only touch flags we know
+ QCString knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT";
+ const imapInfo info = getSelected();
+ if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) {
+ knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED";
+ }
+
+ imapCommand *cmd = doCommand (imapCommand::
+ clientStore (aSequence, "-FLAGS.SILENT", knownFlags));
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef (cmd);
+ error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
+ "failed.").arg(_url.prettyURL()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ if (!newFlags.isEmpty())
+ {
+ cmd = doCommand (imapCommand::
+ clientStore (aSequence, "+FLAGS.SILENT", newFlags));
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef (cmd);
+ error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
+ "failed.").arg(_url.prettyURL()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ finished();
+ break;
+ }
+ case 's':
+ {
+ // seen
+ KURL _url;
+ bool seen;
+ QCString newFlags;
+ stream >> _url >> seen;
+
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ if ( !assureBox(aBox, true) ) // read-only because changing SEEN should be possible even then
+ return;
+
+ imapCommand *cmd;
+ if ( seen )
+ cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
+ else
+ cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
+
+ if (cmd->result () != "OK")
+ {
+ completeQueue.removeRef (cmd);
+ error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
+ "failed.").arg(_url.prettyURL()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+
+ case 'E':
+ {
+ // search
+ specialSearchCommand( stream );
+ break;
+ }
+ case 'X':
+ {
+ // custom command
+ specialCustomCommand( stream );
+ break;
+ }
+ default:
+ kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
+ error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
+ break;
+ }
+}
+
+void
+IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
+{
+ // All commands start with the URL to the box
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+
+ switch( command ) {
+ case 'S': // SETACL
+ {
+ QString user, acl;
+ stream >> user >> acl;
+ kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
+ "for user %2 failed. The server returned: %3")
+ .arg(_url.prettyURL())
+ .arg(user)
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'D': // DELETEACL
+ {
+ QString user;
+ stream >> user;
+ kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
+ "for user %2 failed. The server returned: %3")
+ .arg(_url.prettyURL())
+ .arg(user)
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'G': // GETACL
+ {
+ kdDebug(7116) << "GETACL " << aBox << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
+ "failed. The server returned: %2")
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ // Returning information to the application from a special() command isn't easy.
+ // I'm reusing the infoMessage trick seen above (for capabilities), but this
+ // limits me to a string instead of a stringlist. Using DQUOTE as separator,
+ // because it's forbidden in userids by rfc3501
+ kdDebug(7116) << getResults() << endl;
+ infoMessage(getResults().join( "\"" ));
+ finished();
+ break;
+ }
+ case 'L': // LISTRIGHTS
+ {
+ // Do we need this one? It basically shows which rights are tied together, but that's all?
+ error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
+ break;
+ }
+ case 'M': // MYRIGHTS
+ {
+ kdDebug(7116) << "MYRIGHTS " << aBox << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
+ "failed. The server returned: %2")
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ QStringList lst = getResults();
+ kdDebug(7116) << "myrights results: " << lst << endl;
+ if ( !lst.isEmpty() ) {
+ Q_ASSERT( lst.count() == 1 );
+ infoMessage( lst.first() );
+ }
+ finished();
+ break;
+ }
+ default:
+ kdWarning(7116) << "Unknown special ACL command:" << command << endl;
+ error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
+ }
+}
+
+void
+IMAP4Protocol::specialSearchCommand( QDataStream& stream )
+{
+ kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+ if (!assureBox(aBox, false)) return;
+
+ imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection ));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
+ "failed. The server returned: %2")
+ .arg(aBox)
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef(cmd);
+ QStringList lst = getResults();
+ kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection <<
+ "' returns " << lst << endl;
+ infoMessage( lst.join( " " ) );
+
+ finished();
+}
+
+void
+IMAP4Protocol::specialCustomCommand( QDataStream& stream )
+{
+ kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
+
+ QString command, arguments;
+ int type;
+ stream >> type;
+ stream >> command >> arguments;
+
+ /**
+ * In 'normal' mode we send the command with all information in one go
+ * and retrieve the result.
+ */
+ if ( type == 'N' ) {
+ kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl;
+ imapCommand *cmd = doCommand (imapCommand::clientCustom( command, arguments ));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Custom command %1:%2 "
+ "failed. The server returned: %3")
+ .arg(command)
+ .arg(arguments)
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef(cmd);
+ QStringList lst = getResults();
+ kdDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command <<
+ ":" << arguments <<
+ "' returns " << lst << endl;
+ infoMessage( lst.join( " " ) );
+
+ finished();
+ } else
+ /**
+ * In 'extended' mode we send a first header and push the data of the request in
+ * streaming mode.
+ */
+ if ( type == 'E' ) {
+ kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl;
+ imapCommand *cmd = sendCommand (imapCommand::clientCustom( command, QString() ));
+ while ( !parseLoop () ) ;
+
+ // see if server is waiting
+ if (!cmd->isComplete () && !getContinuation ().isEmpty ())
+ {
+ const QByteArray buffer = arguments.utf8();
+
+ // send data to server
+ bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ());
+ processedSize( buffer.size() );
+
+ if ( !sendOk ) {
+ error ( ERR_CONNECTION_BROKEN, myHost );
+ completeQueue.removeRef ( cmd );
+ setState(ISTATE_CONNECT);
+ closeConnection();
+ return;
+ }
+ }
+ parseWriteLine ("");
+
+ do
+ {
+ while (!parseLoop ()) ;
+ }
+ while (!cmd->isComplete ());
+
+ completeQueue.removeRef (cmd);
+
+ QStringList lst = getResults();
+ kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
+ infoMessage( lst.join( " " ) );
+
+ finished ();
+ }
+}
+
+void
+IMAP4Protocol::specialAnnotateMoreCommand( int command, QDataStream& stream )
+{
+ // All commands start with the URL to the box
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+
+ switch( command ) {
+ case 'S': // SETANNOTATION
+ {
+ // Params:
+ // KURL URL of the mailbox
+ // QString entry (should be an actual entry name, no % or *; empty for server entries)
+ // QMap<QString,QString> attributes (name and value)
+ QString entry;
+ QMap<QString, QString> attributes;
+ stream >> entry >> attributes;
+ kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
+ " failed. The server returned: %3")
+ .arg(entry)
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ finished();
+ break;
+ }
+ case 'G': // GETANNOTATION.
+ {
+ // Params:
+ // KURL URL of the mailbox
+ // QString entry (should be an actual entry name, no % or *; empty for server entries)
+ // QStringList attributes (list of attributes to be retrieved, possibly with % or *)
+ QString entry;
+ QStringList attributeNames;
+ stream >> entry >> attributeNames;
+ kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
+ "failed. The server returned: %3")
+ .arg(entry)
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ // Returning information to the application from a special() command isn't easy.
+ // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this
+ // limits me to a string instead of a stringlist. Let's use \r as separator.
+ kdDebug(7116) << getResults() << endl;
+ infoMessage(getResults().join( "\r" ));
+ finished();
+ break;
+ }
+ default:
+ kdWarning(7116) << "Unknown special annotate command:" << command << endl;
+ error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
+ }
+}
+
+void
+IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
+{
+ // All commands start with the URL to the box
+ KURL _url;
+ stream >> _url;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
+
+ switch( command ) {
+ case 'R': // GETQUOTAROOT
+ {
+ kdDebug(7116) << "QUOTAROOT " << aBox << endl;
+ imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
+ if (cmd->result () != "OK")
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
+ "failed. The server returned: %2")
+ .arg(_url.prettyURL())
+ .arg(cmd->resultInfo()));
+ return;
+ }
+ infoMessage(getResults().join( "\r" ));
+ finished();
+ break;
+ }
+ case 'G': // GETQUOTA
+ {
+ kdDebug(7116) << "GETQUOTA command" << endl;
+ kdWarning(7116) << "UNIMPLEMENTED" << endl;
+ break;
+ }
+ case 'S': // SETQUOTA
+ {
+ kdDebug(7116) << "SETQUOTA command" << endl;
+ kdWarning(7116) << "UNIMPLEMENTED" << endl;
+ break;
+ }
+ default:
+ kdWarning(7116) << "Unknown special quota command:" << command << endl;
+ error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
+ }
+}
+
+void
+IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite)
+{
+ kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
+ QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
+ QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
+ enum IMAP_TYPE sType =
+ parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
+ enum IMAP_TYPE dType =
+ parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
+
+ if (dType == ITYPE_UNKNOWN)
+ {
+ switch (sType)
+ {
+ case ITYPE_BOX:
+ case ITYPE_DIR:
+ case ITYPE_DIR_AND_BOX:
+ {
+ if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
+ {
+ kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl;
+ // mailbox can only be renamed if it is closed
+ imapCommand *cmd = doCommand (imapCommand::clientClose());
+ bool ok = cmd->result() == "OK";
+ completeQueue.removeRef(cmd);
+ if (!ok)
+ {
+ error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
+ return;
+ }
+ setState(ISTATE_LOGIN);
+ }
+ imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
+ if (cmd->result () != "OK") {
+ error (ERR_CANNOT_RENAME, cmd->result ());
+ completeQueue.removeRef (cmd);
+ return;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ break;
+
+ case ITYPE_MSG:
+ case ITYPE_ATTACH:
+ case ITYPE_UNKNOWN:
+ error (ERR_CANNOT_RENAME, src.prettyURL());
+ break;
+ }
+ }
+ else
+ {
+ error (ERR_CANNOT_RENAME, src.prettyURL());
+ return;
+ }
+ finished ();
+}
+
+void
+IMAP4Protocol::slave_status ()
+{
+ bool connected = (getState() != ISTATE_NO) && isConnectionValid();
+ kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
+ slaveStatus ( connected ? myHost : QString::null, connected );
+}
+
+void
+IMAP4Protocol::dispatch (int command, const QByteArray & data)
+{
+ kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
+ KIO::TCPSlaveBase::dispatch (command, data);
+}
+
+void
+IMAP4Protocol::stat (const KURL & _url)
+{
+ kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
+ QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
+ // parseURL with caching
+ enum IMAP_TYPE aType =
+ parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
+ aInfo, true);
+
+ UDSEntry entry;
+ UDSAtom atom;
+
+ atom.m_uds = UDS_NAME;
+ atom.m_str = aBox;
+ entry.append (atom);
+
+ if (!aSection.isEmpty())
+ {
+ if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientClose());
+ bool ok = cmd->result() == "OK";
+ completeQueue.removeRef(cmd);
+ if (!ok)
+ {
+ error(ERR_COULD_NOT_STAT, aBox);
+ return;
+ }
+ setState(ISTATE_LOGIN);
+ }
+ bool ok = false;
+ QString cmdInfo;
+ if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
+ ok = true;
+ else
+ {
+ imapCommand *cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
+ ok = cmd->result() == "OK";
+ cmdInfo = cmd->resultInfo();
+ completeQueue.removeRef(cmd);
+ }
+ if (!ok)
+ {
+ bool found = false;
+ imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
+ if (cmd->result () == "OK")
+ {
+ for (QValueListIterator < imapList > it = listResponses.begin ();
+ it != listResponses.end (); ++it)
+ {
+ if (aBox == (*it).name ()) found = true;
+ }
+ }
+ completeQueue.removeRef (cmd);
+ if (found)
+ error(ERR_COULD_NOT_STAT, aBox);
+ else
+ error(KIO::ERR_DOES_NOT_EXIST, aBox);
+ return;
+ }
+ if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
+ || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
+ {
+ atom.m_uds = UDS_SIZE;
+ atom.m_str = QString::null;
+ atom.m_long = (aSection == "UIDNEXT") ? getStatus().uidNext()
+ : getStatus().unseen();
+ entry.append(atom);
+ }
+ } else
+ if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
+ aType == ITYPE_ATTACH)
+ {
+ ulong validity = 0;
+ // see if the box is already in select/examine state
+ if (aBox == getCurrentBox ())
+ validity = selectInfo.uidValidity ();
+ else
+ {
+ // do a status lookup on the box
+ // only do this if the box is not selected
+ // the server might change the validity for new select/examine
+ imapCommand *cmd =
+ doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
+ completeQueue.removeRef (cmd);
+ validity = getStatus ().uidValidity ();
+ }
+ validity = 0; // temporary
+
+ if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
+ {
+ // has no or an invalid uidvalidity
+ if (validity > 0 && validity != aValidity.toULong ())
+ {
+ //redirect
+ KURL newUrl = _url;
+
+ newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
+ QString::number(validity));
+ kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
+ redirection (newUrl);
+ }
+ }
+ else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
+ {
+ //must determine if this message exists
+ //cause konqueror will check this on paste operations
+
+ // has an invalid uidvalidity
+ // or no messages in box
+ if (validity > 0 && validity != aValidity.toULong ())
+ {
+ aType = ITYPE_UNKNOWN;
+ kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
+ }
+ }
+ }
+
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_str = getMimeType (aType);
+ entry.append (atom);
+
+ kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
+ switch (aType)
+ {
+ case ITYPE_DIR:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFDIR;
+ entry.append (atom);
+ break;
+
+ case ITYPE_BOX:
+ case ITYPE_DIR_AND_BOX:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFDIR;
+ entry.append (atom);
+ break;
+
+ case ITYPE_MSG:
+ case ITYPE_ATTACH:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFREG;
+ entry.append (atom);
+ break;
+
+ case ITYPE_UNKNOWN:
+ error (ERR_DOES_NOT_EXIST, _url.prettyURL());
+ break;
+ }
+
+ statEntry (entry);
+ kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
+ finished ();
+}
+
+void IMAP4Protocol::openConnection()
+{
+ if (makeLogin()) connected();
+}
+
+void IMAP4Protocol::closeConnection()
+{
+ if (getState() == ISTATE_NO) return;
+ if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientExpunge());
+ completeQueue.removeRef (cmd);
+ }
+ if (getState() != ISTATE_CONNECT)
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientLogout());
+ completeQueue.removeRef (cmd);
+ }
+ closeDescriptor();
+ setState(ISTATE_NO);
+ completeQueue.clear();
+ sentQueue.clear();
+ lastHandled = 0;
+ currentBox = QString::null;
+ readBufferLen = 0;
+}
+
+bool IMAP4Protocol::makeLogin ()
+{
+ if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
+ return true;
+
+ kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
+ bool alreadyConnected = getState() == ISTATE_CONNECT;
+ kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
+ if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
+ {
+// fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY));
+
+ setState(ISTATE_CONNECT);
+
+ myAuth = metaData("auth");
+ myTLS = metaData("tls");
+ kdDebug(7116) << "myAuth: " << myAuth << endl;
+
+ imapCommand *cmd;
+
+ unhandled.clear ();
+ if (!alreadyConnected) while (!parseLoop ()) ; //get greeting
+ QString greeting;
+ if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
+ unhandled.clear (); //get rid of it
+ cmd = doCommand (new imapCommand ("CAPABILITY", ""));
+
+ kdDebug(7116) << "IMAP4: setHost: capability" << endl;
+ for (QStringList::Iterator it = imapCapabilities.begin ();
+ it != imapCapabilities.end (); ++it)
+ {
+ kdDebug(7116) << "'" << (*it) << "'" << endl;
+ }
+ completeQueue.removeRef (cmd);
+
+ if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
+ {
+ error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
+ "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
+ .arg(myHost).arg(greeting));
+ closeConnection();
+ return false;
+ }
+
+ if (metaData("nologin") == "on") return TRUE;
+
+ if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
+ {
+ error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
+ "Disable this security feature to connect unencrypted."));
+ closeConnection();
+ return false;
+ }
+ if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
+ hasCapability(QString("STARTTLS")))
+ {
+ imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
+ if (cmd->result () == "OK")
+ {
+ completeQueue.removeRef(cmd);
+ int tlsrc = startTLS();
+ if (tlsrc == 1)
+ {
+ kdDebug(7116) << "TLS mode has been enabled." << endl;
+ imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
+ for (QStringList::Iterator it = imapCapabilities.begin ();
+ it != imapCapabilities.end (); ++it)
+ {
+ kdDebug(7116) << "'" << (*it) << "'" << endl;
+ }
+ completeQueue.removeRef (cmd2);
+ } else {
+ kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl;
+ error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
+ closeConnection();
+ return false;
+ }
+ } else completeQueue.removeRef(cmd);
+ }
+
+ if (myAuth.isEmpty () || myAuth == "*") {
+ if (hasCapability (QString ("LOGINDISABLED"))) {
+ error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
+ closeConnection();
+ return false;
+ }
+ }
+ else {
+ if (!hasCapability (QString ("AUTH=") + myAuth)) {
+ error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
+ "supported by the server.").arg(myAuth));
+ closeConnection();
+ return false;
+ }
+ }
+
+ if ( greeting.contains( QRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
+ removeCapability( "ANNOTATEMORE" );
+ }
+
+ kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
+
+ KIO::AuthInfo authInfo;
+ authInfo.username = myUser;
+ authInfo.password = myPass;
+ authInfo.prompt = i18n ("Username and password for your IMAP account:");
+
+ kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
+
+ QString resultInfo;
+ if (myAuth.isEmpty () || myAuth == "*")
+ {
+ if (myUser.isEmpty () || myPass.isEmpty ()) {
+ if(openPassDlg (authInfo)) {
+ myUser = authInfo.username;
+ myPass = authInfo.password;
+ }
+ }
+ if (!clientLogin (myUser, myPass, resultInfo))
+ error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
+ "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
+ }
+ else
+ {
+#ifdef HAVE_LIBSASL2
+ if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
+ error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
+ "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
+ else {
+ myUser = authInfo.username;
+ myPass = authInfo.password;
+ }
+#else
+ error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
+#endif
+ }
+ if ( hasCapability("NAMESPACE") )
+ {
+ // get all namespaces and save the namespace - delimiter association
+ cmd = doCommand( imapCommand::clientNamespace() );
+ if (cmd->result () == "OK")
+ {
+ kdDebug(7116) << "makeLogin - registered namespaces" << endl;
+ }
+ completeQueue.removeRef (cmd);
+ }
+ // get the default delimiter (empty listing)
+ cmd = doCommand( imapCommand::clientList("", "") );
+ if (cmd->result () == "OK")
+ {
+ QValueListIterator < imapList > it = listResponses.begin();
+ if ( it == listResponses.end() )
+ {
+ // empty answer - this is a buggy imap server
+ // as a fallback we fire a normal listing and take the first answer
+ completeQueue.removeRef (cmd);
+ cmd = doCommand( imapCommand::clientList("", "%") );
+ if (cmd->result () == "OK")
+ {
+ it = listResponses.begin();
+ }
+ }
+ if ( it != listResponses.end() )
+ {
+ namespaceToDelimiter[QString::null] = (*it).hierarchyDelimiter();
+ kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
+ (*it).hierarchyDelimiter() << "'" << endl;
+ if ( !hasCapability("NAMESPACE") )
+ {
+ // server does not support namespaces
+ QString nsentry = QString::number( 0 ) + "=="
+ + (*it).hierarchyDelimiter();
+ imapNamespaces.append( nsentry );
+ }
+ }
+ }
+ completeQueue.removeRef (cmd);
+ } else {
+ kdDebug(7116) << "makeLogin - NO login" << endl;
+ }
+
+ return getState() == ISTATE_LOGIN;
+}
+
+void
+IMAP4Protocol::parseWriteLine (const QString & aStr)
+{
+ //kdDebug(7116) << "Writing: " << aStr << endl;
+ QCString writer = aStr.utf8();
+ int len = writer.length();
+
+ // append CRLF if necessary
+ if (len == 0 || (writer[len - 1] != '\n')) {
+ len += 2;
+ writer += "\r\n";
+ }
+
+ // write it
+ write(writer.data(), len);
+}
+
+QString
+IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
+{
+ switch (aType)
+ {
+ case ITYPE_DIR:
+ return "inode/directory";
+ break;
+
+ case ITYPE_BOX:
+ return "message/digest";
+ break;
+
+ case ITYPE_DIR_AND_BOX:
+ return "message/directory";
+ break;
+
+ case ITYPE_MSG:
+ return "message/rfc822";
+ break;
+
+ // this should be handled by flushOutput
+ case ITYPE_ATTACH:
+ return "application/octet-stream";
+ break;
+
+ case ITYPE_UNKNOWN:
+ default:
+ return "unknown/unknown";
+ }
+}
+
+
+
+void
+IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
+ bool withFlags, bool withSubject)
+{
+ KURL aURL = _url;
+ aURL.setQuery (QString::null);
+ const QString encodedUrl = aURL.url(0, 106); // utf-8
+ doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
+}
+
+
+
+void
+IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
+ bool withFlags, bool withSubject)
+{
+ if (cache)
+ {
+ UDSEntry entry;
+ UDSAtom atom;
+
+ entry.clear ();
+
+ const QString uid = QString::number(cache->getUid());
+
+ atom.m_uds = UDS_NAME;
+ atom.m_str = uid;
+ atom.m_long = 0;
+ if (stretch > 0)
+ {
+ atom.m_str = "0000000000000000" + atom.m_str;
+ atom.m_str = atom.m_str.right (stretch);
+ }
+ if (withSubject)
+ {
+ mailHeader *header = cache->getHeader();
+ if (header)
+ atom.m_str += " " + header->getSubject();
+ }
+ entry.append (atom);
+
+ atom.m_uds = UDS_URL;
+ atom.m_str = encodedUrl; // utf-8
+ if (atom.m_str[atom.m_str.length () - 1] != '/')
+ atom.m_str += '/';
+ atom.m_str += ";UID=" + uid;
+ atom.m_long = 0;
+ entry.append (atom);
+
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFREG;
+ entry.append (atom);
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = cache->getSize();
+ entry.append (atom);
+
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_str = "message/rfc822";
+ atom.m_long = 0;
+ entry.append (atom);
+
+ atom.m_uds = UDS_USER;
+ atom.m_str = myUser;
+ entry.append (atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
+ entry.append (atom);
+
+ listEntry (entry, false);
+ }
+}
+
+void
+IMAP4Protocol::doListEntry (const KURL & _url, const QString & myBox,
+ const imapList & item, bool appendPath)
+{
+ KURL aURL = _url;
+ aURL.setQuery (QString::null);
+ UDSEntry entry;
+ UDSAtom atom;
+ int hdLen = item.hierarchyDelimiter().length();
+
+ {
+ // mailboxName will be appended to the path if appendPath is true
+ QString mailboxName = item.name ();
+
+ // some beautification
+ if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
+ {
+ mailboxName =
+ mailboxName.right (mailboxName.length () - myBox.length ());
+ }
+ if (mailboxName[0] == '/')
+ mailboxName = mailboxName.right (mailboxName.length () - 1);
+ if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
+ mailboxName = mailboxName.right(mailboxName.length () - hdLen);
+ if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
+ mailboxName.truncate(mailboxName.length () - hdLen);
+
+ atom.m_uds = UDS_NAME;
+ if (!item.hierarchyDelimiter().isEmpty() &&
+ mailboxName.find(item.hierarchyDelimiter()) != -1)
+ atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
+ else
+ atom.m_str = mailboxName;
+
+ // konqueror will die with an assertion failure otherwise
+ if (atom.m_str.isEmpty ())
+ atom.m_str = "..";
+
+ if (!atom.m_str.isEmpty ())
+ {
+ atom.m_long = 0;
+ entry.append (atom);
+
+ if (!item.noSelect ())
+ {
+ atom.m_uds = UDS_MIME_TYPE;
+ if (!item.noInferiors ())
+ {
+ atom.m_str = "message/directory";
+ } else {
+ atom.m_str = "message/digest";
+ }
+ atom.m_long = 0;
+ entry.append (atom);
+ mailboxName += '/';
+
+ // explicitly set this as a directory for KFileDialog
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFDIR;
+ entry.append (atom);
+ }
+ else if (!item.noInferiors ())
+ {
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_str = "inode/directory";
+ atom.m_long = 0;
+ entry.append (atom);
+ mailboxName += '/';
+
+ // explicitly set this as a directory for KFileDialog
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = QString::null;
+ atom.m_long = S_IFDIR;
+ entry.append (atom);
+ }
+ else
+ {
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_str = "unknown/unknown";
+ atom.m_long = 0;
+ entry.append (atom);
+ }
+
+ atom.m_uds = UDS_URL;
+ QString path = aURL.path();
+ atom.m_str = aURL.url (0, 106); // utf-8
+ if (appendPath)
+ {
+ if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
+ path.truncate(path.length() - 1);
+ if (!path.isEmpty() && path != "/"
+ && path.right(hdLen) != item.hierarchyDelimiter()) {
+ path += item.hierarchyDelimiter();
+ }
+ path += mailboxName;
+ if (path.upper() == "/INBOX/") {
+ // make sure the client can rely on INBOX
+ path = path.upper();
+ }
+ }
+ aURL.setPath(path);
+ atom.m_str = aURL.url(0, 106); // utf-8
+ atom.m_long = 0;
+ entry.append (atom);
+
+ atom.m_uds = UDS_USER;
+ atom.m_str = myUser;
+ entry.append (atom);
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
+ entry.append (atom);
+
+ atom.m_uds = UDS_EXTRA;
+ atom.m_str = item.attributesAsString();
+ atom.m_long = 0;
+ entry.append (atom);
+
+ listEntry (entry, false);
+ }
+ }
+}
+
+enum IMAP_TYPE
+IMAP4Protocol::parseURL (const KURL & _url, QString & _box,
+ QString & _section, QString & _type, QString & _uid,
+ QString & _validity, QString & _hierarchyDelimiter,
+ QString & _info, bool cache)
+{
+ enum IMAP_TYPE retVal;
+ retVal = ITYPE_UNKNOWN;
+
+ imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
+// kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl;
+
+ // get the delimiter
+ QString myNamespace = namespaceForBox( _box );
+ kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
+ if ( namespaceToDelimiter.contains(myNamespace) )
+ {
+ _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
+ kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
+ }
+
+ if (!_box.isEmpty ())
+ {
+ kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
+
+ if (makeLogin ())
+ {
+ if (getCurrentBox () != _box ||
+ _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
+ {
+ if ( cache )
+ {
+ // assume a normal box
+ retVal = ITYPE_DIR_AND_BOX;
+ } else
+ {
+ // start a listing for the box to get the type
+ imapCommand *cmd;
+
+ cmd = doCommand (imapCommand::clientList ("", _box));
+ if (cmd->result () == "OK")
+ {
+ for (QValueListIterator < imapList > it = listResponses.begin ();
+ it != listResponses.end (); ++it)
+ {
+ //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl;
+ if (_box == (*it).name ())
+ {
+ if ( !(*it).hierarchyDelimiter().isEmpty() )
+ _hierarchyDelimiter = (*it).hierarchyDelimiter();
+ if ((*it).noSelect ())
+ {
+ retVal = ITYPE_DIR;
+ }
+ else if ((*it).noInferiors ())
+ {
+ retVal = ITYPE_BOX;
+ }
+ else
+ {
+ retVal = ITYPE_DIR_AND_BOX;
+ }
+ }
+ }
+ // if we got no list response for the box see if it's a prefix
+ if ( retVal == ITYPE_UNKNOWN &&
+ namespaceToDelimiter.contains(_box) ) {
+ retVal = ITYPE_DIR;
+ }
+ } else {
+ kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
+ }
+ completeQueue.removeRef (cmd);
+ } // cache
+ }
+ else // current == box
+ {
+ retVal = ITYPE_BOX;
+ }
+ }
+ else
+ kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
+
+ }
+ else // empty box
+ {
+ // the root is just a dir
+ kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
+ retVal = ITYPE_DIR;
+ }
+
+ // see if it is a real sequence or a simple uid
+ if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
+ {
+ if (!_uid.isEmpty ())
+ {
+ if (_uid.find (':') == -1 && _uid.find (',') == -1
+ && _uid.find ('*') == -1)
+ retVal = ITYPE_MSG;
+ }
+ }
+ if (retVal == ITYPE_MSG)
+ {
+ if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
+ _section.find ("BODY[", 0, false) != -1) &&
+ _section.find(".MIME") == -1 &&
+ _section.find(".HEADER") == -1 )
+ retVal = ITYPE_ATTACH;
+ }
+ if ( _hierarchyDelimiter.isEmpty() &&
+ (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
+ {
+ // this shouldn't happen but when the delimiter is really empty
+ // we try to reconstruct it from the URL
+ if (!_box.isEmpty())
+ {
+ int start = _url.path().findRev(_box);
+ if (start != -1)
+ _hierarchyDelimiter = _url.path().mid(start-1, start);
+ kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
+ << " from URL " << _url.path() << endl;
+ }
+ if (_hierarchyDelimiter.isEmpty())
+ _hierarchyDelimiter = "/";
+ }
+ kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
+
+ return retVal;
+}
+
+int
+IMAP4Protocol::outputLine (const QCString & _str, int len)
+{
+ if (len == -1) {
+ len = _str.length();
+ }
+
+ if (cacheOutput)
+ {
+ if ( !outputBuffer.isOpen() ) {
+ outputBuffer.open(IO_WriteOnly);
+ }
+ outputBuffer.at(outputBufferIndex);
+ outputBuffer.writeBlock(_str.data(), len);
+ outputBufferIndex += len;
+ return 0;
+ }
+
+ QByteArray temp;
+ bool relay = relayEnabled;
+
+ relayEnabled = true;
+ temp.setRawData (_str.data (), len);
+ parseRelay (temp);
+ temp.resetRawData (_str.data (), len);
+
+ relayEnabled = relay;
+ return 0;
+}
+
+void IMAP4Protocol::flushOutput(QString contentEncoding)
+{
+ // send out cached data to the application
+ if (outputBufferIndex == 0)
+ return;
+ outputBuffer.close();
+ outputCache.resize(outputBufferIndex);
+ if (decodeContent)
+ {
+ // get the coding from the MIME header
+ QByteArray decoded;
+ if (contentEncoding.find("quoted-printable", 0, false) == 0)
+ decoded = KCodecs::quotedPrintableDecode(outputCache);
+ else if (contentEncoding.find("base64", 0, false) == 0)
+ KCodecs::base64Decode(outputCache, decoded);
+ else
+ decoded = outputCache;
+
+ QString mimetype = KMimeType::findByContent( decoded )->name();
+ kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
+ mimeType(mimetype);
+ decodeContent = false;
+ data( decoded );
+ } else {
+ data( outputCache );
+ }
+ mProcessedSize += outputBufferIndex;
+ processedSize( mProcessedSize );
+ outputBufferIndex = 0;
+ outputCache[0] = '\0';
+ outputBuffer.setBuffer(outputCache);
+}
+
+ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
+{
+ if (readBufferLen)
+ {
+ ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
+ memcpy(data, readBuffer, copyLen);
+ readBufferLen -= copyLen;
+ if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
+ return copyLen;
+ }
+ if (!isConnectionValid()) return 0;
+ waitForResponse( responseTimeout() );
+ return read(data, len);
+}
+
+bool
+IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
+{
+ if (aBox.isEmpty()) return false;
+
+ imapCommand *cmd = 0;
+
+ if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
+ {
+ // open the box with the appropriate mode
+ kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
+ selectInfo = imapInfo();
+ cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
+ bool ok = cmd->result() == "OK";
+ QString cmdInfo = cmd->resultInfo();
+ completeQueue.removeRef (cmd);
+
+ if (!ok)
+ {
+ bool found = false;
+ cmd = doCommand (imapCommand::clientList ("", aBox));
+ if (cmd->result () == "OK")
+ {
+ for (QValueListIterator < imapList > it = listResponses.begin ();
+ it != listResponses.end (); ++it)
+ {
+ if (aBox == (*it).name ()) found = true;
+ }
+ }
+ completeQueue.removeRef (cmd);
+ if (found) {
+ if (cmdInfo.find("permission", 0, false) != -1) {
+ // not allowed to enter this folder
+ error(ERR_ACCESS_DENIED, cmdInfo);
+ } else {
+ error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
+ }
+ } else {
+ error(KIO::ERR_DOES_NOT_EXIST, aBox);
+ }
+ return false;
+ }
+ }
+ else
+ {
+ // Give the server a chance to deliver updates every ten seconds.
+ // Doing this means a server roundtrip and since assureBox is called
+ // after every mail, we do it with a timeout.
+ kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
+ if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
+ cmd = doCommand (imapCommand::clientNoop ());
+ completeQueue.removeRef (cmd);
+ mTimeOfLastNoop = QDateTime::currentDateTime();
+ kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
+ }
+ }
+
+ // if it is the mode we want
+ if (!getSelected().readWrite() && !readonly)
+ {
+ error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
+ return false;
+ }
+
+ return true;
+}
diff --git a/kioslaves/imap4/imap4.h b/kioslaves/imap4/imap4.h
new file mode 100644
index 000000000..b86a3815b
--- /dev/null
+++ b/kioslaves/imap4/imap4.h
@@ -0,0 +1,205 @@
+#ifndef _IMAP4_H
+#define _IMAP4_H
+/**********************************************************************
+ *
+ * imap4.h - IMAP4rev1 KIOSlave
+ * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
+ * Copyright (C) 1999 John Corey
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to jcorey@fruity.ath.cx
+ *
+ *********************************************************************/
+
+#include "imapparser.h"
+#include "mimeio.h"
+
+#include <kio/tcpslavebase.h>
+#include <qbuffer.h>
+
+#define IMAP_BUFFER 8192
+
+/** @brief type of object the url refers too */
+enum IMAP_TYPE
+{
+ ITYPE_UNKNOWN, /*< unknown type */
+ ITYPE_DIR, /*< Object is a directory. i.e. does not contain message, just mailboxes */
+ ITYPE_BOX, /*< Object is a mailbox. i.e. contains mails */
+ ITYPE_DIR_AND_BOX, /*< Object contains both mails and mailboxes */
+ ITYPE_MSG, /*< Object is a mail */
+ ITYPE_ATTACH /*< Object is an attachment */
+};
+
+/** @brief IOSlave derived class */
+class IMAP4Protocol:public
+ KIO::TCPSlaveBase,
+ public
+ imapParser,
+ public
+ mimeIO
+{
+
+public:
+
+ // reimplement the TCPSlave
+ IMAP4Protocol (const QCString & pool, const QCString & app, bool isSSL);
+ virtual ~IMAP4Protocol ();
+
+ virtual void openConnection();
+ virtual void closeConnection();
+
+ virtual void setHost (const QString & _host, int _port, const QString & _user,
+ const QString & _pass);
+ /**
+ * @brief get a message or part of a message
+ * the data is normally send as we get it from the server
+ * if you want the slave to decode the content (e.g. for attachments)
+ * then append an additional INFO=DECODE to the URL
+ */
+ virtual void get (const KURL & _url);
+ /**
+ * @brief stat a mailbox, message, attachment
+ */
+ virtual void stat (const KURL & _url);
+ virtual void slave_status ();
+ /**
+ * @brief delete a mailbox
+ */
+ virtual void del (const KURL & _url, bool isFile);
+ /**
+ * @brief Capabilites, NOOP, (Un)subscribe, Change status,
+ * Change ACL
+ */
+ virtual void special (const QByteArray & data);
+ /**
+ * @brief list a directory/mailbox
+ */
+ virtual void listDir (const KURL & _url);
+ virtual void setSubURL (const KURL & _url);
+ virtual void dispatch (int command, const QByteArray & data);
+ /**
+ * @brief create a mailbox
+ */
+ virtual void mkdir (const KURL & url, int permissions);
+ virtual void put (const KURL & url, int permissions, bool overwrite,
+ bool resume);
+ virtual void rename (const KURL & src, const KURL & dest, bool overwrite);
+ virtual void copy (const KURL & src, const KURL & dest, int permissions,
+ bool overwrite);
+
+ /** @brief reimplement the parser
+ * relay hook to send the fetched data directly to an upper level
+ */
+ virtual void parseRelay (const QByteArray & buffer);
+
+ /** @brief reimplement the parser
+ * relay hook to announce the fetched data directly to an upper level
+ */
+ virtual void parseRelay (ulong);
+
+ /** @brief reimplement the parser
+ * read at least len bytes */
+ virtual bool parseRead (QByteArray &buffer,ulong len,ulong relay=0);
+
+ /** @brief reimplement the parser
+ * @brief read at least a line (up to CRLF) */
+ virtual bool parseReadLine (QByteArray & buffer, ulong relay = 0);
+
+ /** @brief reimplement the parser
+ * @brief write argument to the server */
+ virtual void parseWriteLine (const QString &);
+
+ /** @brief reimplement the mimeIO */
+ virtual int outputLine (const QCString & _str, int len = -1);
+
+ /** @brief send out cached data to the application */
+ virtual void flushOutput(QString contentEncoding = QString::null);
+
+protected:
+
+ // select or examine the box if needed
+ bool assureBox (const QString & aBox, bool readonly);
+
+ ssize_t myRead(void *data, ssize_t len);
+
+ /**
+ * @brief Parses the given URL
+ * The return values are set by parsing the URL and querying the server
+ *
+ * If you set caching to true the server is not queried but the type is always
+ * set to ITYPE_DIR_AND_BOX
+ */
+ enum IMAP_TYPE
+ parseURL (const KURL & _url, QString & _box, QString & _section,
+ QString & _type, QString & _uid, QString & _validity,
+ QString & _hierarchyDelimiter, QString & _info,
+ bool cache = false);
+ QString getMimeType (enum IMAP_TYPE);
+
+ bool makeLogin ();
+
+ void outputLineStr (const QString & _str)
+ {
+ outputLine (_str.latin1 (), _str.length());
+ }
+ void doListEntry (const KURL & _url, int stretch, imapCache * cache = NULL,
+ bool withFlags = FALSE, bool withSubject = FALSE);
+
+ /**
+ * Send a list entry (folder) to the application
+ * If @p appendPath is true the foldername will be appended
+ * to the path of @p url
+ */
+ void doListEntry (const KURL & url, const QString & myBox,
+ const imapList & item, bool appendPath = true);
+
+ /** Send an ACL command which is identified by @p command */
+ void specialACLCommand( int command, QDataStream& stream );
+
+ /** Send an annotation command which is identified by @p command */
+ void specialAnnotateMoreCommand( int command, QDataStream& stream );
+ void specialQuotaCommand( int command, QDataStream& stream );
+
+ /** Search current folder, the search string is passed as SECTION */
+ void specialSearchCommand( QDataStream& );
+
+ /** Send a custom command to the server */
+ void specialCustomCommand( QDataStream& );
+
+private:
+
+ // This method behaves like the above method but takes an already encoded url,
+ // so you don't have to call KURL::url() for every mail.
+ void doListEntry (const QString & encodedUrl, int stretch, imapCache * cache = NULL,
+ bool withFlags = FALSE, bool withSubject = FALSE);
+
+ QString myHost, myUser, myPass, myAuth, myTLS;
+ int myPort;
+ bool mySSL;
+
+ bool relayEnabled, cacheOutput, decodeContent;
+ QByteArray outputCache;
+ QBuffer outputBuffer;
+ Q_ULONG outputBufferIndex;
+ KIO::filesize_t mProcessedSize;
+
+ char readBuffer[IMAP_BUFFER];
+ ssize_t readBufferLen;
+ int readSize;
+ QDateTime mTimeOfLastNoop;
+};
+
+#endif
diff --git a/kioslaves/imap4/imap4.protocol b/kioslaves/imap4/imap4.protocol
new file mode 100644
index 000000000..1ab920429
--- /dev/null
+++ b/kioslaves/imap4/imap4.protocol
@@ -0,0 +1,29 @@
+[Protocol]
+# The executable, of course
+#### Temporary name
+exec=kio_imap4
+# protocol that will appear in URLs
+protocol=imap
+
+# input/output can be one of: filesystem, stream, none
+input=stream
+output=filesystem
+
+# Headings for file listings?
+listing=Name,Type,Size,Owner
+deleting=true
+linking=false
+# For now, reading yes, writing no
+reading=true
+writing=false
+# For now, no moving
+moving=false
+
+# Can be source protocol
+source=true
+
+# List of capabilities (e.g. special() commands)
+Capabilities=Subscription,ACL,Quota
+
+Icon=folder_inbox
+DocPath=kioslave/imap.html
diff --git a/kioslaves/imap4/imapcommand.cc b/kioslaves/imap4/imapcommand.cc
new file mode 100644
index 000000000..e5eee776f
--- /dev/null
+++ b/kioslaves/imap4/imapcommand.cc
@@ -0,0 +1,408 @@
+/**********************************************************************
+ *
+ * imapcommand.cc - IMAP4rev1 command handler
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+
+#include "imapcommand.h"
+#include "rfcdecoder.h"
+
+/*#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <qregexp.h>
+#include <qbuffer.h>
+
+#include <kprotocolmanager.h>
+#include <ksock.h>
+#include <kdebug.h>
+#include <kinstance.h>
+#include <kio/connection.h>
+#include <kio/slaveinterface.h>
+#include <kio/passdlg.h>
+#include <klocale.h> */
+
+imapCommand::imapCommand ()
+{
+ mComplete = false;
+ mId = QString::null;
+}
+
+imapCommand::imapCommand (const QString & command, const QString & parameter)
+// aCommand(NULL),
+// mResult(NULL),
+// mParameter(NULL)
+{
+ mComplete = false;
+ aCommand = command;
+ aParameter = parameter;
+ mId = QString::null;
+}
+
+bool
+imapCommand::isComplete ()
+{
+ return mComplete;
+}
+
+const QString &
+imapCommand::result ()
+{
+ return mResult;
+}
+
+const QString &
+imapCommand::resultInfo ()
+{
+ return mResultInfo;
+}
+
+const QString &
+imapCommand::id ()
+{
+ return mId;
+}
+
+const QString &
+imapCommand::parameter ()
+{
+ return aParameter;
+}
+
+const QString &
+imapCommand::command ()
+{
+ return aCommand;
+}
+
+void
+imapCommand::setId (const QString & id)
+{
+ if (mId.isEmpty ())
+ mId = id;
+}
+
+void
+imapCommand::setComplete ()
+{
+ mComplete = true;
+}
+
+void
+imapCommand::setResult (const QString & result)
+{
+ mResult = result;
+}
+
+void
+imapCommand::setResultInfo (const QString & result)
+{
+ mResultInfo = result;
+}
+
+void
+imapCommand::setCommand (const QString & command)
+{
+ aCommand = command;
+}
+
+void
+imapCommand::setParameter (const QString & parameter)
+{
+ aParameter = parameter;
+}
+
+const QString
+imapCommand::getStr ()
+{
+ if (parameter().isEmpty())
+ return id() + " " + command() + "\r\n";
+ else
+ return id() + " " + command() + " " + parameter() + "\r\n";
+}
+
+imapCommand *
+imapCommand::clientNoop ()
+{
+ return new imapCommand ("NOOP", "");
+}
+
+imapCommand *
+imapCommand::clientFetch (ulong uid, const QString & fields, bool nouid)
+{
+ return clientFetch (uid, uid, fields, nouid);
+}
+
+imapCommand *
+imapCommand::clientFetch (ulong fromUid, ulong toUid, const QString & fields,
+ bool nouid)
+{
+ QString uid = QString::number(fromUid);
+
+ if (fromUid != toUid)
+ {
+ uid += ":";
+ if (toUid < fromUid)
+ uid += "*";
+ else
+ uid += QString::number(toUid);
+ }
+ return clientFetch (uid, fields, nouid);
+}
+
+imapCommand *
+imapCommand::clientFetch (const QString & sequence, const QString & fields,
+ bool nouid)
+{
+ return new imapCommand (nouid ? "FETCH" : "UID FETCH",
+ sequence + " (" + fields + ")");
+}
+
+imapCommand *
+imapCommand::clientList (const QString & reference, const QString & path,
+ bool lsub)
+{
+ return new imapCommand (lsub ? "LSUB" : "LIST",
+ QString ("\"") + rfcDecoder::toIMAP (reference) +
+ "\" \"" + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientSelect (const QString & path, bool examine)
+{
+ Q_UNUSED(examine);
+ /** @note We use always SELECT, because UW-IMAP doesn't check for new mail, when
+ used with the "mbox driver" and the folder is opened with EXAMINE
+ and Courier can't append to a mailbox that is in EXAMINE state */
+ return new imapCommand ("SELECT",
+ QString ("\"") + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientClose()
+{
+ return new imapCommand("CLOSE", "");
+}
+
+imapCommand *
+imapCommand::clientCopy (const QString & box, const QString & sequence,
+ bool nouid)
+{
+ return new imapCommand (nouid ? "COPY" : "UID COPY",
+ sequence + " \"" + rfcDecoder::toIMAP (box) + "\"");
+}
+
+imapCommand *
+imapCommand::clientAppend (const QString & box, const QString & flags,
+ ulong size)
+{
+ return new imapCommand ("APPEND",
+ "\"" + rfcDecoder::toIMAP (box) + "\" " +
+ ((flags.isEmpty()) ? "" : ("(" + flags + ") ")) +
+ "{" + QString::number(size) + "}");
+}
+
+imapCommand *
+imapCommand::clientStatus (const QString & path, const QString & parameters)
+{
+ return new imapCommand ("STATUS",
+ QString ("\"") + rfcDecoder::toIMAP (path) +
+ "\" (" + parameters + ")");
+}
+
+imapCommand *
+imapCommand::clientCreate (const QString & path)
+{
+ return new imapCommand ("CREATE",
+ QString ("\"") + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientDelete (const QString & path)
+{
+ return new imapCommand ("DELETE",
+ QString ("\"") + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientSubscribe (const QString & path)
+{
+ return new imapCommand ("SUBSCRIBE",
+ QString ("\"") + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientUnsubscribe (const QString & path)
+{
+ return new imapCommand ("UNSUBSCRIBE",
+ QString ("\"") + rfcDecoder::toIMAP (path) + "\"");
+}
+
+imapCommand *
+imapCommand::clientExpunge ()
+{
+ return new imapCommand ("EXPUNGE", QString (""));
+}
+
+imapCommand *
+imapCommand::clientRename (const QString & src, const QString & dest)
+{
+ return new imapCommand ("RENAME",
+ QString ("\"") + rfcDecoder::toIMAP (src) +
+ "\" \"" + rfcDecoder::toIMAP (dest) + "\"");
+}
+
+imapCommand *
+imapCommand::clientSearch (const QString & search, bool nouid)
+{
+ return new imapCommand (nouid ? "SEARCH" : "UID SEARCH", search);
+}
+
+imapCommand *
+imapCommand::clientStore (const QString & set, const QString & item,
+ const QString & data, bool nouid)
+{
+ return new imapCommand (nouid ? "STORE" : "UID STORE",
+ set + " " + item + " (" + data + ")");
+}
+
+imapCommand *
+imapCommand::clientLogout ()
+{
+ return new imapCommand ("LOGOUT", "");
+}
+
+imapCommand *
+imapCommand::clientStartTLS ()
+{
+ return new imapCommand ("STARTTLS", "");
+}
+
+imapCommand *
+imapCommand::clientSetACL( const QString& box, const QString& user, const QString& acl )
+{
+ return new imapCommand ("SETACL", QString("\"") + rfcDecoder::toIMAP (box)
+ + "\" \"" + rfcDecoder::toIMAP (user)
+ + "\" \"" + rfcDecoder::toIMAP (acl) + "\"");
+}
+
+imapCommand *
+imapCommand::clientDeleteACL( const QString& box, const QString& user )
+{
+ return new imapCommand ("DELETEACL", QString("\"") + rfcDecoder::toIMAP (box)
+ + "\" \"" + rfcDecoder::toIMAP (user)
+ + "\"");
+}
+
+imapCommand *
+imapCommand::clientGetACL( const QString& box )
+{
+ return new imapCommand ("GETACL", QString("\"") + rfcDecoder::toIMAP (box)
+ + "\"");
+}
+
+imapCommand *
+imapCommand::clientListRights( const QString& box, const QString& user )
+{
+ return new imapCommand ("LISTRIGHTS", QString("\"") + rfcDecoder::toIMAP (box)
+ + "\" \"" + rfcDecoder::toIMAP (user)
+ + "\"");
+}
+
+imapCommand *
+imapCommand::clientMyRights( const QString& box )
+{
+ return new imapCommand ("MYRIGHTS", QString("\"") + rfcDecoder::toIMAP (box)
+ + "\"");
+}
+
+imapCommand *
+imapCommand::clientSetAnnotation( const QString& box, const QString& entry, const QMap<QString, QString>& attributes )
+{
+ QString parameter = QString("\"") + rfcDecoder::toIMAP (box)
+ + "\" \"" + rfcDecoder::toIMAP (entry)
+ + "\" (";
+ for( QMap<QString,QString>::ConstIterator it = attributes.begin(); it != attributes.end(); ++it ) {
+ parameter += "\"";
+ parameter += rfcDecoder::toIMAP (it.key());
+ parameter += "\" \"";
+ parameter += rfcDecoder::toIMAP (it.data());
+ parameter += "\" ";
+ }
+ // Turn last space into a ')'
+ parameter[parameter.length()-1] = ')';
+
+ return new imapCommand ("SETANNOTATION", parameter);
+}
+
+imapCommand *
+imapCommand::clientGetAnnotation( const QString& box, const QString& entry, const QStringList& attributeNames )
+{
+ QString parameter = QString("\"") + rfcDecoder::toIMAP (box)
+ + "\" \"" + rfcDecoder::toIMAP (entry)
+ + "\" ";
+ if ( attributeNames.count() == 1 )
+ parameter += "\"" + rfcDecoder::toIMAP (attributeNames.first()) + '"';
+ else {
+ parameter += '(';
+ for( QStringList::ConstIterator it = attributeNames.begin(); it != attributeNames.end(); ++it ) {
+ parameter += "\"" + rfcDecoder::toIMAP (*it) + "\" ";
+ }
+ // Turn last space into a ')'
+ parameter[parameter.length()-1] = ')';
+ }
+ return new imapCommand ("GETANNOTATION", parameter);
+}
+
+imapCommand *
+imapCommand::clientNamespace()
+{
+ return new imapCommand("NAMESPACE", "");
+}
+
+imapCommand *
+imapCommand::clientGetQuotaroot( const QString& box )
+{
+ QString parameter = QString("\"") + rfcDecoder::toIMAP (box) + '"';
+ return new imapCommand ("GETQUOTAROOT", parameter);
+}
+
+imapCommand *
+imapCommand::clientCustom( const QString& command, const QString& arguments )
+{
+ return new imapCommand (command, arguments);
+}
+
diff --git a/kioslaves/imap4/imapcommand.h b/kioslaves/imap4/imapcommand.h
new file mode 100644
index 000000000..f06c5af86
--- /dev/null
+++ b/kioslaves/imap4/imapcommand.h
@@ -0,0 +1,394 @@
+#ifndef _IMAPCOMMAND_H
+#define _IMAPCOMMAND_H
+/**********************************************************************
+ *
+ * imapcommand.h - IMAP4rev1 command handler
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qmap.h>
+
+/**
+ * @brief encapulate a IMAP command
+ * @author Svenn Carstens
+ * @date 2000
+ * @todo fix the documentation
+ */
+
+class imapCommand
+{
+public:
+
+ /**
+ * @brief Constructor
+ */
+ imapCommand ();
+ /**
+ * @fn imapCommand (const QString & command, const QString & parameter);
+ * @brief Constructor
+ * @param command Imap command
+ * @param parameter Parameters to the command
+ * @return none
+ */
+ imapCommand (const QString & command, const QString & parameter);
+ /**
+ * @fn bool isComplete ();
+ * @brief is it complete?
+ * @return whether the command is completed
+ */
+ bool isComplete ();
+ /**
+ * @fn const QString & result ();
+ * @brief get the result of the command
+ * @return The result, i.e. first word of the result line, like OK
+ */
+ const QString & result ();
+ /**
+ * @fn const QString & resultInfo ();
+ * @brief get information about the result
+ * @return Information about the result, i.e. the rest of the result line
+ */
+ const QString & resultInfo ();
+ /**
+ * @fn const QString & parameter ();
+ * @brief get the parameter
+ * @return the parameter
+ */
+ const QString & parameter ();
+ /**
+ * @fn const QString & command ();
+ * @brief get the command
+ * @return the command
+ */
+ const QString & command ();
+ /**
+ * @fn const QString & id ();
+ * @brief get the id
+ * @return the id
+ */
+ const QString & id ();
+
+ /**
+ * @fn void setId (const QString &);
+ * @brief set the id
+ * @param id the id used by the command
+ * @return none
+ */
+ void setId (const QString &);
+ /**
+ * @fn void setComplete ();
+ * @brief set the completed state
+ * @return none
+ */
+ void setComplete ();
+ /**
+ * @fn void setResult (const QString &);
+ * @brief set the completed state
+ * @param result the command result
+ * @return none
+ */
+ void setResult (const QString &);
+ /**
+ * @fn void setResultInfo (const QString &);
+ * @brief set the completed state
+ * @param result the command result information
+ * @return none
+ */
+ void setResultInfo (const QString &);
+ /**
+ * @fn void setCommand (const QString &);
+ * @brief set the command
+ * @param command the imap command
+ * @return none
+ */
+ void setCommand (const QString &);
+ /**
+ * @fn void setParameter (const QString &);
+ * @brief set the command parameter(s)
+ * @param parameter the comand parameter(s)
+ * @return none
+ */
+ void setParameter (const QString &);
+ /**
+ * @fn const QString getStr ();
+ * @brief returns the data to send to the server
+ * The function returns the complete data to be sent to
+ * the server (\<id\> \<command\> [\<parameter\>])
+ * @return the data to send to the server
+ * @todo possibly rename function to be clear of it's purpose
+ */
+ const QString getStr ();
+
+ /**
+ * @fn static imapCommand *clientNoop ();
+ * @brief Create a NOOP command
+ * @return a NOOP imapCommand
+ */
+ static imapCommand *clientNoop ();
+ /**
+ * @fn static imapCommand *clientFetch (ulong uid, const QString & fields, bool nouid = false);
+ * @brief Create a FETCH command
+ * @param uid Uid of the message to fetch
+ * @param fields options to pass to the server
+ * @param nouid Perform a FETCH or UID FETCH command
+ * @return a FETCH imapCommand
+ * Fetch a single uid
+ */
+ static imapCommand *clientFetch (ulong uid, const QString & fields,
+ bool nouid = false);
+ /**
+ * @fn static imapCommand *clientFetch (ulong fromUid, ulong toUid, const QString & fields, bool nouid = false);
+ * @brief Create a FETCH command
+ * @param fromUid start uid of the messages to fetch
+ * @param toUid last uid of the messages to fetch
+ * @param fields options to pass to the server
+ * @param nouid Perform a FETCH or UID FETCH command
+ * @return a FETCH imapCommand
+ * Fetch a range of uids
+ */
+ static imapCommand *clientFetch (ulong fromUid, ulong toUid,
+ const QString & fields, bool nouid =
+ false);
+ /**
+ * @fn static imapCommand *clientFetch (const QString & sequence, const QString & fields, bool nouid = false);
+ * @brief Create a FETCH command
+ * @param sequence a IMAP FETCH sequence string
+ * @param fields options to pass to the server
+ * @param nouid Perform a FETCH or UID FETCH command
+ * @return a FETCH imapCommand
+ * Fetch a range of uids. The other clientFetch functions are just
+ * wrappers around this function.
+ */
+ static imapCommand *clientFetch (const QString & sequence,
+ const QString & fields, bool nouid =
+ false);
+ /**
+ * @fn static imapCommand *clientList (const QString & reference, const QString & path, bool lsub = false);
+ * @brief Create a LIST command
+ * @param reference
+ * @param path The path to list
+ * @param lsub Perform a LIST or a LSUB command
+ * @return a LIST imapCommand
+ */
+ static imapCommand *clientList (const QString & reference,
+ const QString & path, bool lsub = false);
+ /**
+ * @fn static imapCommand *clientSelect (const QString & path, bool examine = false);
+ * @brief Create a SELECT command
+ * @param path The path to select
+ * @param lsub Perform a SELECT or a EXAMINE command
+ * @return a SELECT imapCommand
+ */
+ static imapCommand *clientSelect (const QString & path, bool examine =
+ false);
+ /**
+ * @fn static imapCommand *clientClose();
+ * @brief Create a CLOSE command
+ * @return a CLOSE imapCommand
+ */
+ static imapCommand *clientClose();
+ /**
+ * @brief Create a STATUS command
+ * @param path
+ * @param parameters
+ * @return a STATUS imapCommand
+ */
+ static imapCommand *clientStatus (const QString & path,
+ const QString & parameters);
+ /**
+ * @brief Create a COPY command
+ * @param box
+ * @param sequence
+ * @param nouid Perform a COPY or UID COPY command
+ * @return a COPY imapCommand
+ */
+ static imapCommand *clientCopy (const QString & box,
+ const QString & sequence, bool nouid =
+ false);
+ /**
+ * @brief Create a APPEND command
+ * @param box
+ * @param flags
+ * @param size
+ * @return a APPEND imapCommand
+ */
+ static imapCommand *clientAppend (const QString & box,
+ const QString & flags, ulong size);
+ /**
+ * @brief Create a CREATE command
+ * @param path
+ * @return a CREATE imapCommand
+ */
+ static imapCommand *clientCreate (const QString & path);
+ /**
+ * @brief Create a DELETE command
+ * @param path
+ * @return a DELETE imapCommand
+ */
+ static imapCommand *clientDelete (const QString & path);
+ /**
+ * @brief Create a SUBSCRIBE command
+ * @param path
+ * @return a SUBSCRIBE imapCommand
+ */
+ static imapCommand *clientSubscribe (const QString & path);
+ /**
+ * @brief Create a UNSUBSCRIBE command
+ * @param path
+ * @return a UNSUBSCRIBE imapCommand
+ */
+ static imapCommand *clientUnsubscribe (const QString & path);
+ /**
+ * @brief Create a EXPUNGE command
+ * @return a EXPUNGE imapCommand
+ */
+ static imapCommand *clientExpunge ();
+ /**
+ * @brief Create a RENAME command
+ * @param src Source
+ * @param dest Destination
+ * @return a RENAME imapCommand
+ */
+ static imapCommand *clientRename (const QString & src,
+ const QString & dest);
+ /**
+ * @brief Create a SEARCH command
+ * @param search
+ * @param nouid Perform a UID SEARCH or a SEARCH command
+ * @return a SEARCH imapCommand
+ */
+ static imapCommand *clientSearch (const QString & search, bool nouid =
+ false);
+ /**
+ * @brief Create a STORE command
+ * @param set
+ * @param item
+ * @param data
+ * @param nouid Perform a UID STORE or a STORE command
+ * @return a STORE imapCommand
+ */
+ static imapCommand *clientStore (const QString & set, const QString & item,
+ const QString & data, bool nouid = false);
+ /**
+ * @brief Create a LOGOUT command
+ * @return a LOGOUT imapCommand
+ */
+ static imapCommand *clientLogout ();
+ /**
+ * @brief Create a STARTTLS command
+ * @return a STARTTLS imapCommand
+ */
+ static imapCommand *clientStartTLS ();
+
+ //////////// ACL support (RFC 2086) /////////////
+ /**
+ * @brief Create a SETACL command
+ * @param box mailbox name
+ * @param user authentication identifier
+ * @param acl access right modification (starting with optional +/-)
+ * @return a SETACL imapCommand
+ */
+ static imapCommand *clientSetACL ( const QString& box, const QString& user, const QString& acl );
+
+ /**
+ * @brief Create a DELETEACL command
+ * @param box mailbox name
+ * @param user authentication identifier
+ * @return a DELETEACL imapCommand
+ */
+ static imapCommand *clientDeleteACL ( const QString& box, const QString& user );
+
+ /**
+ * @brief Create a GETACL command
+ * @param box mailbox name
+ * @return a GETACL imapCommand
+ */
+ static imapCommand *clientGetACL ( const QString& box );
+
+ /**
+ * @brief Create a LISTRIGHTS command
+ * @param box mailbox name
+ * @param user authentication identifier
+ * @return a LISTRIGHTS imapCommand
+ */
+ static imapCommand *clientListRights ( const QString& box, const QString& user );
+
+ /**
+ * @brief Create a MYRIGHTS command
+ * @param box mailbox name
+ * @return a MYRIGHTS imapCommand
+ */
+ static imapCommand *clientMyRights ( const QString& box );
+
+ //////////// ANNOTATEMORE support /////////////
+ /**
+ * @brief Create a SETANNOTATION command
+ * @param box mailbox name
+ * @param entry entry specifier
+ * @param attributes map of attribute names + values
+ * @return a SETANNOTATION imapCommand
+ */
+ static imapCommand *clientSetAnnotation ( const QString& box, const QString& entry, const QMap<QString, QString>& attributes );
+
+ /**
+ * @brief Create a GETANNOTATION command
+ * @param box mailbox name
+ * @param entry entry specifier
+ * @param attributeNames attribute specifier
+ * @return a GETANNOTATION imapCommand
+ */
+ static imapCommand *clientGetAnnotation ( const QString& box, const QString& entry, const QStringList& attributeNames );
+
+ /**
+ * @brief Create a NAMESPACE command
+ * @return a NAMESPACE imapCommand
+ */
+ static imapCommand *clientNamespace ();
+
+ /**
+ * @brief Create a GETQUOTAROOT command
+ * @param box mailbox name
+ * @return a GETQUOTAROOT imapCommand
+ */
+ static imapCommand *clientGetQuotaroot ( const QString& box );
+
+ /**
+ * @brief Create a custom command
+ * @param command The custom command
+ * @param arguments The custom arguments
+ * @return a custom imapCommand
+ */
+ static imapCommand *clientCustom ( const QString& command, const QString& arguments );
+
+protected:
+ QString aCommand;
+ QString mId;
+ bool mComplete;
+ QString aParameter;
+ QString mResult;
+ QString mResultInfo;
+
+private:
+ imapCommand & operator = (const imapCommand &);
+};
+
+#endif
diff --git a/kioslaves/imap4/imapinfo.cc b/kioslaves/imap4/imapinfo.cc
new file mode 100644
index 000000000..d06618d75
--- /dev/null
+++ b/kioslaves/imap4/imapinfo.cc
@@ -0,0 +1,236 @@
+/**********************************************************************
+ *
+ * imapinfo.cc - IMAP4rev1 SELECT / EXAMINE handler
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+/*
+ References:
+ RFC 2060 - Internet Message Access Protocol - Version 4rev1 - December 1996
+ RFC 2192 - IMAP URL Scheme - September 1997
+ RFC 1731 - IMAP Authentication Mechanisms - December 1994
+ (Discusses KERBEROSv4, GSSAPI, and S/Key)
+ RFC 2195 - IMAP/POP AUTHorize Extension for Simple Challenge/Response
+ - September 1997 (CRAM-MD5 authentication method)
+ RFC 2104 - HMAC: Keyed-Hashing for Message Authentication - February 1997
+
+ Supported URLs:
+ imap://server/ - Prompt for user/pass, list all folders in home directory
+ imap://user:pass@server/ - Uses LOGIN to log in
+ imap://user;AUTH=method:pass@server/ - Uses AUTHENTICATE to log in
+
+ imap://server/folder/ - List messages in folder
+ */
+
+#include "imapinfo.h"
+#include "imapparser.h"
+
+#include <kdebug.h>
+
+imapInfo::imapInfo ():count_ (0),
+recent_ (0),
+unseen_ (0),
+uidValidity_ (0),
+uidNext_ (0),
+flags_ (0),
+permanentFlags_ (0),
+readWrite_ (false),
+countAvailable_ (false),
+recentAvailable_ (false),
+unseenAvailable_ (false),
+uidValidityAvailable_ (false),
+uidNextAvailable_ (false),
+flagsAvailable_ (false),
+permanentFlagsAvailable_ (false), readWriteAvailable_ (false)
+{
+}
+
+imapInfo::imapInfo (const imapInfo & mi):count_ (mi.count_),
+recent_ (mi.recent_),
+unseen_ (mi.unseen_),
+uidValidity_ (mi.uidValidity_),
+uidNext_ (mi.uidNext_),
+flags_ (mi.flags_),
+permanentFlags_ (mi.permanentFlags_),
+readWrite_ (mi.readWrite_),
+countAvailable_ (mi.countAvailable_),
+recentAvailable_ (mi.recentAvailable_),
+unseenAvailable_ (mi.unseenAvailable_),
+uidValidityAvailable_ (mi.uidValidityAvailable_),
+uidNextAvailable_ (mi.uidNextAvailable_),
+flagsAvailable_ (mi.flagsAvailable_),
+permanentFlagsAvailable_ (mi.permanentFlagsAvailable_),
+readWriteAvailable_ (mi.readWriteAvailable_)
+{
+}
+
+imapInfo & imapInfo::operator = (const imapInfo & mi)
+{
+ // Avoid a = a.
+ if (this == &mi)
+ return *this;
+
+ count_ = mi.count_;
+ recent_ = mi.recent_;
+ unseen_ = mi.unseen_;
+ uidValidity_ = mi.uidValidity_;
+ uidNext_ = mi.uidNext_;
+ flags_ = mi.flags_;
+ permanentFlags_ = mi.permanentFlags_;
+ readWrite_ = mi.readWrite_;
+ countAvailable_ = mi.countAvailable_;
+ recentAvailable_ = mi.recentAvailable_;
+ unseenAvailable_ = mi.unseenAvailable_;
+ uidValidityAvailable_ = mi.uidValidityAvailable_;
+ uidNextAvailable_ = mi.uidNextAvailable_;
+ flagsAvailable_ = mi.flagsAvailable_;
+ permanentFlagsAvailable_ = mi.permanentFlagsAvailable_;
+ readWriteAvailable_ = mi.readWriteAvailable_;
+
+ return *this;
+}
+
+imapInfo::imapInfo (const QStringList & list):count_ (0),
+recent_ (0),
+unseen_ (0),
+uidValidity_ (0),
+uidNext_ (0),
+flags_ (0),
+permanentFlags_ (0),
+readWrite_ (false),
+countAvailable_ (false),
+recentAvailable_ (false),
+unseenAvailable_ (false),
+uidValidityAvailable_ (false),
+uidNextAvailable_ (false),
+flagsAvailable_ (false),
+permanentFlagsAvailable_ (false), readWriteAvailable_ (false)
+{
+ for (QStringList::ConstIterator it (list.begin ()); it != list.end (); ++it)
+ {
+ QString line (*it);
+
+ line.truncate(line.length() - 2);
+ QStringList tokens(QStringList::split (' ', line));
+
+ kdDebug(7116) << "Processing: " << line << endl;
+ if (tokens[0] != "*")
+ continue;
+
+ if (tokens[1] == "OK")
+ {
+ if (tokens[2] == "[UNSEEN")
+ setUnseen (tokens[3].left (tokens[3].length () - 1).toULong ());
+
+ else if (tokens[2] == "[UIDVALIDITY")
+ setUidValidity (tokens[3].left (tokens[3].length () - 1).toULong ());
+
+ else if (tokens[2] == "[UIDNEXT")
+ setUidNext (tokens[3].left (tokens[3].length () - 1).toULong ());
+
+ else if (tokens[2] == "[PERMANENTFLAGS")
+ {
+ int flagsStart = line.find('(');
+ int flagsEnd = line.find(')');
+
+ kdDebug(7116) << "Checking permFlags from " << flagsStart << " to " << flagsEnd << endl;
+ if ((-1 != flagsStart) && (-1 != flagsEnd) && flagsStart < flagsEnd)
+ setPermanentFlags (_flags (line.mid (flagsStart, flagsEnd).latin1()));
+
+ }
+ else if (tokens[2] == "[READ-WRITE")
+ {
+ setReadWrite (true);
+ }
+ else if (tokens[2] == "[READ-ONLY")
+ {
+ setReadWrite (false);
+ }
+ else
+ {
+ kdDebug(7116) << "unknown token2: " << tokens[2] << endl;
+ }
+ }
+ else if (tokens[1] == "FLAGS")
+ {
+ int flagsStart = line.find ('(');
+ int flagsEnd = line.find (')');
+
+ if ((-1 != flagsStart) && (-1 != flagsEnd) && flagsStart < flagsEnd)
+ setFlags (_flags (line.mid (flagsStart, flagsEnd).latin1() ));
+ }
+ else
+ {
+ if (tokens[2] == "EXISTS")
+ setCount (tokens[1].toULong ());
+
+ else if (tokens[2] == "RECENT")
+ setRecent (tokens[1].toULong ());
+
+ else
+ kdDebug(7116) << "unknown token1/2: " << tokens[1] << " " << tokens[2] << endl;
+ }
+ }
+
+}
+
+ulong imapInfo::_flags (const QCString & inFlags)
+{
+ ulong flags = 0;
+ parseString flagsString;
+ flagsString.data.duplicate(inFlags.data(), inFlags.length());
+
+ if (flagsString[0] == '(')
+ flagsString.pos++;
+
+ while (!flagsString.isEmpty () && flagsString[0] != ')')
+ {
+ QCString entry = imapParser::parseOneWordC(flagsString).upper();
+
+ if (entry.isEmpty ())
+ flagsString.clear();
+ else if (0 != entry.contains ("\\SEEN"))
+ flags ^= Seen;
+ else if (0 != entry.contains ("\\ANSWERED"))
+ flags ^= Answered;
+ else if (0 != entry.contains ("\\FLAGGED"))
+ flags ^= Flagged;
+ else if (0 != entry.contains ("\\DELETED"))
+ flags ^= Deleted;
+ else if (0 != entry.contains ("\\DRAFT"))
+ flags ^= Draft;
+ else if (0 != entry.contains ("\\RECENT"))
+ flags ^= Recent;
+ else if (0 != entry.contains ("\\*"))
+ flags ^= User;
+
+ // non standard kmail falgs
+ else if ( entry.contains( "KMAILFORWARDED" ) || entry.contains( "$FORWARDED" ) )
+ flags = flags | Forwarded;
+ else if ( entry.contains( "KMAILTODO" ) || entry.contains( "$TODO" ) )
+ flags = flags | Todo;
+ else if ( entry.contains( "KMAILWATCHED" ) || entry.contains( "$WATCHED" ) )
+ flags = flags | Watched;
+ else if ( entry.contains( "KMAILIGNORED" ) || entry.contains( "$IGNORED" ) )
+ flags = flags | Ignored;
+ }
+
+ return flags;
+}
diff --git a/kioslaves/imap4/imapinfo.h b/kioslaves/imap4/imapinfo.h
new file mode 100644
index 000000000..068e6db54
--- /dev/null
+++ b/kioslaves/imap4/imapinfo.h
@@ -0,0 +1,232 @@
+#ifndef _IMAPINFO_H
+#define _IMAPINFO_H
+/**********************************************************************
+ *
+ * imapinfo.h - IMAP4rev1 SELECT / EXAMINE handler
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+#include <qstringlist.h>
+#include <qstring.h>
+
+//class handling the info we get on EXAMINE and SELECT
+class imapInfo
+{
+public:
+
+
+ enum MessageAttribute
+ {
+ Seen = 1 << 0,
+ Answered = 1 << 1,
+ Flagged = 1 << 2,
+ Deleted = 1 << 3,
+ Draft = 1 << 4,
+ Recent = 1 << 5,
+ User = 1 << 6,
+ // non standard flags
+ Forwarded = 1 << 7,
+ Todo = 1 << 8,
+ Watched = 1 << 9,
+ Ignored = 1 << 10
+ };
+
+
+ imapInfo ();
+ imapInfo (const QStringList &);
+ imapInfo (const imapInfo &);
+ imapInfo & operator = (const imapInfo &);
+
+ static ulong _flags (const QCString &);
+
+ void setCount (ulong l)
+ {
+ countAvailable_ = true;
+ count_ = l;
+ }
+
+ void setRecent (ulong l)
+ {
+ recentAvailable_ = true;
+ recent_ = l;
+ }
+
+ void setUnseen (ulong l)
+ {
+ unseenAvailable_ = true;
+ unseen_ = l;
+ }
+
+ void setUidValidity (ulong l)
+ {
+ uidValidityAvailable_ = true;
+ uidValidity_ = l;
+ }
+
+ void setUidNext (ulong l)
+ {
+ uidNextAvailable_ = true;
+ uidNext_ = l;
+ }
+
+ void setFlags (ulong l)
+ {
+ flagsAvailable_ = true;
+ flags_ = l;
+ }
+
+ void setFlags (const QCString & inFlag)
+ {
+ flagsAvailable_ = true;
+ flags_ = _flags (inFlag);
+ }
+
+ void setPermanentFlags (ulong l)
+ {
+ permanentFlagsAvailable_ = true;
+ permanentFlags_ = l;
+ }
+
+ void setPermanentFlags (const QCString & inFlag)
+ {
+ permanentFlagsAvailable_ = true;
+ permanentFlags_ = _flags (inFlag);
+ }
+
+ void setReadWrite (bool b)
+ {
+ readWriteAvailable_ = true;
+ readWrite_ = b;
+ }
+
+ void setAlert( const char* cstr )
+ {
+ alert_ = cstr;
+ }
+
+ ulong count () const
+ {
+ return count_;
+ }
+
+ ulong recent () const
+ {
+ return recent_;
+ }
+
+ ulong unseen () const
+ {
+ return unseen_;
+ }
+
+ ulong uidValidity () const
+ {
+ return uidValidity_;
+ }
+
+ ulong uidNext () const
+ {
+ return uidNext_;
+ }
+
+ ulong flags () const
+ {
+ return flags_;
+ }
+
+ ulong permanentFlags () const
+ {
+ return permanentFlags_;
+ }
+
+ bool readWrite () const
+ {
+ return readWrite_;
+ }
+
+ ulong countAvailable () const
+ {
+ return countAvailable_;
+ }
+
+ ulong recentAvailable () const
+ {
+ return recentAvailable_;
+ }
+
+ ulong unseenAvailable () const
+ {
+ return unseenAvailable_;
+ }
+
+ ulong uidValidityAvailable () const
+ {
+ return uidValidityAvailable_;
+ }
+
+ ulong uidNextAvailable () const
+ {
+ return uidNextAvailable_;
+ }
+
+ ulong flagsAvailable () const
+ {
+ return flagsAvailable_;
+ }
+
+ ulong permanentFlagsAvailable () const
+ {
+ return permanentFlagsAvailable_;
+ }
+
+ bool readWriteAvailable () const
+ {
+ return readWriteAvailable_;
+ }
+
+ QCString alert() const
+ {
+ return alert_;
+ }
+
+private:
+
+ QCString alert_;
+
+ ulong count_;
+ ulong recent_;
+ ulong unseen_;
+ ulong uidValidity_;
+ ulong uidNext_;
+ ulong flags_;
+ ulong permanentFlags_;
+ bool readWrite_;
+
+ bool countAvailable_;
+ bool recentAvailable_;
+ bool unseenAvailable_;
+ bool uidValidityAvailable_;
+ bool uidNextAvailable_;
+ bool flagsAvailable_;
+ bool permanentFlagsAvailable_;
+ bool readWriteAvailable_;
+};
+
+#endif
diff --git a/kioslaves/imap4/imaplist.cc b/kioslaves/imap4/imaplist.cc
new file mode 100644
index 000000000..6054535c6
--- /dev/null
+++ b/kioslaves/imap4/imaplist.cc
@@ -0,0 +1,135 @@
+/**********************************************************************
+ *
+ * imapinfo.cc - IMAP4rev1 EXAMINE / SELECT handler
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+/*
+ References:
+ RFC 2060 - Internet Message Access Protocol - Version 4rev1 - December 1996
+ RFC 2192 - IMAP URL Scheme - September 1997
+ RFC 1731 - IMAP Authentication Mechanisms - December 1994
+ (Discusses KERBEROSv4, GSSAPI, and S/Key)
+ RFC 2195 - IMAP/POP AUTHorize Extension for Simple Challenge/Response
+ - September 1997 (CRAM-MD5 authentication method)
+ RFC 2104 - HMAC: Keyed-Hashing for Message Authentication - February 1997
+
+ Supported URLs:
+ imap://server/ - Prompt for user/pass, list all folders in home directory
+ imap://user:pass@server/ - Uses LOGIN to log in
+ imap://user;AUTH=method:pass@server/ - Uses AUTHENTICATE to log in
+
+ imap://server/folder/ - List messages in folder
+ */
+
+#include "rfcdecoder.h"
+#include "imaplist.h"
+#include "imapparser.h"
+
+#include <kdebug.h>
+
+imapList::imapList (): parser_(0), noInferiors_ (false),
+noSelect_ (false), marked_ (false), unmarked_ (false),
+hasChildren_ (false), hasNoChildren_ (false)
+{
+}
+
+imapList::imapList (const imapList & lr):parser_(lr.parser_),
+hierarchyDelimiter_ (lr.hierarchyDelimiter_),
+name_ (lr.name_),
+noInferiors_ (lr.noInferiors_),
+noSelect_ (lr.noSelect_), marked_ (lr.marked_), unmarked_ (lr.unmarked_),
+hasChildren_ (lr.hasChildren_), hasNoChildren_ (lr.hasNoChildren_),
+attributes_ (lr.attributes_)
+{
+}
+
+imapList & imapList::operator = (const imapList & lr)
+{
+ // Avoid a = a.
+ if (this == &lr)
+ return *this;
+
+ parser_ = lr.parser_;
+ hierarchyDelimiter_ = lr.hierarchyDelimiter_;
+ name_ = lr.name_;
+ noInferiors_ = lr.noInferiors_;
+ noSelect_ = lr.noSelect_;
+ marked_ = lr.marked_;
+ unmarked_ = lr.unmarked_;
+ hasChildren_ = lr.hasChildren_;
+ hasNoChildren_ = lr.hasNoChildren_;
+ attributes_ = lr.attributes_;
+
+ return *this;
+}
+
+imapList::imapList (const QString & inStr, imapParser &parser)
+: parser_(&parser),
+noInferiors_ (false),
+noSelect_ (false),
+marked_ (false), unmarked_ (false), hasChildren_ (false),
+hasNoChildren_ (false)
+{
+ parseString s;
+ s.data.duplicate(inStr.latin1(), inStr.length());
+
+ if (s[0] != '(')
+ return; //not proper format for us
+
+ s.pos++; // tie off (
+
+ parseAttributes( s );
+
+ s.pos++; // tie off )
+ parser_->skipWS (s);
+
+ hierarchyDelimiter_ = parser_->parseOneWordC(s);
+ if (hierarchyDelimiter_ == "NIL")
+ hierarchyDelimiter_ = QString::null;
+ name_ = rfcDecoder::fromIMAP (parser_->parseLiteral (s)); // decode modified UTF7
+}
+
+void imapList::parseAttributes( parseString & str )
+{
+ QCString attribute, orig;
+
+ while ( !str.isEmpty () && str[0] != ')' )
+ {
+ orig = parser_->parseOneWordC(str);
+ attributes_ << orig;
+ attribute = orig.lower();
+ if (-1 != attribute.find ("\\noinferiors"))
+ noInferiors_ = true;
+ else if (-1 != attribute.find ("\\noselect"))
+ noSelect_ = true;
+ else if (-1 != attribute.find ("\\marked"))
+ marked_ = true;
+ else if (-1 != attribute.find ("\\unmarked"))
+ unmarked_ = true;
+ else if (-1 != attribute.find ("\\haschildren"))
+ hasChildren_ = true;
+ else if (-1 != attribute.find ("\\hasnochildren"))
+ hasNoChildren_ = true;
+ else
+ kdDebug(7116) << "imapList::imapList: bogus attribute " << attribute << endl;
+ }
+}
+
diff --git a/kioslaves/imap4/imaplist.h b/kioslaves/imap4/imaplist.h
new file mode 100644
index 000000000..5945011f4
--- /dev/null
+++ b/kioslaves/imap4/imaplist.h
@@ -0,0 +1,137 @@
+#ifndef _IMAPLIST_H
+#define _IMAPLIST_H
+/**********************************************************************
+ *
+ * imaplist.h - IMAP4rev1 list response handler
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+#include <qstringlist.h>
+#include <qstring.h>
+
+class parseString;
+class imapParser;
+
+//the class handling the responses from list
+class imapList
+{
+public:
+
+ imapList ();
+ imapList (const QString &, imapParser &);
+ imapList (const imapList &);
+ imapList & operator = (const imapList &);
+
+ // process the attributes
+ void parseAttributes( parseString & );
+
+ // return all atributes concatenated
+ QString attributesAsString() const
+ {
+ return attributes_.join(",");
+ }
+
+ QString hierarchyDelimiter () const
+ {
+ return hierarchyDelimiter_;
+ }
+ void setHierarchyDelimiter (const QString & _str)
+ {
+ hierarchyDelimiter_ = _str;
+ }
+
+ QString name () const
+ {
+ return name_;
+ }
+ void setName (const QString & _str)
+ {
+ name_ = _str;
+ }
+
+ bool noInferiors () const
+ {
+ return noInferiors_;
+ }
+ void setNoInferiors (bool _val)
+ {
+ noInferiors_ = _val;
+ }
+
+ bool noSelect () const
+ {
+ return noSelect_;
+ }
+ void setNoSelect (bool _val)
+ {
+ noSelect_ = _val;
+ }
+
+ bool hasChildren () const
+ {
+ return hasChildren_;
+ }
+ void setHasChildren (bool _val)
+ {
+ hasChildren_ = _val;
+ }
+
+ bool hasNoChildren () const
+ {
+ return hasNoChildren_;
+ }
+ void setHasNoChildren (bool _val)
+ {
+ hasNoChildren_ = _val;
+ }
+
+ bool marked () const
+ {
+ return marked_;
+ }
+ void setMarked (bool _val)
+ {
+ marked_ = _val;
+ }
+
+ bool unmarked () const
+ {
+ return unmarked_;
+ }
+ void setUnmarked (bool _val)
+ {
+ unmarked_ = _val;
+ }
+
+private:
+
+ imapParser* parser_;
+ QString hierarchyDelimiter_;
+ QString name_;
+ bool noInferiors_;
+ bool noSelect_;
+ bool marked_;
+ bool unmarked_;
+ bool hasChildren_;
+ bool hasNoChildren_;
+ QStringList attributes_;
+};
+
+#endif
diff --git a/kioslaves/imap4/imapparser.cc b/kioslaves/imap4/imapparser.cc
new file mode 100644
index 000000000..cf3465a4c
--- /dev/null
+++ b/kioslaves/imap4/imapparser.cc
@@ -0,0 +1,2085 @@
+/**********************************************************************
+ *
+ * imapparser.cc - IMAP4rev1 Parser
+ * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "rfcdecoder.h"
+
+#include "imapparser.h"
+
+#include "imapinfo.h"
+
+#include "mailheader.h"
+#include "mimeheader.h"
+#include "mailaddress.h"
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBSASL2
+extern "C" {
+#include <sasl/sasl.h>
+}
+#endif
+
+#include <qregexp.h>
+#include <qbuffer.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kurl.h>
+
+#include <kasciistricmp.h>
+#include <kasciistringtools.h>
+
+#ifdef HAVE_LIBSASL2
+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 }
+};
+#endif
+
+imapParser::imapParser ()
+{
+ sentQueue.setAutoDelete (false);
+ completeQueue.setAutoDelete (true);
+ currentState = ISTATE_NO;
+ commandCounter = 0;
+ lastHandled = 0;
+}
+
+imapParser::~imapParser ()
+{
+ delete lastHandled;
+ lastHandled = 0;
+}
+
+imapCommand *
+imapParser::doCommand (imapCommand * aCmd)
+{
+ int pl = 0;
+ sendCommand (aCmd);
+ while (pl != -1 && !aCmd->isComplete ()) {
+ while ((pl = parseLoop ()) == 0)
+ ;
+ }
+
+ return aCmd;
+}
+
+imapCommand *
+imapParser::sendCommand (imapCommand * aCmd)
+{
+ aCmd->setId (QString::number(commandCounter++));
+ sentQueue.append (aCmd);
+
+ continuation.resize(0);
+ const QString& command = aCmd->command();
+
+ if (command == "SELECT" || command == "EXAMINE")
+ {
+ // we need to know which box we are selecting
+ parseString p;
+ p.fromString(aCmd->parameter());
+ currentBox = parseOneWordC(p);
+ kdDebug(7116) << "imapParser::sendCommand - setting current box to " << currentBox << endl;
+ }
+ else if (command == "CLOSE")
+ {
+ // we no longer have a box open
+ currentBox = QString::null;
+ }
+ else if (command.find ("SEARCH") != -1
+ || command == "GETACL"
+ || command == "LISTRIGHTS"
+ || command == "MYRIGHTS"
+ || command == "GETANNOTATION"
+ || command == "NAMESPACE"
+ || command == "GETQUOTAROOT"
+ || command == "GETQUOTA"
+ || command == "X-GET-OTHER-USERS"
+ || command == "X-GET-DELEGATES"
+ || command == "X-GET-OUT-OF-OFFICE")
+ {
+ lastResults.clear ();
+ }
+ else if (command == "LIST"
+ || command == "LSUB")
+ {
+ listResponses.clear ();
+ }
+ parseWriteLine (aCmd->getStr ());
+ return aCmd;
+}
+
+bool
+imapParser::clientLogin (const QString & aUser, const QString & aPass,
+ QString & resultInfo)
+{
+ imapCommand *cmd;
+ bool retVal = false;
+
+ cmd =
+ doCommand (new
+ imapCommand ("LOGIN", "\"" + rfcDecoder::quoteIMAP(aUser)
+ + "\" \"" + rfcDecoder::quoteIMAP(aPass) + "\""));
+
+ if (cmd->result () == "OK")
+ {
+ currentState = ISTATE_LOGIN;
+ retVal = true;
+ }
+ resultInfo = cmd->resultInfo();
+ completeQueue.removeRef (cmd);
+
+ return retVal;
+}
+
+#ifdef HAVE_LIBSASL2
+static bool sasl_interact( KIO::SlaveBase *slave, KIO::AuthInfo &ai, void *in )
+{
+ kdDebug(7116) << "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 ( ai.username.isEmpty() || ai.password.isEmpty() ) {
+ if (!slave->openPassDlg(ai))
+ return false;
+ }
+ break;
+ }
+ }
+
+ interact = ( sasl_interact_t * ) in;
+ while( interact->id != SASL_CB_LIST_END ) {
+ kdDebug(7116) << "SASL_INTERACT id: " << interact->id << endl;
+ switch( interact->id ) {
+ case SASL_CB_USER:
+ case SASL_CB_AUTHNAME:
+ kdDebug(7116) << "SASL_CB_[USER|AUTHNAME]: '" << ai.username << "'" << endl;
+ interact->result = strdup( ai.username.utf8() );
+ interact->len = strlen( (const char *) interact->result );
+ break;
+ case SASL_CB_PASS:
+ kdDebug(7116) << "SASL_CB_PASS: [hidden] " << endl;
+ interact->result = strdup( ai.password.utf8() );
+ interact->len = strlen( (const char *) interact->result );
+ break;
+ default:
+ interact->result = 0;
+ interact->len = 0;
+ break;
+ }
+ interact++;
+ }
+ return true;
+}
+#endif
+
+bool
+imapParser::clientAuthenticate ( KIO::SlaveBase *slave, KIO::AuthInfo &ai,
+ const QString & aFQDN, const QString & aAuth, bool isSSL, QString & resultInfo)
+{
+ bool retVal = false;
+#ifdef HAVE_LIBSASL2
+ int result;
+ sasl_conn_t *conn = 0;
+ sasl_interact_t *client_interact = 0;
+ const char *out = 0;
+ uint outlen = 0;
+ const char *mechusing = 0;
+ QByteArray tmp, challenge;
+
+ kdDebug(7116) << "aAuth: " << aAuth << " FQDN: " << aFQDN << " isSSL: " << isSSL << endl;
+
+ // see if server supports this authenticator
+ if (!hasCapability ("AUTH=" + aAuth))
+ return false;
+
+// result = sasl_client_new( isSSL ? "imaps" : "imap",
+ result = sasl_client_new( "imap", /* FIXME: with cyrus-imapd, even imaps' digest-uri
+ must be 'imap'. I don't know if it's good or bad. */
+ aFQDN.latin1(),
+ 0, 0, callbacks, 0, &conn );
+
+ if ( result != SASL_OK ) {
+ kdDebug(7116) << "sasl_client_new failed with: " << result << endl;
+ resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
+ return false;
+ }
+
+ do {
+ result = sasl_client_start(conn, aAuth.latin1(), &client_interact,
+ hasCapability("SASL-IR") ? &out : 0, &outlen, &mechusing);
+
+ if ( result == SASL_INTERACT ) {
+ if ( !sasl_interact( slave, ai, client_interact ) ) {
+ sasl_dispose( &conn );
+ return false;
+ }
+ }
+ } while ( result == SASL_INTERACT );
+
+ if ( result != SASL_CONTINUE && result != SASL_OK ) {
+ kdDebug(7116) << "sasl_client_start failed with: " << result << endl;
+ resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
+ sasl_dispose( &conn );
+ return false;
+ }
+ imapCommand *cmd;
+
+ tmp.setRawData( out, outlen );
+ KCodecs::base64Encode( tmp, challenge );
+ tmp.resetRawData( out, outlen );
+ // then lets try it
+ QString firstCommand = aAuth;
+ if ( !challenge.isEmpty() ) {
+ firstCommand += " ";
+ firstCommand += QString::fromLatin1( challenge.data(), challenge.size() );
+ }
+ cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1()));
+
+ while ( true )
+ {
+ //read the next line
+ while (parseLoop() == 0) ;
+ if ( cmd->isComplete() ) break;
+
+ if (!continuation.isEmpty())
+ {
+// kdDebug(7116) << "S: " << QCString(continuation.data(),continuation.size()+1) << endl;
+ if ( continuation.size() > 4 ) {
+ tmp.setRawData( continuation.data() + 2, continuation.size() - 4 );
+ KCodecs::base64Decode( tmp, challenge );
+// kdDebug(7116) << "S-1: " << QCString(challenge.data(),challenge.size()+1) << endl;
+ tmp.resetRawData( continuation.data() + 2, continuation.size() - 4 );
+ }
+
+ do {
+ result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
+ challenge.size(),
+ &client_interact,
+ &out, &outlen);
+
+ if (result == SASL_INTERACT) {
+ if ( !sasl_interact( slave, ai, client_interact ) ) {
+ sasl_dispose( &conn );
+ return false;
+ }
+ }
+ } while ( result == SASL_INTERACT );
+
+ if ( result != SASL_CONTINUE && result != SASL_OK ) {
+ kdDebug(7116) << "sasl_client_step failed with: " << result << endl;
+ resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
+ sasl_dispose( &conn );
+ return false;
+ }
+
+ tmp.setRawData( out, outlen );
+// kdDebug(7116) << "C-1: " << QCString(tmp.data(),tmp.size()+1) << endl;
+ KCodecs::base64Encode( tmp, challenge );
+ tmp.resetRawData( out, outlen );
+// kdDebug(7116) << "C: " << QCString(challenge.data(),challenge.size()+1) << endl;
+ parseWriteLine (challenge);
+ continuation.resize(0);
+ }
+ }
+
+ if (cmd->result () == "OK")
+ {
+ currentState = ISTATE_LOGIN;
+ retVal = true;
+ }
+ resultInfo = cmd->resultInfo();
+ completeQueue.removeRef (cmd);
+
+ sasl_dispose( &conn ); //we don't use sasl_en/decode(), so it's safe to dispose the connection.
+#endif //HAVE_LIBSASL2
+ return retVal;
+}
+
+void
+imapParser::parseUntagged (parseString & result)
+{
+ //kdDebug(7116) << "imapParser::parseUntagged - '" << result.cstr() << "'" << endl;
+
+ parseOneWordC(result); // *
+ QByteArray what = parseLiteral (result); // see whats coming next
+
+ switch (what[0])
+ {
+ //the status responses
+ case 'B': // BAD or BYE
+ if (qstrncmp(what, "BAD", what.size()) == 0)
+ {
+ parseResult (what, result);
+ }
+ else if (qstrncmp(what, "BYE", what.size()) == 0)
+ {
+ parseResult (what, result);
+ if ( sentQueue.count() ) {
+ // BYE that interrupts a command -> copy the reason for it
+ imapCommand *current = sentQueue.at (0);
+ current->setResultInfo(result.cstr());
+ }
+ currentState = ISTATE_NO;
+ }
+ break;
+
+ case 'N': // NO
+ if (what[1] == 'O' && what.size() == 2)
+ {
+ parseResult (what, result);
+ }
+ else if (qstrncmp(what, "NAMESPACE", what.size()) == 0)
+ {
+ parseNamespace (result);
+ }
+ break;
+
+ case 'O': // OK
+ if (what[1] == 'K' && what.size() == 2)
+ {
+ parseResult (what, result);
+ } else if (qstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER
+ parseOtherUser (result);
+ } else if (qstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE
+ parseOutOfOffice (result);
+ }
+ break;
+ case 'D':
+ if (qstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES
+ parseDelegate (result);
+ }
+ break;
+
+ case 'P': // PREAUTH
+ if (qstrncmp(what, "PREAUTH", what.size()) == 0)
+ {
+ parseResult (what, result);
+ currentState = ISTATE_LOGIN;
+ }
+ break;
+
+ // parse the other responses
+ case 'C': // CAPABILITY
+ if (qstrncmp(what, "CAPABILITY", what.size()) == 0)
+ {
+ parseCapability (result);
+ }
+ break;
+
+ case 'F': // FLAGS
+ if (qstrncmp(what, "FLAGS", what.size()) == 0)
+ {
+ parseFlags (result);
+ }
+ break;
+
+ case 'L': // LIST or LSUB or LISTRIGHTS
+ if (qstrncmp(what, "LIST", what.size()) == 0)
+ {
+ parseList (result);
+ }
+ else if (qstrncmp(what, "LSUB", what.size()) == 0)
+ {
+ parseLsub (result);
+ }
+ else if (qstrncmp(what, "LISTRIGHTS", what.size()) == 0)
+ {
+ parseListRights (result);
+ }
+ break;
+
+ case 'M': // MYRIGHTS
+ if (qstrncmp(what, "MYRIGHTS", what.size()) == 0)
+ {
+ parseMyRights (result);
+ }
+ break;
+ case 'S': // SEARCH or STATUS
+ if (qstrncmp(what, "SEARCH", what.size()) == 0)
+ {
+ parseSearch (result);
+ }
+ else if (qstrncmp(what, "STATUS", what.size()) == 0)
+ {
+ parseStatus (result);
+ }
+ break;
+
+ case 'A': // ACL or ANNOTATION
+ if (qstrncmp(what, "ACL", what.size()) == 0)
+ {
+ parseAcl (result);
+ }
+ else if (qstrncmp(what, "ANNOTATION", what.size()) == 0)
+ {
+ parseAnnotation (result);
+ }
+ break;
+ case 'Q': // QUOTA or QUOTAROOT
+ if ( what.size() > 5 && qstrncmp(what, "QUOTAROOT", what.size()) == 0)
+ {
+ parseQuotaRoot( result );
+ }
+ else if (qstrncmp(what, "QUOTA", what.size()) == 0)
+ {
+ parseQuota( result );
+ }
+ break;
+ case 'X': // Custom command
+ {
+ parseCustom( result );
+ }
+ break;
+ default:
+ //better be a number
+ {
+ ulong number;
+ bool valid;
+
+ number = QCString(what, what.size() + 1).toUInt(&valid);
+ if (valid)
+ {
+ what = parseLiteral (result);
+ switch (what[0])
+ {
+ case 'E':
+ if (qstrncmp(what, "EXISTS", what.size()) == 0)
+ {
+ parseExists (number, result);
+ }
+ else if (qstrncmp(what, "EXPUNGE", what.size()) == 0)
+ {
+ parseExpunge (number, result);
+ }
+ break;
+
+ case 'F':
+ if (qstrncmp(what, "FETCH", what.size()) == 0)
+ {
+ seenUid = QString::null;
+ parseFetch (number, result);
+ }
+ break;
+
+ case 'S':
+ if (qstrncmp(what, "STORE", what.size()) == 0) // deprecated store
+ {
+ seenUid = QString::null;
+ parseFetch (number, result);
+ }
+ break;
+
+ case 'R':
+ if (qstrncmp(what, "RECENT", what.size()) == 0)
+ {
+ parseRecent (number, result);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ } //switch
+} //func
+
+
+void
+imapParser::parseResult (QByteArray & result, parseString & rest,
+ const QString & command)
+{
+ if (command == "SELECT")
+ selectInfo.setReadWrite(true);
+
+ if (rest[0] == '[')
+ {
+ rest.pos++;
+ QCString option = parseOneWordC(rest, TRUE);
+
+ switch (option[0])
+ {
+ case 'A': // ALERT
+ if (option == "ALERT")
+ {
+ rest.pos = rest.data.find(']', rest.pos) + 1;
+ // The alert text is after [ALERT].
+ // Is this correct or do we need to care about litterals?
+ selectInfo.setAlert( rest.cstr() );
+ }
+ break;
+
+ case 'N': // NEWNAME
+ if (option == "NEWNAME")
+ {
+ }
+ break;
+
+ case 'P': //PARSE or PERMANENTFLAGS
+ if (option == "PARSE")
+ {
+ }
+ else if (option == "PERMANENTFLAGS")
+ {
+ uint end = rest.data.find(']', rest.pos);
+ QCString flags(rest.data.data() + rest.pos, end - rest.pos);
+ selectInfo.setPermanentFlags (flags);
+ rest.pos = end;
+ }
+ break;
+
+ case 'R': //READ-ONLY or READ-WRITE
+ if (option == "READ-ONLY")
+ {
+ selectInfo.setReadWrite (false);
+ }
+ else if (option == "READ-WRITE")
+ {
+ selectInfo.setReadWrite (true);
+ }
+ break;
+
+ case 'T': //TRYCREATE
+ if (option == "TRYCREATE")
+ {
+ }
+ break;
+
+ case 'U': //UIDVALIDITY or UNSEEN
+ if (option == "UIDVALIDITY")
+ {
+ ulong value;
+ if (parseOneNumber (rest, value))
+ selectInfo.setUidValidity (value);
+ }
+ else if (option == "UNSEEN")
+ {
+ ulong value;
+ if (parseOneNumber (rest, value))
+ selectInfo.setUnseen (value);
+ }
+ else if (option == "UIDNEXT")
+ {
+ ulong value;
+ if (parseOneNumber (rest, value))
+ selectInfo.setUidNext (value);
+ }
+ else
+ break;
+
+ }
+ if (rest[0] == ']')
+ rest.pos++; //tie off ]
+ skipWS (rest);
+ }
+
+ if (command.isEmpty())
+ {
+ // This happens when parsing an intermediate result line (those that start with '*').
+ // No state change involved, so we can stop here.
+ return;
+ }
+
+ switch (command[0].latin1 ())
+ {
+ case 'A':
+ if (command == "AUTHENTICATE")
+ if (qstrncmp(result, "OK", result.size()) == 0)
+ currentState = ISTATE_LOGIN;
+ break;
+
+ case 'L':
+ if (command == "LOGIN")
+ if (qstrncmp(result, "OK", result.size()) == 0)
+ currentState = ISTATE_LOGIN;
+ break;
+
+ case 'E':
+ if (command == "EXAMINE")
+ {
+ if (qstrncmp(result, "OK", result.size()) == 0)
+ currentState = ISTATE_SELECT;
+ else
+ {
+ if (currentState == ISTATE_SELECT)
+ currentState = ISTATE_LOGIN;
+ currentBox = QString::null;
+ }
+ kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
+ }
+ break;
+
+ case 'S':
+ if (command == "SELECT")
+ {
+ if (qstrncmp(result, "OK", result.size()) == 0)
+ currentState = ISTATE_SELECT;
+ else
+ {
+ if (currentState == ISTATE_SELECT)
+ currentState = ISTATE_LOGIN;
+ currentBox = QString::null;
+ }
+ kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+void imapParser::parseCapability (parseString & result)
+{
+ QCString temp( result.cstr() );
+ imapCapabilities = QStringList::split ( ' ', KPIM::kAsciiToLower( temp.data() ) );
+}
+
+void imapParser::parseFlags (parseString & result)
+{
+ selectInfo.setFlags(result.cstr());
+}
+
+void imapParser::parseList (parseString & result)
+{
+ imapList this_one;
+
+ if (result[0] != '(')
+ return; //not proper format for us
+
+ result.pos++; // tie off (
+
+ this_one.parseAttributes( result );
+
+ result.pos++; // tie off )
+ skipWS (result);
+
+ this_one.setHierarchyDelimiter(parseLiteralC(result));
+ this_one.setName (rfcDecoder::fromIMAP(parseLiteralC(result))); // decode modified UTF7
+
+ listResponses.append (this_one);
+}
+
+void imapParser::parseLsub (parseString & result)
+{
+ imapList this_one (result.cstr(), *this);
+ listResponses.append (this_one);
+}
+
+void imapParser::parseListRights (parseString & result)
+{
+ parseOneWordC (result); // skip mailbox name
+ parseOneWordC (result); // skip user id
+ int outlen = 1;
+ while ( outlen ) {
+ QCString word = parseOneWordC (result, false, &outlen);
+ lastResults.append (word);
+ }
+}
+
+void imapParser::parseAcl (parseString & result)
+{
+ parseOneWordC (result); // skip mailbox name
+ int outlen = 1;
+ // The result is user1 perm1 user2 perm2 etc. The caller will sort it out.
+ while ( outlen && !result.isEmpty() ) {
+ QCString word = parseLiteralC (result, false, false, &outlen);
+ lastResults.append (word);
+ }
+}
+
+void imapParser::parseAnnotation (parseString & result)
+{
+ parseOneWordC (result); // skip mailbox name
+ skipWS (result);
+ parseOneWordC (result); // skip entry name (we know it since we don't allow wildcards in it)
+ skipWS (result);
+ if (result.isEmpty() || result[0] != '(')
+ return;
+ result.pos++;
+ skipWS (result);
+ int outlen = 1;
+ // The result is name1 value1 name2 value2 etc. The caller will sort it out.
+ while ( outlen && !result.isEmpty() && result[0] != ')' ) {
+ QCString word = parseLiteralC (result, false, false, &outlen);
+ lastResults.append (word);
+ }
+}
+
+
+void imapParser::parseQuota (parseString & result)
+{
+ // quota_response ::= "QUOTA" SP astring SP quota_list
+ // quota_list ::= "(" #quota_resource ")"
+ // quota_resource ::= atom SP number SP number
+ QCString root = parseOneWordC( result );
+ if ( root.isEmpty() ) {
+ lastResults.append( "" );
+ } else {
+ lastResults.append( root );
+ }
+ if (result.isEmpty() || result[0] != '(')
+ return;
+ result.pos++;
+ skipWS (result);
+ QStringList triplet;
+ int outlen = 1;
+ while ( outlen && !result.isEmpty() && result[0] != ')' ) {
+ QCString word = parseLiteralC (result, false, false, &outlen);
+ triplet.append(word);
+ }
+ lastResults.append( triplet.join(" ") );
+}
+
+void imapParser::parseQuotaRoot (parseString & result)
+{
+ // quotaroot_response
+ // ::= "QUOTAROOT" SP astring *(SP astring)
+ parseOneWordC (result); // skip mailbox name
+ skipWS (result);
+ if ( result.isEmpty() )
+ return;
+ QStringList roots;
+ int outlen = 1;
+ while ( outlen && !result.isEmpty() ) {
+ QCString word = parseLiteralC (result, false, false, &outlen);
+ roots.append (word);
+ }
+ lastResults.append( roots.isEmpty()? "" : roots.join(" ") );
+}
+
+void imapParser::parseCustom (parseString & result)
+{
+ int outlen = 1;
+ QCString word = parseLiteralC (result, false, false, &outlen);
+ lastResults.append( word );
+}
+
+void imapParser::parseOtherUser (parseString & result)
+{
+ lastResults.append( parseOneWordC( result ) );
+}
+
+void imapParser::parseDelegate (parseString & result)
+{
+ const QString email = parseOneWordC( result );
+
+ QStringList rights;
+ int outlen = 1;
+ while ( outlen && !result.isEmpty() ) {
+ QCString word = parseLiteralC( result, false, false, &outlen );
+ rights.append( word );
+ }
+
+ lastResults.append( email + ":" + rights.join( "," ) );
+}
+
+void imapParser::parseOutOfOffice (parseString & result)
+{
+ const QString state = parseOneWordC (result);
+ parseOneWordC (result); // skip encoding
+
+ int outlen = 1;
+ QCString msg = parseLiteralC (result, false, false, &outlen);
+
+ lastResults.append( state + "^" + QString::fromUtf8( msg ) );
+}
+
+void imapParser::parseMyRights (parseString & result)
+{
+ parseOneWordC (result); // skip mailbox name
+ Q_ASSERT( lastResults.isEmpty() ); // we can only be called once
+ lastResults.append (parseOneWordC (result) );
+}
+
+void imapParser::parseSearch (parseString & result)
+{
+ ulong value;
+
+ while (parseOneNumber (result, value))
+ {
+ lastResults.append (QString::number(value));
+ }
+}
+
+void imapParser::parseStatus (parseString & inWords)
+{
+ lastStatus = imapInfo ();
+
+ parseLiteralC(inWords); // swallow the box
+ if (inWords.isEmpty() || inWords[0] != '(')
+ return;
+
+ inWords.pos++;
+ skipWS (inWords);
+
+ while (!inWords.isEmpty() && inWords[0] != ')')
+ {
+ ulong value;
+
+ QCString label = parseOneWordC(inWords);
+ if (parseOneNumber (inWords, value))
+ {
+ if (label == "MESSAGES")
+ lastStatus.setCount (value);
+ else if (label == "RECENT")
+ lastStatus.setRecent (value);
+ else if (label == "UIDVALIDITY")
+ lastStatus.setUidValidity (value);
+ else if (label == "UNSEEN")
+ lastStatus.setUnseen (value);
+ else if (label == "UIDNEXT")
+ lastStatus.setUidNext (value);
+ }
+ }
+
+ if (inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+}
+
+void imapParser::parseExists (ulong value, parseString & result)
+{
+ selectInfo.setCount (value);
+ result.pos = result.data.size();
+}
+
+void imapParser::parseExpunge (ulong value, parseString & result)
+{
+ Q_UNUSED(value);
+ Q_UNUSED(result);
+}
+
+void imapParser::parseAddressList (parseString & inWords, QPtrList<mailAddress>& list)
+{
+ if (inWords.isEmpty())
+ return;
+ if (inWords[0] != '(')
+ {
+ parseOneWordC (inWords); // parse NIL
+ }
+ else
+ {
+ inWords.pos++;
+ skipWS (inWords);
+
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ if (inWords[0] == '(') {
+ mailAddress *addr = new mailAddress;
+ parseAddress(inWords, *addr);
+ list.append(addr);
+ } else {
+ break;
+ }
+ }
+
+ if (!inWords.isEmpty() && inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+ }
+}
+
+const mailAddress& imapParser::parseAddress (parseString & inWords, mailAddress& retVal)
+{
+ inWords.pos++;
+ skipWS (inWords);
+
+ retVal.setFullName(parseLiteralC(inWords));
+ retVal.setCommentRaw(parseLiteralC(inWords));
+ retVal.setUser(parseLiteralC(inWords));
+ retVal.setHost(parseLiteralC(inWords));
+
+ if (!inWords.isEmpty() && inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+
+ return retVal;
+}
+
+mailHeader * imapParser::parseEnvelope (parseString & inWords)
+{
+ mailHeader *envelope = 0;
+
+ if (inWords[0] != '(')
+ return envelope;
+ inWords.pos++;
+ skipWS (inWords);
+
+ envelope = new mailHeader;
+
+ //date
+ envelope->setDate(parseLiteralC(inWords));
+
+ //subject
+ envelope->setSubject(parseLiteralC(inWords));
+
+ QPtrList<mailAddress> list;
+ list.setAutoDelete(true);
+
+ //from
+ parseAddressList(inWords, list);
+ if (!list.isEmpty()) {
+ envelope->setFrom(*list.last());
+ list.clear();
+ }
+
+ //sender
+ parseAddressList(inWords, list);
+ if (!list.isEmpty()) {
+ envelope->setSender(*list.last());
+ list.clear();
+ }
+
+ //reply-to
+ parseAddressList(inWords, list);
+ if (!list.isEmpty()) {
+ envelope->setReplyTo(*list.last());
+ list.clear();
+ }
+
+ //to
+ parseAddressList (inWords, envelope->to());
+
+ //cc
+ parseAddressList (inWords, envelope->cc());
+
+ //bcc
+ parseAddressList (inWords, envelope->bcc());
+
+ //in-reply-to
+ envelope->setInReplyTo(parseLiteralC(inWords));
+
+ //message-id
+ envelope->setMessageId(parseLiteralC(inWords));
+
+ // see if we have more to come
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ //eat the extensions to this part
+ if (inWords[0] == '(')
+ parseSentence (inWords);
+ else
+ parseLiteralC (inWords);
+ }
+
+ if (!inWords.isEmpty() && inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+
+ return envelope;
+}
+
+// parse parameter pairs into a dictionary
+// caller must clean up the dictionary items
+QAsciiDict < QString > imapParser::parseDisposition (parseString & inWords)
+{
+ QCString disposition;
+ QAsciiDict < QString > retVal (17, false);
+
+ // return value is a shallow copy
+ retVal.setAutoDelete (false);
+
+ if (inWords[0] != '(')
+ {
+ //disposition only
+ disposition = parseOneWordC (inWords);
+ }
+ else
+ {
+ inWords.pos++;
+ skipWS (inWords);
+
+ //disposition
+ disposition = parseOneWordC (inWords);
+ retVal = parseParameters (inWords);
+ if (inWords[0] != ')')
+ return retVal;
+ inWords.pos++;
+ skipWS (inWords);
+ }
+
+ if (!disposition.isEmpty ())
+ {
+ retVal.insert ("content-disposition", new QString(disposition));
+ }
+
+ return retVal;
+}
+
+// parse parameter pairs into a dictionary
+// caller must clean up the dictionary items
+QAsciiDict < QString > imapParser::parseParameters (parseString & inWords)
+{
+ QAsciiDict < QString > retVal (17, false);
+
+ // return value is a shallow copy
+ retVal.setAutoDelete (false);
+
+ if (inWords[0] != '(')
+ {
+ //better be NIL
+ parseOneWordC (inWords);
+ }
+ else
+ {
+ inWords.pos++;
+ skipWS (inWords);
+
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ QCString l1 = parseLiteralC(inWords);
+ QCString l2 = parseLiteralC(inWords);
+ retVal.insert (l1, new QString(l2));
+ }
+
+ if (inWords[0] != ')')
+ return retVal;
+ inWords.pos++;
+ skipWS (inWords);
+ }
+
+ return retVal;
+}
+
+mimeHeader * imapParser::parseSimplePart (parseString & inWords,
+ QString & inSection, mimeHeader * localPart)
+{
+ QCString subtype;
+ QCString typeStr;
+ QAsciiDict < QString > parameters (17, false);
+ ulong size;
+
+ parameters.setAutoDelete (true);
+
+ if (inWords[0] != '(')
+ return 0;
+
+ if (!localPart)
+ localPart = new mimeHeader;
+
+ localPart->setPartSpecifier (inSection);
+
+ inWords.pos++;
+ skipWS (inWords);
+
+ //body type
+ typeStr = parseLiteralC(inWords);
+
+ //body subtype
+ subtype = parseLiteralC(inWords);
+
+ localPart->setType (typeStr + "/" + subtype);
+
+ //body parameter parenthesized list
+ parameters = parseParameters (inWords);
+ {
+ QAsciiDictIterator < QString > it (parameters);
+
+ while (it.current ())
+ {
+ localPart->setTypeParm (it.currentKey (), *(it.current ()));
+ ++it;
+ }
+ parameters.clear ();
+ }
+
+ //body id
+ localPart->setID (parseLiteralC(inWords));
+
+ //body description
+ localPart->setDescription (parseLiteralC(inWords));
+
+ //body encoding
+ localPart->setEncoding (parseLiteralC(inWords));
+
+ //body size
+ if (parseOneNumber (inWords, size))
+ localPart->setLength (size);
+
+ // type specific extensions
+ if (localPart->getType().upper() == "MESSAGE/RFC822")
+ {
+ //envelope structure
+ mailHeader *envelope = parseEnvelope (inWords);
+
+ //body structure
+ parseBodyStructure (inWords, inSection, envelope);
+
+ localPart->setNestedMessage (envelope);
+
+ //text lines
+ ulong lines;
+ parseOneNumber (inWords, lines);
+ }
+ else
+ {
+ if (typeStr == "TEXT")
+ {
+ //text lines
+ ulong lines;
+ parseOneNumber (inWords, lines);
+ }
+
+ // md5
+ parseLiteralC(inWords);
+
+ // body disposition
+ parameters = parseDisposition (inWords);
+ {
+ QString *disposition = parameters["content-disposition"];
+
+ if (disposition)
+ localPart->setDisposition (disposition->ascii ());
+ parameters.remove ("content-disposition");
+ QAsciiDictIterator < QString > it (parameters);
+ while (it.current ())
+ {
+ localPart->setDispositionParm (it.currentKey (),
+ *(it.current ()));
+ ++it;
+ }
+
+ parameters.clear ();
+ }
+
+ // body language
+ parseSentence (inWords);
+ }
+
+ // see if we have more to come
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ //eat the extensions to this part
+ if (inWords[0] == '(')
+ parseSentence (inWords);
+ else
+ parseLiteralC(inWords);
+ }
+ if (inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+
+ return localPart;
+}
+
+mimeHeader * imapParser::parseBodyStructure (parseString & inWords,
+ QString & inSection, mimeHeader * localPart)
+{
+ bool init = false;
+ if (inSection.isEmpty())
+ {
+ // first run
+ init = true;
+ // assume one part
+ inSection = "1";
+ }
+ int section = 0;
+
+ if (inWords[0] != '(')
+ {
+ // skip ""
+ parseOneWordC (inWords);
+ return 0;
+ }
+ inWords.pos++;
+ skipWS (inWords);
+
+ if (inWords[0] == '(')
+ {
+ QByteArray subtype;
+ QAsciiDict < QString > parameters (17, false);
+ QString outSection;
+ parameters.setAutoDelete (true);
+ if (!localPart)
+ localPart = new mimeHeader;
+ else
+ {
+ // might be filled from an earlier run
+ localPart->clearNestedParts ();
+ localPart->clearTypeParameters ();
+ localPart->clearDispositionParameters ();
+ // an envelope was passed in so this is the multipart header
+ outSection = inSection + ".HEADER";
+ }
+ if (inWords[0] == '(' && init)
+ inSection = "0";
+
+ // set the section
+ if ( !outSection.isEmpty() ) {
+ localPart->setPartSpecifier(outSection);
+ } else {
+ localPart->setPartSpecifier(inSection);
+ }
+
+ // is multipart (otherwise its a simplepart and handled later)
+ while (inWords[0] == '(')
+ {
+ outSection = QString::number(++section);
+ if (!init)
+ outSection = inSection + "." + outSection;
+ mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
+ localPart->addNestedPart (subpart);
+ }
+
+ // fetch subtype
+ subtype = parseOneWordC (inWords);
+
+ localPart->setType ("MULTIPART/" + b2c(subtype));
+
+ // fetch parameters
+ parameters = parseParameters (inWords);
+ {
+ QAsciiDictIterator < QString > it (parameters);
+
+ while (it.current ())
+ {
+ localPart->setTypeParm (it.currentKey (), *(it.current ()));
+ ++it;
+ }
+ parameters.clear ();
+ }
+
+ // body disposition
+ parameters = parseDisposition (inWords);
+ {
+ QString *disposition = parameters["content-disposition"];
+
+ if (disposition)
+ localPart->setDisposition (disposition->ascii ());
+ parameters.remove ("content-disposition");
+ QAsciiDictIterator < QString > it (parameters);
+ while (it.current ())
+ {
+ localPart->setDispositionParm (it.currentKey (),
+ *(it.current ()));
+ ++it;
+ }
+ parameters.clear ();
+ }
+
+ // body language
+ parseSentence (inWords);
+
+ }
+ else
+ {
+ // is simple part
+ inWords.pos--;
+ inWords.data[inWords.pos] = '('; //fake a sentence
+ if ( localPart )
+ inSection = inSection + ".1";
+ localPart = parseSimplePart (inWords, inSection, localPart);
+ inWords.pos--;
+ inWords.data[inWords.pos] = ')'; //remove fake
+ }
+
+ // see if we have more to come
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ //eat the extensions to this part
+ if (inWords[0] == '(')
+ parseSentence (inWords);
+ else
+ parseLiteralC(inWords);
+ }
+
+ if (inWords[0] == ')')
+ inWords.pos++;
+ skipWS (inWords);
+
+ return localPart;
+}
+
+void imapParser::parseBody (parseString & inWords)
+{
+ // see if we got a part specifier
+ if (inWords[0] == '[')
+ {
+ QCString specifier;
+ QCString label;
+ inWords.pos++;
+
+ specifier = parseOneWordC (inWords, TRUE);
+
+ if (inWords[0] == '(')
+ {
+ inWords.pos++;
+
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ label = parseOneWordC (inWords);
+ }
+
+ if (inWords[0] == ')')
+ inWords.pos++;
+ }
+ if (inWords[0] == ']')
+ inWords.pos++;
+ skipWS (inWords);
+
+ // parse the header
+ if (specifier == "0")
+ {
+ mailHeader *envelope = 0;
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+
+ if (!envelope || seenUid.isEmpty ())
+ {
+ kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
+ // don't know where to put it, throw it away
+ parseLiteralC(inWords, true);
+ }
+ else
+ {
+ kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
+ // fill it up with data
+ QString theHeader = parseLiteralC(inWords, true);
+ mimeIOQString myIO;
+
+ myIO.setString (theHeader);
+ envelope->parseHeader (myIO);
+
+ }
+ }
+ else if (specifier == "HEADER.FIELDS")
+ {
+ // BODY[HEADER.FIELDS (References)] {n}
+ //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: "
+ // << QCString(label.data(), label.size()+1) << endl;
+ if (label == "REFERENCES")
+ {
+ mailHeader *envelope = 0;
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+
+ if (!envelope || seenUid.isEmpty ())
+ {
+ kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
+ // don't know where to put it, throw it away
+ parseLiteralC (inWords, true);
+ }
+ else
+ {
+ QCString references = parseLiteralC(inWords, true);
+ int start = references.find ('<');
+ int end = references.findRev ('>');
+ if (start < end)
+ references = references.mid (start, end - start + 1);
+ envelope->setReferences(references.simplifyWhiteSpace());
+ }
+ }
+ else
+ { // not a header we care about throw it away
+ parseLiteralC(inWords, true);
+ }
+ }
+ else
+ {
+ if (specifier.find(".MIME") != -1)
+ {
+ mailHeader *envelope = new mailHeader;
+ QString theHeader = parseLiteralC(inWords, false);
+ mimeIOQString myIO;
+ myIO.setString (theHeader);
+ envelope->parseHeader (myIO);
+ if (lastHandled)
+ lastHandled->setHeader (envelope);
+ return;
+ }
+ // throw it away
+ kdDebug(7116) << "imapParser::parseBody - discarding " << seenUid.ascii () << endl;
+ parseLiteralC(inWords, true);
+ }
+
+ }
+ else // no part specifier
+ {
+ mailHeader *envelope = 0;
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+
+ if (!envelope || seenUid.isEmpty ())
+ {
+ kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
+ // don't know where to put it, throw it away
+ parseSentence (inWords);
+ }
+ else
+ {
+ kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
+ // fill it up with data
+ QString section;
+ mimeHeader *body = parseBodyStructure (inWords, section, envelope);
+ if (body != envelope)
+ delete body;
+ }
+ }
+}
+
+void imapParser::parseFetch (ulong /* value */, parseString & inWords)
+{
+ if (inWords[0] != '(')
+ return;
+ inWords.pos++;
+ skipWS (inWords);
+
+ delete lastHandled;
+ lastHandled = 0;
+
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ if (inWords[0] == '(')
+ parseSentence (inWords);
+ else
+ {
+ QCString word = parseLiteralC(inWords, false, true);
+
+ switch (word[0])
+ {
+ case 'E':
+ if (word == "ENVELOPE")
+ {
+ mailHeader *envelope = 0;
+
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+ else
+ lastHandled = new imapCache();
+
+ if (envelope && !envelope->getMessageId ().isEmpty ())
+ {
+ // we have seen this one already
+ // or don't know where to put it
+ parseSentence (inWords);
+ }
+ else
+ {
+ envelope = parseEnvelope (inWords);
+ if (envelope)
+ {
+ envelope->setPartSpecifier (seenUid + ".0");
+ lastHandled->setHeader (envelope);
+ lastHandled->setUid (seenUid.toULong ());
+ }
+ }
+ }
+ break;
+
+ case 'B':
+ if (word == "BODY")
+ {
+ parseBody (inWords);
+ }
+ else if (word == "BODY[]" )
+ {
+ // Do the same as with "RFC822"
+ parseLiteralC(inWords, true);
+ }
+ else if (word == "BODYSTRUCTURE")
+ {
+ mailHeader *envelope = 0;
+
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+
+ // fill it up with data
+ QString section;
+ mimeHeader *body =
+ parseBodyStructure (inWords, section, envelope);
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly );
+ if (body) body->serialize(stream);
+ parseRelay(data);
+
+ delete body;
+ }
+ break;
+
+ case 'U':
+ if (word == "UID")
+ {
+ seenUid = parseOneWordC(inWords);
+ mailHeader *envelope = 0;
+ if (lastHandled)
+ envelope = lastHandled->getHeader ();
+ else
+ lastHandled = new imapCache();
+
+ if (seenUid.isEmpty ())
+ {
+ // unknown what to do
+ kdDebug(7116) << "imapParser::parseFetch - UID empty" << endl;
+ }
+ else
+ {
+ lastHandled->setUid (seenUid.toULong ());
+ }
+ if (envelope)
+ envelope->setPartSpecifier (seenUid);
+ }
+ break;
+
+ case 'R':
+ if (word == "RFC822.SIZE")
+ {
+ ulong size;
+ parseOneNumber (inWords, size);
+
+ if (!lastHandled) lastHandled = new imapCache();
+ lastHandled->setSize (size);
+ }
+ else if (word.find ("RFC822") == 0)
+ {
+ // might be RFC822 RFC822.TEXT RFC822.HEADER
+ parseLiteralC(inWords, true);
+ }
+ break;
+
+ case 'I':
+ if (word == "INTERNALDATE")
+ {
+ QCString date = parseOneWordC(inWords);
+ if (!lastHandled) lastHandled = new imapCache();
+ lastHandled->setDate(date);
+ }
+ break;
+
+ case 'F':
+ if (word == "FLAGS")
+ {
+ //kdDebug(7116) << "GOT FLAGS " << inWords.cstr() << endl;
+ if (!lastHandled) lastHandled = new imapCache();
+ lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
+ }
+ break;
+
+ default:
+ parseLiteralC(inWords);
+ break;
+ }
+ }
+ }
+
+ // see if we have more to come
+ while (!inWords.isEmpty () && inWords[0] != ')')
+ {
+ //eat the extensions to this part
+ if (inWords[0] == '(')
+ parseSentence (inWords);
+ else
+ parseLiteralC(inWords);
+ }
+
+ if (inWords.isEmpty() || inWords[0] != ')')
+ return;
+ inWords.pos++;
+ skipWS (inWords);
+}
+
+
+// default parser
+void imapParser::parseSentence (parseString & inWords)
+{
+ bool first = true;
+ int stack = 0;
+
+ //find the first nesting parentheses
+
+ while (!inWords.isEmpty () && (stack != 0 || first))
+ {
+ first = false;
+ skipWS (inWords);
+
+ unsigned char ch = inWords[0];
+ switch (ch)
+ {
+ case '(':
+ inWords.pos++;
+ ++stack;
+ break;
+ case ')':
+ inWords.pos++;
+ --stack;
+ break;
+ case '[':
+ inWords.pos++;
+ ++stack;
+ break;
+ case ']':
+ inWords.pos++;
+ --stack;
+ break;
+ default:
+ parseLiteralC(inWords);
+ skipWS (inWords);
+ break;
+ }
+ }
+ skipWS (inWords);
+}
+
+void imapParser::parseRecent (ulong value, parseString & result)
+{
+ selectInfo.setRecent (value);
+ result.pos = result.data.size();
+}
+
+void imapParser::parseNamespace (parseString & result)
+{
+ if ( result[0] != '(' )
+ return;
+
+ QString delimEmpty;
+ if ( namespaceToDelimiter.contains( QString::null ) )
+ delimEmpty = namespaceToDelimiter[QString::null];
+
+ namespaceToDelimiter.clear();
+ imapNamespaces.clear();
+
+ // remember what section we're in (user, other users, shared)
+ int ns = -1;
+ bool personalAvailable = false;
+ while ( !result.isEmpty() )
+ {
+ if ( result[0] == '(' )
+ {
+ result.pos++; // tie off (
+ if ( result[0] == '(' )
+ {
+ // new namespace section
+ result.pos++; // tie off (
+ ++ns;
+ }
+ // namespace prefix
+ QCString prefix = parseOneWordC( result );
+ // delimiter
+ QCString delim = parseOneWordC( result );
+ kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix <<
+ "',delim='" << delim << "'" << endl;
+ if ( ns == 0 )
+ {
+ // at least one personal ns
+ personalAvailable = true;
+ }
+ QString nsentry = QString::number( ns ) + "=" + QString(prefix) +
+ "=" + QString(delim);
+ imapNamespaces.append( nsentry );
+ if ( prefix.right( 1 ) == delim ) {
+ // strip delimiter to get a correct entry for comparisons
+ prefix.resize( prefix.length() );
+ }
+ namespaceToDelimiter[prefix] = delim;
+
+ result.pos++; // tie off )
+ skipWS( result );
+ } else if ( result[0] == ')' )
+ {
+ result.pos++; // tie off )
+ skipWS( result );
+ } else if ( result[0] == 'N' )
+ {
+ // drop NIL
+ ++ns;
+ parseOneWordC( result );
+ } else {
+ // drop whatever it is
+ parseOneWordC( result );
+ }
+ }
+ if ( !delimEmpty.isEmpty() ) {
+ // remember default delimiter
+ namespaceToDelimiter[QString::null] = delimEmpty;
+ if ( !personalAvailable )
+ {
+ // at least one personal ns would be nice
+ kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl;
+ QString nsentry = "0==" + delimEmpty;
+ imapNamespaces.append( nsentry );
+ }
+ }
+}
+
+int imapParser::parseLoop ()
+{
+ parseString result;
+
+ if (!parseReadLine(result.data)) return -1;
+
+ //kdDebug(7116) << result.cstr(); // includes \n
+
+ if (result.data.isEmpty())
+ return 0;
+ if (!sentQueue.count ())
+ {
+ // maybe greeting or BYE everything else SHOULD not happen, use NOOP or IDLE
+ kdDebug(7116) << "imapParser::parseLoop - unhandledResponse: \n" << result.cstr() << endl;
+ unhandled << result.cstr();
+ }
+ else
+ {
+ imapCommand *current = sentQueue.at (0);
+ switch (result[0])
+ {
+ case '*':
+ result.data.resize(result.data.size() - 2); // tie off CRLF
+ parseUntagged (result);
+ break;
+ case '+':
+ continuation.duplicate(result.data);
+ break;
+ default:
+ {
+ QCString tag = parseLiteralC(result);
+ if (current->id() == tag.data())
+ {
+ result.data.resize(result.data.size() - 2); // tie off CRLF
+ QByteArray resultCode = parseLiteral (result); //the result
+ current->setResult (resultCode);
+ current->setResultInfo(result.cstr());
+ current->setComplete ();
+
+ sentQueue.removeRef (current);
+ completeQueue.append (current);
+ if (result.length())
+ parseResult (resultCode, result, current->command());
+ }
+ else
+ {
+ kdDebug(7116) << "imapParser::parseLoop - unknown tag '" << tag << "'" << endl;
+ QCString cstr = tag + " " + result.cstr();
+ result.data = cstr;
+ result.pos = 0;
+ result.data.resize(cstr.length());
+ }
+ }
+ break;
+ }
+ }
+
+ return 1;
+}
+
+void
+imapParser::parseRelay (const QByteArray & buffer)
+{
+ Q_UNUSED(buffer);
+ qWarning
+ ("imapParser::parseRelay - virtual function not reimplemented - data lost");
+}
+
+void
+imapParser::parseRelay (ulong len)
+{
+ Q_UNUSED(len);
+ qWarning
+ ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
+}
+
+bool imapParser::parseRead (QByteArray & buffer, ulong len, ulong relay)
+{
+ Q_UNUSED(buffer);
+ Q_UNUSED(len);
+ Q_UNUSED(relay);
+ qWarning
+ ("imapParser::parseRead - virtual function not reimplemented - no data read");
+ return FALSE;
+}
+
+bool imapParser::parseReadLine (QByteArray & buffer, ulong relay)
+{
+ Q_UNUSED(buffer);
+ Q_UNUSED(relay);
+ qWarning
+ ("imapParser::parseReadLine - virtual function not reimplemented - no data read");
+ return FALSE;
+}
+
+void
+imapParser::parseWriteLine (const QString & str)
+{
+ Q_UNUSED(str);
+ qWarning
+ ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
+}
+
+void
+imapParser::parseURL (const KURL & _url, QString & _box, QString & _section,
+ QString & _type, QString & _uid, QString & _validity, QString & _info)
+{
+ QStringList parameters;
+
+ _box = _url.path ();
+ kdDebug(7116) << "imapParser::parseURL " << _box << endl;
+ int paramStart = _box.find("/;");
+ if ( paramStart > -1 )
+ {
+ QString paramString = _box.right( _box.length() - paramStart-2 );
+ parameters = QStringList::split (';', paramString); //split parameters
+ _box.truncate( paramStart ); // strip parameters
+ }
+ // extract parameters
+ for (QStringList::ConstIterator it (parameters.begin ());
+ it != parameters.end (); ++it)
+ {
+ QString temp = (*it);
+
+ int pt = temp.find ('/');
+ if (pt > 0)
+ {
+ if (temp.findRev ('"', pt) == -1 || temp.find('"', pt) == -1)
+ {
+ // if we have non-quoted '/' separator we'll just nuke it
+ temp.truncate(pt);
+ }
+ }
+ if (temp.find ("section=", 0, false) == 0)
+ _section = temp.right (temp.length () - 8);
+ else if (temp.find ("type=", 0, false) == 0)
+ _type = temp.right (temp.length () - 5);
+ else if (temp.find ("uid=", 0, false) == 0)
+ _uid = temp.right (temp.length () - 4);
+ else if (temp.find ("uidvalidity=", 0, false) == 0)
+ _validity = temp.right (temp.length () - 12);
+ else if (temp.find ("info=", 0, false) == 0)
+ _info = temp.right (temp.length () - 5);
+ }
+// kdDebug(7116) << "URL: section= " << _section << ", type= " << _type << ", uid= " << _uid << endl;
+// kdDebug(7116) << "URL: user() " << _url.user() << endl;
+// kdDebug(7116) << "URL: path() " << _url.path() << endl;
+// kdDebug(7116) << "URL: encodedPathAndQuery() " << _url.encodedPathAndQuery() << endl;
+
+ if (!_box.isEmpty ())
+ {
+ // strip /
+ if (_box[0] == '/')
+ _box = _box.right (_box.length () - 1);
+ if (!_box.isEmpty () && _box[_box.length () - 1] == '/')
+ _box.truncate(_box.length() - 1);
+ }
+ kdDebug(7116) << "URL: box= " << _box << ", section= " << _section << ", type= "
+ << _type << ", uid= " << _uid << ", validity= " << _validity << ", info= " << _info << endl;
+}
+
+
+QCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) {
+
+ if (!inWords.isEmpty() && inWords[0] == '{')
+ {
+ QCString retVal;
+ ulong runLen = inWords.find ('}', 1);
+ if (runLen > 0)
+ {
+ bool proper;
+ ulong runLenSave = runLen + 1;
+ QCString tmpstr(runLen);
+ inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
+ runLen = tmpstr.toULong (&proper);
+ inWords.pos += runLenSave;
+ if (proper)
+ {
+ //now get the literal from the server
+ if (relay)
+ parseRelay (runLen);
+ QByteArray rv;
+ parseRead (rv, runLen, relay ? runLen : 0);
+ rv.resize(QMAX(runLen, rv.size())); // what's the point?
+ retVal = b2c(rv);
+ inWords.clear();
+ parseReadLine (inWords.data); // must get more
+
+ // no duplicate data transfers
+ relay = false;
+ }
+ else
+ {
+ kdDebug(7116) << "imapParser::parseLiteral - error parsing {} - " /*<< strLen*/ << endl;
+ }
+ }
+ else
+ {
+ inWords.clear();
+ kdDebug(7116) << "imapParser::parseLiteral - error parsing unmatched {" << endl;
+ }
+ if (outlen) {
+ *outlen = retVal.length(); // optimize me
+ }
+ skipWS (inWords);
+ return retVal;
+ }
+
+ return parseOneWordC(inWords, stopAtBracket, outlen);
+}
+
+// does not know about literals ( {7} literal )
+QCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen)
+{
+ uint retValSize = 0;
+ uint len = inWords.length();
+ if (len == 0) {
+ return QCString();
+ }
+
+ if (len > 0 && inWords[0] == '"')
+ {
+ unsigned int i = 1;
+ bool quote = FALSE;
+ while (i < len && (inWords[i] != '"' || quote))
+ {
+ if (inWords[i] == '\\') quote = !quote;
+ else quote = FALSE;
+ i++;
+ }
+ if (i < len)
+ {
+ QCString retVal(i);
+ inWords.pos++;
+ inWords.takeLeftNoResize(retVal, i - 1);
+ len = i - 1;
+ int offset = 0;
+ for (unsigned int j = 0; j <= len; j++) {
+ if (retVal[j] == '\\') {
+ offset++;
+ j++;
+ }
+ retVal[j - offset] = retVal[j];
+ }
+ retVal[len - offset] = 0;
+ retValSize = len - offset;
+ inWords.pos += i;
+ skipWS (inWords);
+ if (outLen) {
+ *outLen = retValSize;
+ }
+ return retVal;
+ }
+ else
+ {
+ kdDebug(7116) << "imapParser::parseOneWord - error parsing unmatched \"" << endl;
+ QCString retVal = inWords.cstr();
+ retValSize = len;
+ inWords.clear();
+ if (outLen) {
+ *outLen = retValSize;
+ }
+ return retVal;
+ }
+ }
+ else
+ {
+ // not quoted
+ unsigned int i;
+ // search for end
+ for (i = 0; i < len; ++i) {
+ char ch = inWords[i];
+ if (ch <= ' ' || ch == '(' || ch == ')' ||
+ (stopAtBracket && (ch == '[' || ch == ']')))
+ break;
+ }
+
+ QCString retVal(i+1);
+ inWords.takeLeftNoResize(retVal, i);
+ retValSize = i;
+ inWords.pos += i;
+
+ if (retVal == "NIL") {
+ retVal.truncate(0);
+ retValSize = 0;
+ }
+ skipWS (inWords);
+ if (outLen) {
+ *outLen = retValSize;
+ }
+ return retVal;
+ }
+}
+
+bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
+{
+ bool valid;
+ num = parseOneWordC(inWords, TRUE).toULong(&valid);
+ return valid;
+}
+
+bool imapParser::hasCapability (const QString & cap)
+{
+ QString c = cap.lower();
+// kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl;
+ for (QStringList::ConstIterator it = imapCapabilities.begin ();
+ it != imapCapabilities.end (); ++it)
+ {
+// kdDebug(7116) << "imapParser::hasCapability - Examining '" << (*it) << "'" << endl;
+ if ( !(kasciistricmp(c.ascii(), (*it).ascii())) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void imapParser::removeCapability (const QString & cap)
+{
+ imapCapabilities.remove(cap.lower());
+}
+
+QString imapParser::namespaceForBox( const QString & box )
+{
+ kdDebug(7116) << "imapParse::namespaceForBox " << box << endl;
+ QString myNamespace;
+ if ( !box.isEmpty() )
+ {
+ QValueList<QString> list = namespaceToDelimiter.keys();
+ QString cleanPrefix;
+ for ( QValueList<QString>::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ if ( !(*it).isEmpty() && box.find( *it ) != -1 )
+ return (*it);
+ }
+ }
+ return myNamespace;
+}
+
diff --git a/kioslaves/imap4/imapparser.h b/kioslaves/imap4/imapparser.h
new file mode 100644
index 000000000..74fad1457
--- /dev/null
+++ b/kioslaves/imap4/imapparser.h
@@ -0,0 +1,500 @@
+#ifndef _IMAPPARSER_H
+#define _IMAPPARSER_H
+/**********************************************************************
+ *
+ * imapparser.h - IMAP4rev1 Parser
+ * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include <qasciidict.h>
+
+#include <kio/authinfo.h>
+#include <kio/slavebase.h>
+
+#include "imaplist.h"
+#include "imapcommand.h"
+#include "imapinfo.h"
+
+#include "mailheader.h"
+
+class KURL;
+class QString;
+class mailAddress;
+class mimeHeader;
+
+
+/** @brief a string used during parsing
+ * the string allows you to move the effective start of the string using
+ * str.pos++ and str.pos--.
+ * @bug it is possible to move past the beginning and end of the string
+ */
+class parseString
+{
+public:
+ parseString() { pos = 0; }
+ char operator[](uint i) const { return data[i + pos]; }
+ bool isEmpty() const { return pos >= data.size(); }
+ QCString cstr() const
+ {
+ if (pos >= data.size()) return QCString();
+ return QCString(data.data() + pos, data.size() - pos + 1);
+ }
+ int find(char c, int index = 0)
+ {
+ int res = data.find(c, index + pos);
+ return (res == -1) ? res : (res - pos);
+ }
+ // Warning: does not check for going past end of "data"
+ void takeLeft(QCString& dest, uint len) const
+ {
+ dest.resize(len + 1);
+ qmemmove(dest.data(), data.data() + pos, len);
+ }
+ // Warning: does not check for going past end of "data"
+ void takeLeftNoResize(QCString& dest, uint len) const
+ {
+ qmemmove(dest.data(), data.data() + pos, len);
+ }
+ // Warning: does not check for going past end of "data"
+ void takeMid(QCString& dest, uint start, uint len) const
+ {
+ dest.resize(len + 1);
+ qmemmove(dest.data(), data.data() + pos + start, len);
+ }
+ // Warning: does not check for going past end of "data"
+ void takeMidNoResize(QCString& dest, uint start, uint len) const
+ {
+ qmemmove(dest.data(), data.data() + pos + start, len);
+ }
+ void clear()
+ {
+ data.resize(0);
+ pos = 0;
+ }
+ uint length()
+ {
+ return data.size() - pos;
+ }
+ void fromString(const QString &s)
+ {
+ clear();
+ data.duplicate(s.latin1(), s.length());
+ }
+ QByteArray data;
+ uint pos;
+};
+
+class imapCache
+{
+public:
+ imapCache ()
+ {
+ myHeader = NULL;
+ mySize = 0;
+ myFlags = 0;
+ myUid = 0;
+ }
+
+ ~imapCache ()
+ {
+ if (myHeader) delete myHeader;
+ }
+
+ mailHeader *getHeader ()
+ {
+ return myHeader;
+ }
+ void setHeader (mailHeader * inHeader)
+ {
+ myHeader = inHeader;
+ }
+
+ ulong getSize ()
+ {
+ return mySize;
+ }
+ void setSize (ulong inSize)
+ {
+ mySize = inSize;
+ }
+
+ ulong getUid ()
+ {
+ return myUid;
+ }
+ void setUid (ulong inUid)
+ {
+ myUid = inUid;
+ }
+
+ ulong getFlags ()
+ {
+ return myFlags;
+ }
+ void setFlags (ulong inFlags)
+ {
+ myFlags = inFlags;
+ }
+
+ QCString getDate ()
+ {
+ return myDate;
+ }
+ void setDate (const QCString & _str)
+ {
+ myDate = _str;
+ }
+ void clear()
+ {
+ if (myHeader) delete myHeader;
+ myHeader = NULL;
+ mySize = 0;
+ myFlags = 0;
+ myDate = QCString();
+ myUid = 0;
+ }
+
+protected:
+ mailHeader * myHeader;
+ ulong mySize;
+ ulong myFlags;
+ ulong myUid;
+ QCString myDate;
+};
+
+
+class imapParser
+{
+
+public:
+
+ /** the different states the client can be in */
+ enum IMAP_STATE
+ {
+ ISTATE_NO, /**< Not connected */
+ ISTATE_CONNECT, /**< Connected but not logged in */
+ ISTATE_LOGIN, /**< Logged in */
+ ISTATE_SELECT /**< A folder is currently selected */
+ };
+
+public:
+ imapParser ();
+ virtual ~ imapParser ();
+
+ /** @brief Get the current state */
+ enum IMAP_STATE getState () { return currentState; }
+ /** @brief Set the current state */
+ void setState(enum IMAP_STATE state) { currentState = state; }
+
+ /* @brief return the currently selected mailbox */
+ const QString getCurrentBox ()
+ {
+ return rfcDecoder::fromIMAP(currentBox);
+ };
+
+ /**
+ * @brief do setup and send the command to parseWriteLine
+ * @param aCmd The command to perform
+ * @return The completed command
+ */
+ imapCommand *sendCommand (imapCommand * aCmd);
+ /**
+ * @brief perform a command and wait to parse the result
+ * @param aCmd The command to perform
+ * @return The completed command
+ */
+ imapCommand *doCommand (imapCommand * aCmd);
+
+
+ /**
+ * @brief plaintext login
+ * @param aUser Username
+ * @param aPass Password
+ * @param resultInfo The resultinfo from the command
+ * @return success or failure
+ */
+ bool clientLogin (const QString & aUser, const QString & aPass, QString & resultInfo);
+ /**
+ * @brief non-plaintext login
+ * @param aUser Username
+ * @param aPass Password
+ * @param aAuth authentication method
+ * @param isSSL are we using SSL
+ * @param resultInfo The resultinfo from the command
+ * @return success or failure
+ */
+ bool clientAuthenticate (KIO::SlaveBase *slave, KIO::AuthInfo &ai, const QString & aFQDN,
+ const QString & aAuth, bool isSSL, QString & resultInfo);
+
+ /**
+ * main loop for the parser
+ * reads one line and dispatches it to the appropriate sub parser
+ */
+ int parseLoop ();
+
+ /**
+ * @brief parses all untagged responses and passes them on to the
+ * following parsers
+ */
+ void parseUntagged (parseString & result);
+
+ /** @brief parse a RECENT line */
+ void parseRecent (ulong value, parseString & result);
+ /** @brief parse a RESULT line */
+ void parseResult (QByteArray & result, parseString & rest,
+ const QString & command = QString::null);
+ /** @brief parse a CAPABILITY line */
+ void parseCapability (parseString & result);
+ /** @brief parse a FLAGS line */
+ void parseFlags (parseString & result);
+ /** @brief parse a LIST line */
+ void parseList (parseString & result);
+ /** @brief parse a LSUB line */
+ void parseLsub (parseString & result);
+ /** @brief parse a LISTRIGHTS line */
+ void parseListRights (parseString & result);
+ /** @brief parse a MYRIGHTS line */
+ void parseMyRights (parseString & result);
+ /** @brief parse a SEARCH line */
+ void parseSearch (parseString & result);
+ /** @brief parse a STATUS line */
+ void parseStatus (parseString & result);
+ /** @brief parse a EXISTS line */
+ void parseExists (ulong value, parseString & result);
+ /** @brief parse a EXPUNGE line */
+ void parseExpunge (ulong value, parseString & result);
+ /** @brief parse a ACL line */
+ void parseAcl (parseString & result);
+ /** @brief parse a ANNOTATION line */
+ void parseAnnotation (parseString & result);
+ /** @brief parse a NAMESPACE line */
+ void parseNamespace (parseString & result);
+ /** @brief parse a QUOTAROOT line */
+ void parseQuotaRoot (parseString & result);
+ /** @brief parse a QUOTA line */
+ void parseQuota (parseString & result);
+ /** @brief parse a custom command line */
+ void parseCustom (parseString & result);
+ /** @brief parse a OTHER-USER line */
+ void parseOtherUser (parseString & result);
+ /** @brief parse a DELEGATE line */
+ void parseDelegate (parseString & result);
+ /** @brief parse a OUT-OF-OFFICE line */
+ void parseOutOfOffice (parseString & result);
+
+ /**
+ * parses the results of a fetch command
+ * processes it with the following sub parsers
+ */
+ void parseFetch (ulong value, parseString & inWords);
+
+ /** read a envelope from imap and parse the addresses */
+ mailHeader *parseEnvelope (parseString & inWords);
+ /** @brief parse an address list and return a list of addresses */
+ void parseAddressList (parseString & inWords, QPtrList<mailAddress>& list);
+ /** @brief parse an address and return the ref again */
+ const mailAddress& parseAddress (parseString & inWords, mailAddress& buffer);
+
+ /** parse the result of the body command */
+ void parseBody (parseString & inWords);
+
+ /** parse the body structure recursively */
+ mimeHeader *parseBodyStructure (parseString & inWords,
+ QString & section, mimeHeader * inHeader = 0);
+
+ /** parse only one not nested part */
+ mimeHeader *parseSimplePart (parseString & inWords, QString & section,
+ mimeHeader * localPart = 0);
+
+ /** parse a parameter list (name value pairs) */
+ QAsciiDict < QString > parseParameters (parseString & inWords);
+
+ /**
+ * parse the disposition list (disposition (name value pairs))
+ * the disposition has the key 'content-disposition'
+ */
+ QAsciiDict < QString > parseDisposition (parseString & inWords);
+
+ // reimplement these
+
+ /** relay hook to send the fetched data directly to an upper level */
+ virtual void parseRelay (const QByteArray & buffer);
+
+ /** relay hook to announce the fetched data directly to an upper level
+ */
+ virtual void parseRelay (ulong);
+
+ /** read at least len bytes */
+ virtual bool parseRead (QByteArray & buffer, ulong len, ulong relay = 0);
+
+ /** read at least a line (up to CRLF) */
+ virtual bool parseReadLine (QByteArray & buffer, ulong relay = 0);
+
+ /** write argument to server */
+ virtual void parseWriteLine (const QString &);
+
+ // generic parser routines
+
+ /** parse a parenthesized list */
+ void parseSentence (parseString & inWords);
+
+ /** parse a literal or word, may require more data */
+ QCString parseLiteralC(parseString & inWords, bool relay = false,
+ bool stopAtBracket = false, int *outlen = 0);
+ inline QByteArray parseLiteral (parseString & inWords, bool relay = false,
+ bool stopAtBracket = false) {
+ int len = 0; // string size
+ // Choice: we can create an extra QCString, or we can get the buffer in
+ // the wrong size to start. Let's try option b.
+ QCString tmp = parseLiteralC(inWords, relay, stopAtBracket, &len);
+ return QByteArray().duplicate(tmp.data(), len);
+ }
+
+ // static parser routines, can be used elsewhere
+
+ static QCString b2c(const QByteArray &ba)
+ { return QCString(ba.data(), ba.size() + 1); }
+
+ /** parse one word (maybe quoted) upto next space " ) ] } */
+ static QCString parseOneWordC (parseString & inWords,
+ bool stopAtBracket = FALSE, int *len = 0);
+
+ /** parse one number using parseOneWord */
+ static bool parseOneNumber (parseString & inWords, ulong & num);
+
+ /** extract the box,section,list type, uid, uidvalidity,info from an url */
+ static void parseURL (const KURL & _url, QString & _box, QString & _section,
+ QString & _type, QString & _uid, QString & _validity,
+ QString & _info);
+
+
+ /** @brief return the last handled foo
+ * @todo work out what a foo is
+ */
+ imapCache *getLastHandled ()
+ {
+ return lastHandled;
+ };
+
+/** @brief return the last results */
+ const QStringList & getResults ()
+ {
+ return lastResults;
+ };
+
+ /** @brief return the last status code */
+ const imapInfo & getStatus ()
+ {
+ return lastStatus;
+ };
+ /** return the select info */
+ const imapInfo & getSelected ()
+ {
+ return selectInfo;
+ };
+
+ const QByteArray & getContinuation ()
+ {
+ return continuation;
+ };
+
+ /** @brief see if server has a capability */
+ bool hasCapability (const QString &);
+
+ void removeCapability (const QString & cap);
+
+ static inline void skipWS (parseString & inWords)
+ {
+ char c;
+ while (!inWords.isEmpty() &&
+ ((c = inWords[0]) == ' ' || c == '\t' || c == '\r' || c == '\n'))
+ {
+ inWords.pos++;
+ }
+ }
+
+ /** @brief find the namespace for the given box */
+ QString namespaceForBox( const QString & box );
+
+
+protected:
+
+ /** the current state we're in */
+ enum IMAP_STATE currentState;
+
+ /** the box selected */
+ QString currentBox;
+
+ /** @brief here we store the result from select/examine and unsolicited updates */
+ imapInfo selectInfo;
+
+ /** @brief the results from the last status command */
+ imapInfo lastStatus;
+
+ /** @brief the results from the capabilities, split at ' ' */
+ QStringList imapCapabilities;
+
+ /** @brief the results from list/lsub/listrights commands */
+ QValueList < imapList > listResponses;
+
+ /** @brief queues handling the running commands */
+ QPtrList < imapCommand > sentQueue; // no autodelete
+ QPtrList < imapCommand > completeQueue; // autodelete !!
+
+ /**
+ * everything we didn't handle, everything but the greeting is bogus
+ */
+ QStringList unhandled;
+
+ /** the last continuation request (there MUST not be more than one pending) */
+ QByteArray continuation;
+
+ /** the last uid seen while a fetch */
+ QString seenUid;
+ imapCache *lastHandled;
+
+ ulong commandCounter;
+
+ /** @brief the results from search/acl commands */
+ QStringList lastResults;
+
+ /**
+ * @brief namespace prefix - delimiter association
+ * The namespace is cleaned before so that it does not contain the delimiter
+ */
+ QMap<QString, QString> namespaceToDelimiter;
+
+ /**
+ * @brief list of namespaces in the form: section=namespace=delimiter
+ * section is 0 (personal), 1 (other users) or 2 (shared)
+ */
+ QStringList imapNamespaces;
+
+private:
+
+ /** we don't want to be able to copy this object */
+ imapParser & operator = (const imapParser &); // hide the copy ctor
+
+};
+#endif
diff --git a/kioslaves/imap4/imaps.protocol b/kioslaves/imap4/imaps.protocol
new file mode 100644
index 000000000..1846bd3cb
--- /dev/null
+++ b/kioslaves/imap4/imaps.protocol
@@ -0,0 +1,30 @@
+[Protocol]
+# The executable, of course
+#### Temporary name
+exec=kio_imap4
+# protocol that will appear in URLs
+#### This ioslave is temporarily named imaps, while imaps remains in kdebase
+protocol=imaps
+
+# input/output can be one of: filesystem, stream, none
+input=stream
+output=filesystem
+
+# Headings for file listings?
+listing=Name,Type,Size,Owner
+deleting=true
+linking=false
+# For now, reading yes, writing no
+reading=true
+writing=false
+# For now, no moving
+moving=false
+
+# Can be source protocol
+source=true
+
+# List of capabilities (e.g. special() commands)
+Capabilities=Subscription,ACL,Quota
+
+Icon=folder_inbox
+DocPath=kioslave/imaps.html
diff --git a/kioslaves/imap4/mailaddress.cc b/kioslaves/imap4/mailaddress.cc
new file mode 100644
index 000000000..a70b2d591
--- /dev/null
+++ b/kioslaves/imap4/mailaddress.cc
@@ -0,0 +1,323 @@
+/**********************************************************************
+ *
+ * mailaddress.cc - mail address parser
+ * Copyright (C) 2000 Sven Carstens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+
+#include "mailaddress.h"
+#include "rfcdecoder.h"
+#include "mimehdrline.h"
+#include <kmime_util.h>
+
+mailAddress::mailAddress ()
+{
+}
+
+mailAddress::mailAddress (const mailAddress & lr):
+user (lr.user),
+host (lr.host),
+rawFullName (lr.rawFullName),
+rawComment (lr.rawComment)
+{
+// kdDebug(7116) << "mailAddress::mailAddress - " << getStr() << endl;
+}
+
+mailAddress & mailAddress::operator = (const mailAddress & lr)
+{
+ // Avoid a = a.
+ if (this == &lr)
+ return *this;
+
+ user = lr.user;
+ host = lr.host;
+ rawFullName = lr.rawFullName;
+ rawComment = lr.rawComment;
+
+// kdDebug(7116) << "mailAddress::operator= - " << getStr() << endl;
+
+ return *this;
+}
+
+
+
+
+mailAddress::~mailAddress ()
+{
+}
+
+mailAddress::mailAddress (char *aCStr)
+{
+ parseAddress (aCStr);
+}
+
+int
+mailAddress::parseAddress (char *aCStr)
+{
+ int retVal = 0;
+ int skip;
+ uint len;
+ int pt;
+
+ if (aCStr)
+ {
+ //skip leading white space
+ skip = mimeHdrLine::skipWS ((const char *) aCStr);
+ if (skip > 0)
+ {
+ aCStr += skip;
+ retVal += skip;
+ }
+ while (*aCStr)
+ {
+ int advance;
+
+ switch (*aCStr)
+ {
+ case '"':
+ advance = mimeHdrLine::parseQuoted ('"', '"', aCStr);
+ rawFullName += QCString (aCStr, advance + 1);
+ break;
+ case '(':
+ advance = mimeHdrLine::parseQuoted ('(', ')', aCStr);
+ rawComment += QCString (aCStr, advance + 1);
+ break;
+ case '<':
+ advance = mimeHdrLine::parseQuoted ('<', '>', aCStr);
+ user = QCString (aCStr, advance + 1); // copy it
+ len = advance;
+ user = user.mid (1, len - 2); // strip <>
+ len -= 2;
+ pt = user.find('@');
+ host = user.right (len - pt - 1); // split it into host
+ user.truncate(pt); // and user
+ break;
+ default:
+ advance = mimeHdrLine::parseWord ((const char *) aCStr);
+ //if we've seen a FQ mailname the rest must be quoted or is just junk
+ if (user.isEmpty ())
+ {
+ if (*aCStr != ',')
+ {
+ rawFullName += QCString (aCStr, advance + 1);
+ if (mimeHdrLine::skipWS ((const char *) &aCStr[advance]) > 0)
+ {
+ rawFullName += ' ';
+ }
+ }
+ }
+ break;
+ }
+ if (advance)
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ else
+ break;
+ advance = mimeHdrLine::skipWS ((const char *) aCStr);
+ if (advance > 0)
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ //reached end of current address
+ if (*aCStr == ',')
+ {
+ advance++;
+ break;
+ }
+ }
+ //let's see what we've got
+ if (rawFullName.isEmpty ())
+ {
+ if (user.isEmpty ())
+ retVal = 0;
+ else
+ {
+ if (host.isEmpty ())
+ {
+ rawFullName = user;
+ user.truncate(0);
+ }
+ }
+ }
+ else if (user.isEmpty ())
+ {
+ pt = rawFullName.find ('@');
+ if (pt >= 0)
+ {
+ user = rawFullName;
+ host = user.right (user.length () - pt - 1);
+ user.truncate(pt);
+ rawFullName.truncate(0);
+ }
+ }
+
+#if 0
+// dead
+ if (!rawFullName.isEmpty ())
+ {
+// if(fullName[0] == '"')
+// fullName = fullName.mid(1,fullName.length()-2);
+// fullName = fullName.simplifyWhiteSpace().stripWhiteSpace();
+// fullName = rfcDecoder::decodeRFC2047String(fullName.ascii());
+ }
+#endif
+ if (!rawComment.isEmpty ())
+ {
+ if (rawComment[0] == '(')
+ rawComment = rawComment.mid (1, rawComment.length () - 2);
+ rawComment = rawComment.stripWhiteSpace ();
+// comment = rfcDecoder::decodeRFC2047String(comment.ascii());
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+const QCString
+mailAddress::getStr ()
+{
+ QCString retVal(128); // Should be generally big enough
+
+ if (!rawFullName.isEmpty ())
+ {
+ KMime::addQuotes( rawFullName, false );
+ retVal = rawFullName + " ";
+ }
+ if (!user.isEmpty ())
+ {
+ retVal += '<';
+ retVal += user;
+ if (!host.isEmpty ()) {
+ retVal += '@';
+ retVal += host;
+ }
+ retVal += '>';
+ }
+ if (!rawComment.isEmpty ())
+ {
+ retVal = '(' + rawComment + ')';
+ }
+// kdDebug(7116) << "mailAddress::getStr - '" << retVal << "'" << endl;
+ return retVal;
+}
+
+bool
+mailAddress::isEmpty () const
+{
+ return user.isEmpty ();
+}
+
+void
+mailAddress::setFullName (const QString & _str)
+{
+ rawFullName = rfcDecoder::encodeRFC2047String (_str).latin1 ();
+}
+const QString
+mailAddress::getFullName () const
+{
+ return rfcDecoder::decodeRFC2047String (rawFullName);
+}
+
+void
+mailAddress::setCommentRaw (const QCString & _str)
+{
+ rawComment = _str;
+}
+
+void
+mailAddress::setComment (const QString & _str)
+{
+ rawComment = rfcDecoder::encodeRFC2047String (_str).latin1 ();
+}
+const QString
+mailAddress::getComment () const
+{
+ return rfcDecoder::decodeRFC2047String (rawComment);
+}
+
+const QCString &
+mailAddress::getCommentRaw () const
+{
+ return rawComment;
+}
+
+QString
+mailAddress::emailAddrAsAnchor (const mailAddress & adr, bool shortAdr)
+{
+ QString retVal;
+ if (!adr.getFullName ().isEmpty ())
+ {
+ // should do some umlaut escaping
+ retVal += adr.getFullName () + " ";
+ }
+ if (!adr.getUser ().isEmpty () && !shortAdr)
+ {
+ retVal += "&lt;" + adr.getUser ();
+ if (!adr.getHost ().isEmpty ())
+ retVal += "@" + adr.getHost ();
+ retVal += "&gt; ";
+ }
+ if (!adr.getComment ().isEmpty ())
+ {
+ // should do some umlaut escaping
+ retVal = '(' + adr.getComment () + ')';
+ }
+
+ if (!adr.getUser ().isEmpty ())
+ {
+ QString mail;
+ mail = adr.getUser ();
+ if (!mail.isEmpty () && !adr.getHost ().isEmpty ())
+ mail += "@" + adr.getHost ();
+ if (!mail.isEmpty ())
+ retVal = "<A HREF=\"mailto:" + mail + "\">" + retVal + "</A>";
+ }
+ return retVal;
+}
+
+QString
+mailAddress::emailAddrAsAnchor (const QPtrList < mailAddress > &list, bool value)
+{
+ QString retVal;
+ QPtrListIterator < mailAddress > it (list);
+
+ while (it.current ())
+ {
+ retVal += emailAddrAsAnchor ((*it.current ()), value) + "<BR></BR>\n";
+ ++it;
+ }
+
+ return retVal;
+}
+
+
+void mailAddress::clear() {
+ user.truncate(0);
+ host.truncate(0);
+ rawFullName.truncate(0);
+ rawComment.truncate(0);
+}
+
diff --git a/kioslaves/imap4/mailaddress.h b/kioslaves/imap4/mailaddress.h
new file mode 100644
index 000000000..4ee68a396
--- /dev/null
+++ b/kioslaves/imap4/mailaddress.h
@@ -0,0 +1,81 @@
+#ifndef _MAILADDRESS_H
+#define _MAILADDRESS_H
+/**********************************************************************
+ *
+ * mailaddress.h - mail address handler
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+
+#include <qptrlist.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include "rfcdecoder.h"
+
+class mailAddress
+{
+public:
+ mailAddress ();
+ ~mailAddress ();
+ mailAddress (char *aCStr);
+ mailAddress (const mailAddress &);
+ mailAddress & operator = (const mailAddress &);
+
+ void setUser (const QCString & aUser)
+ {
+ user = aUser;
+ }
+ const QCString & getUser () const
+ {
+ return user;
+ }
+ void setHost (const QCString & aHost)
+ {
+ host = aHost;
+ }
+ const QCString & getHost () const
+ {
+ return host;
+ }
+
+ void setFullName (const QString & aFull);
+ const QString getFullName () const;
+
+ void setComment (const QString & aComment);
+ void setCommentRaw (const QCString &);
+ const QString getComment () const;
+ const QCString & getCommentRaw () const;
+
+ int parseAddress (char *);
+ const QCString getStr ();
+ bool isEmpty () const;
+
+ static QString emailAddrAsAnchor (const mailAddress &, bool);
+ static QString emailAddrAsAnchor (const QPtrList < mailAddress > &, bool);
+
+ void clear();
+
+private:
+ QCString user;
+ QCString host;
+ QCString rawFullName;
+ QCString rawComment;
+};
+
+#endif
diff --git a/kioslaves/imap4/mailheader.cc b/kioslaves/imap4/mailheader.cc
new file mode 100644
index 000000000..960e5ba52
--- /dev/null
+++ b/kioslaves/imap4/mailheader.cc
@@ -0,0 +1,203 @@
+/***************************************************************************
+ mailheader.cc - description
+ -------------------
+ begin : Tue Oct 24 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "mailheader.h"
+#include "rfcdecoder.h"
+
+mailHeader::mailHeader ()
+{
+ toAdr.setAutoDelete (true);
+ ccAdr.setAutoDelete (true);
+ bccAdr.setAutoDelete (true);
+ setType ("text/plain");
+ gmt_offset = 0;
+}
+
+mailHeader::~mailHeader ()
+{
+}
+
+void
+mailHeader::addHdrLine (mimeHdrLine * inLine)
+{
+ mimeHdrLine *addLine = new mimeHdrLine (inLine);
+
+ const QCString label(addLine->getLabel());
+ const QCString value(addLine->getValue());
+
+ if (!qstricmp (label, "Return-Path")) {
+ returnpathAdr.parseAddress (value.data ());
+ goto out;
+ }
+ if (!qstricmp (label, "Sender")) {
+ senderAdr.parseAddress (value.data ());
+ goto out;
+ }
+ if (!qstricmp (label, "From")) {
+ fromAdr.parseAddress (value.data ());
+ goto out;
+ }
+ if (!qstricmp (label, "Reply-To")) {
+ replytoAdr.parseAddress (value.data ());
+ goto out;
+ }
+ if (!qstricmp (label, "To")) {
+ mailHeader::parseAddressList (value, &toAdr);
+ goto out;
+ }
+ if (!qstricmp (label, "CC")) {
+ mailHeader::parseAddressList (value, &ccAdr);
+ goto out;
+ }
+ if (!qstricmp (label, "BCC")) {
+ mailHeader::parseAddressList (value, &bccAdr);
+ goto out;
+ }
+ if (!qstricmp (label, "Subject")) {
+ _subject = value.simplifyWhiteSpace();
+ goto out;
+ }
+ if (!qstricmp (label.data (), "Date")) {
+ mDate = value;
+ goto out;
+ }
+ if (!qstricmp (label.data (), "Message-ID")) {
+ int start = value.findRev ('<');
+ int end = value.findRev ('>');
+ if (start < end)
+ messageID = value.mid (start, end - start + 1);
+ else {
+ qWarning("bad Message-ID");
+ /* messageID = value; */
+ }
+ goto out;
+ }
+ if (!qstricmp (label.data (), "In-Reply-To")) {
+ int start = value.findRev ('<');
+ int end = value.findRev ('>');
+ if (start < end)
+ inReplyTo = value.mid (start, end - start + 1);
+ goto out;
+ }
+
+ // everything else is handled by mimeHeader
+ mimeHeader::addHdrLine (inLine);
+ delete addLine;
+ return;
+
+ out:
+// cout << label.data() << ": '" << value.data() << "'" << endl;
+
+ //need only to add this line if not handled by mimeHeader
+ originalHdrLines.append (addLine);
+}
+
+void
+mailHeader::outputHeader (mimeIO & useIO)
+{
+ static const QCString __returnPath("Return-Path: ", 14);
+ static const QCString __from ("From: ", 7);
+ static const QCString __sender ("Sender: ", 9);
+ static const QCString __replyTo ("Reply-To: ", 11);
+ static const QCString __to ("To: ", 5);
+ static const QCString __cc ("CC: ", 5);
+ static const QCString __bcc ("BCC: ", 6);
+ static const QCString __subject ("Subject: ", 10);
+ static const QCString __messageId ("Message-ID: ", 13);
+ static const QCString __inReplyTo ("In-Reply-To: ", 14);
+ static const QCString __references("References: ", 13);
+ static const QCString __date ("Date: ", 7);
+
+ if (!returnpathAdr.isEmpty())
+ useIO.outputMimeLine(__returnPath + returnpathAdr.getStr());
+ if (!fromAdr.isEmpty())
+ useIO.outputMimeLine(__from + fromAdr.getStr());
+ if (!senderAdr.isEmpty())
+ useIO.outputMimeLine(__sender + senderAdr.getStr());
+ if (!replytoAdr.isEmpty())
+ useIO.outputMimeLine(__replyTo + replytoAdr.getStr());
+
+ if (toAdr.count())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__to +
+ mailHeader::getAddressStr(&toAdr)));
+ if (ccAdr.count())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__cc +
+ mailHeader::getAddressStr(&ccAdr)));
+ if (bccAdr.count())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__bcc +
+ mailHeader::getAddressStr(&bccAdr)));
+ if (!_subject.isEmpty())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__subject + _subject));
+ if (!messageID.isEmpty())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__messageId + messageID));
+ if (!inReplyTo.isEmpty())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__inReplyTo + inReplyTo));
+ if (!references.isEmpty())
+ useIO.outputMimeLine(mimeHdrLine::truncateLine(__references + references));
+
+ if (!mDate.isEmpty())
+ useIO.outputMimeLine(__date + mDate);
+ mimeHeader::outputHeader(useIO);
+}
+
+int
+mailHeader::parseAddressList (const char *inCStr,
+ QPtrList < mailAddress > *aList)
+{
+ int advance = 0;
+ int skip = 1;
+ char *aCStr = (char *) inCStr;
+
+ if (!aCStr || !aList)
+ return 0;
+ while (skip > 0)
+ {
+ mailAddress *aAddress = new mailAddress;
+ skip = aAddress->parseAddress (aCStr);
+ if (skip)
+ {
+ aCStr += skip;
+ if (skip < 0)
+ advance -= skip;
+ else
+ advance += skip;
+ aList->append (aAddress);
+ }
+ else
+ {
+ delete aAddress;
+ break;
+ }
+ }
+ return advance;
+}
+
+QCString
+mailHeader::getAddressStr (QPtrList < mailAddress > *aList)
+{
+ QCString retVal;
+
+ QPtrListIterator < mailAddress > it = QPtrListIterator < mailAddress > (*aList);
+ while (it.current ())
+ {
+ retVal += it.current ()->getStr ();
+ ++it;
+ if (it.current ())
+ retVal += ", ";
+ }
+ return retVal;
+}
diff --git a/kioslaves/imap4/mailheader.h b/kioslaves/imap4/mailheader.h
new file mode 100644
index 000000000..0586b3a0c
--- /dev/null
+++ b/kioslaves/imap4/mailheader.h
@@ -0,0 +1,190 @@
+/***************************************************************************
+ mailheader.h - description
+ -------------------
+ begin : Tue Oct 24 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MAILHEADER_H
+#define MAILHEADER_H
+
+#include "mimeheader.h"
+#include "mailaddress.h"
+#include "mimeio.h"
+#include "rfcdecoder.h"
+
+/**
+ *@author Sven Carstens
+ */
+
+class mailHeader:public mimeHeader
+{
+public:
+ mailHeader ();
+ ~mailHeader ();
+
+ virtual void addHdrLine (mimeHdrLine *);
+ virtual void outputHeader (mimeIO &);
+
+ void addTo (const mailAddress & _adr)
+ {
+ toAdr.append (new mailAddress (_adr));
+ }
+ void addCC (const mailAddress & _adr)
+ {
+ ccAdr.append (new mailAddress (_adr));
+ }
+ void addBCC (const mailAddress & _adr)
+ {
+ bccAdr.append (new mailAddress (_adr));
+ }
+
+ void setFrom (const mailAddress & _adr)
+ {
+ fromAdr = _adr;
+ }
+ void setSender (const mailAddress & _adr)
+ {
+ senderAdr = _adr;
+ }
+ void setReturnPath (const mailAddress & _adr)
+ {
+ returnpathAdr = _adr;
+ }
+ void setReplyTo (const mailAddress & _adr)
+ {
+ replytoAdr = _adr;
+ }
+
+ const QCString& getMessageId ()
+ {
+ return messageID;
+ }
+ void setMessageId (const QCString & _str)
+ {
+ messageID = _str;
+ }
+
+ const QCString& getInReplyTo ()
+ {
+ return inReplyTo;
+ }
+ void setInReplyTo (const QCString & _str)
+ {
+ inReplyTo = _str;
+ }
+
+ const QCString& getReferences ()
+ {
+ return references;
+ }
+ void setReferences (const QCString & _str)
+ {
+ references = _str;
+ }
+
+ /**
+ * set a unicode subject
+ */
+ void setSubject (const QString & _str)
+ {
+ _subject = rfcDecoder::encodeRFC2047String(_str).latin1();
+ }
+ /**
+ * set a encoded subject
+ */
+ void setSubjectEncoded (const QCString & _str)
+ {
+ _subject = _str.simplifyWhiteSpace();
+ }
+
+ /**
+ * get the unicode subject
+ */
+ const QString getSubject ()
+ {
+ return rfcDecoder::decodeRFC2047String(_subject);
+ }
+ /**
+ * get the encoded subject
+ */
+ const QCString& getSubjectEncoded ()
+ {
+ return _subject;
+ }
+
+ /**
+ * set the date
+ */
+ void setDate (const QCString & _str)
+ {
+ mDate = _str;
+ }
+
+ /**
+ * get the date
+ */
+ const QCString& date ()
+ {
+ return mDate;
+ }
+
+ static int parseAddressList (const char *, QPtrList < mailAddress > *);
+ static QCString getAddressStr (QPtrList < mailAddress > *);
+ QPtrList < mailAddress > &to ()
+ {
+ return toAdr;
+ }
+ QPtrList < mailAddress > &cc ()
+ {
+ return ccAdr;
+ }
+ QPtrList < mailAddress > &bcc ()
+ {
+ return bccAdr;
+ }
+#ifdef KMAIL_COMPATIBLE
+ QString subject ()
+ {
+ return getSubject ();
+ }
+ const mailAddress & from ()
+ {
+ return fromAdr;
+ }
+ const mailAddress & replyTo ()
+ {
+ return replytoAdr;
+ }
+ void readConfig (void)
+ {;
+ }
+#endif
+
+private:
+ QPtrList < mailAddress > toAdr;
+ QPtrList < mailAddress > ccAdr;
+ QPtrList < mailAddress > bccAdr;
+ mailAddress fromAdr;
+ mailAddress senderAdr;
+ mailAddress returnpathAdr;
+ mailAddress replytoAdr;
+ QCString _subject;
+ QCString mDate;
+ int gmt_offset;
+ QCString messageID;
+ QCString inReplyTo;
+ QCString references;
+};
+
+#endif
diff --git a/kioslaves/imap4/mimehdrline.cc b/kioslaves/imap4/mimehdrline.cc
new file mode 100644
index 000000000..9b03ec923
--- /dev/null
+++ b/kioslaves/imap4/mimehdrline.cc
@@ -0,0 +1,521 @@
+/***************************************************************************
+ mimehdrline.cc - description
+ -------------------
+ begin : Wed Oct 11 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+#include <iostream>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mimehdrline.h"
+#include "rfcdecoder.h"
+
+using namespace std;
+
+const char *wdays[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+const char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+mimeHdrLine::mimeHdrLine ():
+mimeValue ((const char *) NULL), mimeLabel ((const char *) NULL)
+{
+}
+
+mimeHdrLine::mimeHdrLine (const QCString & aLabel, const QCString & aValue):
+mimeValue (aValue),
+mimeLabel (aLabel)
+{
+}
+
+mimeHdrLine::mimeHdrLine (mimeHdrLine * aHdrLine):
+mimeValue (aHdrLine->mimeValue), mimeLabel (aHdrLine->mimeLabel)
+{
+}
+
+mimeHdrLine::~mimeHdrLine ()
+{
+}
+
+int
+mimeHdrLine::appendStr (const char *inCStr)
+{
+ int retVal = 0;
+ int skip;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr)
+ {
+ skip = skipWS (aCStr);
+ if (skip && !mimeLabel.isEmpty ())
+ {
+ if (skip > 0)
+ {
+ mimeValue += QCString (aCStr, skip + 1);
+ aCStr += skip;
+ retVal += skip;
+ skip = parseFullLine (aCStr);
+ mimeValue += QCString (aCStr, skip + 1);
+ retVal += skip;
+ aCStr += skip;
+ }
+ }
+ else
+ {
+ if (mimeLabel.isEmpty ())
+ return setStr (aCStr);
+ }
+ }
+ return retVal;
+}
+
+/** parse a Line into the class
+move input ptr accordingly
+and report characters slurped */
+int
+mimeHdrLine::setStr (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+// char *begin = aCStr;
+ mimeLabel = QCString ((const char *) NULL);
+ mimeValue = QCString ((const char *) NULL);
+
+ if (aCStr)
+ {
+ // can't have spaces on normal lines
+ if (!skipWS (aCStr))
+ {
+ int label = 0, advance;
+ while ((advance = parseWord (&aCStr[label])))
+ {
+ label += advance;
+ }
+ if (label && aCStr[label - 1] != ':')
+ retVal = 0;
+ else
+ {
+ mimeLabel = QCString (aCStr, label); //length including zero
+ retVal += label;
+ aCStr += label;
+ }
+ }
+ if (retVal)
+ {
+ int skip;
+ skip = skipWS (aCStr);
+ if (skip < 0)
+ skip *= -1;
+ aCStr += skip;
+ retVal += skip;
+ skip = parseFullLine (aCStr);
+ mimeValue = QCString (aCStr, skip + 1);
+ retVal += skip;
+ aCStr += skip;
+ }
+ else
+ {
+ //Skip malformed line
+ while (*aCStr && *aCStr != '\r' && *aCStr != '\n')
+ {
+ retVal--;
+ aCStr++;
+ }
+ if (*aCStr == '\r')
+ {
+ retVal--;
+ aCStr++;
+ }
+ if (*aCStr == '\n')
+ {
+ retVal--;
+ aCStr++;
+ }
+ }
+ }
+ else
+ {
+ //debug
+ }
+ return retVal;
+}
+
+/** slurp one word*/
+int
+mimeHdrLine::parseWord (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr && *aCStr)
+ {
+ if (*aCStr == '"')
+ return mimeHdrLine::parseQuoted ('"', '"', aCStr);
+ else
+ return mimeHdrLine::parseHalfWord (aCStr);
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** slurp one word*/
+int
+mimeHdrLine::parseQuoted (char startQuote, char endQuote, const char *inCStr)
+{
+ char *aCStr = (char *) inCStr;
+ int retVal = 0;
+
+ if (aCStr && *aCStr)
+ {
+ if (*aCStr == startQuote)
+ {
+ aCStr++;
+ retVal++;
+ }
+ else
+ return 0;
+ while (*aCStr && *aCStr != endQuote)
+ {
+ //skip over backticks
+ if (*aCStr == '\\')
+ {
+ aCStr++;
+ retVal++;
+ }
+ //eat this
+ aCStr++;
+ retVal++;
+ }
+ if (*aCStr == endQuote)
+ {
+ aCStr++;
+ retVal++;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** slurp one alphanumerical word without continuation*/
+int
+mimeHdrLine::parseAlphaNum (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr)
+ {
+ while (*aCStr && isalnum (*aCStr))
+ {
+ //skip over backticks
+ if (*aCStr == '\\')
+ {
+ aCStr++;
+ retVal++;
+ }
+ //eat this
+ aCStr++;
+ retVal++;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+int
+mimeHdrLine::parseHalfWord (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr && *aCStr)
+ {
+ if (isalnum (*aCStr))
+ return mimeHdrLine::parseAlphaNum (aCStr);
+ //skip over backticks
+ if (*aCStr == '\\')
+ {
+ aCStr++;
+ retVal++;
+ }
+ else if (!isspace (*aCStr))
+ {
+ //eat this
+ aCStr++;
+ retVal++;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** slurp one line without continuation*/
+int
+mimeHdrLine::parseHalfLine (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr)
+ {
+ while (*aCStr && *aCStr != '\n')
+ {
+ //skip over backticks
+ if (*aCStr == '\\')
+ {
+ aCStr++;
+ retVal++;
+ }
+ //eat this
+ aCStr++;
+ retVal++;
+ }
+ if (*aCStr == '\n')
+ {
+ aCStr++;
+ retVal++;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** skip all white space characters including continuation*/
+int
+mimeHdrLine::skipWS (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+
+ if (aCStr && *aCStr)
+ {
+ while (*aCStr == ' ' || *aCStr == '\t')
+ {
+ aCStr++;
+ retVal++;
+ }
+ //check out for continuation lines
+ if (*aCStr == '\r')
+ {
+ aCStr++;
+ retVal++;
+ }
+ if (*aCStr++ == '\n')
+ if (*aCStr == '\t' || *aCStr == ' ')
+ {
+ int skip = mimeHdrLine::skipWS (aCStr);
+ if (skip < 0)
+ skip *= -1;
+ retVal += 1 + skip;
+ }
+ else
+ {
+ retVal = -retVal - 1;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** parses continuated lines */
+int
+mimeHdrLine::parseFullLine (const char *inCStr)
+{
+ int retVal = 0;
+ char *aCStr = (char *) inCStr;
+ int skip;
+
+ if (aCStr)
+ {
+ //skip leading white space
+ skip = skipWS (aCStr);
+ if (skip > 0)
+ {
+ aCStr += skip;
+ retVal += skip;
+ }
+ while (*aCStr)
+ {
+ int advance;
+
+ if ((advance = parseHalfLine (aCStr)))
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ else if ((advance = skipWS (aCStr)))
+ {
+ if (advance > 0)
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ else
+ {
+ retVal -= advance;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** parses continuated lines */
+int
+mimeHdrLine::parseSeparator (char separator, const char *inCStr)
+{
+ char *aCStr = (char *) inCStr;
+ int retVal = 0;
+ int skip;
+
+ if (aCStr)
+ {
+ //skip leading white space
+ skip = skipWS (aCStr);
+ if (skip > 0)
+ {
+ aCStr += skip;
+ retVal += skip;
+ }
+ while (*aCStr)
+ {
+ int advance;
+
+ if (*aCStr != separator)
+ {
+ if ((advance = mimeHdrLine::parseWord (aCStr)))
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ else if ((advance = mimeHdrLine::skipWS (aCStr)))
+ {
+ if (advance > 0)
+ {
+ retVal += advance;
+ aCStr += advance;
+ }
+ else
+ {
+ retVal -= advance;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ else
+ {
+ //include separator in result
+ retVal++;
+ aCStr++;
+ break;
+ }
+ }
+ }
+ else
+ {
+ //debug();
+ }
+ return retVal;
+}
+
+/** return the label */
+
+const QCString&
+mimeHdrLine::getLabel ()
+{
+ return mimeLabel;
+}
+
+/** return the value */
+const QCString&
+mimeHdrLine::getValue ()
+{
+ return mimeValue;
+}
+
+
+// FIXME: very inefficient still
+QCString
+mimeHdrLine::truncateLine(QCString aLine, unsigned int truncate)
+{
+ int cutHere;
+ QCString retVal;
+ uint len = aLine.length();
+
+ // see if we have a line of the form "key: value" (like "Subject: bla")
+ // then we do not want to truncate between key and value
+ int validStart = aLine.find(": ");
+ if (validStart > -1) {
+ validStart += 2;
+ }
+ while (len > truncate) {
+ cutHere = aLine.findRev(' ', truncate);
+ if (cutHere < 1 || cutHere < validStart) {
+ cutHere = aLine.findRev('\t', truncate);
+ if (cutHere < 1) {
+ cutHere = aLine.find(' ', 1);
+ if (cutHere < 1) {
+ cutHere = aLine.find('\t', 1);
+ if (cutHere < 1) {
+ // simply truncate
+ return aLine.left(truncate);
+ }
+ }
+ }
+ }
+
+ retVal += aLine.left(cutHere) + '\n';
+ int chop = len - cutHere;
+ aLine = aLine.right(chop);
+ len -= chop;
+ }
+ retVal += aLine;
+
+ return retVal;
+}
+
diff --git a/kioslaves/imap4/mimehdrline.h b/kioslaves/imap4/mimehdrline.h
new file mode 100644
index 000000000..ae8fdd57c
--- /dev/null
+++ b/kioslaves/imap4/mimehdrline.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ mimehdrline.h - description
+ -------------------
+ begin : Wed Oct 11 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MIMEHDRLINE_H
+#define MIMEHDRLINE_H
+
+
+#include <qcstring.h>
+#include <qasciidict.h>
+
+/**
+ *@author Sven Carstens
+ */
+
+class mimeHdrLine
+{
+public:
+ mimeHdrLine ();
+ mimeHdrLine (mimeHdrLine *);
+ mimeHdrLine (const QCString &, const QCString &);
+ ~mimeHdrLine ();
+ /** parse a Line into the class
+and report characters slurped */
+ int setStr (const char *);
+ int appendStr (const char *);
+ /** return the value */
+ const QCString& getValue ();
+ /** return the label */
+ const QCString& getLabel ();
+ static QCString truncateLine (QCString, unsigned int truncate = 80);
+ static int parseSeparator (char, const char *);
+ static int parseQuoted (char, char, const char *);
+ /** skip all white space characters */
+ static int skipWS (const char *);
+ /** slurp one word respecting backticks */
+ static int parseHalfWord (const char *);
+ static int parseWord (const char *);
+ static int parseAlphaNum (const char *);
+
+protected: // Protected attributes
+ /** contains the Value
+ */
+ QCString mimeValue;
+ /** contains the Label of the line
+ */
+ QCString mimeLabel;
+protected: // Protected methods
+ /** parses a continuated line */
+ int parseFullLine (const char *);
+ int parseHalfLine (const char *);
+};
+
+#endif
diff --git a/kioslaves/imap4/mimeheader.cc b/kioslaves/imap4/mimeheader.cc
new file mode 100644
index 000000000..17d3a3fd8
--- /dev/null
+++ b/kioslaves/imap4/mimeheader.cc
@@ -0,0 +1,745 @@
+/***************************************************************************
+ mimeheader.cc - description
+ -------------------
+ begin : Fri Oct 20 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "mimeheader.h"
+#include "mimehdrline.h"
+#include "mailheader.h"
+#include "rfcdecoder.h"
+
+#include <qregexp.h>
+
+// #include <iostream.h>
+#include <kglobal.h>
+#include <kinstance.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+#include <kmimemagic.h>
+#include <kmdcodec.h>
+#include <kdebug.h>
+
+mimeHeader::mimeHeader ():
+typeList (17, false), dispositionList (17, false)
+{
+ // Case insensitive hashes are killing us. Also are they too small?
+ originalHdrLines.setAutoDelete (true);
+ additionalHdrLines.setAutoDelete (false); // is also in original lines
+ nestedParts.setAutoDelete (true);
+ typeList.setAutoDelete (true);
+ dispositionList.setAutoDelete (true);
+ nestedMessage = NULL;
+ contentLength = 0;
+ contentType = "application/octet-stream";
+}
+
+mimeHeader::~mimeHeader ()
+{
+}
+
+/*
+QPtrList<mimeHeader> mimeHeader::getAllParts()
+{
+ QPtrList<mimeHeader> retVal;
+
+ // caller is responsible for clearing
+ retVal.setAutoDelete( false );
+ nestedParts.setAutoDelete( false );
+
+ // shallow copy
+ retVal = nestedParts;
+
+ // can't have duplicate pointers
+ nestedParts.clear();
+
+ // restore initial state
+ nestedParts.setAutoDelete( true );
+
+ return retVal;
+} */
+
+void
+mimeHeader::addHdrLine (mimeHdrLine * aHdrLine)
+{
+ mimeHdrLine *addLine = new mimeHdrLine (aHdrLine);
+ if (addLine)
+ {
+ originalHdrLines.append (addLine);
+ if (qstrnicmp (addLine->getLabel (), "Content-", 8))
+ {
+ additionalHdrLines.append (addLine);
+ }
+ else
+ {
+ int skip;
+ char *aCStr = addLine->getValue ().data ();
+ QDict < QString > *aList = 0;
+
+ skip = mimeHdrLine::parseSeparator (';', aCStr);
+ if (skip > 0)
+ {
+ int cut = 0;
+ if (skip >= 2)
+ {
+ if (aCStr[skip - 1] == '\r')
+ cut++;
+ if (aCStr[skip - 1] == '\n')
+ cut++;
+ if (aCStr[skip - 2] == '\r')
+ cut++;
+ if (aCStr[skip - 1] == ';')
+ cut++;
+ }
+ QCString mimeValue = QCString (aCStr, skip - cut + 1); // cutting of one because of 0x00
+
+
+ if (!qstricmp (addLine->getLabel (), "Content-Disposition"))
+ {
+ aList = &dispositionList;
+ _contentDisposition = mimeValue;
+ }
+ else if (!qstricmp (addLine->getLabel (), "Content-Type"))
+ {
+ aList = &typeList;
+ contentType = mimeValue;
+ }
+ else
+ if (!qstricmp (addLine->getLabel (), "Content-Transfer-Encoding"))
+ {
+ contentEncoding = mimeValue;
+ }
+ else if (!qstricmp (addLine->getLabel (), "Content-ID"))
+ {
+ contentID = mimeValue;
+ }
+ else if (!qstricmp (addLine->getLabel (), "Content-Description"))
+ {
+ _contentDescription = mimeValue;
+ }
+ else if (!qstricmp (addLine->getLabel (), "Content-MD5"))
+ {
+ contentMD5 = mimeValue;
+ }
+ else if (!qstricmp (addLine->getLabel (), "Content-Length"))
+ {
+ contentLength = mimeValue.toULong ();
+ }
+ else
+ {
+ additionalHdrLines.append (addLine);
+ }
+// cout << addLine->getLabel().data() << ": '" << mimeValue.data() << "'" << endl;
+
+ aCStr += skip;
+ while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
+ {
+ if (skip > 0)
+ {
+ addParameter (QCString (aCStr, skip).simplifyWhiteSpace(), aList);
+// cout << "-- '" << aParm.data() << "'" << endl;
+ mimeValue = QCString (addLine->getValue ().data (), skip);
+ aCStr += skip;
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+}
+
+void
+mimeHeader::addParameter (const QCString& aParameter, QDict < QString > *aList)
+{
+ if ( !aList )
+ return;
+
+ QString *aValue;
+ QCString aLabel;
+ int pos = aParameter.find ('=');
+// cout << aParameter.left(pos).data();
+ aValue = new QString ();
+ aValue->setLatin1 (aParameter.right (aParameter.length () - pos - 1));
+ aLabel = aParameter.left (pos);
+ if ((*aValue)[0] == '"')
+ *aValue = aValue->mid (1, aValue->length () - 2);
+
+ aList->insert (aLabel, aValue);
+// cout << "=" << aValue->data() << endl;
+}
+
+QString
+mimeHeader::getDispositionParm (const QCString& aStr)
+{
+ return getParameter (aStr, &dispositionList);
+}
+
+QString
+mimeHeader::getTypeParm (const QCString& aStr)
+{
+ return getParameter (aStr, &typeList);
+}
+
+void
+mimeHeader::setDispositionParm (const QCString& aLabel, const QString& aValue)
+{
+ setParameter (aLabel, aValue, &dispositionList);
+ return;
+}
+
+void
+mimeHeader::setTypeParm (const QCString& aLabel, const QString& aValue)
+{
+ setParameter (aLabel, aValue, &typeList);
+}
+
+QDictIterator < QString > mimeHeader::getDispositionIterator ()
+{
+ return QDictIterator < QString > (dispositionList);
+}
+
+QDictIterator < QString > mimeHeader::getTypeIterator ()
+{
+ return QDictIterator < QString > (typeList);
+}
+
+QPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator ()
+{
+ return QPtrListIterator < mimeHdrLine > (originalHdrLines);
+}
+
+QPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator ()
+{
+ return QPtrListIterator < mimeHdrLine > (additionalHdrLines);
+}
+
+void
+mimeHeader::outputHeader (mimeIO & useIO)
+{
+ if (!getDisposition ().isEmpty ())
+ {
+ useIO.outputMimeLine (QCString ("Content-Disposition: ")
+ + getDisposition ()
+ + outputParameter (&dispositionList));
+ }
+
+ if (!getType ().isEmpty ())
+ {
+ useIO.outputMimeLine (QCString ("Content-Type: ")
+ + getType () + outputParameter (&typeList));
+ }
+ if (!getDescription ().isEmpty ())
+ useIO.outputMimeLine (QCString ("Content-Description: ") +
+ getDescription ());
+ if (!getID ().isEmpty ())
+ useIO.outputMimeLine (QCString ("Content-ID: ") + getID ());
+ if (!getMD5 ().isEmpty ())
+ useIO.outputMimeLine (QCString ("Content-MD5: ") + getMD5 ());
+ if (!getEncoding ().isEmpty ())
+ useIO.outputMimeLine (QCString ("Content-Transfer-Encoding: ") +
+ getEncoding ());
+
+ QPtrListIterator < mimeHdrLine > ait = getAdditionalIterator ();
+ while (ait.current ())
+ {
+ useIO.outputMimeLine (ait.current ()->getLabel () + ": " +
+ ait.current ()->getValue ());
+ ++ait;
+ }
+ useIO.outputMimeLine (QCString (""));
+}
+
+QString
+mimeHeader::getParameter (const QCString& aStr, QDict < QString > *aDict)
+{
+ QString retVal, *found;
+ if (aDict)
+ {
+ //see if it is a normal parameter
+ found = aDict->find (aStr);
+ if (!found)
+ {
+ //might be a continuated or encoded parameter
+ found = aDict->find (aStr + "*");
+ if (!found)
+ {
+ //continuated parameter
+ QString decoded, encoded;
+ int part = 0;
+
+ do
+ {
+ QCString search;
+ search.setNum (part);
+ search = aStr + "*" + search;
+ found = aDict->find (search);
+ if (!found)
+ {
+ found = aDict->find (search + "*");
+ if (found)
+ encoded += rfcDecoder::encodeRFC2231String (*found);
+ }
+ else
+ {
+ encoded += *found;
+ }
+ part++;
+ }
+ while (found);
+ if (encoded.find ('\'') >= 0)
+ {
+ retVal = rfcDecoder::decodeRFC2231String (encoded.local8Bit ());
+ }
+ else
+ {
+ retVal =
+ rfcDecoder::decodeRFC2231String (QCString ("''") +
+ encoded.local8Bit ());
+ }
+ }
+ else
+ {
+ //simple encoded parameter
+ retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ());
+ }
+ }
+ else
+ {
+ retVal = *found;
+ }
+ }
+ return retVal;
+}
+
+void
+mimeHeader::setParameter (const QCString& aLabel, const QString& aValue,
+ QDict < QString > *aDict)
+{
+ bool encoded = true;
+ uint vlen, llen;
+ QString val = aValue;
+
+ if (aDict)
+ {
+
+ //see if it needs to get encoded
+ if (encoded && aLabel.find ('*') == -1)
+ {
+ val = rfcDecoder::encodeRFC2231String (aValue);
+ }
+ //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl;
+ //see if it needs to be truncated
+ vlen = val.length();
+ llen = aLabel.length();
+ if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 )
+ {
+ const int limit = 80 - 8 - 2 - (int)llen;
+ // the -2 is there to allow extending the length of a part of val
+ // by 1 or 2 in order to prevent an encoded character from being
+ // split in half
+ int i = 0;
+ QString shortValue;
+ QCString shortLabel;
+
+ while (!val.isEmpty ())
+ {
+ int partLen; // the length of the next part of the value
+ if ( limit >= int(vlen) ) {
+ // the rest of the value fits completely into one continued header
+ partLen = vlen;
+ }
+ else {
+ partLen = limit;
+ // make sure that we don't split an encoded char in half
+ if ( val[partLen-1] == '%' ) {
+ partLen += 2;
+ }
+ else if ( partLen > 1 && val[partLen-2] == '%' ) {
+ partLen += 1;
+ }
+ // make sure partLen does not exceed vlen (could happen in case of
+ // an incomplete encoded char)
+ if ( partLen > int(vlen) ) {
+ partLen = vlen;
+ }
+ }
+ shortValue = val.left( partLen );
+ shortLabel.setNum (i);
+ shortLabel = aLabel + "*" + shortLabel;
+ val = val.right( vlen - partLen );
+ vlen = vlen - partLen;
+ if (encoded)
+ {
+ if (i == 0)
+ {
+ shortValue = "''" + shortValue;
+ }
+ shortLabel += "*";
+ }
+ //kdDebug(7116) << "mimeHeader::setParameter() - shortLabel = '" << shortLabel << "'" << endl;
+ //kdDebug(7116) << "mimeHeader::setParameter() - shortValue = '" << shortValue << "'" << endl;
+ //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl;
+ aDict->insert (shortLabel, new QString (shortValue));
+ i++;
+ }
+ }
+ else
+ {
+ aDict->insert (aLabel, new QString (val));
+ }
+ }
+}
+
+QCString
+mimeHeader::outputParameter (QDict < QString > *aDict)
+{
+ QCString retVal;
+ if (aDict)
+ {
+ QDictIterator < QString > it (*aDict);
+ while (it.current ())
+ {
+ retVal += (";\n\t" + it.currentKey () + "=").latin1 ();
+ if (it.current ()->find (' ') > 0 || it.current ()->find (';') > 0)
+ {
+ retVal += '"' + it.current ()->utf8 () + '"';
+ }
+ else
+ {
+ retVal += it.current ()->utf8 ();
+ }
+ // << it.current()->utf8() << "'";
+ ++it;
+ }
+ retVal += "\n";
+ }
+ return retVal;
+}
+
+void
+mimeHeader::outputPart (mimeIO & useIO)
+{
+ QPtrListIterator < mimeHeader > nestedParts = getNestedIterator ();
+ QCString boundary;
+ if (!getTypeParm ("boundary").isEmpty ())
+ boundary = getTypeParm ("boundary").latin1 ();
+
+ outputHeader (useIO);
+ if (!getPreBody ().isEmpty ())
+ useIO.outputMimeLine (getPreBody ());
+ if (getNestedMessage ())
+ getNestedMessage ()->outputPart (useIO);
+ while (nestedParts.current ())
+ {
+ if (!boundary.isEmpty ())
+ useIO.outputMimeLine ("--" + boundary);
+ nestedParts.current ()->outputPart (useIO);
+ ++nestedParts;
+ }
+ if (!boundary.isEmpty ())
+ useIO.outputMimeLine ("--" + boundary + "--");
+ if (!getPostBody ().isEmpty ())
+ useIO.outputMimeLine (getPostBody ());
+}
+
+int
+mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
+{
+ int retVal = 0;
+ bool mbox = false;
+ QCString preNested, postNested;
+ mbox = parseHeader (useIO);
+
+ kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl;
+ if (!qstrnicmp (getType (), "Multipart", 9))
+ {
+ retVal = parseBody (useIO, preNested, getTypeParm ("boundary")); //this is a message in mime format stuff
+ setPreBody (preNested);
+ int localRetVal;
+ do
+ {
+ mimeHeader *aHeader = new mimeHeader;
+
+ // set default type for multipart/digest
+ if (!qstrnicmp (getType (), "Multipart/Digest", 16))
+ aHeader->setType ("Message/RFC822");
+
+ localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary"));
+ addNestedPart (aHeader);
+ }
+ while (localRetVal); //get nested stuff
+ }
+ if (!qstrnicmp (getType (), "Message/RFC822", 14))
+ {
+ mailHeader *msgHeader = new mailHeader;
+ retVal = msgHeader->parsePart (useIO, boundary);
+ setNestedMessage (msgHeader);
+ }
+ else
+ {
+ retVal = parseBody (useIO, postNested, boundary, mbox); //just a simple part remaining
+ setPostBody (postNested);
+ }
+ return retVal;
+}
+
+int
+mimeHeader::parseBody (mimeIO & useIO, QCString & messageBody,
+ const QString& boundary, bool mbox)
+{
+ QCString inputStr;
+ QCString buffer;
+ QString partBoundary;
+ QString partEnd;
+ int retVal = 0; //default is last part
+
+ if (!boundary.isEmpty ())
+ {
+ partBoundary = QString ("--") + boundary;
+ partEnd = QString ("--") + boundary + "--";
+ }
+
+ while (useIO.inputLine (inputStr))
+ {
+ //check for the end of all parts
+ if (!partEnd.isEmpty ()
+ && !qstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1))
+ {
+ retVal = 0; //end of these parts
+ break;
+ }
+ else if (!partBoundary.isEmpty ()
+ && !qstrnicmp (inputStr, partBoundary.latin1 (),
+ partBoundary.length () - 1))
+ {
+ retVal = 1; //continue with next part
+ break;
+ }
+ else if (mbox && inputStr.find ("From ") == 0)
+ {
+ retVal = 0; // end of mbox
+ break;
+ }
+ buffer += inputStr;
+ if (buffer.length () > 16384)
+ {
+ messageBody += buffer;
+ buffer = "";
+ }
+ }
+
+ messageBody += buffer;
+ return retVal;
+}
+
+bool
+mimeHeader::parseHeader (mimeIO & useIO)
+{
+ bool mbox = false;
+ bool first = true;
+ mimeHdrLine my_line;
+ QCString inputStr;
+
+ kdDebug(7116) << "mimeHeader::parseHeader - starting parsing" << endl;
+ while (useIO.inputLine (inputStr))
+ {
+ int appended;
+ if (inputStr.find ("From ") != 0 || !first)
+ {
+ first = false;
+ appended = my_line.appendStr (inputStr);
+ if (!appended)
+ {
+ addHdrLine (&my_line);
+ appended = my_line.setStr (inputStr);
+ }
+ if (appended <= 0)
+ break;
+ }
+ else
+ {
+ mbox = true;
+ first = false;
+ }
+ inputStr = (const char *) NULL;
+ }
+
+ kdDebug(7116) << "mimeHeader::parseHeader - finished parsing" << endl;
+ return mbox;
+}
+
+mimeHeader *
+mimeHeader::bodyPart (const QString & _str)
+{
+ // see if it is nested a little deeper
+ int pt = _str.find('.');
+ if (pt != -1)
+ {
+ QString tempStr = _str;
+ mimeHeader *tempPart;
+
+ tempStr = _str.right (_str.length () - pt - 1);
+ if (nestedMessage)
+ {
+ kdDebug(7116) << "mimeHeader::bodyPart - recursing message" << endl;
+ tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
+ }
+ else
+ {
+ kdDebug(7116) << "mimeHeader::bodyPart - recursing mixed" << endl;
+ tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
+ }
+ if (tempPart)
+ tempPart = tempPart->bodyPart (tempStr);
+ return tempPart;
+ }
+
+ kdDebug(7116) << "mimeHeader::bodyPart - returning part " << _str << endl;
+ // or pick just the plain part
+ if (nestedMessage)
+ {
+ kdDebug(7116) << "mimeHeader::bodyPart - message" << endl;
+ return nestedMessage->nestedParts.at (_str.toULong () - 1);
+ }
+ kdDebug(7116) << "mimeHeader::bodyPart - mixed" << endl;
+ return nestedParts.at (_str.toULong () - 1);
+}
+
+void mimeHeader::serialize(QDataStream& stream)
+{
+ int nestedcount = nestedParts.count();
+ if (nestedParts.isEmpty() && nestedMessage)
+ nestedcount = 1;
+ stream << nestedcount << contentType << QString (getTypeParm ("name")) << _contentDescription
+ << _contentDisposition << contentEncoding << contentLength << partSpecifier;
+ // serialize nested message
+ if (nestedMessage)
+ nestedMessage->serialize(stream);
+
+ // serialize nested parts
+ if (!nestedParts.isEmpty())
+ {
+ QPtrListIterator < mimeHeader > it(nestedParts);
+ mimeHeader* part;
+ while ( (part = it.current()) != 0 )
+ {
+ ++it;
+ part->serialize(stream);
+ }
+ }
+}
+
+#ifdef KMAIL_COMPATIBLE
+// compatibility subroutines
+QString
+mimeHeader::bodyDecoded ()
+{
+ kdDebug(7116) << "mimeHeader::bodyDecoded" << endl;
+ QByteArray temp;
+
+ temp = bodyDecodedBinary ();
+ return QString::fromLatin1 (temp.data (), temp.count ());
+}
+
+QByteArray
+mimeHeader::bodyDecodedBinary ()
+{
+ QByteArray retVal;
+
+ if (contentEncoding.find ("quoted-printable", 0, false) == 0)
+ retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
+ else if (contentEncoding.find ("base64", 0, false) == 0)
+ KCodecs::base64Decode(postMultipartBody, retVal);
+ else retVal = postMultipartBody;
+
+ kdDebug(7116) << "mimeHeader::bodyDecodedBinary - size is " << retVal.size () << endl;
+ return retVal;
+}
+
+void
+mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
+{
+ setBodyEncoded (_arr);
+}
+
+void
+mimeHeader::setBodyEncoded (const QByteArray & _arr)
+{
+ QByteArray setVal;
+
+ kdDebug(7116) << "mimeHeader::setBodyEncoded - in size " << _arr.size () << endl;
+ if (contentEncoding.find ("quoted-printable", 0, false) == 0)
+ setVal = KCodecs::quotedPrintableEncode(_arr);
+ else if (contentEncoding.find ("base64", 0, false) == 0)
+ KCodecs::base64Encode(_arr, setVal);
+ else
+ setVal.duplicate (_arr);
+ kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << setVal.size () << endl;
+
+ postMultipartBody.duplicate (setVal);
+ kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << postMultipartBody.size () << endl;
+}
+
+QString
+mimeHeader::iconName ()
+{
+ QString fileName;
+
+ // FIXME: bug? Why throw away this data?
+ fileName =
+ KMimeType::mimeType (contentType.lower ())->icon (QString::null, false);
+ fileName =
+ KGlobal::instance ()->iconLoader ()->iconPath (fileName, KIcon::Desktop);
+// if (fileName.isEmpty())
+// fileName = KGlobal::instance()->iconLoader()->iconPath( "unknown", KIcon::Desktop );
+ return fileName;
+}
+
+void
+mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
+{
+// if(nestedMessage && destroy) delete nestedMessage;
+ nestedMessage = inPart;
+}
+
+QString
+mimeHeader::headerAsString ()
+{
+ mimeIOQString myIO;
+
+ outputHeader (myIO);
+ return myIO.getString ();
+}
+
+QString
+mimeHeader::magicSetType (bool aAutoDecode)
+{
+ QString mimetype;
+ QByteArray body;
+ KMimeMagicResult *result;
+
+ KMimeMagic::self ()->setFollowLinks (TRUE); // is it necessary ?
+
+ if (aAutoDecode)
+ body = bodyDecodedBinary ();
+ else
+ body = postMultipartBody;
+
+ result = KMimeMagic::self ()->findBufferType (body);
+ mimetype = result->mimeType ();
+ contentType = mimetype;
+ return mimetype;
+}
+#endif
diff --git a/kioslaves/imap4/mimeheader.h b/kioslaves/imap4/mimeheader.h
new file mode 100644
index 000000000..a56ec6cb8
--- /dev/null
+++ b/kioslaves/imap4/mimeheader.h
@@ -0,0 +1,337 @@
+/***************************************************************************
+ mimeheader.h - description
+ -------------------
+ begin : Fri Oct 20 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MIMEHEADER_H
+#define MIMEHEADER_H
+
+#include <qptrlist.h>
+#include <qdict.h>
+
+#include "mimehdrline.h"
+#include "mimeio.h"
+#include "rfcdecoder.h"
+
+/**
+ *@author Sven Carstens
+ */
+
+class mimeHeader
+{
+public:
+ mimeHeader ();
+ virtual ~ mimeHeader ();
+
+ virtual void addHdrLine (mimeHdrLine *);
+ virtual void outputHeader (mimeIO &);
+ virtual void outputPart (mimeIO &);
+
+
+ QCString outputParameter (QDict < QString > *);
+
+ int parsePart (mimeIO &, const QString&);
+ int parseBody (mimeIO &, QCString &, const QString&, bool mbox = false);
+
+ // parse a header. returns true if it had a leading 'From ' line
+ bool parseHeader (mimeIO &);
+
+ QString getDispositionParm (const QCString&);
+ void setDispositionParm (const QCString&, const QString&);
+ QDictIterator < QString > getDispositionIterator ();
+
+ QString getTypeParm (const QCString&);
+ void setTypeParm (const QCString&, const QString&);
+ QDictIterator < QString > getTypeIterator ();
+
+ // recursively serialize all important contents to the QDataStream
+ void serialize(QDataStream& stream);
+
+ const QCString& getType ()
+ {
+ return contentType;
+ }
+ void setType (const QCString & _str)
+ {
+ contentType = _str;
+ }
+
+ const QCString& getDescription ()
+ {
+ return _contentDescription;
+ }
+ void setDescription (const QCString & _str)
+ {
+ _contentDescription = _str;
+ }
+
+ QCString getDisposition ()
+ {
+ return _contentDisposition;
+ }
+ void setDisposition (const QCString & _str)
+ {
+ _contentDisposition = _str;
+ }
+
+ QCString getEncoding ()
+ {
+ return contentEncoding;
+ }
+ void setEncoding (const QCString & _str)
+ {
+ contentEncoding = _str;
+ }
+
+ QCString getMD5 ()
+ {
+ return contentMD5;
+ }
+ void setMD5 (const QCString & _str)
+ {
+ contentMD5 = _str;
+ }
+
+ QCString getID ()
+ {
+ return contentID;
+ }
+ void setID (const QCString & _str)
+ {
+ contentID = _str;
+ }
+
+ unsigned long getLength ()
+ {
+ return contentLength;
+ }
+ void setLength (unsigned long _len)
+ {
+ contentLength = _len;
+ }
+
+ const QString & getPartSpecifier ()
+ {
+ return partSpecifier;
+ }
+ void setPartSpecifier (const QString & _str)
+ {
+ partSpecifier = _str;
+ }
+
+ QPtrListIterator < mimeHdrLine > getOriginalIterator ();
+ QPtrListIterator < mimeHdrLine > getAdditionalIterator ();
+ void setContent (const QCString &aContent)
+ {
+ mimeContent = aContent;
+ }
+ QCString getContent ()
+ {
+ return mimeContent;
+ }
+
+ QCString getBody ()
+ {
+ return preMultipartBody + postMultipartBody;
+ }
+ QCString getPreBody ()
+ {
+ return preMultipartBody;
+ }
+ void setPreBody (QCString & inBody)
+ {
+ preMultipartBody = inBody;
+ }
+
+ QCString getPostBody ()
+ {
+ return postMultipartBody;
+ }
+ void setPostBody (QCString & inBody)
+ {
+ postMultipartBody = inBody;
+ contentLength = inBody.length ();
+ }
+
+ mimeHeader *getNestedMessage ()
+ {
+ return nestedMessage;
+ }
+ void setNestedMessage (mimeHeader * inPart, bool destroy = true)
+ {
+ if (nestedMessage && destroy)
+ delete nestedMessage;
+ nestedMessage = inPart;
+ }
+
+// mimeHeader *getNestedPart() { return nestedPart; };
+ void addNestedPart (mimeHeader * inPart)
+ {
+ nestedParts.append (inPart);
+ }
+ QPtrListIterator < mimeHeader > getNestedIterator ()
+ {
+ return QPtrListIterator < mimeHeader > (nestedParts);
+ }
+
+ // clears all parts and deletes them from memory
+ void clearNestedParts ()
+ {
+ nestedParts.clear ();
+ }
+
+ // clear all parameters to content-type
+ void clearTypeParameters ()
+ {
+ typeList.clear ();
+ }
+
+ // clear all parameters to content-disposition
+ void clearDispositionParameters ()
+ {
+ dispositionList.clear ();
+ }
+
+ // return the specified body part or NULL
+ mimeHeader *bodyPart (const QString &);
+
+#ifdef KMAIL_COMPATIBLE
+ ulong msgSize ()
+ {
+ return contentLength;
+ }
+ uint numBodyParts ()
+ {
+ return nestedParts.count ();
+ }
+ mimeHeader *bodyPart (int which, mimeHeader ** ret = NULL)
+ {
+ if (ret)
+ (*ret) = nestedParts.at (which);
+ return nestedParts.at (which);
+ }
+ void write (const QString &)
+ {
+ }
+ QString typeStr ()
+ {
+ return QString (contentType.left (contentType.find ('/')));
+ }
+ void setTypeStr (const QString & _str)
+ {
+ contentType = QCString (_str.latin1 ()) + "/" + subtypeStr ().latin1 ();
+ }
+ QString subtypeStr ()
+ {
+ return QString (contentType.
+ right (contentType.length () - contentType.find ('/') -
+ 1));
+ }
+ void setSubtypeStr (const QString & _str)
+ {
+ contentType = QCString (typeStr ().latin1 ()) + "/" + _str.latin1 ();
+ }
+ QString cteStr ()
+ {
+ return QString (getEncoding ());
+ }
+ void setCteStr (const QString & _str)
+ {
+ setEncoding (_str.latin1 ());
+ }
+ QString contentDisposition ()
+ {
+ return QString (_contentDisposition);
+ }
+ QString body ()
+ {
+ return QString (postMultipartBody);
+ }
+ QString charset ()
+ {
+ return getTypeParm ("charset");
+ }
+ QString bodyDecoded ();
+ void setBodyEncoded (const QByteArray &);
+ void setBodyEncodedBinary (const QByteArray &);
+ QByteArray bodyDecodedBinary ();
+ QString name ()
+ {
+ return QString (getTypeParm ("name"));
+ }
+ void setName (const QString & _str)
+ {
+ setTypeParm ("name", _str);
+ }
+ QString fileName ()
+ {
+ return QString (getDispositionParm ("filename"));
+ }
+ QString contentDescription ()
+ {
+ return QString (rfcDecoder::decodeRFC2047String (_contentDescription));
+ }
+ void setContentDescription (const QString & _str)
+ {
+ _contentDescription = rfcDecoder::encodeRFC2047String (_str).latin1 ();
+ }
+ QString msgIdMD5 ()
+ {
+ return QString (contentMD5);
+ }
+ QString iconName ();
+ QString magicSetType (bool aAutoDecode = true);
+ QString headerAsString ();
+ ulong size ()
+ {
+ return 0;
+ }
+ void fromString (const QByteArray &)
+ {;
+ }
+ void setContentDisposition (const QString & _str)
+ {
+ setDisposition (_str.latin1 ());
+ }
+#endif
+
+protected:
+ static void addParameter (const QCString&, QDict < QString > *);
+ static QString getParameter (const QCString&, QDict < QString > *);
+ static void setParameter (const QCString&, const QString&, QDict < QString > *);
+
+ QPtrList < mimeHdrLine > originalHdrLines;
+
+private:
+ QPtrList < mimeHdrLine > additionalHdrLines;
+ QDict < QString > typeList;
+ QDict < QString > dispositionList;
+ QCString contentType;
+ QCString _contentDisposition;
+ QCString contentEncoding;
+ QCString _contentDescription;
+ QCString contentID;
+ QCString contentMD5;
+ unsigned long contentLength;
+ QCString mimeContent;
+ QCString preMultipartBody;
+ QCString postMultipartBody;
+ mimeHeader *nestedMessage;
+ QPtrList < mimeHeader > nestedParts;
+ QString partSpecifier;
+
+};
+
+#endif
diff --git a/kioslaves/imap4/mimeio.cc b/kioslaves/imap4/mimeio.cc
new file mode 100644
index 000000000..7a41c843a
--- /dev/null
+++ b/kioslaves/imap4/mimeio.cc
@@ -0,0 +1,188 @@
+/***************************************************************************
+ mimeio.cc - description
+ -------------------
+ begin : Wed Oct 25 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <iostream>
+using namespace std;
+
+#include "mimeio.h"
+
+mimeIO::mimeIO ()
+{
+ theCRLF = "\r\n";
+ crlfLen = 2;
+}
+
+mimeIO::~mimeIO ()
+{
+}
+
+int
+mimeIO::inputLine (QCString & aLine)
+{
+ char input;
+
+ aLine = (const char *) NULL;
+ while (inputChar (input))
+ {
+ aLine += input;
+ if (input == '\n')
+ break;
+ }
+// cout << aLine.length() << " - " << aLine;
+ return aLine.length ();
+}
+
+int
+mimeIO::outputLine (const QCString & aLine, int len)
+{
+ int i;
+
+ if (len == -1) {
+ len = aLine.length();
+ }
+ int start = len;
+ for (i = 0; i < start; i++)
+ if (!outputChar (aLine[i]))
+ break;
+ return i;
+}
+
+int
+mimeIO::outputMimeLine (const QCString & inLine)
+{
+ int retVal = 0;
+ QCString aLine = inLine;
+ int len = inLine.length();
+
+ int theLF = aLine.findRev ('\n');
+ if (theLF == len - 1 && theLF != -1)
+ {
+ //we have a trailing LF, now check for CR
+ if (aLine[theLF - 1] == '\r')
+ theLF--;
+ //truncate the line
+ aLine.truncate(theLF);
+ len = theLF;
+ theLF = -1;
+ }
+ //now truncate the line
+ {
+ int start, end, offset;
+ start = 0;
+ end = aLine.find ('\n', start);
+ while (end >= 0)
+ {
+ offset = 1;
+ if (end && aLine[end - 1] == '\r')
+ {
+ offset++;
+ end--;
+ }
+ outputLine (aLine.mid (start, end - start) + theCRLF, end - start + crlfLen);
+ start = end + offset;
+ end = aLine.find ('\n', start);
+ }
+ outputLine (aLine.mid (start, len - start) + theCRLF, len - start + crlfLen);
+ }
+ return retVal;
+}
+
+int
+mimeIO::inputChar (char &aChar)
+{
+ if (cin.eof ())
+ {
+// cout << "EOF" << endl;
+ return 0;
+ }
+ cin.get (aChar);
+ return 1;
+}
+
+int
+mimeIO::outputChar (char aChar)
+{
+ cout << aChar;
+ return 1;
+}
+
+void
+mimeIO::setCRLF (const char *aCRLF)
+{
+ theCRLF = aCRLF;
+ crlfLen = strlen(aCRLF);
+}
+
+mimeIOQFile::mimeIOQFile (const QString & aName):
+mimeIO (),
+myFile (aName)
+{
+ myFile.open (IO_ReadOnly);
+}
+
+mimeIOQFile::~mimeIOQFile ()
+{
+ myFile.close ();
+}
+
+int
+mimeIOQFile::outputLine (const QCString &, int)
+{
+ return 0;
+}
+
+int
+mimeIOQFile::inputLine (QCString & data)
+{
+ data.resize( 1024 );
+ myFile.readLine (data.data(), 1024);
+
+ return data.length ();
+}
+
+mimeIOQString::mimeIOQString ()
+{
+}
+
+mimeIOQString::~mimeIOQString ()
+{
+}
+
+int
+mimeIOQString::outputLine (const QCString & _str, int len)
+{
+ if (len == -1) {
+ len = _str.length();
+ }
+ theString += _str;
+ return len;
+}
+
+int
+mimeIOQString::inputLine (QCString & _str)
+{
+ if (theString.isEmpty ())
+ return 0;
+
+ int i = theString.find ('\n');
+
+ if (i == -1)
+ return 0;
+ _str = theString.left (i + 1).latin1 ();
+ theString = theString.right (theString.length () - i - 1);
+ return _str.length ();
+}
diff --git a/kioslaves/imap4/mimeio.h b/kioslaves/imap4/mimeio.h
new file mode 100644
index 000000000..0de2fbd4d
--- /dev/null
+++ b/kioslaves/imap4/mimeio.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ mimeio.h - description
+ -------------------
+ begin : Wed Oct 25 2000
+ copyright : (C) 2000 by Sven Carstens
+ email : s.carstens@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MIMEIO_H
+#define MIMEIO_H
+
+#include <qcstring.h>
+#include <qfile.h>
+
+/**
+ *@author Sven Carstens
+ */
+
+class mimeIO
+{
+public:
+ mimeIO ();
+ virtual ~ mimeIO ();
+
+ virtual int outputLine (const QCString &, int len = -1);
+ virtual int outputMimeLine (const QCString &);
+ virtual int inputLine (QCString &);
+ virtual int outputChar (char);
+ virtual int inputChar (char &);
+
+ void setCRLF (const char *);
+
+protected:
+ QCString theCRLF;
+ int crlfLen;
+};
+
+class mimeIOQFile:public mimeIO
+{
+public:
+ mimeIOQFile (const QString &);
+ virtual ~ mimeIOQFile ();
+ virtual int outputLine (const QCString &, int len = -1);
+ virtual int inputLine (QCString &);
+
+protected:
+ QFile myFile;
+};
+
+class mimeIOQString:public mimeIO
+{
+public:
+ mimeIOQString ();
+ virtual ~ mimeIOQString ();
+ virtual int outputLine (const QCString &, int len = -1);
+ virtual int inputLine (QCString &);
+ const QString& getString () const
+ {
+ return theString;
+ }
+ void setString (const QString & _str)
+ {
+ theString = _str;
+ }
+
+protected:
+ QString theString;
+};
+
+#endif
diff --git a/kioslaves/imap4/rfcdecoder.cc b/kioslaves/imap4/rfcdecoder.cc
new file mode 100644
index 000000000..0e2bc9f73
--- /dev/null
+++ b/kioslaves/imap4/rfcdecoder.cc
@@ -0,0 +1,668 @@
+/**********************************************************************
+ *
+ * rfcdecoder.cc - handler for various rfc/mime encodings
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+#include "rfcdecoder.h"
+
+#include <ctype.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <qtextcodec.h>
+#include <qbuffer.h>
+#include <qregexp.h>
+#include <kmdcodec.h>
+
+// This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997.
+// adapted to QT-Toolkit by Sven Carstens <s.carstens@gmx.de> 2000
+
+static unsigned char base64chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+#define UNDEFINED 64
+#define MAXLINE 76
+
+/* UTF16 definitions */
+#define UTF16MASK 0x03FFUL
+#define UTF16SHIFT 10
+#define UTF16BASE 0x10000UL
+#define UTF16HIGHSTART 0xD800UL
+#define UTF16HIGHEND 0xDBFFUL
+#define UTF16LOSTART 0xDC00UL
+#define UTF16LOEND 0xDFFFUL
+
+/* Convert an IMAP mailbox to a Unicode path
+ */
+QString rfcDecoder::fromIMAP (const QString & inSrc)
+{
+ unsigned char c, i, bitcount;
+ unsigned long ucs4, utf16, bitbuf;
+ unsigned char base64[256], utf8[6];
+ unsigned long srcPtr = 0;
+ QCString dst;
+ QCString src = inSrc.ascii ();
+ uint srcLen = inSrc.length();
+
+ /* initialize modified base64 decoding table */
+ memset (base64, UNDEFINED, sizeof (base64));
+ for (i = 0; i < sizeof (base64chars); ++i)
+ {
+ base64[(int)base64chars[i]] = i;
+ }
+
+ /* loop until end of string */
+ while (srcPtr < srcLen)
+ {
+ c = src[srcPtr++];
+ /* deal with literal characters and &- */
+ if (c != '&' || src[srcPtr] == '-')
+ {
+ /* encode literally */
+ dst += c;
+ /* skip over the '-' if this is an &- sequence */
+ if (c == '&')
+ srcPtr++;
+ }
+ else
+ {
+ /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
+ bitbuf = 0;
+ bitcount = 0;
+ ucs4 = 0;
+ while ((c = base64[(unsigned char) src[srcPtr]]) != UNDEFINED)
+ {
+ ++srcPtr;
+ bitbuf = (bitbuf << 6) | c;
+ bitcount += 6;
+ /* enough bits for a UTF-16 character? */
+ if (bitcount >= 16)
+ {
+ bitcount -= 16;
+ utf16 = (bitcount ? bitbuf >> bitcount : bitbuf) & 0xffff;
+ /* convert UTF16 to UCS4 */
+ if (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND)
+ {
+ ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
+ continue;
+ }
+ else if (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND)
+ {
+ ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
+ }
+ else
+ {
+ ucs4 = utf16;
+ }
+ /* convert UTF-16 range of UCS4 to UTF-8 */
+ if (ucs4 <= 0x7fUL)
+ {
+ utf8[0] = ucs4;
+ i = 1;
+ }
+ else if (ucs4 <= 0x7ffUL)
+ {
+ utf8[0] = 0xc0 | (ucs4 >> 6);
+ utf8[1] = 0x80 | (ucs4 & 0x3f);
+ i = 2;
+ }
+ else if (ucs4 <= 0xffffUL)
+ {
+ utf8[0] = 0xe0 | (ucs4 >> 12);
+ utf8[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
+ utf8[2] = 0x80 | (ucs4 & 0x3f);
+ i = 3;
+ }
+ else
+ {
+ utf8[0] = 0xf0 | (ucs4 >> 18);
+ utf8[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
+ utf8[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
+ utf8[3] = 0x80 | (ucs4 & 0x3f);
+ i = 4;
+ }
+ /* copy it */
+ for (c = 0; c < i; ++c)
+ {
+ dst += utf8[c];
+ }
+ }
+ }
+ /* skip over trailing '-' in modified UTF-7 encoding */
+ if (src[srcPtr] == '-')
+ ++srcPtr;
+ }
+ }
+ return QString::fromUtf8 (dst.data ());
+}
+
+/* replace " with \" and \ with \\ " and \ characters */
+QString rfcDecoder::quoteIMAP(const QString &src)
+{
+ uint len = src.length();
+ QString result;
+ result.reserve(2 * len);
+ for (unsigned int i = 0; i < len; i++)
+ {
+ if (src[i] == '"' || src[i] == '\\')
+ result += '\\';
+ result += src[i];
+ }
+ //result.squeeze(); - unnecessary and slow
+ return result;
+}
+
+/* Convert Unicode path to modified UTF-7 IMAP mailbox
+ */
+QString rfcDecoder::toIMAP (const QString & inSrc)
+{
+ unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
+ unsigned long ucs4, bitbuf;
+ QCString src = inSrc.utf8 ();
+ QString dst;
+
+ ulong srcPtr = 0;
+ utf7mode = 0;
+ utf8total = 0;
+ bitstogo = 0;
+ utf8pos = 0;
+ bitbuf = 0;
+ ucs4 = 0;
+ while (srcPtr < src.length ())
+ {
+ c = (unsigned char) src[srcPtr++];
+ /* normal character? */
+ if (c >= ' ' && c <= '~')
+ {
+ /* switch out of UTF-7 mode */
+ if (utf7mode)
+ {
+ if (bitstogo)
+ {
+ dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ bitstogo = 0;
+ }
+ dst += '-';
+ utf7mode = 0;
+ }
+ dst += c;
+ /* encode '&' as '&-' */
+ if (c == '&')
+ {
+ dst += '-';
+ }
+ continue;
+ }
+ /* switch to UTF-7 mode */
+ if (!utf7mode)
+ {
+ dst += '&';
+ utf7mode = 1;
+ }
+ /* Encode US-ASCII characters as themselves */
+ if (c < 0x80)
+ {
+ ucs4 = c;
+ utf8total = 1;
+ }
+ else if (utf8total)
+ {
+ /* save UTF8 bits into UCS4 */
+ ucs4 = (ucs4 << 6) | (c & 0x3FUL);
+ if (++utf8pos < utf8total)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ utf8pos = 1;
+ if (c < 0xE0)
+ {
+ utf8total = 2;
+ ucs4 = c & 0x1F;
+ }
+ else if (c < 0xF0)
+ {
+ utf8total = 3;
+ ucs4 = c & 0x0F;
+ }
+ else
+ {
+ /* NOTE: can't convert UTF8 sequences longer than 4 */
+ utf8total = 4;
+ ucs4 = c & 0x03;
+ }
+ continue;
+ }
+ /* loop to split ucs4 into two utf16 chars if necessary */
+ utf8total = 0;
+ do
+ {
+ if (ucs4 >= UTF16BASE)
+ {
+ ucs4 -= UTF16BASE;
+ bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT) + UTF16HIGHSTART);
+ ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
+ utf16flag = 1;
+ }
+ else
+ {
+ bitbuf = (bitbuf << 16) | ucs4;
+ utf16flag = 0;
+ }
+ bitstogo += 16;
+ /* spew out base64 */
+ while (bitstogo >= 6)
+ {
+ bitstogo -= 6;
+ dst += base64chars[(bitstogo ? (bitbuf >> bitstogo) : bitbuf) & 0x3F];
+ }
+ }
+ while (utf16flag);
+ }
+ /* if in UTF-7 mode, finish in ASCII */
+ if (utf7mode)
+ {
+ if (bitstogo)
+ {
+ dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ }
+ dst += '-';
+ }
+ return quoteIMAP(dst);
+}
+
+//-----------------------------------------------------------------------------
+QString rfcDecoder::decodeQuoting(const QString &aStr)
+{
+ QString result;
+ unsigned int strLength(aStr.length());
+ for (unsigned int i = 0; i < strLength ; i++)
+ {
+ if (aStr[i] == "\\") i++;
+ result += aStr[i];
+ }
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+QTextCodec *
+rfcDecoder::codecForName (const QString & _str)
+{
+ if (_str.isEmpty ())
+ return NULL;
+ return QTextCodec::codecForName (_str.lower ().
+ replace ("windows", "cp").latin1 ());
+}
+
+//-----------------------------------------------------------------------------
+const QString
+rfcDecoder::decodeRFC2047String (const QString & _str)
+{
+ QString throw_away;
+
+ return decodeRFC2047String (_str, throw_away);
+}
+
+//-----------------------------------------------------------------------------
+const QString
+rfcDecoder::decodeRFC2047String (const QString & _str, QString & charset)
+{
+ QString throw_away;
+
+ return decodeRFC2047String (_str, charset, throw_away);
+}
+
+//-----------------------------------------------------------------------------
+const QString
+rfcDecoder::decodeRFC2047String (const QString & _str, QString & charset,
+ QString & language)
+{
+ //do we have a rfc string
+ if (_str.find("=?") < 0)
+ return _str;
+
+ QCString aStr = _str.ascii (); // QString.length() means Unicode chars
+ QCString result;
+ char *pos, *beg, *end, *mid = NULL;
+ QCString str;
+ char encoding = 0, ch;
+ bool valid;
+ const int maxLen = 200;
+ int i;
+
+// result.truncate(aStr.length());
+ for (pos = aStr.data (); *pos; pos++)
+ {
+ if (pos[0] != '=' || pos[1] != '?')
+ {
+ result += *pos;
+ continue;
+ }
+ beg = pos + 2;
+ end = beg;
+ valid = TRUE;
+ // parse charset name
+ for (i = 2, pos += 2;
+ i < maxLen && (*pos != '?' && (ispunct (*pos) || isalnum (*pos)));
+ i++)
+ pos++;
+ if (*pos != '?' || i < 4 || i >= maxLen)
+ valid = FALSE;
+ else
+ {
+ charset = QCString (beg, i - 1); // -2 + 1 for the zero
+ int pt = charset.findRev('*');
+ if (pt != -1)
+ {
+ // save language for later usage
+ language = charset.right (charset.length () - pt - 1);
+
+ // tie off language as defined in rfc2047
+ charset.truncate(pt);
+ }
+ // get encoding and check delimiting question marks
+ encoding = toupper (pos[1]);
+ if (pos[2] != '?'
+ || (encoding != 'Q' && encoding != 'B' && encoding != 'q'
+ && encoding != 'b'))
+ valid = FALSE;
+ pos += 3;
+ i += 3;
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - charset " << charset << " - language " << language << " - '" << pos << "'" << endl;
+ }
+ if (valid)
+ {
+ mid = pos;
+ // search for end of encoded part
+ while (i < maxLen && *pos && !(*pos == '?' && *(pos + 1) == '='))
+ {
+ i++;
+ pos++;
+ }
+ end = pos + 2; //end now points to the first char after the encoded string
+ if (i >= maxLen || !*pos)
+ valid = FALSE;
+ }
+ if (valid)
+ {
+ ch = *pos;
+ *pos = '\0';
+ str = QCString (mid).left ((int) (mid - pos - 1));
+ if (encoding == 'Q')
+ {
+ // decode quoted printable text
+ for (i = str.length () - 1; i >= 0; i--)
+ if (str[i] == '_')
+ str[i] = ' ';
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - before QP '" << str << "'" << endl;
+
+ str = KCodecs::quotedPrintableDecode(str);
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - after QP '" << str << "'" << endl;
+ }
+ else
+ {
+ // decode base64 text
+ str = KCodecs::base64Decode(str);
+ }
+ *pos = ch;
+ int len = str.length();
+ for (i = 0; i < len; i++)
+ result += (char) (QChar) str[i];
+
+ pos = end - 1;
+ }
+ else
+ {
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - invalid" << endl;
+ //result += "=?";
+ //pos = beg -1; // because pos gets increased shortly afterwards
+ pos = beg - 2;
+ result += *pos++;
+ result += *pos;
+ }
+ }
+ if (!charset.isEmpty ())
+ {
+ QTextCodec *aCodec = codecForName (charset.ascii ());
+ if (aCodec)
+ {
+// kdDebug(7116) << "Codec is " << aCodec->name() << endl;
+ return aCodec->toUnicode (result);
+ }
+ }
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const char especials[17] = "()<>@,;:\"/[]?.= ";
+
+const QString
+rfcDecoder::encodeRFC2047String (const QString & _str)
+{
+ if (_str.isEmpty ())
+ return _str;
+ const signed char *latin = reinterpret_cast<const signed char *>(_str.latin1()), *l, *start, *stop;
+ char hexcode;
+ int numQuotes, i;
+ int rptr = 0;
+ // My stats show this number results in 12 resize() out of 73,000
+ int resultLen = 3 * _str.length() / 2;
+ QCString result(resultLen);
+
+ while (*latin)
+ {
+ l = latin;
+ start = latin;
+ while (*l)
+ {
+ if (*l == 32)
+ start = l + 1;
+ if (*l < 0)
+ break;
+ l++;
+ }
+ if (*l)
+ {
+ numQuotes = 1;
+ while (*l)
+ {
+ /* The encoded word must be limited to 75 character */
+ for (i = 0; i < 16; i++)
+ if (*l == especials[i])
+ numQuotes++;
+ if (*l < 0)
+ numQuotes++;
+ /* Stop after 58 = 75 - 17 characters or at "<user@host..." */
+ if (l - start + 2 * numQuotes >= 58 || *l == 60)
+ break;
+ l++;
+ }
+ if (*l)
+ {
+ stop = l - 1;
+ while (stop >= start && *stop != 32)
+ stop--;
+ if (stop <= start)
+ stop = l;
+ }
+ else
+ stop = l;
+ if (resultLen - rptr - 1 <= start - latin + 1 + 16 /* =?iso-88... */) {
+ resultLen += (start - latin + 1) * 2 + 20; // more space
+ result.resize(resultLen);
+ }
+ while (latin < start)
+ {
+ result[rptr++] = *latin;
+ latin++;
+ }
+ strcpy(&result[rptr], "=?iso-8859-1?q?"); rptr += 15;
+ if (resultLen - rptr - 1 <= 3*(stop - latin + 1)) {
+ resultLen += (stop - latin + 1) * 4 + 20; // more space
+ result.resize(resultLen);
+ }
+ while (latin < stop) // can add up to 3 chars/iteration
+ {
+ numQuotes = 0;
+ for (i = 0; i < 16; i++)
+ if (*latin == especials[i])
+ numQuotes = 1;
+ if (*latin < 0)
+ numQuotes = 1;
+ if (numQuotes)
+ {
+ result[rptr++] = '=';
+ hexcode = ((*latin & 0xF0) >> 4) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result[rptr++] = hexcode;
+ hexcode = (*latin & 0x0F) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result[rptr++] = hexcode;
+ }
+ else
+ {
+ result[rptr++] = *latin;
+ }
+ latin++;
+ }
+ result[rptr++] = '?';
+ result[rptr++] = '=';
+ }
+ else
+ {
+ while (*latin)
+ {
+ if (rptr == resultLen - 1) {
+ resultLen += 30;
+ result.resize(resultLen);
+ }
+ result[rptr++] = *latin;
+ latin++;
+ }
+ }
+ }
+ result[rptr] = 0;
+ //free (latinStart);
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const QString
+rfcDecoder::encodeRFC2231String (const QString & _str)
+{
+ if (_str.isEmpty ())
+ return _str;
+ signed char *latin = (signed char *) calloc (1, _str.length () + 1);
+ char *latin_us = (char *) latin;
+ strcpy (latin_us, _str.latin1 ());
+ signed char *l = latin;
+ char hexcode;
+ int i;
+ bool quote;
+ while (*l)
+ {
+ if (*l < 0)
+ break;
+ l++;
+ }
+ if (!*l) {
+ free(latin);
+ return _str.ascii ();
+ }
+ QCString result;
+ l = latin;
+ while (*l)
+ {
+ quote = *l < 0;
+ for (i = 0; i < 16; i++)
+ if (*l == especials[i])
+ quote = true;
+ if (quote)
+ {
+ result += "%";
+ hexcode = ((*l & 0xF0) >> 4) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result += hexcode;
+ hexcode = (*l & 0x0F) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result += hexcode;
+ }
+ else
+ {
+ result += *l;
+ }
+ l++;
+ }
+ free (latin);
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const QString
+rfcDecoder::decodeRFC2231String (const QString & _str)
+{
+ int p = _str.find ('\'');
+
+ //see if it is an rfc string
+ if (p < 0)
+ return _str;
+
+ int l = _str.findRev ('\'');
+
+ //second is language
+ if (p >= l)
+ return _str;
+
+ //first is charset or empty
+ QString charset = _str.left (p);
+ QString st = _str.mid (l + 1);
+ QString language = _str.mid (p + 1, l - p - 1);
+
+ //kdDebug(7116) << "Charset: " << charset << " Language: " << language << endl;
+
+ char ch, ch2;
+ p = 0;
+ while (p < (int) st.length ())
+ {
+ if (st.at (p) == 37)
+ {
+ ch = st.at (p + 1).latin1 () - 48;
+ if (ch > 16)
+ ch -= 7;
+ ch2 = st.at (p + 2).latin1 () - 48;
+ if (ch2 > 16)
+ ch2 -= 7;
+ st.at (p) = ch * 16 + ch2;
+ st.remove (p + 1, 2);
+ }
+ p++;
+ }
+ return st;
+}
diff --git a/kioslaves/imap4/rfcdecoder.h b/kioslaves/imap4/rfcdecoder.h
new file mode 100644
index 000000000..1df5c0dae
--- /dev/null
+++ b/kioslaves/imap4/rfcdecoder.h
@@ -0,0 +1,89 @@
+#ifndef RFCDECODER_H
+#define RFCDECODER_H
+/**********************************************************************
+ *
+ * rfcdecoder.h - handler for various rfc/mime encodings
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+
+#include <qstring.h>
+
+class QTextCodec;
+
+/**
+ * handler for various rfc/mime encodings
+ * @author Sven Carstens <s.carstens@gmx.de>
+ * @date 2000
+ * @todo rename to rfcCodecs as it encodes too.
+ */
+class rfcDecoder
+{
+
+public:
+
+/** Convert an IMAP mailbox to a Unicode path
+ */
+ static QString fromIMAP (const QString & src);
+/** Convert Unicode path to modified UTF-7 IMAP mailbox
+ */
+ static QString toIMAP (const QString & inSrc);
+/** replace " with \" and \ with \\ " and \ characters */
+ static QString quoteIMAP (const QString & src);
+
+ /** remove \ from a string
+ * @bug I'm pretty sure this doesn't do what the author meant it to do
+ */
+ static QString decodeQuoting(const QString &aStr);
+
+ /**
+ * fetch a codec by name
+ * @return Text Codec object
+ */
+ static QTextCodec *codecForName (const QString &);
+
+ // decoder for RFC2047 and RFC1522
+ /** decode a RFC2047 String */
+ static const QString decodeRFC2047String (const QString & _str,
+ QString & charset,
+ QString & language);
+ /** decode a RFC2047 String */
+ static const QString decodeRFC2047String (const QString & _str,
+ QString & charset);
+ /** decode a RFC2047 String */
+ static const QString decodeRFC2047String (const QString & _str);
+
+ // encoder for RFC2047 and RFC1522
+ /** encode a RFC2047 String */
+ static const QString encodeRFC2047String (const QString & _str,
+ QString & charset,
+ QString & language);
+ /** encode a RFC2047 String */
+ static const QString encodeRFC2047String (const QString & _str,
+ QString & charset);
+ /** encode a RFC2047 String */
+ static const QString encodeRFC2047String (const QString & _str);
+
+ /** encode a RFC2231 String */
+ static const QString encodeRFC2231String (const QString & _str);
+ /** decode a RFC2231 String */
+ static const QString decodeRFC2231String (const QString & _str);
+};
+
+#endif
diff --git a/kioslaves/imap4/selectinfo.h b/kioslaves/imap4/selectinfo.h
new file mode 100644
index 000000000..c059a952f
--- /dev/null
+++ b/kioslaves/imap4/selectinfo.h
@@ -0,0 +1,202 @@
+#ifndef _IMAPINFO_H
+#define _IMAPINFO_H
+/**********************************************************************
+ *
+ * imapinfo.h - IMAP4rev1 SELECT / EXAMINE handler
+ * Copyright (C) 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to
+ *
+ *********************************************************************/
+
+#include <qstringlist.h>
+#include <qstring.h>
+
+//class handling the info we get on EXAMINE and SELECT
+class imapInfo
+{
+public:
+
+ imapInfo ();
+ imapInfo (const QStringList &);
+ imapInfo (const imapInfo &);
+ imapInfo & operator = (const imapInfo &);
+
+ ulong _flags (const QString &) const;
+
+ void setCount (ulong l)
+ {
+ countAvailable_ = true;
+ count_ = l;
+ }
+
+ void setRecent (ulong l)
+ {
+ recentAvailable_ = true;
+ recent_ = l;
+ }
+
+ void setUnseen (ulong l)
+ {
+ unseenAvailable_ = true;
+ unseen_ = l;
+ }
+
+ void setUidValidity (ulong l)
+ {
+ uidValidityAvailable_ = true;
+ uidValidity_ = l;
+ }
+
+ void setUidNext (ulong l)
+ {
+ uidNextAvailable_ = true;
+ uidNext_ = l;
+ }
+
+ void setFlags (ulong l)
+ {
+ flagsAvailable_ = true;
+ flags_ = l;
+ }
+
+ void setFlags (const QString & inFlag)
+ {
+ flagsAvailable_ = true;
+ flags_ = _flags (inFlag);
+ }
+
+ void setPermanentFlags (ulong l)
+ {
+ permanentFlagsAvailable_ = true;
+ permanentFlags_ = l;
+ }
+
+ void setPermanentFlags (const QString & inFlag)
+ {
+ permanentFlagsAvailable_ = true;
+ permanentFlags_ = _flags (inFlag);
+ }
+
+ void setReadWrite (bool b)
+ {
+ readWriteAvailable_ = true;
+ readWrite_ = b;
+ }
+
+ ulong count () const
+ {
+ return count_;
+ }
+
+ ulong recent () const
+ {
+ return recent_;
+ }
+
+ ulong unseen () const
+ {
+ return unseen_;
+ }
+
+ ulong uidValidity () const
+ {
+ return uidValidity_;
+ }
+
+ ulong uidNext () const
+ {
+ return uidNext_;
+ }
+
+ ulong flags () const
+ {
+ return flags_;
+ }
+
+ ulong permanentFlags () const
+ {
+ return permanentFlags_;
+ }
+
+ bool readWrite () const
+ {
+ return readWrite_;
+ }
+
+ ulong countAvailable () const
+ {
+ return countAvailable_;
+ }
+
+ ulong recentAvailable () const
+ {
+ return recentAvailable_;
+ }
+
+ ulong unseenAvailable () const
+ {
+ return unseenAvailable_;
+ }
+
+ ulong uidValidityAvailable () const
+ {
+ return uidValidityAvailable_;
+ }
+
+ ulong uidNextAvailable () const
+ {
+ return uidNextAvailable_;
+ }
+
+ ulong flagsAvailable () const
+ {
+ return flagsAvailable_;
+ }
+
+ ulong permanentFlagsAvailable () const
+ {
+ return permanentFlagsAvailable_;
+ }
+
+ bool readWriteAvailable () const
+ {
+ return readWriteAvailable_;
+ }
+
+private:
+
+ ulong count_;
+ ulong recent_;
+ ulong unseen_;
+ ulong uidValidity_;
+ ulong uidNext_;
+ ulong flags_;
+ ulong permanentFlags_;
+ bool readWrite_;
+
+ bool countAvailable_;
+ bool recentAvailable_;
+ bool unseenAvailable_;
+ bool uidValidityAvailable_;
+ bool uidNextAvailable_;
+ bool flagsAvailable_;
+ bool permanentFlagsAvailable_;
+ bool readWriteAvailable_;
+};
+
+#endif
diff --git a/kioslaves/mbox/AUTHORS b/kioslaves/mbox/AUTHORS
new file mode 100644
index 000000000..333010f6c
--- /dev/null
+++ b/kioslaves/mbox/AUTHORS
@@ -0,0 +1 @@
+Mart Kelder <mart.kde@hccnet.nl>
diff --git a/kioslaves/mbox/Makefile.am b/kioslaves/mbox/Makefile.am
new file mode 100644
index 000000000..b4840ee72
--- /dev/null
+++ b/kioslaves/mbox/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES= $(all_includes)
+
+####### Files
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kio_mbox.la
+
+kio_mbox_la_SOURCES = \
+ mbox.cc \
+ mboxfile.cc \
+ readmbox.cc \
+ stat.cc \
+ urlinfo.cc
+kio_mbox_la_LIBADD = $(LIB_KIO)
+kio_mbox_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = \
+ mbox.h \
+ mboxfile.h \
+ readmbox.h \
+ stat.h \
+ urlinfo.h
+
+kdelnk_DATA = mbox.protocol
+kdelnkdir = $(kde_servicesdir)
+
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/kioslaves/mbox/README b/kioslaves/mbox/README
new file mode 100644
index 000000000..f0441661c
--- /dev/null
+++ b/kioslaves/mbox/README
@@ -0,0 +1,7 @@
+This is a simple kioslave for accessing mbox-files with kio.
+
+At the moment, it doesn't support locking or writing, it is just reading and listing messages.
+
+Usage:
+ mbox:/path/to/mbox/file For listing the files in a mbox-file
+ mbox:/path/to/mbox/file/id For getting an id out of a mbox-file.
diff --git a/kioslaves/mbox/mbox.cc b/kioslaves/mbox/mbox.cc
new file mode 100644
index 000000000..8ad8d09ab
--- /dev/null
+++ b/kioslaves/mbox/mbox.cc
@@ -0,0 +1,168 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "mbox.h"
+
+#include "readmbox.h"
+#include "stat.h"
+#include "urlinfo.h"
+
+#include <qstring.h>
+#include <qcstring.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kurl.h>
+#include <kio/global.h>
+
+#include <stdlib.h>
+
+#include "kdepimmacros.h"
+
+#include "mbox.h"
+
+extern "C" { KDE_EXPORT int kdemain(int argc, char* argv[]); }
+
+int kdemain( int argc, char * argv[] )
+{
+ KLocale::setMainCatalogue("kdelibs");
+ KInstance instance("kio_mbox");
+ (void) KGlobal::locale();
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: kio_mbox protocol "
+ "domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ MBoxProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+}
+
+MBoxProtocol::MBoxProtocol( const QCString& arg1, const QCString& arg2 )
+ : KIO::SlaveBase( "mbox2", arg1, arg2 ),
+ m_errorState( true )
+{
+
+}
+
+MBoxProtocol::~MBoxProtocol()
+{
+}
+
+void MBoxProtocol::get( const KURL& url )
+{
+ m_errorState = false;
+
+ UrlInfo info( url, UrlInfo::message );
+ QString line;
+ QByteArray ba_line;
+
+ if( info.type() == UrlInfo::invalid && !m_errorState )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, info.url() );
+ return;
+ }
+
+ ReadMBox mbox( &info, this );
+
+ while( !mbox.atEnd() && !m_errorState)
+ {
+ line = mbox.currentLine();
+ line += '\n';
+ ba_line = QCString( line.utf8() );
+ ba_line.truncate( ba_line.size() - 1 ); //Removing training '\0'
+ data( ba_line );
+ mbox.nextLine();
+ };
+
+ if( !m_errorState )
+ {
+ data( QByteArray() );
+ finished();
+ }
+}
+
+void MBoxProtocol::listDir( const KURL& url )
+{
+ m_errorState = false;
+
+ KIO::UDSEntry entry;
+ UrlInfo info( url, UrlInfo::directory );
+ ReadMBox mbox( &info, this, hasMetaData( "onlynew" ), hasMetaData( "savetime" ) );
+
+ if( m_errorState )
+ return;
+
+ if( info.type() != UrlInfo::directory )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, info.url() );
+ return;
+ }
+
+ while( !mbox.atEnd() && !m_errorState )
+ {
+ entry = Stat::stat( mbox, info );
+ if( mbox.inListing() )
+ listEntry( entry, false );
+ }
+
+ listEntry( KIO::UDSEntry(), true );
+ finished();
+}
+
+void MBoxProtocol::stat( const KURL& url )
+{
+ UrlInfo info( url );
+ if( info.type() == UrlInfo::invalid )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, url.path() );
+ return;
+ } else
+ {
+ statEntry( Stat::stat( info ) );
+ }
+ finished();
+}
+
+void MBoxProtocol::mimetype( const KURL& url )
+{
+ m_errorState = false;
+
+ UrlInfo info( url );
+
+ if( m_errorState )
+ return;
+
+ if( info.type() == UrlInfo::invalid )
+ error( KIO::ERR_DOES_NOT_EXIST, i18n( "Invalid URL" ) );
+ else
+ mimeType( info.mimetype() );
+ finished();
+}
+
+void MBoxProtocol::emitError( int errno, const QString& arg )
+{
+ m_errorState = true;
+ error( errno, arg );
+}
+
diff --git a/kioslaves/mbox/mbox.h b/kioslaves/mbox/mbox.h
new file mode 100644
index 000000000..4d3768bd4
--- /dev/null
+++ b/kioslaves/mbox/mbox.h
@@ -0,0 +1,81 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef MBOX_H
+#define MBOX_H
+
+#include <kio/slavebase.h>
+
+class QCString;
+class KURL;
+
+/**
+ * This class is the main class and implements all function
+ * which can be called through the user.
+ */
+class MBoxProtocol : public KIO::SlaveBase
+{
+public:
+ /**
+ * Constructor, for the parameters, See KIO::SlaveBase
+ */
+ MBoxProtocol( const QCString&, const QCString& );
+ /**
+ * Empty destructor
+ */
+ virtual ~MBoxProtocol();
+
+ /**
+ * This functions is used when an user or a program wants to
+ * get a file from a mbox-file
+ * @param url The url which points to the virtual file to get
+ */
+ virtual void get( const KURL& url );
+
+ /**
+ * This functions gives a listing back.
+ * @param url The url to the mbox-file.
+ */
+ virtual void listDir( const KURL& url );
+
+ /**
+ * This functions gives general properties about a mbox-file,
+ * or mbox-email back.
+ */
+ virtual void stat( const KURL& url );
+
+ /**
+ * This functions determinate the mimetype of a given mbox-file or mbox-email.
+ * @param url The url to get the mimetype from
+ */
+ virtual void mimetype( const KURL& url );
+
+ /**
+ * Through this functions, other class which have an instance to this
+ * class (classes which are part of kio_mbox) can emit an error with
+ * this function
+ * @param errno The error number to be thrown
+ * @param arg The argument of the error message of the error number.
+ */
+ void emitError( int errno, const QString& arg );
+private:
+ bool m_errorState;
+};
+
+#endif
+
diff --git a/kioslaves/mbox/mbox.protocol b/kioslaves/mbox/mbox.protocol
new file mode 100644
index 000000000..8b6b412a8
--- /dev/null
+++ b/kioslaves/mbox/mbox.protocol
@@ -0,0 +1,14 @@
+[Protocol]
+exec=kio_mbox
+protocol=mbox
+input=none
+output=filesystem
+listing=Name,Type,Size
+reading=true
+writing=false
+makedir=false
+deleting=false
+Icon=folder_inbox
+maxInstances=2
+DocPath=
+Class=:local
diff --git a/kioslaves/mbox/mboxfile.cc b/kioslaves/mbox/mboxfile.cc
new file mode 100644
index 000000000..271b71019
--- /dev/null
+++ b/kioslaves/mbox/mboxfile.cc
@@ -0,0 +1,45 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "mboxfile.h"
+
+#include <assert.h>
+
+MBoxFile::MBoxFile( const UrlInfo* info, MBoxProtocol* parent )
+ : m_info( info ),
+ m_mbox( parent )
+{
+ assert( m_info );
+}
+
+MBoxFile::~MBoxFile()
+{
+}
+
+bool MBoxFile::lock()
+{
+ //Not implemented
+ return true;
+}
+
+void MBoxFile::unlock()
+{
+ //Not implemented
+}
+
+
diff --git a/kioslaves/mbox/mboxfile.h b/kioslaves/mbox/mboxfile.h
new file mode 100644
index 000000000..b8a98973e
--- /dev/null
+++ b/kioslaves/mbox/mboxfile.h
@@ -0,0 +1,68 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef MBOXFILE_H
+#define MBOXFILE_H
+class MBoxProtocol;
+class UrlInfo;
+
+/**
+ * This class can be used to lock files when implemented.
+ * It is a base class for all classes that needs locking and/ir
+ * an UrlInfo*.
+ */
+class MBoxFile
+{
+public:
+ /**
+ * Constructor
+ * @param info The urlinfo which must be used
+ * @param parent The MBoxProtocol parent instance, used to throw errors.
+ */
+ MBoxFile( const UrlInfo* info, MBoxProtocol* parent );
+
+ /**
+ * Empty destructor
+ */
+ ~MBoxFile();
+
+protected:
+ /**
+ * When implemented, this function handles the locking of the file.
+ * @return true if the locking was done succesfully.
+ */
+ bool lock();
+
+ /**
+ * When implemented, this function unlocks the file.
+ */
+ void unlock();
+
+protected:
+ /**
+ * This can be used to get information about the file.
+ * The file specified here is the file that must be used.
+ */
+ const UrlInfo* const m_info;
+
+ /**
+ * A instance of the parent protocol, meant to throw errors if neccesairy.
+ */
+ MBoxProtocol* const m_mbox;
+};
+#endif
diff --git a/kioslaves/mbox/readmbox.cc b/kioslaves/mbox/readmbox.cc
new file mode 100644
index 000000000..c7513c6eb
--- /dev/null
+++ b/kioslaves/mbox/readmbox.cc
@@ -0,0 +1,204 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <config.h>
+
+#include "readmbox.h"
+
+#include "mbox.h"
+#include "urlinfo.h"
+
+#include <kdebug.h>
+#include <kio/global.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <utime.h>
+
+ReadMBox::ReadMBox( const UrlInfo* info, MBoxProtocol* parent, bool onlynew, bool savetime )
+ : MBoxFile( info, parent ),
+ m_file( 0 ),
+ m_stream( 0 ),
+ m_current_line( new QString( QString::null ) ),
+ m_current_id( new QString( QString::null ) ),
+ m_atend( true ),
+ m_prev_time( 0 ),
+ m_only_new( onlynew ),
+ m_savetime( savetime ),
+ m_status( false ),
+ m_prev_status( false ),
+ m_header( true )
+
+{
+ if( m_info->type() == UrlInfo::invalid )
+ m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );
+
+ if( !open( savetime ) )
+ m_mbox->emitError( KIO::ERR_CANNOT_OPEN_FOR_READING, info->url() );
+
+ if( m_info->type() == UrlInfo::message )
+ if( !searchMessage( m_info->id() ) )
+ m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );
+}
+
+ReadMBox::~ReadMBox()
+{
+ delete m_current_line;
+ close();
+}
+
+QString ReadMBox::currentLine() const
+{
+ return *m_current_line;
+}
+
+QString ReadMBox::currentID() const
+{
+ return *m_current_id;
+}
+
+bool ReadMBox::nextLine()
+{
+ if( !m_stream )
+ return true;
+
+ *m_current_line = m_stream->readLine();
+ m_atend = m_current_line->isNull();
+ if( m_atend ) // Cursor was at EOF
+ {
+ *m_current_id = QString::null;
+ m_prev_status = m_status;
+ return true;
+ }
+
+ //New message
+ if( m_current_line->left( 5 ) == "From " )
+ {
+ *m_current_id = *m_current_line;
+ m_prev_status = m_status;
+ m_status = true;
+ m_header = true;
+ return true;
+ } else if( m_only_new )
+ {
+ if( m_header && m_current_line->left( 7 ) == "Status:" &&
+ ! m_current_line->contains( "U" ) && ! m_current_line->contains( "N" ) )
+ {
+ m_status = false;
+ }
+ }
+
+ if( m_current_line->stripWhiteSpace().isEmpty() )
+ m_header = false;
+
+ return false;
+}
+
+bool ReadMBox::searchMessage( const QString& id )
+{
+ if( !m_stream )
+ return false;
+
+ while( !m_atend && *m_current_id != id )
+ nextLine();
+
+ return *m_current_id == id;
+}
+
+unsigned int ReadMBox::skipMessage()
+{
+ unsigned int result = m_current_line->length();
+
+ if( !m_stream )
+ return 0;
+
+ while( !nextLine() )
+ result += m_current_line->length();
+
+ return result;
+}
+
+void ReadMBox::rewind()
+{
+ if( !m_stream )
+ return; //Rewinding not possible
+
+ m_stream->device()->reset();
+ m_atend = m_stream->atEnd();
+}
+
+bool ReadMBox::atEnd() const
+{
+ if( !m_stream )
+ return true;
+
+ return m_atend || ( m_info->type() == UrlInfo::message && *m_current_id != m_info->id() );
+}
+
+bool ReadMBox::inListing() const
+{
+ return !m_only_new || m_prev_status;
+}
+
+bool ReadMBox::open( bool savetime )
+{
+ if( savetime )
+ {
+ QFileInfo info( m_info->filename() );
+
+ m_prev_time = new utimbuf;
+ m_prev_time->actime = info.lastRead().toTime_t();
+ m_prev_time->modtime = info.lastModified().toTime_t();
+ }
+
+ if( m_file )
+ return false; //File already open
+
+ m_file = new QFile( m_info->filename() );
+ if( !m_file->open( IO_ReadOnly ) )
+ {
+ delete m_file;
+ m_file = 0;
+ return false;
+ }
+ m_stream = new QTextStream( m_file );
+ skipMessage();
+
+ return true;
+}
+
+void ReadMBox::close()
+{
+ if( !m_stream )
+ return;
+
+ delete m_stream; m_stream = 0;
+ m_file->close();
+ delete m_file; m_file = 0;
+
+ if( m_prev_time )
+ utime( QFile::encodeName( m_info->filename() ), m_prev_time );
+}
+
diff --git a/kioslaves/mbox/readmbox.h b/kioslaves/mbox/readmbox.h
new file mode 100644
index 000000000..01b813df7
--- /dev/null
+++ b/kioslaves/mbox/readmbox.h
@@ -0,0 +1,132 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef READMBOX_H
+#define READMBOX_H
+
+#include "mboxfile.h"
+
+class UrlInfo;
+class MBox;
+
+class QFile;
+class QString;
+class QTextStream;
+
+struct utimbuf;
+
+/**
+ * This class handels reading from a mbox-file.
+ */
+class ReadMBox : public MBoxFile
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param info The information of the file to read
+ * @param parent The instance of the parent MBoxProtocol.
+ * @param onlynew Only read new messages from the MBox file.
+ * @param savetime If true, the atime of the mbox-file is preserved (note that this touch the ctime).
+ */
+ ReadMBox( const UrlInfo* info, MBoxProtocol* parent, bool onlynew = false, bool savetime = false );
+
+ /**
+ * Destructor
+ */
+ ~ReadMBox();
+
+ /**
+ * This functions return the current line
+ * @return The line last read, or QString::null if there wasn't such last line
+ */
+ QString currentLine() const;
+
+ /**
+ * This function returns the current id. The id is the first line of an email,
+ * and is used in filenaming. The id normally starts with "From ".
+ * @return The current ID, or QString::null if no id was found yet.
+ */
+ QString currentID() const;
+
+ /**
+ * This function reads the next line. The next line can be read by the currentLine()
+ * function call.
+ *
+ * @return true if succesfull, otherwise false.
+ */
+ bool nextLine();
+
+ /**
+ * This function search the file for a certain id.
+ * If not found, the position is EOF.
+ * @param id The id of the message to be found.
+ * @return true if the message was found, false otherwise.
+ */
+ bool searchMessage( const QString& id );
+
+ /**
+ * Skips all lines which belongs to the current message. The cursor is on the first line
+ * of a new message message at the end of this function, or at EOF if the cursor was already
+ * on the last message.
+ * @return The number of bytes read while skipping the message.
+ */
+ unsigned int skipMessage();
+
+ /**
+ * Sets the cursor back to the beginning of the file
+ */
+ void rewind();
+
+ /**
+ * Returns true if the cursor is at EOF.
+ * @return true if and only if the cursor is at EOF.
+ */
+ bool atEnd() const;
+
+ /**
+ * Return true if the message is a new message, or all messages are listed
+ * @return true if it must be listed
+ */
+ bool inListing() const;
+private:
+ /**
+ * Opens a file
+ * @return true Returns true if opening was succesful.
+ */
+ bool open( bool savetime );
+
+ /**
+ * Closes a file.
+ */
+ void close();
+
+private:
+ QFile* m_file;
+ QTextStream* m_stream;
+ QString* m_current_line;
+ QString* m_current_id;
+ bool m_atend;
+
+ struct utimbuf* m_prev_time;
+
+ bool m_only_new, m_savetime;
+
+ bool m_status, m_prev_status, m_header;
+};
+#endif
diff --git a/kioslaves/mbox/stat.cc b/kioslaves/mbox/stat.cc
new file mode 100644
index 000000000..e3418cbbd
--- /dev/null
+++ b/kioslaves/mbox/stat.cc
@@ -0,0 +1,115 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "stat.h"
+
+#include "readmbox.h"
+#include "urlinfo.h"
+
+#include <kdebug.h>
+#include <kio/global.h>
+
+#include <sys/stat.h>
+
+KIO::UDSEntry Stat::stat( const UrlInfo& info )
+{
+ if( info.type() == UrlInfo::message )
+ return Stat::statMessage( info );
+ else if( info.type() == UrlInfo::directory )
+ return Stat::statDirectory( info );
+ else
+ return KIO::UDSEntry();
+}
+
+KIO::UDSEntry Stat::stat( ReadMBox& mbox, const UrlInfo& info )
+{
+ kdDebug() << "Stat::stat()" << endl;
+ KIO::UDSEntry entry;
+ QString url;
+
+ if( info.type() == UrlInfo::invalid )
+ return entry;
+ else if( info.type() == UrlInfo::message )
+ mbox.searchMessage( info.id() );
+
+ Stat::addAtom( entry, KIO::UDS_FILE_TYPE, S_IFREG );
+ Stat::addAtom( entry, KIO::UDS_MIME_TYPE, "message/rfc822" );
+
+ url = QString( "mbox:%1/%2" ).arg( info.filename(), mbox.currentID() );
+ Stat::addAtom( entry, KIO::UDS_URL, url );
+ if( mbox.currentID().isEmpty() )
+ Stat::addAtom( entry, KIO::UDS_NAME, "foobar" );
+ else
+ Stat::addAtom( entry, KIO::UDS_NAME, mbox.currentID() );
+
+
+ Stat::addAtom( entry, KIO::UDS_SIZE, mbox.skipMessage() );
+
+ return entry;
+}
+
+KIO::UDSEntry Stat::statDirectory( const UrlInfo& info )
+{
+ kdDebug() << "statDirectory()" << endl;
+ KIO::UDSEntry entry;
+
+ //Specific things for a directory
+ Stat::addAtom( entry, KIO::UDS_FILE_TYPE, S_IFDIR );
+ Stat::addAtom( entry, KIO::UDS_NAME, info.filename() );
+
+ return entry;
+}
+
+KIO::UDSEntry Stat::statMessage( const UrlInfo& info )
+{
+ kdDebug() << "statMessage( " << info.url() << " )" << endl;
+ KIO::UDSEntry entry;
+ QString url = QString( "mbox:%1" ).arg( info.url() );
+
+ //Specific things for a message
+ Stat::addAtom( entry, KIO::UDS_FILE_TYPE, S_IFREG );
+ Stat::addAtom( entry, KIO::UDS_MIME_TYPE, "message/rfc822" );
+
+ Stat::addAtom( entry, KIO::UDS_URL, url );
+ url = url.right( url.length() - url.findRev( "/" ) - 1 );
+ Stat::addAtom( entry, KIO::UDS_NAME, url );
+
+ return entry;
+}
+
+void Stat::addAtom( KIO::UDSEntry& entry, unsigned int uds, const QString& str )
+{
+ KIO::UDSAtom atom;
+ atom.m_uds = uds;
+ atom.m_str = str;
+ atom.m_long = 0;
+
+ entry.append( atom );
+}
+
+
+void Stat::addAtom( KIO::UDSEntry& entry, unsigned int uds, long lng )
+{
+ KIO::UDSAtom atom;
+ atom.m_uds = uds;
+ atom.m_str = QString::null;
+ atom.m_long = lng;
+
+ entry.append( atom );
+}
+
diff --git a/kioslaves/mbox/stat.h b/kioslaves/mbox/stat.h
new file mode 100644
index 000000000..f26f4338d
--- /dev/null
+++ b/kioslaves/mbox/stat.h
@@ -0,0 +1,82 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef STAT_H
+#define STAT_H
+
+#include <kio/global.h>
+
+class ReadMBox;
+class UrlInfo;
+
+class KURL;
+
+class QString;
+
+/**
+ * This class is used to get the stats of a mbox-email or mbox-file.
+ * This class only uses static members.
+ */
+class Stat
+{
+public:
+ /**
+ * Empty constructor
+ */
+ Stat() {}
+
+ /**
+ * Emtpy destructor
+ */
+ ~Stat() {}
+
+ /**
+ * This functions gives information with a given UrlInfo.
+ * @param info The file information
+ * @return The information of the file as destribed in UrlInfo.
+ */
+ static KIO::UDSEntry stat( const UrlInfo& info );
+ /**
+ * This function gives information with a given ReadMBox and UrlInfo.
+ * Through this, it is possible to ask the stats of the next message,
+ * without reopening the mbox-file.
+ * @param mbox The ReadMBox instance, used to search the mbox-email in.
+ * @param info The url information.
+ * @return The requesteds information.
+ */
+ static KIO::UDSEntry stat( ReadMBox& mbox, const UrlInfo& info );
+
+ /**
+ * This function gets the stats of a given mbox-file in an UDSEntry.
+ * @param info The location of the mbox-file.
+ * @return A list of Atoms.
+ */
+ static KIO::UDSEntry statDirectory( const UrlInfo& info );
+
+ /**
+ * This function gets the stats of a geven mbox-message in a UDSEntry.
+ * @param info The url of the mbox-message.
+ * @return Information shipped in an UDSEntry.
+ */
+ static KIO::UDSEntry statMessage( const UrlInfo& info );
+private:
+ static void addAtom( KIO::UDSEntry& entry, unsigned int key, const QString& value );
+ static void addAtom( KIO::UDSEntry& entry, unsigned int key, const long value );
+};
+
+#endif
diff --git a/kioslaves/mbox/urlinfo.cc b/kioslaves/mbox/urlinfo.cc
new file mode 100644
index 000000000..710dc6f0c
--- /dev/null
+++ b/kioslaves/mbox/urlinfo.cc
@@ -0,0 +1,133 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "urlinfo.h"
+
+#include <kdebug.h>
+#include <kurl.h>
+
+#include <qfileinfo.h>
+#include <qstring.h>
+
+UrlInfo::UrlInfo( const KURL& url, const UrlType type )
+ : m_type( invalid ),
+ m_filename( new QString ),
+ m_id( new QString )
+{
+ calculateInfo( url, type );
+}
+
+UrlInfo::~UrlInfo()
+{
+ delete m_filename;
+ delete m_id;
+}
+
+QString UrlInfo::mimetype() const
+{
+ switch( m_type )
+ {
+ case message:
+ return "message/rfc822";
+ case directory:
+ return "inode/directory";
+ case invalid:
+ default:
+ return "invalid";
+ }
+}
+
+QString UrlInfo::filename() const
+{
+ return *m_filename;
+}
+
+QString UrlInfo::id() const
+{
+ return *m_id;
+}
+
+QString UrlInfo::url() const
+{
+ return *m_filename + "/" + *m_id;
+}
+
+
+void UrlInfo::calculateInfo( const KURL& url, const UrlType type )
+{
+ bool found = false;
+
+ if( !found && type & UrlInfo::message )
+ found = isMessage( url );
+ if( !found && type & UrlInfo::directory )
+ found = isDirectory( url );
+ if( !found )
+ {
+ m_type = invalid;
+ *m_filename = "";
+ *m_id = "";
+ }
+}
+
+bool UrlInfo::isDirectory( const KURL& url )
+{
+ //Check is url is in the form mbox://{filename}
+ QString filename = url.path();
+ QFileInfo info;
+
+ //Remove ending /
+ while( filename.length() > 1 && filename.right( 1 ) == "/" )
+ filename.remove( filename.length()-2, 1 );
+
+ //Is this a directory?
+ info.setFile( filename );
+ if( !info.isFile() )
+ return false;
+
+ //Setting paramaters
+ *m_filename = filename;
+ *m_id = QString::null;
+ m_type = directory;
+ kdDebug() << "urlInfo::isDirectory( " << url << " )" << endl;
+ return true;
+}
+
+bool UrlInfo::isMessage( const KURL& url )
+{
+ QString path = url.path();
+ QFileInfo info;
+ int cutindex = path.findRev( '/' );
+
+ //Does it contain at least one /?
+ if( cutindex < 0 )
+ return false;
+
+ //Does the mbox-file exists?
+ info.setFile( path.left( cutindex ) );
+ if( !info.isFile() )
+ return false;
+
+ //Settings parameters
+ kdDebug() << "urlInfo::isMessage( " << url << " )" << endl;
+ m_type = message;
+ *m_id = path.right( path.length() - cutindex - 1 );
+ *m_filename = path.left( cutindex );
+
+ return true;
+}
+
diff --git a/kioslaves/mbox/urlinfo.h b/kioslaves/mbox/urlinfo.h
new file mode 100644
index 000000000..c3177db5e
--- /dev/null
+++ b/kioslaves/mbox/urlinfo.h
@@ -0,0 +1,82 @@
+/*
+ * This is a simple kioslave to handle mbox-files.
+ * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef URLINFO_H
+#define URLINFO_H
+class KURL;
+
+class QString;
+
+class UrlInfo
+{
+public:
+ /**
+ * This enum is used to determe the url type.
+ */
+ enum UrlType { invalid = 0, message = 1, directory = 2 };
+
+ /**
+ * Constructor
+ *
+ * @param url The url: this url is used to split the location data off.
+ * @param type The possible types of the url
+ */
+ UrlInfo( const KURL &url, const UrlType type = (UrlType)( message | directory ) );
+
+ /**
+ * Destructor
+ */
+ ~UrlInfo();
+
+ /**
+ * Returns the type of the url
+ * @return the type of the url
+ */
+ UrlType type() const { return m_type; }
+
+ /**
+ * @return the mimetype of the url
+ */
+ QString mimetype() const;
+
+ /**
+ * @return The location of the mbox-file
+ */
+ QString filename() const;
+ /**
+ * @return The id given in the url.
+ */
+ QString id() const;
+
+ /**
+ * @return the while url as QString
+ */
+ QString url() const;
+private:
+ void calculateInfo( const KURL& url, const UrlType type );
+
+ bool isDirectory( const KURL& url );
+ bool isMessage( const KURL& url );
+
+private:
+ UrlType m_type;
+ QString *m_filename;
+ QString *m_id;
+};
+
+#endif
diff --git a/kioslaves/opengroupware/Makefile.am b/kioslaves/opengroupware/Makefile.am
new file mode 100644
index 000000000..bd4104904
--- /dev/null
+++ b/kioslaves/opengroupware/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/kresources/opengroupware/soap \
+ $(all_includes)
+
+noinst_HEADERS = opengroupware.h
+
+METASOURCES = AUTO
+
+kdelnkdir = $(kde_servicesdir)
+kdelnk_DATA = opengroupware.protocol opengroupwares.protocol
+
+kde_module_LTLIBRARIES = kio_opengroupware.la
+
+kio_opengroupware_la_SOURCES = opengroupware.cpp webdavhandler.cpp
+kio_opengroupware_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \
+ $(top_builddir)/libkdepim/libkdepim.la $(LIB_KIO)
+kio_opengroupware_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
diff --git a/kioslaves/opengroupware/opengroupware.cpp b/kioslaves/opengroupware/opengroupware.cpp
new file mode 100644
index 000000000..76abeb10e
--- /dev/null
+++ b/kioslaves/opengroupware/opengroupware.cpp
@@ -0,0 +1,250 @@
+/*
+ This file is part of KDE.
+
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "opengroupware.h"
+#include "webdavhandler.h"
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kio/job.h>
+#include <kio/davjob.h>
+#include <klocale.h>
+
+#include <libkdepim/kabcresourcecached.h>
+
+#include <libkcal/freebusy.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/scheduler.h>
+#include <libkcal/calendarlocal.h>
+
+#include <kabc/vcardconverter.h>
+
+#include <kinstance.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktempfile.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <kdepimmacros.h>
+
+namespace KABC {
+
+class ResourceMemory : public ResourceCached
+{
+ public:
+ ResourceMemory() : ResourceCached( 0 ) {}
+
+ Ticket *requestSaveTicket() { return 0; }
+ bool load() { return true; }
+ bool save( Ticket * ) { return true; }
+ void releaseSaveTicket( Ticket * ) {}
+};
+
+}
+
+
+extern "C" {
+KDE_EXPORT int kdemain( int argc, char **argv );
+}
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_OpenGroupware" );
+
+ kdDebug(7000) << "Starting kio_OpenGroupware(pid: " << getpid() << ")" << endl;
+
+ if (argc != 4) {
+ fprintf( stderr, "Usage: kio_OpenGroupware protocol domain-socket1 domain-socket2\n");
+ exit( -1 );
+ }
+
+ OpenGroupware slave( argv[1], argv[2], argv[3] );
+ slave.dispatchLoop();
+
+ return 0;
+}
+
+OpenGroupware::OpenGroupware( const QCString &protocol, const QCString &pool,
+ const QCString &app )
+ : SlaveBase( protocol, pool, app )
+{
+}
+
+void OpenGroupware::get( const KURL &url )
+{
+ kdDebug(7000) << "OpenGroupware::get()" << endl;
+ kdDebug(7000) << " URL: " << url.url() << endl;
+ #if 1
+ kdDebug(7000) << " Path: " << url.path() << endl;
+ kdDebug(7000) << " Query: " << url.query() << endl;
+ kdDebug(7000) << " Protocol: " << url.protocol() << endl;
+ kdDebug(7000) << " Filename: " << url.filename() << endl;
+ #endif
+
+ mimeType( "text/plain" );
+
+ QString path = url.path();
+ debugMessage( "Path: " + path );
+
+ if ( path.startsWith( "/freebusy/" ) ) {
+ getFreeBusy( url );
+ } else if ( path.startsWith( "/calendar/" ) ) {
+ getCalendar( url );
+ } else if ( path.startsWith( "/addressbook/" ) ) {
+ getAddressbook( url );
+ } else {
+ QString error = i18n("Unknown path. Known paths are '/freebusy/', "
+ "'/calendar/' and '/addressbook/'.");
+ errorMessage( error );
+ }
+
+ kdDebug(7000) << "OpenGroupwareCgiProtocol::get() done" << endl;
+}
+
+void OpenGroupware::getFreeBusy( const KURL &url )
+{
+ QString file = url.filename();
+ if ( file.right( 4 ) != ".ifb" ) {
+ QString error = i18n("Illegal filename. File has to have '.ifb' suffix.");
+ errorMessage( error );
+ } else {
+ QString email = file.left( file.length() - 4 );
+ debugMessage( "Email: " + email );
+
+ QString user = url.user();
+ QString pass = url.pass();
+
+ debugMessage( "URL: " );
+ debugMessage( "User: " + user );
+ debugMessage( "Password: " + pass );
+
+ KCal::FreeBusy *fb = new KCal::FreeBusy;
+
+ if ( user.isEmpty() || pass.isEmpty() ) {
+ errorMessage( i18n("Need username and password.") );
+ } else {
+ // FIXME get from server
+
+ // FIXME: Read range from configuration or URL parameters.
+ QDate start = QDate::currentDate().addDays( -3 );
+ QDate end = QDate::currentDate().addDays( 60 );
+
+ fb->setDtStart( start );
+ fb->setDtEnd( end );
+
+ kdDebug() << "Login" << endl;
+
+ }
+
+#if 0
+ QDateTime s = QDateTime( QDate( 2004, 9, 27 ), QTime( 10, 0 ) );
+ QDateTime e = QDateTime( QDate( 2004, 9, 27 ), QTime( 11, 0 ) );
+
+ fb->addPeriod( s, e );
+#endif
+
+ KCal::ICalFormat format;
+
+ QString ical = format.createScheduleMessage( fb, KCal::Scheduler::Publish );
+
+ data( ical.utf8() );
+
+ finished();
+ }
+}
+
+
+void OpenGroupware::getCalendar( const KURL &_url )
+{
+
+ KURL url( _url ); // we'll be changing it
+ QString user = url.user();
+ QString pass = url.pass();
+
+ QDomDocument props = WebdavHandler::createAllPropsRequest();
+
+ debugMessage( "URL: " );
+ debugMessage( "User: " + user );
+ debugMessage( "Password: " + pass );
+
+ url.setProtocol( "webdav" );
+ url.setPath ( "/zidestore/dav/till/" );
+
+ kdDebug(7000) << "getCalendar: " << url.prettyURL() << endl;
+
+ // FIXME do progress handling
+ mListEventsJob = KIO::davPropFind( url, props, "0", false );
+ connect( mListEventsJob, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotGetCalendarListingResult( KIO::Job * ) ) );
+}
+
+void OpenGroupware::getAddressbook( const KURL &url )
+{
+
+}
+
+void OpenGroupware::errorMessage( const QString &msg )
+{
+ error( KIO::ERR_SLAVE_DEFINED, msg );
+}
+
+void OpenGroupware::debugMessage( const QString &msg )
+{
+#if 0
+ data( ( msg + "\n" ).utf8() );
+#else
+ Q_UNUSED( msg );
+#endif
+}
+
+
+void OpenGroupware::slotGetCalendarListingResult( KIO::Job *job )
+{
+
+ kdDebug(7000) << k_funcinfo << endl;
+
+ if ( job->error() ) {
+ job->showErrorDialog( 0 );
+ } else {
+ kdDebug() << "ResourceSlox::slotResult() success" << endl;
+
+ QDomDocument doc = mListEventsJob->response();
+
+ }
+ KCal::ICalFormat format;
+ KCal::CalendarLocal calendar;
+
+ QString ical = format.toString( &calendar );
+
+ data( ical.utf8() );
+
+ finished();
+}
+
+
+void OpenGroupware::slotGetCalendarResult( KIO::Job *job )
+{
+ Q_UNUSED( job );
+}
+#include "opengroupware.moc"
+
diff --git a/kioslaves/opengroupware/opengroupware.h b/kioslaves/opengroupware/opengroupware.h
new file mode 100644
index 000000000..04eadf456
--- /dev/null
+++ b/kioslaves/opengroupware/opengroupware.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of KDE.
+
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#ifndef GROUPWISE_H
+#define GROUPWISE_H
+
+#include <kio/slavebase.h>
+
+#include <qobject.h>
+
+namespace KIO {
+ class Job;
+ class DavJob;
+}
+
+
+class OpenGroupware : public QObject, public KIO::SlaveBase
+{
+ Q_OBJECT
+ public:
+ OpenGroupware( const QCString &protocol, const QCString &pool,
+ const QCString &app );
+
+ void get( const KURL &url );
+
+ protected:
+ void debugMessage( const QString & );
+ void errorMessage( const QString & );
+
+ void getFreeBusy( const KURL &url );
+ void getCalendar( const KURL &url );
+ void getAddressbook( const KURL &url );
+ protected slots:
+ void slotGetCalendarListingResult( KIO::Job* );
+ void slotGetCalendarResult( KIO::Job* );
+ private:
+ KIO::DavJob *mListEventsJob;
+};
+
+#endif
diff --git a/kioslaves/opengroupware/opengroupware.protocol b/kioslaves/opengroupware/opengroupware.protocol
new file mode 100644
index 000000000..89c0b9ea2
--- /dev/null
+++ b/kioslaves/opengroupware/opengroupware.protocol
@@ -0,0 +1,7 @@
+[Protocol]
+DocPath=kioslave/opengroupware.html
+exec=kio_opengroupware
+input=none
+output=filesystem
+protocol=opengroupware
+reading=true
diff --git a/kioslaves/opengroupware/opengroupwares.protocol b/kioslaves/opengroupware/opengroupwares.protocol
new file mode 100644
index 000000000..595057b27
--- /dev/null
+++ b/kioslaves/opengroupware/opengroupwares.protocol
@@ -0,0 +1,7 @@
+[Protocol]
+DocPath=kioslave/opengroupware.html
+exec=kio_opengroupware
+input=none
+output=filesystem
+protocol=opengroupwares
+reading=true
diff --git a/kioslaves/opengroupware/webdavhandler.cpp b/kioslaves/opengroupware/webdavhandler.cpp
new file mode 100644
index 000000000..683302003
--- /dev/null
+++ b/kioslaves/opengroupware/webdavhandler.cpp
@@ -0,0 +1,81 @@
+/*
+ This file is part of kdepim.
+
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+
+#include "webdavhandler.h"
+
+#include <limits.h>
+
+#include <libkdepim/kpimprefs.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+
+#include <qfile.h>
+
+
+WebdavHandler::WebdavHandler()
+{
+}
+
+
+QDomElement WebdavHandler::addElement( QDomDocument &doc, QDomNode &node,
+ const QString &tag )
+{
+ QDomElement el = doc.createElement( tag );
+ node.appendChild( el );
+ return el;
+}
+
+QDomElement WebdavHandler::addDavElement( QDomDocument &doc, QDomNode &node,
+ const QString &tag )
+{
+ QDomElement el = doc.createElementNS( "DAV", tag );
+ node.appendChild( el );
+ return el;
+}
+
+QDomElement WebdavHandler::addSloxElement( QDomDocument &doc, QDomNode &node,
+ const QString &tag,
+ const QString &text )
+{
+ QDomElement el = doc.createElementNS( "SLOX", tag );
+ if ( !text.isEmpty() ) {
+ QDomText textnode = doc.createTextNode( text );
+ el.appendChild( textnode );
+ }
+ node.appendChild( el );
+ return el;
+}
+
+QDomDocument WebdavHandler::createAllPropsRequest()
+{
+ QDomDocument doc;
+
+ QDomElement root = WebdavHandler::addDavElement( doc, doc, "propfind" );
+ QDomElement prop = WebdavHandler::addDavElement( doc, root, "prop" );
+ WebdavHandler::addDavElement( doc, prop, "getcontentlength");
+ WebdavHandler::addDavElement( doc, prop, "getlastmodified" );
+ WebdavHandler::addDavElement( doc, prop, "displayname" );
+ WebdavHandler::addDavElement( doc, prop, "resourcetype" );
+ prop.appendChild( doc.createElementNS( "http://apache.org/dav/props/", "executable" ) );
+ return doc;
+}
diff --git a/kioslaves/opengroupware/webdavhandler.h b/kioslaves/opengroupware/webdavhandler.h
new file mode 100644
index 000000000..3c1320812
--- /dev/null
+++ b/kioslaves/opengroupware/webdavhandler.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of kdepim.
+
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#ifndef WEBDAVHANDLER_H
+#define WEBDAVHANDLER_H
+
+#include <qvaluelist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qdom.h>
+
+class WebdavHandler
+{
+ public:
+ WebdavHandler();
+
+
+ static QDomElement addElement( QDomDocument &, QDomNode &,
+ const QString &tag );
+ static QDomElement addDavElement( QDomDocument &, QDomNode &,
+ const QString &tag );
+ static QDomElement addSloxElement( QDomDocument &, QDomNode &,
+ const QString &tag,
+ const QString &text = QString::null );
+ static QDomDocument createAllPropsRequest();
+};
+
+#endif
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]=邮件过滤协议仆人