summaryrefslogtreecommitdiffstats
path: root/kioslaves/imap4
diff options
context:
space:
mode:
Diffstat (limited to 'kioslaves/imap4')
-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
29 files changed, 10887 insertions, 0 deletions
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