From 4bd4ac21f11fdb5b76ffda985397398102a81e9b Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Tue, 15 Dec 2020 11:30:44 +0900 Subject: Renaming of files in preparation for code style tools. Signed-off-by: Michele Calgaro (cherry picked from commit 3a75bdfe83b71ef1dbc2fbf52f2d18b8174e22e5) --- tdeioslave/imap4/CMakeLists.txt | 6 +- tdeioslave/imap4/Makefile.am | 8 +- tdeioslave/imap4/imap4.cc | 2746 -------------------------------------- tdeioslave/imap4/imap4.cpp | 2746 ++++++++++++++++++++++++++++++++++++++ tdeioslave/imap4/imapcommand.cc | 408 ------ tdeioslave/imap4/imapcommand.cpp | 408 ++++++ tdeioslave/imap4/imapinfo.cc | 239 ---- tdeioslave/imap4/imapinfo.cpp | 239 ++++ tdeioslave/imap4/imaplist.cc | 135 -- tdeioslave/imap4/imaplist.cpp | 135 ++ tdeioslave/imap4/imapparser.cc | 2094 ----------------------------- tdeioslave/imap4/imapparser.cpp | 2094 +++++++++++++++++++++++++++++ tdeioslave/imap4/mailaddress.cc | 324 ----- tdeioslave/imap4/mailaddress.cpp | 324 +++++ tdeioslave/imap4/mailheader.cc | 203 --- tdeioslave/imap4/mailheader.cpp | 203 +++ tdeioslave/imap4/mimehdrline.cc | 521 -------- tdeioslave/imap4/mimehdrline.cpp | 521 ++++++++ tdeioslave/imap4/mimeheader.cc | 745 ----------- tdeioslave/imap4/mimeheader.cpp | 745 +++++++++++ tdeioslave/imap4/mimeio.cc | 188 --- tdeioslave/imap4/mimeio.cpp | 188 +++ tdeioslave/imap4/rfcdecoder.cc | 668 ---------- tdeioslave/imap4/rfcdecoder.cpp | 668 ++++++++++ 24 files changed, 8278 insertions(+), 8278 deletions(-) delete mode 100644 tdeioslave/imap4/imap4.cc create mode 100644 tdeioslave/imap4/imap4.cpp delete mode 100644 tdeioslave/imap4/imapcommand.cc create mode 100644 tdeioslave/imap4/imapcommand.cpp delete mode 100644 tdeioslave/imap4/imapinfo.cc create mode 100644 tdeioslave/imap4/imapinfo.cpp delete mode 100644 tdeioslave/imap4/imaplist.cc create mode 100644 tdeioslave/imap4/imaplist.cpp delete mode 100644 tdeioslave/imap4/imapparser.cc create mode 100644 tdeioslave/imap4/imapparser.cpp delete mode 100644 tdeioslave/imap4/mailaddress.cc create mode 100644 tdeioslave/imap4/mailaddress.cpp delete mode 100644 tdeioslave/imap4/mailheader.cc create mode 100644 tdeioslave/imap4/mailheader.cpp delete mode 100644 tdeioslave/imap4/mimehdrline.cc create mode 100644 tdeioslave/imap4/mimehdrline.cpp delete mode 100644 tdeioslave/imap4/mimeheader.cc create mode 100644 tdeioslave/imap4/mimeheader.cpp delete mode 100644 tdeioslave/imap4/mimeio.cc create mode 100644 tdeioslave/imap4/mimeio.cpp delete mode 100644 tdeioslave/imap4/rfcdecoder.cc create mode 100644 tdeioslave/imap4/rfcdecoder.cpp (limited to 'tdeioslave/imap4') diff --git a/tdeioslave/imap4/CMakeLists.txt b/tdeioslave/imap4/CMakeLists.txt index 6316fe32f..041aeffca 100644 --- a/tdeioslave/imap4/CMakeLists.txt +++ b/tdeioslave/imap4/CMakeLists.txt @@ -38,9 +38,9 @@ tde_create_translated_desktop( tde_add_kpart( tdeio_imap4 SOURCES - imapcommand.cc imaplist.cc mailaddress.cc mimeheader.cc - rfcdecoder.cc imap4.cc imapinfo.cc imapparser.cc - mailheader.cc mimehdrline.cc mimeio.cc + imapcommand.cpp imaplist.cpp mailaddress.cpp mimeheader.cpp + rfcdecoder.cpp imap4.cpp imapinfo.cpp imapparser.cpp + mailheader.cpp mimehdrline.cpp mimeio.cpp LINK emailfunctions-static kmime-shared tdeio-shared ${SASL_LIBRARY} DESTINATION ${PLUGIN_INSTALL_DIR} ) diff --git a/tdeioslave/imap4/Makefile.am b/tdeioslave/imap4/Makefile.am index f52af2c62..34497bf66 100644 --- a/tdeioslave/imap4/Makefile.am +++ b/tdeioslave/imap4/Makefile.am @@ -7,9 +7,9 @@ INCLUDES= -I$(top_srcdir)/libkmime \ kde_module_LTLIBRARIES = tdeio_imap4.la -tdeio_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 +tdeio_imap4_la_SOURCES = imapcommand.cpp imaplist.cpp mailaddress.cpp \ + mimeheader.cpp rfcdecoder.cpp imap4.cpp imapinfo.cpp imapparser.cpp mailheader.cpp \ + mimehdrline.cpp mimeio.cpp tdeio_imap4_la_LIBADD = $(LIB_TDEIO) $(SASL2_LIBS) ../../libkmime/libkmime.la \ ../../libemailfunctions/libemailfunctions.la tdeio_imap4_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -module $(KDE_PLUGIN) @@ -21,6 +21,6 @@ kdelnk_DATA = imap4.protocol imaps.protocol kdelnkdir = $(kde_servicesdir) messages: - $(XGETTEXT) *.cc -o $(podir)/tdeio_imap4.pot + $(XGETTEXT) *.cpp -o $(podir)/tdeio_imap4.pot include $(top_srcdir)/admin/Doxyfile.am diff --git a/tdeioslave/imap4/imap4.cc b/tdeioslave/imap4/imap4.cc deleted file mode 100644 index 7dea2c5de..000000000 --- a/tdeioslave/imap4/imap4.cc +++ /dev/null @@ -1,2746 +0,0 @@ -/********************************************************************** - * - * imap4.cc - IMAP4rev1 KIOSlave - * Copyright (C) 2001-2002 Michael Haeckel - * 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 -#endif - -#include "imap4.h" - -#include "rfcdecoder.h" - -#include - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LIBSASL2 -extern "C" { -#include -} -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tdepimmacros.h" - -#define IMAP_PROTOCOL "imap" -#define IMAP_SSL_PROTOCOL "imaps" -const int ImapPort = 143; -const int ImapsPort = 993; - -using namespace TDEIO; - -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; - - TDEInstance instance ("tdeio_imap4"); - if (argc != 4) - { - fprintf(stderr, "Usage: tdeio_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) -{ - // A signal handler that calls for example waitpid has to save errno - // before and restore it afterwards. - // (cf. https://www.securecoding.cert.org/confluence/display/cplusplus/ERR32-CPP.+Do+not+rely+on+indeterminate+values+of+errno) - const int save_errno = errno; - int pid, status; - - while (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); - break; - } - } - - errno = save_errno; -} - -IMAP4Protocol::IMAP4Protocol (const TQCString & pool, const TQCString & 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 = TQDateTime(); -} - -IMAP4Protocol::~IMAP4Protocol () -{ - closeDescriptor(); - kdDebug(7116) << "IMAP4: Finishing" << endl; -} - -void -IMAP4Protocol::get (const KURL & _url) -{ - if (!makeLogin()) return; - kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl; - TQString 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. - - TQString 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: " + - TQString::number(selectInfo.recent ()) + "\r\n"); - if (selectInfo.countAvailable ()) - outputLineStr ("X-Count: " + TQString::number(selectInfo.count ()) + - "\r\n"); - if (selectInfo.unseenAvailable ()) - outputLineStr ("X-Unseen: " + - TQString::number(selectInfo.unseen ()) + "\r\n"); - if (selectInfo.uidValidityAvailable ()) - outputLineStr ("X-uidValidity: " + - TQString::number(selectInfo.uidValidity ()) + - "\r\n"); - if (selectInfo.uidNextAvailable ()) - outputLineStr ("X-UidNext: " + - TQString::number(selectInfo.uidNext ()) + "\r\n"); - if (selectInfo.flagsAvailable ()) - outputLineStr ("X-Flags: " + TQString::number(selectInfo.flags ()) + - "\r\n"); - if (selectInfo.permanentFlagsAvailable ()) - outputLineStr ("X-PermanentFlags: " + - TQString::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(TQString()); - cacheOutput = false; - } - - if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent)) - relayEnabled = true; // normal mode, relay data - - if (aSequence != "0:0") - { - TQString contentEncoding; - if (aEnum == ITYPE_ATTACH && decodeContent) - { - // get the MIME header and fill getLastHandled() - TQString 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: " + - TQString::number(cache->getUid ()) + "\r\n"); - if (cache && cache->getSize () != 0) - outputLineStr ("X-Length: " + - TQString::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: " + - TQString::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 (TQByteArray ()); - - 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; - } - - TQString 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) - { - TQString 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") - { - TQString 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 - TQValueList listResponsesSave = listResponses; - doCommand (imapCommand::clientList ("", listStr, false)); - for (TQValueListIterator < imapList > it = listResponsesSave.begin (); - it != listResponsesSave.end (); ++it) - { - bool boxOk = false; - for (TQValueListIterator < 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 (TQValueListIterator < 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 (TQString()); - const TQString encodedUrl = aURL.url(0, 106); // utf-8 - - if (!_url.query ().isEmpty ()) - { - TQString 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); - - TQStringList list = getResults (); - int stretch = 0; - - if (selectInfo.uidNextAvailable ()) - stretch = TQString::number(selectInfo.uidNext ()).length (); - UDSEntry entry; - imapCache fake; - - for (TQStringList::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=" + - TQString::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 = TQString::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, TQString(selectInfo.alert()) ) ); - } - selectInfo.setAlert( 0 ); - } - - kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl; - finished (); -} - -void -IMAP4Protocol::setHost (const TQString & _host, int _port, - const TQString & _user, const TQString & _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; - if (_port == 0) - myPort = (mySSL) ? ImapsPort : ImapPort; - else - myPort = _port; - myUser = _user; - myPass = _pass; - } -} - -void -IMAP4Protocol::parseRelay (const TQByteArray & 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(TQByteArray & buffer, ulong len, ulong relay) -{ - char buf[8192]; - while (buffer.size() < len) - { - ssize_t readLen = myRead(buf, TQMIN(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()) - { - TQByteArray relayData; - ssize_t relbuf = relay - buffer.size(); - int currentRelay = TQMIN(relbuf, readLen); - relayData.setRawData(buf, currentRelay); - parseRelay(relayData); - relayData.resetRawData(buf, currentRelay); - } - { - TQBuffer stream (buffer); - stream.open (IO_WriteOnly); - stream.at (buffer.size ()); - stream.writeBlock (buf, readLen); - stream.close (); - } - } - return (buffer.size() == len); -} - - -bool IMAP4Protocol::parseReadLine (TQByteArray & 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) - { - TQByteArray 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 - { - TQBuffer 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; - TDEIO::TCPSlaveBase::setSubURL (_url); -} - -void -IMAP4Protocol::put (const KURL & _url, int, bool, bool) -{ - kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl; -// TDEIO::TCPSlaveBase::put(_url,permissions,overwrite,resume); - TQString 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 - { - TQPtrList < TQByteArray > bufferList; - int length = 0; - - int result; - // Loop until we got 'dataEnd' - do - { - TQByteArray *buffer = new TQByteArray (); - 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; - - TQByteArray *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")) - { - TQString 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; - TQString 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; - TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; - TQString 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; - - TQString subDir = dBox.right (dBox.length () - dBox.findRev ('/')); - TQString 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")) - { - TQString 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; - TQString 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 (TQCString) - * 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 TQByteArray & aData) -{ - kdDebug(7116) << "IMAP4Protocol::special" << endl; - if (!makeLogin()) return; - - TQDataStream 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; - TQString 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; - TQString 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; - TQCString newFlags; - stream >> _url >> newFlags; - - TQString 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 - TQCString 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; - TQCString newFlags; - stream >> _url >> seen; - - TQString 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, TQString(TQChar(tmp)) ); - break; - } -} - -void -IMAP4Protocol::specialACLCommand( int command, TQDataStream& stream ) -{ - // All commands start with the URL to the box - KURL _url; - stream >> _url; - TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; - parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); - - switch( command ) { - case 'S': // SETACL - { - TQString 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 - { - TQString 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, TQString(TQChar(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; - } - TQStringList 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, TQString(TQChar(command)) ); - } -} - -void -IMAP4Protocol::specialSearchCommand( TQDataStream& stream ) -{ - kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl; - KURL _url; - stream >> _url; - TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; - parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); - if (!assureBox(aBox, true)) 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); - TQStringList lst = getResults(); - kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection << - "' returns " << lst << endl; - infoMessage( lst.join( " " ) ); - - finished(); -} - -void -IMAP4Protocol::specialCustomCommand( TQDataStream& stream ) -{ - kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl; - - TQString 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); - TQStringList 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, TQString() )); - while ( !parseLoop () ) ; - - // see if server is waiting - if (!cmd->isComplete () && !getContinuation ().isEmpty ()) - { - const TQByteArray 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); - - TQStringList lst = getResults(); - kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl; - infoMessage( lst.join( " " ) ); - - finished (); - } -} - -void -IMAP4Protocol::specialAnnotateMoreCommand( int command, TQDataStream& stream ) -{ - // All commands start with the URL to the box - KURL _url; - stream >> _url; - TQString 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 - // TQString entry (should be an actual entry name, no % or *; empty for server entries) - // TQMap attributes (name and value) - TQString entry; - TQMap 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 - // TQString entry (should be an actual entry name, no % or *; empty for server entries) - // TQStringList attributes (list of attributes to be retrieved, possibly with % or *) - TQString entry; - TQStringList 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, TQString(TQChar(command)) ); - } -} - -void -IMAP4Protocol::specialQuotaCommand( int command, TQDataStream& stream ) -{ - // All commands start with the URL to the box - KURL _url; - stream >> _url; - TQString 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, TQString(TQChar(command)) ); - } -} - -void -IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite) -{ - kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl; - TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; - TQString 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) - { - kdWarning(7116) << "Unable to close mailbox!" << endl; - error(ERR_CANNOT_RENAME, src.path()); - return; - } - setState(ISTATE_LOGIN); - } - imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox)); - if (cmd->result () != "OK") { - error (ERR_CANNOT_RENAME, src.path()); - completeQueue.removeRef (cmd); - return; - } - completeQueue.removeRef (cmd); - } - break; - - case ITYPE_MSG: - case ITYPE_ATTACH: - case ITYPE_UNKNOWN: - error (ERR_CANNOT_RENAME, src.path()); - break; - } - } - else - { - error (ERR_CANNOT_RENAME, src.path()); - return; - } - finished (); -} - -void -IMAP4Protocol::slave_status () -{ - bool connected = (getState() != ISTATE_NO) && isConnectionValid(); - kdDebug(7116) << "IMAP4::slave_status " << connected << endl; - slaveStatus ( connected ? myHost : TQString(), connected ); -} - -void -IMAP4Protocol::dispatch (int command, const TQByteArray & data) -{ - kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl; - TDEIO::TCPSlaveBase::dispatch (command, data); -} - -void -IMAP4Protocol::stat (const KURL & _url) -{ - kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl; - TQString 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; - TQString cmdInfo; - if (aType == ITYPE_MSG || aType == ITYPE_ATTACH) - ok = true; - else - { - imapCommand *cmd = doCommand(imapCommand::clienStatus(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 (TQValueListIterator < 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(TDEIO::ERR_DOES_NOT_EXIST, aBox); - return; - } - if ((aSection == "UIDNEXT" && geStatus().uidNextAvailable()) - || (aSection == "UNSEEN" && geStatus().unseenAvailable())) - { - atom.m_uds = UDS_SIZE; - atom.m_str = TQString(); - atom.m_long = (aSection == "UIDNEXT") ? geStatus().uidNext() - : geStatus().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::clienStatus (aBox, "UIDVALIDITY")); - completeQueue.removeRef (cmd); - validity = geStatus ().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=" + - TQString::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 = TQString(); - 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 = TQString(); - atom.m_long = S_IFDIR; - entry.append (atom); - break; - - case ITYPE_MSG: - case ITYPE_ATTACH: - atom.m_uds = UDS_FILE_TYPE; - atom.m_str = TQString(); - 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 = TQString(); - 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 - TQString 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 (TQStringList::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(TQString("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(TQString("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 (TQStringList::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 (TQString ("LOGINDISABLED"))) { - error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server.")); - closeConnection(); - return false; - } - } - else { - if (!hasCapability (TQString ("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( TQRegExp( "Cyrus IMAP4 v2.1" ) ) ) { - removeCapability( "ANNOTATEMORE" ); - } - - // starting from Cyrus IMAP 2.3.9, shared seen flags are available - TQRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false ); - if ( regExp.search( greeting ) >= 0 ) { - const int major = regExp.cap( 1 ).toInt(); - const int minor = regExp.cap( 2 ).toInt(); - const int patch = regExp.cap( 3 ).toInt(); - if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) { - kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl; - imapCapabilities.append( "x-kmail-sharedseen" ); - } - } - - kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl; - - TDEIO::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; - - TQString 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(TDEIO::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(TDEIO::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(TDEIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into tdeio_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") - { - TQValueListIterator < 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[TQString()] = (*it).hierarchyDelimiter(); - kdDebug(7116) << "makeLogin - delimiter for empty ns='" << - (*it).hierarchyDelimiter() << "'" << endl; - if ( !hasCapability("NAMESPACE") ) - { - // server does not support namespaces - TQString nsentry = TQString::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 TQString & aStr) -{ - //kdDebug(7116) << "Writing: " << aStr << endl; - TQCString 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); -} - -TQString -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 (TQString()); - const TQString encodedUrl = aURL.url(0, 106); // utf-8 - doListEntry(encodedUrl, stretch, cache, withFlags, withSubject); -} - - - -void -IMAP4Protocol::doListEntry (const TQString & encodedUrl, int stretch, imapCache * cache, - bool withFlags, bool withSubject) -{ - if (cache) - { - UDSEntry entry; - UDSAtom atom; - - entry.clear (); - - const TQString uid = TQString::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 = TQString(); - 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 = TDEIO::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 TQString & myBox, - const imapList & item, bool appendPath) -{ - KURL aURL = _url; - aURL.setQuery (TQString()); - UDSEntry entry; - UDSAtom atom; - int hdLen = item.hierarchyDelimiter().length(); - - { - // mailboxName will be appended to the path if appendPath is true - TQString 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 = TQString(); - 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 = TQString(); - 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; - TQString 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, TQString & _box, - TQString & _section, TQString & _type, TQString & _uid, - TQString & _validity, TQString & _hierarchyDelimiter, - TQString & _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 - TQString 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 (TQValueListIterator < 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 TQCString & _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; - } - - TQByteArray 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(TQString 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 - TQByteArray 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; - - TQString 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) memmove(readBuffer, &readBuffer[copyLen], readBufferLen); - return copyLen; - } - if (!isConnectionValid()) return 0; - waitForResponse( responseTimeout() ); - return read(data, len); -} - -bool -IMAP4Protocol::assureBox (const TQString & 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"; - TQString cmdInfo = cmd->resultInfo(); - completeQueue.removeRef (cmd); - - if (!ok) - { - bool found = false; - cmd = doCommand (imapCommand::clientList ("", aBox)); - if (cmd->result () == "OK") - { - for (TQValueListIterator < 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(TDEIO::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( TQDateTime::currentDateTime() ) > 10 ) { - cmd = doCommand (imapCommand::clientNoop ()); - completeQueue.removeRef (cmd); - mTimeOfLastNoop = TQDateTime::currentDateTime(); - kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl; - } - } - - // if it is the mode we want - if (!getSelected().readWrite() && !readonly) - { - error(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox); - return false; - } - - return true; -} diff --git a/tdeioslave/imap4/imap4.cpp b/tdeioslave/imap4/imap4.cpp new file mode 100644 index 000000000..280cbde70 --- /dev/null +++ b/tdeioslave/imap4/imap4.cpp @@ -0,0 +1,2746 @@ +/********************************************************************** + * + * imap4.cpp - IMAP4rev1 KIOSlave + * Copyright (C) 2001-2002 Michael Haeckel + * 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 +#endif + +#include "imap4.h" + +#include "rfcdecoder.h" + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBSASL2 +extern "C" { +#include +} +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tdepimmacros.h" + +#define IMAP_PROTOCOL "imap" +#define IMAP_SSL_PROTOCOL "imaps" +const int ImapPort = 143; +const int ImapsPort = 993; + +using namespace TDEIO; + +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; + + TDEInstance instance ("tdeio_imap4"); + if (argc != 4) + { + fprintf(stderr, "Usage: tdeio_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) +{ + // A signal handler that calls for example waitpid has to save errno + // before and restore it afterwards. + // (cf. https://www.securecoding.cert.org/confluence/display/cplusplus/ERR32-CPP.+Do+not+rely+on+indeterminate+values+of+errno) + const int save_errno = errno; + int pid, status; + + while (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); + break; + } + } + + errno = save_errno; +} + +IMAP4Protocol::IMAP4Protocol (const TQCString & pool, const TQCString & 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 = TQDateTime(); +} + +IMAP4Protocol::~IMAP4Protocol () +{ + closeDescriptor(); + kdDebug(7116) << "IMAP4: Finishing" << endl; +} + +void +IMAP4Protocol::get (const KURL & _url) +{ + if (!makeLogin()) return; + kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl; + TQString 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. + + TQString 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: " + + TQString::number(selectInfo.recent ()) + "\r\n"); + if (selectInfo.countAvailable ()) + outputLineStr ("X-Count: " + TQString::number(selectInfo.count ()) + + "\r\n"); + if (selectInfo.unseenAvailable ()) + outputLineStr ("X-Unseen: " + + TQString::number(selectInfo.unseen ()) + "\r\n"); + if (selectInfo.uidValidityAvailable ()) + outputLineStr ("X-uidValidity: " + + TQString::number(selectInfo.uidValidity ()) + + "\r\n"); + if (selectInfo.uidNextAvailable ()) + outputLineStr ("X-UidNext: " + + TQString::number(selectInfo.uidNext ()) + "\r\n"); + if (selectInfo.flagsAvailable ()) + outputLineStr ("X-Flags: " + TQString::number(selectInfo.flags ()) + + "\r\n"); + if (selectInfo.permanentFlagsAvailable ()) + outputLineStr ("X-PermanentFlags: " + + TQString::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(TQString()); + cacheOutput = false; + } + + if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent)) + relayEnabled = true; // normal mode, relay data + + if (aSequence != "0:0") + { + TQString contentEncoding; + if (aEnum == ITYPE_ATTACH && decodeContent) + { + // get the MIME header and fill getLastHandled() + TQString 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: " + + TQString::number(cache->getUid ()) + "\r\n"); + if (cache && cache->getSize () != 0) + outputLineStr ("X-Length: " + + TQString::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: " + + TQString::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 (TQByteArray ()); + + 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; + } + + TQString 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) + { + TQString 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") + { + TQString 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 + TQValueList listResponsesSave = listResponses; + doCommand (imapCommand::clientList ("", listStr, false)); + for (TQValueListIterator < imapList > it = listResponsesSave.begin (); + it != listResponsesSave.end (); ++it) + { + bool boxOk = false; + for (TQValueListIterator < 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 (TQValueListIterator < 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 (TQString()); + const TQString encodedUrl = aURL.url(0, 106); // utf-8 + + if (!_url.query ().isEmpty ()) + { + TQString 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); + + TQStringList list = getResults (); + int stretch = 0; + + if (selectInfo.uidNextAvailable ()) + stretch = TQString::number(selectInfo.uidNext ()).length (); + UDSEntry entry; + imapCache fake; + + for (TQStringList::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=" + + TQString::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 = TQString::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, TQString(selectInfo.alert()) ) ); + } + selectInfo.setAlert( 0 ); + } + + kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl; + finished (); +} + +void +IMAP4Protocol::setHost (const TQString & _host, int _port, + const TQString & _user, const TQString & _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; + if (_port == 0) + myPort = (mySSL) ? ImapsPort : ImapPort; + else + myPort = _port; + myUser = _user; + myPass = _pass; + } +} + +void +IMAP4Protocol::parseRelay (const TQByteArray & 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(TQByteArray & buffer, ulong len, ulong relay) +{ + char buf[8192]; + while (buffer.size() < len) + { + ssize_t readLen = myRead(buf, TQMIN(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()) + { + TQByteArray relayData; + ssize_t relbuf = relay - buffer.size(); + int currentRelay = TQMIN(relbuf, readLen); + relayData.setRawData(buf, currentRelay); + parseRelay(relayData); + relayData.resetRawData(buf, currentRelay); + } + { + TQBuffer stream (buffer); + stream.open (IO_WriteOnly); + stream.at (buffer.size ()); + stream.writeBlock (buf, readLen); + stream.close (); + } + } + return (buffer.size() == len); +} + + +bool IMAP4Protocol::parseReadLine (TQByteArray & 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) + { + TQByteArray 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 + { + TQBuffer 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; + TDEIO::TCPSlaveBase::setSubURL (_url); +} + +void +IMAP4Protocol::put (const KURL & _url, int, bool, bool) +{ + kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl; +// TDEIO::TCPSlaveBase::put(_url,permissions,overwrite,resume); + TQString 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 + { + TQPtrList < TQByteArray > bufferList; + int length = 0; + + int result; + // Loop until we got 'dataEnd' + do + { + TQByteArray *buffer = new TQByteArray (); + 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; + + TQByteArray *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")) + { + TQString 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; + TQString 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; + TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; + TQString 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; + + TQString subDir = dBox.right (dBox.length () - dBox.findRev ('/')); + TQString 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")) + { + TQString 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; + TQString 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 (TQCString) + * 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 TQByteArray & aData) +{ + kdDebug(7116) << "IMAP4Protocol::special" << endl; + if (!makeLogin()) return; + + TQDataStream 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; + TQString 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; + TQString 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; + TQCString newFlags; + stream >> _url >> newFlags; + + TQString 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 + TQCString 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; + TQCString newFlags; + stream >> _url >> seen; + + TQString 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, TQString(TQChar(tmp)) ); + break; + } +} + +void +IMAP4Protocol::specialACLCommand( int command, TQDataStream& stream ) +{ + // All commands start with the URL to the box + KURL _url; + stream >> _url; + TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; + parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); + + switch( command ) { + case 'S': // SETACL + { + TQString 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 + { + TQString 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, TQString(TQChar(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; + } + TQStringList 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, TQString(TQChar(command)) ); + } +} + +void +IMAP4Protocol::specialSearchCommand( TQDataStream& stream ) +{ + kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl; + KURL _url; + stream >> _url; + TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; + parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); + if (!assureBox(aBox, true)) 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); + TQStringList lst = getResults(); + kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection << + "' returns " << lst << endl; + infoMessage( lst.join( " " ) ); + + finished(); +} + +void +IMAP4Protocol::specialCustomCommand( TQDataStream& stream ) +{ + kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl; + + TQString 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); + TQStringList 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, TQString() )); + while ( !parseLoop () ) ; + + // see if server is waiting + if (!cmd->isComplete () && !getContinuation ().isEmpty ()) + { + const TQByteArray 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); + + TQStringList lst = getResults(); + kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl; + infoMessage( lst.join( " " ) ); + + finished (); + } +} + +void +IMAP4Protocol::specialAnnotateMoreCommand( int command, TQDataStream& stream ) +{ + // All commands start with the URL to the box + KURL _url; + stream >> _url; + TQString 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 + // TQString entry (should be an actual entry name, no % or *; empty for server entries) + // TQMap attributes (name and value) + TQString entry; + TQMap 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 + // TQString entry (should be an actual entry name, no % or *; empty for server entries) + // TQStringList attributes (list of attributes to be retrieved, possibly with % or *) + TQString entry; + TQStringList 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, TQString(TQChar(command)) ); + } +} + +void +IMAP4Protocol::specialQuotaCommand( int command, TQDataStream& stream ) +{ + // All commands start with the URL to the box + KURL _url; + stream >> _url; + TQString 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, TQString(TQChar(command)) ); + } +} + +void +IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite) +{ + kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl; + TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; + TQString 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) + { + kdWarning(7116) << "Unable to close mailbox!" << endl; + error(ERR_CANNOT_RENAME, src.path()); + return; + } + setState(ISTATE_LOGIN); + } + imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox)); + if (cmd->result () != "OK") { + error (ERR_CANNOT_RENAME, src.path()); + completeQueue.removeRef (cmd); + return; + } + completeQueue.removeRef (cmd); + } + break; + + case ITYPE_MSG: + case ITYPE_ATTACH: + case ITYPE_UNKNOWN: + error (ERR_CANNOT_RENAME, src.path()); + break; + } + } + else + { + error (ERR_CANNOT_RENAME, src.path()); + return; + } + finished (); +} + +void +IMAP4Protocol::slave_status () +{ + bool connected = (getState() != ISTATE_NO) && isConnectionValid(); + kdDebug(7116) << "IMAP4::slave_status " << connected << endl; + slaveStatus ( connected ? myHost : TQString(), connected ); +} + +void +IMAP4Protocol::dispatch (int command, const TQByteArray & data) +{ + kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl; + TDEIO::TCPSlaveBase::dispatch (command, data); +} + +void +IMAP4Protocol::stat (const KURL & _url) +{ + kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl; + TQString 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; + TQString cmdInfo; + if (aType == ITYPE_MSG || aType == ITYPE_ATTACH) + ok = true; + else + { + imapCommand *cmd = doCommand(imapCommand::clienStatus(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 (TQValueListIterator < 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(TDEIO::ERR_DOES_NOT_EXIST, aBox); + return; + } + if ((aSection == "UIDNEXT" && geStatus().uidNextAvailable()) + || (aSection == "UNSEEN" && geStatus().unseenAvailable())) + { + atom.m_uds = UDS_SIZE; + atom.m_str = TQString(); + atom.m_long = (aSection == "UIDNEXT") ? geStatus().uidNext() + : geStatus().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::clienStatus (aBox, "UIDVALIDITY")); + completeQueue.removeRef (cmd); + validity = geStatus ().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=" + + TQString::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 = TQString(); + 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 = TQString(); + atom.m_long = S_IFDIR; + entry.append (atom); + break; + + case ITYPE_MSG: + case ITYPE_ATTACH: + atom.m_uds = UDS_FILE_TYPE; + atom.m_str = TQString(); + 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 = TQString(); + 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 + TQString 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 (TQStringList::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(TQString("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(TQString("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 (TQStringList::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 (TQString ("LOGINDISABLED"))) { + error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server.")); + closeConnection(); + return false; + } + } + else { + if (!hasCapability (TQString ("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( TQRegExp( "Cyrus IMAP4 v2.1" ) ) ) { + removeCapability( "ANNOTATEMORE" ); + } + + // starting from Cyrus IMAP 2.3.9, shared seen flags are available + TQRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false ); + if ( regExp.search( greeting ) >= 0 ) { + const int major = regExp.cap( 1 ).toInt(); + const int minor = regExp.cap( 2 ).toInt(); + const int patch = regExp.cap( 3 ).toInt(); + if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) { + kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl; + imapCapabilities.append( "x-kmail-sharedseen" ); + } + } + + kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl; + + TDEIO::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; + + TQString 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(TDEIO::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(TDEIO::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(TDEIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into tdeio_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") + { + TQValueListIterator < 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[TQString()] = (*it).hierarchyDelimiter(); + kdDebug(7116) << "makeLogin - delimiter for empty ns='" << + (*it).hierarchyDelimiter() << "'" << endl; + if ( !hasCapability("NAMESPACE") ) + { + // server does not support namespaces + TQString nsentry = TQString::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 TQString & aStr) +{ + //kdDebug(7116) << "Writing: " << aStr << endl; + TQCString 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); +} + +TQString +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 (TQString()); + const TQString encodedUrl = aURL.url(0, 106); // utf-8 + doListEntry(encodedUrl, stretch, cache, withFlags, withSubject); +} + + + +void +IMAP4Protocol::doListEntry (const TQString & encodedUrl, int stretch, imapCache * cache, + bool withFlags, bool withSubject) +{ + if (cache) + { + UDSEntry entry; + UDSAtom atom; + + entry.clear (); + + const TQString uid = TQString::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 = TQString(); + 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 = TDEIO::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 TQString & myBox, + const imapList & item, bool appendPath) +{ + KURL aURL = _url; + aURL.setQuery (TQString()); + UDSEntry entry; + UDSAtom atom; + int hdLen = item.hierarchyDelimiter().length(); + + { + // mailboxName will be appended to the path if appendPath is true + TQString 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 = TQString(); + 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 = TQString(); + 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; + TQString 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, TQString & _box, + TQString & _section, TQString & _type, TQString & _uid, + TQString & _validity, TQString & _hierarchyDelimiter, + TQString & _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 + TQString 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 (TQValueListIterator < 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 TQCString & _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; + } + + TQByteArray 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(TQString 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 + TQByteArray 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; + + TQString 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) memmove(readBuffer, &readBuffer[copyLen], readBufferLen); + return copyLen; + } + if (!isConnectionValid()) return 0; + waitForResponse( responseTimeout() ); + return read(data, len); +} + +bool +IMAP4Protocol::assureBox (const TQString & 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"; + TQString cmdInfo = cmd->resultInfo(); + completeQueue.removeRef (cmd); + + if (!ok) + { + bool found = false; + cmd = doCommand (imapCommand::clientList ("", aBox)); + if (cmd->result () == "OK") + { + for (TQValueListIterator < 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(TDEIO::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( TQDateTime::currentDateTime() ) > 10 ) { + cmd = doCommand (imapCommand::clientNoop ()); + completeQueue.removeRef (cmd); + mTimeOfLastNoop = TQDateTime::currentDateTime(); + kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl; + } + } + + // if it is the mode we want + if (!getSelected().readWrite() && !readonly) + { + error(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox); + return false; + } + + return true; +} diff --git a/tdeioslave/imap4/imapcommand.cc b/tdeioslave/imap4/imapcommand.cc deleted file mode 100644 index e3e68fa8b..000000000 --- a/tdeioslave/imap4/imapcommand.cc +++ /dev/null @@ -1,408 +0,0 @@ -/********************************************************************** - * - * imapcommand.cc - 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 s.carstens@gmx.de - * - *********************************************************************/ - -#include "imapcommand.h" -#include "rfcdecoder.h" - -/*#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include */ - -imapCommand::imapCommand () -{ - mComplete = false; - mId = TQString(); -} - -imapCommand::imapCommand (const TQString & command, const TQString & parameter) -// aCommand(NULL), -// mResult(NULL), -// mParameter(NULL) -{ - mComplete = false; - aCommand = command; - aParameter = parameter; - mId = TQString(); -} - -bool -imapCommand::isComplete () -{ - return mComplete; -} - -const TQString & -imapCommand::result () -{ - return mResult; -} - -const TQString & -imapCommand::resultInfo () -{ - return mResultInfo; -} - -const TQString & -imapCommand::id () -{ - return mId; -} - -const TQString & -imapCommand::parameter () -{ - return aParameter; -} - -const TQString & -imapCommand::command () -{ - return aCommand; -} - -void -imapCommand::setId (const TQString & id) -{ - if (mId.isEmpty ()) - mId = id; -} - -void -imapCommand::setComplete () -{ - mComplete = true; -} - -void -imapCommand::setResult (const TQString & result) -{ - mResult = result; -} - -void -imapCommand::setResultInfo (const TQString & result) -{ - mResultInfo = result; -} - -void -imapCommand::setCommand (const TQString & command) -{ - aCommand = command; -} - -void -imapCommand::setParameter (const TQString & parameter) -{ - aParameter = parameter; -} - -const TQString -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 TQString & fields, bool nouid) -{ - return clientFetch (uid, uid, fields, nouid); -} - -imapCommand * -imapCommand::clientFetch (ulong fromUid, ulong toUid, const TQString & fields, - bool nouid) -{ - TQString uid = TQString::number(fromUid); - - if (fromUid != toUid) - { - uid += ":"; - if (toUid < fromUid) - uid += "*"; - else - uid += TQString::number(toUid); - } - return clientFetch (uid, fields, nouid); -} - -imapCommand * -imapCommand::clientFetch (const TQString & sequence, const TQString & fields, - bool nouid) -{ - return new imapCommand (nouid ? "FETCH" : "UID FETCH", - sequence + " (" + fields + ")"); -} - -imapCommand * -imapCommand::clientList (const TQString & reference, const TQString & path, - bool lsub) -{ - return new imapCommand (lsub ? "LSUB" : "LIST", - TQString ("\"") + rfcDecoder::toIMAP (reference) + - "\" \"" + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientSelect (const TQString & 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", - TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientClose() -{ - return new imapCommand("CLOSE", ""); -} - -imapCommand * -imapCommand::clientCopy (const TQString & box, const TQString & sequence, - bool nouid) -{ - return new imapCommand (nouid ? "COPY" : "UID COPY", - sequence + " \"" + rfcDecoder::toIMAP (box) + "\""); -} - -imapCommand * -imapCommand::clientAppend (const TQString & box, const TQString & flags, - ulong size) -{ - return new imapCommand ("APPEND", - "\"" + rfcDecoder::toIMAP (box) + "\" " + - ((flags.isEmpty()) ? "" : ("(" + flags + ") ")) + - "{" + TQString::number(size) + "}"); -} - -imapCommand * -imapCommand::clienStatus (const TQString & path, const TQString & parameters) -{ - return new imapCommand ("STATUS", - TQString ("\"") + rfcDecoder::toIMAP (path) + - "\" (" + parameters + ")"); -} - -imapCommand * -imapCommand::clientCreate (const TQString & path) -{ - return new imapCommand ("CREATE", - TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientDelete (const TQString & path) -{ - return new imapCommand ("DELETE", - TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientSubscribe (const TQString & path) -{ - return new imapCommand ("SUBSCRIBE", - TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientUnsubscribe (const TQString & path) -{ - return new imapCommand ("UNSUBSCRIBE", - TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); -} - -imapCommand * -imapCommand::clientExpunge () -{ - return new imapCommand ("EXPUNGE", TQString ("")); -} - -imapCommand * -imapCommand::clientRename (const TQString & src, const TQString & dest) -{ - return new imapCommand ("RENAME", - TQString ("\"") + rfcDecoder::toIMAP (src) + - "\" \"" + rfcDecoder::toIMAP (dest) + "\""); -} - -imapCommand * -imapCommand::clientSearch (const TQString & search, bool nouid) -{ - return new imapCommand (nouid ? "SEARCH" : "UID SEARCH", search); -} - -imapCommand * -imapCommand::clientStore (const TQString & set, const TQString & item, - const TQString & 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 TQString& box, const TQString& user, const TQString& acl ) -{ - return new imapCommand ("SETACL", TQString("\"") + rfcDecoder::toIMAP (box) - + "\" \"" + rfcDecoder::toIMAP (user) - + "\" \"" + rfcDecoder::toIMAP (acl) + "\""); -} - -imapCommand * -imapCommand::clientDeleteACL( const TQString& box, const TQString& user ) -{ - return new imapCommand ("DELETEACL", TQString("\"") + rfcDecoder::toIMAP (box) - + "\" \"" + rfcDecoder::toIMAP (user) - + "\""); -} - -imapCommand * -imapCommand::clientGetACL( const TQString& box ) -{ - return new imapCommand ("GETACL", TQString("\"") + rfcDecoder::toIMAP (box) - + "\""); -} - -imapCommand * -imapCommand::clientListRights( const TQString& box, const TQString& user ) -{ - return new imapCommand ("LISTRIGHTS", TQString("\"") + rfcDecoder::toIMAP (box) - + "\" \"" + rfcDecoder::toIMAP (user) - + "\""); -} - -imapCommand * -imapCommand::clientMyRights( const TQString& box ) -{ - return new imapCommand ("MYRIGHTS", TQString("\"") + rfcDecoder::toIMAP (box) - + "\""); -} - -imapCommand * -imapCommand::clientSetAnnotation( const TQString& box, const TQString& entry, const TQMap& attributes ) -{ - TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) - + "\" \"" + rfcDecoder::toIMAP (entry) - + "\" ("; - for( TQMap::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 TQString& box, const TQString& entry, const TQStringList& attributeNames ) -{ - TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) - + "\" \"" + rfcDecoder::toIMAP (entry) - + "\" "; - if ( attributeNames.count() == 1 ) - parameter += "\"" + rfcDecoder::toIMAP (attributeNames.first()) + '"'; - else { - parameter += '('; - for( TQStringList::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 TQString& box ) -{ - TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) + '"'; - return new imapCommand ("GETQUOTAROOT", parameter); -} - -imapCommand * -imapCommand::clientCustom( const TQString& command, const TQString& arguments ) -{ - return new imapCommand (command, arguments); -} - diff --git a/tdeioslave/imap4/imapcommand.cpp b/tdeioslave/imap4/imapcommand.cpp new file mode 100644 index 000000000..97d0a91c6 --- /dev/null +++ b/tdeioslave/imap4/imapcommand.cpp @@ -0,0 +1,408 @@ +/********************************************************************** + * + * imapcommand.cpp - 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 s.carstens@gmx.de + * + *********************************************************************/ + +#include "imapcommand.h" +#include "rfcdecoder.h" + +/*#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include */ + +imapCommand::imapCommand () +{ + mComplete = false; + mId = TQString(); +} + +imapCommand::imapCommand (const TQString & command, const TQString & parameter) +// aCommand(NULL), +// mResult(NULL), +// mParameter(NULL) +{ + mComplete = false; + aCommand = command; + aParameter = parameter; + mId = TQString(); +} + +bool +imapCommand::isComplete () +{ + return mComplete; +} + +const TQString & +imapCommand::result () +{ + return mResult; +} + +const TQString & +imapCommand::resultInfo () +{ + return mResultInfo; +} + +const TQString & +imapCommand::id () +{ + return mId; +} + +const TQString & +imapCommand::parameter () +{ + return aParameter; +} + +const TQString & +imapCommand::command () +{ + return aCommand; +} + +void +imapCommand::setId (const TQString & id) +{ + if (mId.isEmpty ()) + mId = id; +} + +void +imapCommand::setComplete () +{ + mComplete = true; +} + +void +imapCommand::setResult (const TQString & result) +{ + mResult = result; +} + +void +imapCommand::setResultInfo (const TQString & result) +{ + mResultInfo = result; +} + +void +imapCommand::setCommand (const TQString & command) +{ + aCommand = command; +} + +void +imapCommand::setParameter (const TQString & parameter) +{ + aParameter = parameter; +} + +const TQString +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 TQString & fields, bool nouid) +{ + return clientFetch (uid, uid, fields, nouid); +} + +imapCommand * +imapCommand::clientFetch (ulong fromUid, ulong toUid, const TQString & fields, + bool nouid) +{ + TQString uid = TQString::number(fromUid); + + if (fromUid != toUid) + { + uid += ":"; + if (toUid < fromUid) + uid += "*"; + else + uid += TQString::number(toUid); + } + return clientFetch (uid, fields, nouid); +} + +imapCommand * +imapCommand::clientFetch (const TQString & sequence, const TQString & fields, + bool nouid) +{ + return new imapCommand (nouid ? "FETCH" : "UID FETCH", + sequence + " (" + fields + ")"); +} + +imapCommand * +imapCommand::clientList (const TQString & reference, const TQString & path, + bool lsub) +{ + return new imapCommand (lsub ? "LSUB" : "LIST", + TQString ("\"") + rfcDecoder::toIMAP (reference) + + "\" \"" + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientSelect (const TQString & 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", + TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientClose() +{ + return new imapCommand("CLOSE", ""); +} + +imapCommand * +imapCommand::clientCopy (const TQString & box, const TQString & sequence, + bool nouid) +{ + return new imapCommand (nouid ? "COPY" : "UID COPY", + sequence + " \"" + rfcDecoder::toIMAP (box) + "\""); +} + +imapCommand * +imapCommand::clientAppend (const TQString & box, const TQString & flags, + ulong size) +{ + return new imapCommand ("APPEND", + "\"" + rfcDecoder::toIMAP (box) + "\" " + + ((flags.isEmpty()) ? "" : ("(" + flags + ") ")) + + "{" + TQString::number(size) + "}"); +} + +imapCommand * +imapCommand::clienStatus (const TQString & path, const TQString & parameters) +{ + return new imapCommand ("STATUS", + TQString ("\"") + rfcDecoder::toIMAP (path) + + "\" (" + parameters + ")"); +} + +imapCommand * +imapCommand::clientCreate (const TQString & path) +{ + return new imapCommand ("CREATE", + TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientDelete (const TQString & path) +{ + return new imapCommand ("DELETE", + TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientSubscribe (const TQString & path) +{ + return new imapCommand ("SUBSCRIBE", + TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientUnsubscribe (const TQString & path) +{ + return new imapCommand ("UNSUBSCRIBE", + TQString ("\"") + rfcDecoder::toIMAP (path) + "\""); +} + +imapCommand * +imapCommand::clientExpunge () +{ + return new imapCommand ("EXPUNGE", TQString ("")); +} + +imapCommand * +imapCommand::clientRename (const TQString & src, const TQString & dest) +{ + return new imapCommand ("RENAME", + TQString ("\"") + rfcDecoder::toIMAP (src) + + "\" \"" + rfcDecoder::toIMAP (dest) + "\""); +} + +imapCommand * +imapCommand::clientSearch (const TQString & search, bool nouid) +{ + return new imapCommand (nouid ? "SEARCH" : "UID SEARCH", search); +} + +imapCommand * +imapCommand::clientStore (const TQString & set, const TQString & item, + const TQString & 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 TQString& box, const TQString& user, const TQString& acl ) +{ + return new imapCommand ("SETACL", TQString("\"") + rfcDecoder::toIMAP (box) + + "\" \"" + rfcDecoder::toIMAP (user) + + "\" \"" + rfcDecoder::toIMAP (acl) + "\""); +} + +imapCommand * +imapCommand::clientDeleteACL( const TQString& box, const TQString& user ) +{ + return new imapCommand ("DELETEACL", TQString("\"") + rfcDecoder::toIMAP (box) + + "\" \"" + rfcDecoder::toIMAP (user) + + "\""); +} + +imapCommand * +imapCommand::clientGetACL( const TQString& box ) +{ + return new imapCommand ("GETACL", TQString("\"") + rfcDecoder::toIMAP (box) + + "\""); +} + +imapCommand * +imapCommand::clientListRights( const TQString& box, const TQString& user ) +{ + return new imapCommand ("LISTRIGHTS", TQString("\"") + rfcDecoder::toIMAP (box) + + "\" \"" + rfcDecoder::toIMAP (user) + + "\""); +} + +imapCommand * +imapCommand::clientMyRights( const TQString& box ) +{ + return new imapCommand ("MYRIGHTS", TQString("\"") + rfcDecoder::toIMAP (box) + + "\""); +} + +imapCommand * +imapCommand::clientSetAnnotation( const TQString& box, const TQString& entry, const TQMap& attributes ) +{ + TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) + + "\" \"" + rfcDecoder::toIMAP (entry) + + "\" ("; + for( TQMap::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 TQString& box, const TQString& entry, const TQStringList& attributeNames ) +{ + TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) + + "\" \"" + rfcDecoder::toIMAP (entry) + + "\" "; + if ( attributeNames.count() == 1 ) + parameter += "\"" + rfcDecoder::toIMAP (attributeNames.first()) + '"'; + else { + parameter += '('; + for( TQStringList::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 TQString& box ) +{ + TQString parameter = TQString("\"") + rfcDecoder::toIMAP (box) + '"'; + return new imapCommand ("GETQUOTAROOT", parameter); +} + +imapCommand * +imapCommand::clientCustom( const TQString& command, const TQString& arguments ) +{ + return new imapCommand (command, arguments); +} + diff --git a/tdeioslave/imap4/imapinfo.cc b/tdeioslave/imap4/imapinfo.cc deleted file mode 100644 index 3e21ee772..000000000 --- a/tdeioslave/imap4/imapinfo.cc +++ /dev/null @@ -1,239 +0,0 @@ -/********************************************************************** - * - * 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 - -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 TQStringList & 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 (TQStringList::ConstIterator it (list.begin ()); it != list.end (); ++it) - { - TQString line (*it); - - line.truncate(line.length() - 2); - TQStringList tokens(TQStringList::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 TQCString & inFlags) -{ - ulong flags = 0; - parseString flagsString; - flagsString.data.duplicate(inFlags.data(), inFlags.length()); - if ( flagsString.isEmpty() ) { - return flags; - } - - if (flagsString[0] == '(') - flagsString.pos++; - - while (!flagsString.isEmpty () && flagsString[0] != ')') - { - TQCString 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/tdeioslave/imap4/imapinfo.cpp b/tdeioslave/imap4/imapinfo.cpp new file mode 100644 index 000000000..453808e71 --- /dev/null +++ b/tdeioslave/imap4/imapinfo.cpp @@ -0,0 +1,239 @@ +/********************************************************************** + * + * imapinfo.cpp - 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 + +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 TQStringList & 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 (TQStringList::ConstIterator it (list.begin ()); it != list.end (); ++it) + { + TQString line (*it); + + line.truncate(line.length() - 2); + TQStringList tokens(TQStringList::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 TQCString & inFlags) +{ + ulong flags = 0; + parseString flagsString; + flagsString.data.duplicate(inFlags.data(), inFlags.length()); + if ( flagsString.isEmpty() ) { + return flags; + } + + if (flagsString[0] == '(') + flagsString.pos++; + + while (!flagsString.isEmpty () && flagsString[0] != ')') + { + TQCString 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/tdeioslave/imap4/imaplist.cc b/tdeioslave/imap4/imaplist.cc deleted file mode 100644 index f9113bdab..000000000 --- a/tdeioslave/imap4/imaplist.cc +++ /dev/null @@ -1,135 +0,0 @@ -/********************************************************************** - * - * 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 - -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 TQString & 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_ = TQString(); - name_ = rfcDecoder::fromIMAP (parser_->parseLiteral (s)); // decode modified UTF7 -} - -void imapList::parseAttributes( parseString & str ) -{ - TQCString 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/tdeioslave/imap4/imaplist.cpp b/tdeioslave/imap4/imaplist.cpp new file mode 100644 index 000000000..51062c474 --- /dev/null +++ b/tdeioslave/imap4/imaplist.cpp @@ -0,0 +1,135 @@ +/********************************************************************** + * + * imapinfo.cpp - 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 + +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 TQString & 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_ = TQString(); + name_ = rfcDecoder::fromIMAP (parser_->parseLiteral (s)); // decode modified UTF7 +} + +void imapList::parseAttributes( parseString & str ) +{ + TQCString 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/tdeioslave/imap4/imapparser.cc b/tdeioslave/imap4/imapparser.cc deleted file mode 100644 index eb154cc5d..000000000 --- a/tdeioslave/imap4/imapparser.cc +++ /dev/null @@ -1,2094 +0,0 @@ -/********************************************************************** - * - * imapparser.cc - IMAP4rev1 Parser - * Copyright (C) 2001-2002 Michael Haeckel - * 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 -#endif - -#include "rfcdecoder.h" - -#include "imapparser.h" - -#include "imapinfo.h" - -#include "mailheader.h" -#include "mimeheader.h" -#include "mailaddress.h" - -#include - -#include -#include - -#ifdef HAVE_LIBSASL2 -extern "C" { -#include -} -#endif - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#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 (TQString::number(commandCounter++)); - sentQueue.append (aCmd); - - continuation.resize(0); - const TQString& 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 = TQString(); - } - 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 TQString & aUser, const TQString & aPass, - TQString & 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( TDEIO::SlaveBase *slave, TDEIO::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 ( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai, - const TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & 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; - TQByteArray tmp, challenge; - - kdDebug(7116) << "aAuth: " << aAuth << " FTQDN: " << aFTQDN << " 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. */ - aFTQDN.latin1(), - 0, 0, callbacks, 0, &conn ); - - if ( result != SASL_OK ) { - kdDebug(7116) << "sasl_client_new failed with: " << result << endl; - resultInfo = TQString::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 = TQString::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 - TQString firstCommand = aAuth; - if ( !challenge.isEmpty() ) { - firstCommand += " "; - firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() ); - } - cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1())); - - int pl = 0; - while ( pl != -1 && !cmd->isComplete () ) - { - //read the next line - while ((pl = parseLoop()) == 0) ; - - if (!continuation.isEmpty()) - { -// kdDebug(7116) << "S: " << TQCString(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: " << TQCString(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 = TQString::fromUtf8( sasl_errdetail( conn ) ); - sasl_dispose( &conn ); - return false; - } - - tmp.setRawData( out, outlen ); -// kdDebug(7116) << "C-1: " << TQCString(tmp.data(),tmp.size()+1) << endl; - KCodecs::base64Encode( tmp, challenge ); - tmp.resetRawData( out, outlen ); -// kdDebug(7116) << "C: " << TQCString(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); // * - TQByteArray what = parseLiteral (result); // see whats coming next - - if(!what.isEmpty ()) { - switch (what[0]) - { - //the status responses - case 'B': // BAD or BYE - if (tqstrncmp(what, "BAD", what.size()) == 0) - { - parseResult (what, result); - } - else if (tqstrncmp(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 (tqstrncmp(what, "NAMESPACE", what.size()) == 0) - { - parseNamespace (result); - } - break; - - case 'O': // OK - if (what[1] == 'K' && what.size() == 2) - { - parseResult (what, result); - } else if (tqstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER - parseOtherUser (result); - } else if (tqstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE - parseOutOfOffice (result); - } - break; - case 'D': - if (tqstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES - parseDelegate (result); - } - break; - - case 'P': // PREAUTH - if (tqstrncmp(what, "PREAUTH", what.size()) == 0) - { - parseResult (what, result); - currentState = ISTATE_LOGIN; - } - break; - - // parse the other responses - case 'C': // CAPABILITY - if (tqstrncmp(what, "CAPABILITY", what.size()) == 0) - { - parseCapability (result); - } - break; - - case 'F': // FLAGS - if (tqstrncmp(what, "FLAGS", what.size()) == 0) - { - parseFlags (result); - } - break; - - case 'L': // LIST or LSUB or LISTRIGHTS - if (tqstrncmp(what, "LIST", what.size()) == 0) - { - parseList (result); - } - else if (tqstrncmp(what, "LSUB", what.size()) == 0) - { - parseLsub (result); - } - else if (tqstrncmp(what, "LISTRIGHTS", what.size()) == 0) - { - parseListRights (result); - } - break; - - case 'M': // MYRIGHTS - if (tqstrncmp(what, "MYRIGHTS", what.size()) == 0) - { - parseMyRights (result); - } - break; - case 'S': // SEARCH or STATUS - if (tqstrncmp(what, "SEARCH", what.size()) == 0) - { - parseSearch (result); - } - else if (tqstrncmp(what, "STATUS", what.size()) == 0) - { - parsetStatus (result); - } - break; - - case 'A': // ACL or ANNOTATION - if (tqstrncmp(what, "ACL", what.size()) == 0) - { - parseAcl (result); - } - else if (tqstrncmp(what, "ANNOTATION", what.size()) == 0) - { - parseAnnotation (result); - } - break; - case 'Q': // QUOTA or QUOTAROOT - if ( what.size() > 5 && tqstrncmp(what, "QUOTAROOT", what.size()) == 0) - { - parseQuotaRoot( result ); - } - else if (tqstrncmp(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 = TQCString(what, what.size() + 1).toUInt(&valid); - if (valid) - { - what = parseLiteral (result); - if(!what.isEmpty ()) { - switch (what[0]) - { - case 'E': - if (tqstrncmp(what, "EXISTS", what.size()) == 0) - { - parseExists (number, result); - } - else if (tqstrncmp(what, "EXPUNGE", what.size()) == 0) - { - parseExpunge (number, result); - } - break; - - case 'F': - if (tqstrncmp(what, "FETCH", what.size()) == 0) - { - seenUid = TQString(); - parseFetch (number, result); - } - break; - - case 'S': - if (tqstrncmp(what, "STORE", what.size()) == 0) // deprecated store - { - seenUid = TQString(); - parseFetch (number, result); - } - break; - - case 'R': - if (tqstrncmp(what, "RECENT", what.size()) == 0) - { - parseRecent (number, result); - } - break; - default: - break; - } - } - } - } - break; - } //switch - } -} //func - - -void -imapParser::parseResult (TQByteArray & result, parseString & rest, - const TQString & command) -{ - if (command == "SELECT") - selectInfo.setReadWrite(true); - - if (rest[0] == '[') - { - rest.pos++; - TQCString 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); - TQCString 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 (tqstrncmp(result, "OK", result.size()) == 0) - currentState = ISTATE_LOGIN; - break; - - case 'L': - if (command == "LOGIN") - if (tqstrncmp(result, "OK", result.size()) == 0) - currentState = ISTATE_LOGIN; - break; - - case 'E': - if (command == "EXAMINE") - { - if (tqstrncmp(result, "OK", result.size()) == 0) - currentState = ISTATE_SELECT; - else - { - if (currentState == ISTATE_SELECT) - currentState = ISTATE_LOGIN; - currentBox = TQString(); - } - kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; - } - break; - - case 'S': - if (command == "SELECT") - { - if (tqstrncmp(result, "OK", result.size()) == 0) - currentState = ISTATE_SELECT; - else - { - if (currentState == ISTATE_SELECT) - currentState = ISTATE_LOGIN; - currentBox = TQString(); - } - kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; - } - break; - - default: - break; - } - -} - -void imapParser::parseCapability (parseString & result) -{ - TQCString temp( result.cstr() ); - imapCapabilities = TQStringList::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 ) { - TQCString 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() ) { - TQCString 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] != ')' ) { - TQCString 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 - TQCString root = parseOneWordC( result ); - if ( root.isEmpty() ) { - lastResults.append( "" ); - } else { - lastResults.append( root ); - } - if (result.isEmpty() || result[0] != '(') - return; - result.pos++; - skipWS (result); - TQStringList triplet; - int outlen = 1; - while ( outlen && !result.isEmpty() && result[0] != ')' ) { - TQCString 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; - TQStringList roots; - int outlen = 1; - while ( outlen && !result.isEmpty() ) { - TQCString word = parseLiteralC (result, false, false, &outlen); - roots.append (word); - } - lastResults.append( roots.isEmpty()? "" : roots.join(" ") ); -} - -void imapParser::parseCustom (parseString & result) -{ - int outlen = 1; - TQCString word = parseLiteralC (result, false, false, &outlen); - lastResults.append( word ); -} - -void imapParser::parseOtherUser (parseString & result) -{ - lastResults.append( parseOneWordC( result ) ); -} - -void imapParser::parseDelegate (parseString & result) -{ - const TQString email = parseOneWordC( result ); - - TQStringList rights; - int outlen = 1; - while ( outlen && !result.isEmpty() ) { - TQCString word = parseLiteralC( result, false, false, &outlen ); - rights.append( word ); - } - - lastResults.append( email + ':' + rights.join( "," ) ); -} - -void imapParser::parseOutOfOffice (parseString & result) -{ - const TQString state = parseOneWordC (result); - parseOneWordC (result); // skip encoding - - int outlen = 1; - TQCString msg = parseLiteralC (result, false, false, &outlen); - - lastResults.append( state + '^' + TQString::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 (TQString::number(value)); - } -} - -void imapParser::parsetStatus (parseString & inWords) -{ - lasStatus = imapInfo (); - - parseLiteralC(inWords); // swallow the box - if (inWords.isEmpty() || inWords[0] != '(') - return; - - inWords.pos++; - skipWS (inWords); - - while (!inWords.isEmpty() && inWords[0] != ')') - { - ulong value; - - TQCString label = parseOneWordC(inWords); - if (parseOneNumber (inWords, value)) - { - if (label == "MESSAGES") - lasStatus.setCount (value); - else if (label == "RECENT") - lasStatus.setRecent (value); - else if (label == "UIDVALIDITY") - lasStatus.setUidValidity (value); - else if (label == "UNSEEN") - lasStatus.setUnseen (value); - else if (label == "UIDNEXT") - lasStatus.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, TQPtrList& 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)); - - TQPtrList 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 -TQAsciiDict < TQString > imapParser::parseDisposition (parseString & inWords) -{ - TQCString disposition; - TQAsciiDict < TQString > 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 TQString(disposition)); - } - - return retVal; -} - -// parse parameter pairs into a dictionary -// caller must clean up the dictionary items -TQAsciiDict < TQString > imapParser::parseParameters (parseString & inWords) -{ - TQAsciiDict < TQString > 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] != ')') - { - TQCString l1 = parseLiteralC(inWords); - TQCString l2 = parseLiteralC(inWords); - retVal.insert (l1, new TQString(l2)); - } - - if (inWords[0] != ')') - return retVal; - inWords.pos++; - skipWS (inWords); - } - - return retVal; -} - -mimeHeader * imapParser::parseSimplePart (parseString & inWords, - TQString & inSection, mimeHeader * localPart) -{ - TQCString subtype; - TQCString typeStr; - TQAsciiDict < TQString > 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); - { - TQAsciiDictIterator < TQString > 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); - { - TQString *disposition = parameters["content-disposition"]; - - if (disposition) - localPart->setDisposition (disposition->ascii ()); - parameters.remove ("content-disposition"); - TQAsciiDictIterator < TQString > 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, - TQString & 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] == '(') - { - TQByteArray subtype; - TQAsciiDict < TQString > parameters (17, false); - TQString 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 it is a simplepart and handled later) - while (inWords[0] == '(') - { - outSection = TQString::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); - { - TQAsciiDictIterator < TQString > it (parameters); - - while (it.current ()) - { - localPart->setTypeParm (it.currentKey (), *(it.current ())); - ++it; - } - parameters.clear (); - } - - // body disposition - parameters = parseDisposition (inWords); - { - TQString *disposition = parameters["content-disposition"]; - - if (disposition) - localPart->setDisposition (disposition->ascii ()); - parameters.remove ("content-disposition"); - TQAsciiDictIterator < TQString > 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] == '[') - { - TQCString specifier; - TQCString label; - inWords.pos++; - - specifier = parseOneWordC (inWords, TRUE); - - if (inWords[0] == '(') - { - inWords.pos++; - - while (!inWords.isEmpty () && inWords[0] != ')') - { - label = parseOneWordC (inWords); - } - - if (!inWords.isEmpty () && inWords[0] == ')') - inWords.pos++; - } - if (!inWords.isEmpty () && 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 - TQString theHeader = parseLiteralC(inWords, true); - mimeIOTQString myIO; - - myIO.setString (theHeader); - envelope->parseHeader (myIO); - - } - } - else if (specifier == "HEADER.FIELDS") - { - // BODY[HEADER.FIELDS (References)] {n} - //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: " - // << TQCString(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 - { - TQCString 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; - TQString theHeader = parseLiteralC(inWords, false); - mimeIOTQString 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 - TQString 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 - { - TQCString word = parseLiteralC(inWords, false, true); - - if(!word.isEmpty()) { - 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 - TQString section; - mimeHeader *body = - parseBodyStructure (inWords, section, envelope); - TQByteArray data; - TQDataStream 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") - { - TQCString 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; - } - } else { - 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] != ')') - 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; - - TQString delimEmpty; - if ( namespaceToDelimiter.contains( TQString() ) ) - delimEmpty = namespaceToDelimiter[TQString()]; - - 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 - TQCString prefix = parseOneWordC( result ); - // delimiter - TQCString delim = parseOneWordC( result ); - kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix << - "',delim='" << delim << "'" << endl; - if ( ns == 0 ) - { - // at least one personal ns - personalAvailable = true; - } - TQString nsentry = TQString::number( ns ) + "=" + TQString(prefix) + - "=" + TQString(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[TQString()] = delimEmpty; - if ( !personalAvailable ) - { - // at least one personal ns would be nice - kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl; - TQString 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: - { - TQCString tag = parseLiteralC(result); - if (current->id() == tag.data()) - { - result.data.resize(result.data.size() - 2); // tie off CRLF - TQByteArray 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; - TQCString cstr = tag + " " + result.cstr(); - result.data = cstr; - result.pos = 0; - result.data.resize(cstr.length()); - } - } - break; - } - } - - return 1; -} - -void -imapParser::parseRelay (const TQByteArray & buffer) -{ - Q_UNUSED(buffer); - tqWarning - ("imapParser::parseRelay - virtual function not reimplemented - data lost"); -} - -void -imapParser::parseRelay (ulong len) -{ - Q_UNUSED(len); - tqWarning - ("imapParser::parseRelay - virtual function not reimplemented - announcement lost"); -} - -bool imapParser::parseRead (TQByteArray & buffer, ulong len, ulong relay) -{ - Q_UNUSED(buffer); - Q_UNUSED(len); - Q_UNUSED(relay); - tqWarning - ("imapParser::parseRead - virtual function not reimplemented - no data read"); - return FALSE; -} - -bool imapParser::parseReadLine (TQByteArray & buffer, ulong relay) -{ - Q_UNUSED(buffer); - Q_UNUSED(relay); - tqWarning - ("imapParser::parseReadLine - virtual function not reimplemented - no data read"); - return FALSE; -} - -void -imapParser::parseWriteLine (const TQString & str) -{ - Q_UNUSED(str); - tqWarning - ("imapParser::parseWriteLine - virtual function not reimplemented - no data written"); -} - -void -imapParser::parseURL (const KURL & _url, TQString & _box, TQString & _section, - TQString & _type, TQString & _uid, TQString & _validity, TQString & _info) -{ - TQStringList parameters; - - _box = _url.path (); - kdDebug(7116) << "imapParser::parseURL " << _box << endl; - int paramStart = _box.find("/;"); - if ( paramStart > -1 ) - { - TQString paramString = _box.right( _box.length() - paramStart-2 ); - parameters = TQStringList::split (';', paramString); //split parameters - _box.truncate( paramStart ); // strip parameters - } - // extract parameters - for (TQStringList::ConstIterator it (parameters.begin ()); - it != parameters.end (); ++it) - { - TQString 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; -} - - -TQCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) { - - if (!inWords.isEmpty() && inWords[0] == '{') - { - TQCString retVal; - long srunLen = inWords.find ('}', 1); // Can return -1, so use a signed long - if (srunLen > 0) - { - ulong runLen = (ulong)srunLen; - bool proper; - ulong runLenSave = runLen + 1; - TQCString 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); - TQByteArray rv; - parseRead (rv, runLen, relay ? runLen : 0); - rv.resize(TQMAX(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 ) -TQCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen) -{ - uint retValSize = 0; - uint len = inWords.length(); - if (len == 0) { - return TQCString(); - } - - 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) - { - TQCString 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; - TQCString 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; - } - - TQCString 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 TQString & cap) -{ - TQString c = cap.lower(); -// kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl; - for (TQStringList::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 TQString & cap) -{ - imapCapabilities.remove(cap.lower()); -} - -TQString imapParser::namespaceForBox( const TQString & box ) -{ - kdDebug(7116) << "imapParse::namespaceForBox " << box << endl; - TQString myNamespace; - if ( !box.isEmpty() ) - { - TQValueList list = namespaceToDelimiter.keys(); - TQString cleanPrefix; - for ( TQValueList::Iterator it = list.begin(); it != list.end(); ++it ) - { - if ( !(*it).isEmpty() && box.find( *it ) != -1 ) - return (*it); - } - } - return myNamespace; -} - diff --git a/tdeioslave/imap4/imapparser.cpp b/tdeioslave/imap4/imapparser.cpp new file mode 100644 index 000000000..d833b639e --- /dev/null +++ b/tdeioslave/imap4/imapparser.cpp @@ -0,0 +1,2094 @@ +/********************************************************************** + * + * imapparser.cpp - IMAP4rev1 Parser + * Copyright (C) 2001-2002 Michael Haeckel + * 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 +#endif + +#include "rfcdecoder.h" + +#include "imapparser.h" + +#include "imapinfo.h" + +#include "mailheader.h" +#include "mimeheader.h" +#include "mailaddress.h" + +#include + +#include +#include + +#ifdef HAVE_LIBSASL2 +extern "C" { +#include +} +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 (TQString::number(commandCounter++)); + sentQueue.append (aCmd); + + continuation.resize(0); + const TQString& 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 = TQString(); + } + 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 TQString & aUser, const TQString & aPass, + TQString & 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( TDEIO::SlaveBase *slave, TDEIO::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 ( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai, + const TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & 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; + TQByteArray tmp, challenge; + + kdDebug(7116) << "aAuth: " << aAuth << " FTQDN: " << aFTQDN << " 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. */ + aFTQDN.latin1(), + 0, 0, callbacks, 0, &conn ); + + if ( result != SASL_OK ) { + kdDebug(7116) << "sasl_client_new failed with: " << result << endl; + resultInfo = TQString::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 = TQString::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 + TQString firstCommand = aAuth; + if ( !challenge.isEmpty() ) { + firstCommand += " "; + firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() ); + } + cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1())); + + int pl = 0; + while ( pl != -1 && !cmd->isComplete () ) + { + //read the next line + while ((pl = parseLoop()) == 0) ; + + if (!continuation.isEmpty()) + { +// kdDebug(7116) << "S: " << TQCString(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: " << TQCString(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 = TQString::fromUtf8( sasl_errdetail( conn ) ); + sasl_dispose( &conn ); + return false; + } + + tmp.setRawData( out, outlen ); +// kdDebug(7116) << "C-1: " << TQCString(tmp.data(),tmp.size()+1) << endl; + KCodecs::base64Encode( tmp, challenge ); + tmp.resetRawData( out, outlen ); +// kdDebug(7116) << "C: " << TQCString(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); // * + TQByteArray what = parseLiteral (result); // see whats coming next + + if(!what.isEmpty ()) { + switch (what[0]) + { + //the status responses + case 'B': // BAD or BYE + if (tqstrncmp(what, "BAD", what.size()) == 0) + { + parseResult (what, result); + } + else if (tqstrncmp(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 (tqstrncmp(what, "NAMESPACE", what.size()) == 0) + { + parseNamespace (result); + } + break; + + case 'O': // OK + if (what[1] == 'K' && what.size() == 2) + { + parseResult (what, result); + } else if (tqstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER + parseOtherUser (result); + } else if (tqstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE + parseOutOfOffice (result); + } + break; + case 'D': + if (tqstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES + parseDelegate (result); + } + break; + + case 'P': // PREAUTH + if (tqstrncmp(what, "PREAUTH", what.size()) == 0) + { + parseResult (what, result); + currentState = ISTATE_LOGIN; + } + break; + + // parse the other responses + case 'C': // CAPABILITY + if (tqstrncmp(what, "CAPABILITY", what.size()) == 0) + { + parseCapability (result); + } + break; + + case 'F': // FLAGS + if (tqstrncmp(what, "FLAGS", what.size()) == 0) + { + parseFlags (result); + } + break; + + case 'L': // LIST or LSUB or LISTRIGHTS + if (tqstrncmp(what, "LIST", what.size()) == 0) + { + parseList (result); + } + else if (tqstrncmp(what, "LSUB", what.size()) == 0) + { + parseLsub (result); + } + else if (tqstrncmp(what, "LISTRIGHTS", what.size()) == 0) + { + parseListRights (result); + } + break; + + case 'M': // MYRIGHTS + if (tqstrncmp(what, "MYRIGHTS", what.size()) == 0) + { + parseMyRights (result); + } + break; + case 'S': // SEARCH or STATUS + if (tqstrncmp(what, "SEARCH", what.size()) == 0) + { + parseSearch (result); + } + else if (tqstrncmp(what, "STATUS", what.size()) == 0) + { + parsetStatus (result); + } + break; + + case 'A': // ACL or ANNOTATION + if (tqstrncmp(what, "ACL", what.size()) == 0) + { + parseAcl (result); + } + else if (tqstrncmp(what, "ANNOTATION", what.size()) == 0) + { + parseAnnotation (result); + } + break; + case 'Q': // QUOTA or QUOTAROOT + if ( what.size() > 5 && tqstrncmp(what, "QUOTAROOT", what.size()) == 0) + { + parseQuotaRoot( result ); + } + else if (tqstrncmp(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 = TQCString(what, what.size() + 1).toUInt(&valid); + if (valid) + { + what = parseLiteral (result); + if(!what.isEmpty ()) { + switch (what[0]) + { + case 'E': + if (tqstrncmp(what, "EXISTS", what.size()) == 0) + { + parseExists (number, result); + } + else if (tqstrncmp(what, "EXPUNGE", what.size()) == 0) + { + parseExpunge (number, result); + } + break; + + case 'F': + if (tqstrncmp(what, "FETCH", what.size()) == 0) + { + seenUid = TQString(); + parseFetch (number, result); + } + break; + + case 'S': + if (tqstrncmp(what, "STORE", what.size()) == 0) // deprecated store + { + seenUid = TQString(); + parseFetch (number, result); + } + break; + + case 'R': + if (tqstrncmp(what, "RECENT", what.size()) == 0) + { + parseRecent (number, result); + } + break; + default: + break; + } + } + } + } + break; + } //switch + } +} //func + + +void +imapParser::parseResult (TQByteArray & result, parseString & rest, + const TQString & command) +{ + if (command == "SELECT") + selectInfo.setReadWrite(true); + + if (rest[0] == '[') + { + rest.pos++; + TQCString 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); + TQCString 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 (tqstrncmp(result, "OK", result.size()) == 0) + currentState = ISTATE_LOGIN; + break; + + case 'L': + if (command == "LOGIN") + if (tqstrncmp(result, "OK", result.size()) == 0) + currentState = ISTATE_LOGIN; + break; + + case 'E': + if (command == "EXAMINE") + { + if (tqstrncmp(result, "OK", result.size()) == 0) + currentState = ISTATE_SELECT; + else + { + if (currentState == ISTATE_SELECT) + currentState = ISTATE_LOGIN; + currentBox = TQString(); + } + kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; + } + break; + + case 'S': + if (command == "SELECT") + { + if (tqstrncmp(result, "OK", result.size()) == 0) + currentState = ISTATE_SELECT; + else + { + if (currentState == ISTATE_SELECT) + currentState = ISTATE_LOGIN; + currentBox = TQString(); + } + kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; + } + break; + + default: + break; + } + +} + +void imapParser::parseCapability (parseString & result) +{ + TQCString temp( result.cstr() ); + imapCapabilities = TQStringList::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 ) { + TQCString 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() ) { + TQCString 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] != ')' ) { + TQCString 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 + TQCString root = parseOneWordC( result ); + if ( root.isEmpty() ) { + lastResults.append( "" ); + } else { + lastResults.append( root ); + } + if (result.isEmpty() || result[0] != '(') + return; + result.pos++; + skipWS (result); + TQStringList triplet; + int outlen = 1; + while ( outlen && !result.isEmpty() && result[0] != ')' ) { + TQCString 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; + TQStringList roots; + int outlen = 1; + while ( outlen && !result.isEmpty() ) { + TQCString word = parseLiteralC (result, false, false, &outlen); + roots.append (word); + } + lastResults.append( roots.isEmpty()? "" : roots.join(" ") ); +} + +void imapParser::parseCustom (parseString & result) +{ + int outlen = 1; + TQCString word = parseLiteralC (result, false, false, &outlen); + lastResults.append( word ); +} + +void imapParser::parseOtherUser (parseString & result) +{ + lastResults.append( parseOneWordC( result ) ); +} + +void imapParser::parseDelegate (parseString & result) +{ + const TQString email = parseOneWordC( result ); + + TQStringList rights; + int outlen = 1; + while ( outlen && !result.isEmpty() ) { + TQCString word = parseLiteralC( result, false, false, &outlen ); + rights.append( word ); + } + + lastResults.append( email + ':' + rights.join( "," ) ); +} + +void imapParser::parseOutOfOffice (parseString & result) +{ + const TQString state = parseOneWordC (result); + parseOneWordC (result); // skip encoding + + int outlen = 1; + TQCString msg = parseLiteralC (result, false, false, &outlen); + + lastResults.append( state + '^' + TQString::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 (TQString::number(value)); + } +} + +void imapParser::parsetStatus (parseString & inWords) +{ + lasStatus = imapInfo (); + + parseLiteralC(inWords); // swallow the box + if (inWords.isEmpty() || inWords[0] != '(') + return; + + inWords.pos++; + skipWS (inWords); + + while (!inWords.isEmpty() && inWords[0] != ')') + { + ulong value; + + TQCString label = parseOneWordC(inWords); + if (parseOneNumber (inWords, value)) + { + if (label == "MESSAGES") + lasStatus.setCount (value); + else if (label == "RECENT") + lasStatus.setRecent (value); + else if (label == "UIDVALIDITY") + lasStatus.setUidValidity (value); + else if (label == "UNSEEN") + lasStatus.setUnseen (value); + else if (label == "UIDNEXT") + lasStatus.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, TQPtrList& 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)); + + TQPtrList 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 +TQAsciiDict < TQString > imapParser::parseDisposition (parseString & inWords) +{ + TQCString disposition; + TQAsciiDict < TQString > 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 TQString(disposition)); + } + + return retVal; +} + +// parse parameter pairs into a dictionary +// caller must clean up the dictionary items +TQAsciiDict < TQString > imapParser::parseParameters (parseString & inWords) +{ + TQAsciiDict < TQString > 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] != ')') + { + TQCString l1 = parseLiteralC(inWords); + TQCString l2 = parseLiteralC(inWords); + retVal.insert (l1, new TQString(l2)); + } + + if (inWords[0] != ')') + return retVal; + inWords.pos++; + skipWS (inWords); + } + + return retVal; +} + +mimeHeader * imapParser::parseSimplePart (parseString & inWords, + TQString & inSection, mimeHeader * localPart) +{ + TQCString subtype; + TQCString typeStr; + TQAsciiDict < TQString > 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); + { + TQAsciiDictIterator < TQString > 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); + { + TQString *disposition = parameters["content-disposition"]; + + if (disposition) + localPart->setDisposition (disposition->ascii ()); + parameters.remove ("content-disposition"); + TQAsciiDictIterator < TQString > 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, + TQString & 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] == '(') + { + TQByteArray subtype; + TQAsciiDict < TQString > parameters (17, false); + TQString 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 it is a simplepart and handled later) + while (inWords[0] == '(') + { + outSection = TQString::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); + { + TQAsciiDictIterator < TQString > it (parameters); + + while (it.current ()) + { + localPart->setTypeParm (it.currentKey (), *(it.current ())); + ++it; + } + parameters.clear (); + } + + // body disposition + parameters = parseDisposition (inWords); + { + TQString *disposition = parameters["content-disposition"]; + + if (disposition) + localPart->setDisposition (disposition->ascii ()); + parameters.remove ("content-disposition"); + TQAsciiDictIterator < TQString > 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] == '[') + { + TQCString specifier; + TQCString label; + inWords.pos++; + + specifier = parseOneWordC (inWords, TRUE); + + if (inWords[0] == '(') + { + inWords.pos++; + + while (!inWords.isEmpty () && inWords[0] != ')') + { + label = parseOneWordC (inWords); + } + + if (!inWords.isEmpty () && inWords[0] == ')') + inWords.pos++; + } + if (!inWords.isEmpty () && 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 + TQString theHeader = parseLiteralC(inWords, true); + mimeIOTQString myIO; + + myIO.setString (theHeader); + envelope->parseHeader (myIO); + + } + } + else if (specifier == "HEADER.FIELDS") + { + // BODY[HEADER.FIELDS (References)] {n} + //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: " + // << TQCString(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 + { + TQCString 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; + TQString theHeader = parseLiteralC(inWords, false); + mimeIOTQString 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 + TQString 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 + { + TQCString word = parseLiteralC(inWords, false, true); + + if(!word.isEmpty()) { + 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 + TQString section; + mimeHeader *body = + parseBodyStructure (inWords, section, envelope); + TQByteArray data; + TQDataStream 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") + { + TQCString 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; + } + } else { + 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] != ')') + 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; + + TQString delimEmpty; + if ( namespaceToDelimiter.contains( TQString() ) ) + delimEmpty = namespaceToDelimiter[TQString()]; + + 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 + TQCString prefix = parseOneWordC( result ); + // delimiter + TQCString delim = parseOneWordC( result ); + kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix << + "',delim='" << delim << "'" << endl; + if ( ns == 0 ) + { + // at least one personal ns + personalAvailable = true; + } + TQString nsentry = TQString::number( ns ) + "=" + TQString(prefix) + + "=" + TQString(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[TQString()] = delimEmpty; + if ( !personalAvailable ) + { + // at least one personal ns would be nice + kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl; + TQString 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: + { + TQCString tag = parseLiteralC(result); + if (current->id() == tag.data()) + { + result.data.resize(result.data.size() - 2); // tie off CRLF + TQByteArray 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; + TQCString cstr = tag + " " + result.cstr(); + result.data = cstr; + result.pos = 0; + result.data.resize(cstr.length()); + } + } + break; + } + } + + return 1; +} + +void +imapParser::parseRelay (const TQByteArray & buffer) +{ + Q_UNUSED(buffer); + tqWarning + ("imapParser::parseRelay - virtual function not reimplemented - data lost"); +} + +void +imapParser::parseRelay (ulong len) +{ + Q_UNUSED(len); + tqWarning + ("imapParser::parseRelay - virtual function not reimplemented - announcement lost"); +} + +bool imapParser::parseRead (TQByteArray & buffer, ulong len, ulong relay) +{ + Q_UNUSED(buffer); + Q_UNUSED(len); + Q_UNUSED(relay); + tqWarning + ("imapParser::parseRead - virtual function not reimplemented - no data read"); + return FALSE; +} + +bool imapParser::parseReadLine (TQByteArray & buffer, ulong relay) +{ + Q_UNUSED(buffer); + Q_UNUSED(relay); + tqWarning + ("imapParser::parseReadLine - virtual function not reimplemented - no data read"); + return FALSE; +} + +void +imapParser::parseWriteLine (const TQString & str) +{ + Q_UNUSED(str); + tqWarning + ("imapParser::parseWriteLine - virtual function not reimplemented - no data written"); +} + +void +imapParser::parseURL (const KURL & _url, TQString & _box, TQString & _section, + TQString & _type, TQString & _uid, TQString & _validity, TQString & _info) +{ + TQStringList parameters; + + _box = _url.path (); + kdDebug(7116) << "imapParser::parseURL " << _box << endl; + int paramStart = _box.find("/;"); + if ( paramStart > -1 ) + { + TQString paramString = _box.right( _box.length() - paramStart-2 ); + parameters = TQStringList::split (';', paramString); //split parameters + _box.truncate( paramStart ); // strip parameters + } + // extract parameters + for (TQStringList::ConstIterator it (parameters.begin ()); + it != parameters.end (); ++it) + { + TQString 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; +} + + +TQCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) { + + if (!inWords.isEmpty() && inWords[0] == '{') + { + TQCString retVal; + long srunLen = inWords.find ('}', 1); // Can return -1, so use a signed long + if (srunLen > 0) + { + ulong runLen = (ulong)srunLen; + bool proper; + ulong runLenSave = runLen + 1; + TQCString 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); + TQByteArray rv; + parseRead (rv, runLen, relay ? runLen : 0); + rv.resize(TQMAX(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 ) +TQCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen) +{ + uint retValSize = 0; + uint len = inWords.length(); + if (len == 0) { + return TQCString(); + } + + 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) + { + TQCString 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; + TQCString 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; + } + + TQCString 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 TQString & cap) +{ + TQString c = cap.lower(); +// kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl; + for (TQStringList::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 TQString & cap) +{ + imapCapabilities.remove(cap.lower()); +} + +TQString imapParser::namespaceForBox( const TQString & box ) +{ + kdDebug(7116) << "imapParse::namespaceForBox " << box << endl; + TQString myNamespace; + if ( !box.isEmpty() ) + { + TQValueList list = namespaceToDelimiter.keys(); + TQString cleanPrefix; + for ( TQValueList::Iterator it = list.begin(); it != list.end(); ++it ) + { + if ( !(*it).isEmpty() && box.find( *it ) != -1 ) + return (*it); + } + } + return myNamespace; +} + diff --git a/tdeioslave/imap4/mailaddress.cc b/tdeioslave/imap4/mailaddress.cc deleted file mode 100644 index 38a1a330a..000000000 --- a/tdeioslave/imap4/mailaddress.cc +++ /dev/null @@ -1,324 +0,0 @@ -/********************************************************************** - * - * 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 - -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 += TQCString (aCStr, advance + 1); - break; - case '(': - advance = mimeHdrLine::parseQuoted ('(', ')', aCStr); - rawComment += TQCString (aCStr, advance + 1); - break; - case '<': - advance = mimeHdrLine::parseQuoted ('<', '>', aCStr); - user = TQCString (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 += TQCString (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 TQCString -mailAddress::getStr () const -{ - TQCString retVal(128); // Should be generally big enough - - if (!rawFullName.isEmpty ()) - { - TQCString tmpName( rawFullName ); - KMime::addQuotes( tmpName, false ); - retVal = tmpName + " "; - } - 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 TQString & _str) -{ - rawFullName = rfcDecoder::encodeRFC2047String (_str).latin1 (); -} -const TQString -mailAddress::getFullName () const -{ - return rfcDecoder::decodeRFC2047String (rawFullName); -} - -void -mailAddress::setCommentRaw (const TQCString & _str) -{ - rawComment = _str; -} - -void -mailAddress::setComment (const TQString & _str) -{ - rawComment = rfcDecoder::encodeRFC2047String (_str).latin1 (); -} -const TQString -mailAddress::getComment () const -{ - return rfcDecoder::decodeRFC2047String (rawComment); -} - -const TQCString & -mailAddress::getCommentRaw () const -{ - return rawComment; -} - -TQString -mailAddress::emailAddrAsAnchor (const mailAddress & adr, bool shortAdr) -{ - TQString retVal; - if (!adr.getFullName ().isEmpty ()) - { - // should do some umlaut escaping - retVal += adr.getFullName () + " "; - } - if (!adr.getUser ().isEmpty () && !shortAdr) - { - retVal += "<" + adr.getUser (); - if (!adr.getHost ().isEmpty ()) - retVal += "@" + adr.getHost (); - retVal += "> "; - } - if (!adr.getComment ().isEmpty ()) - { - // should do some umlaut escaping - retVal = '(' + adr.getComment () + ')'; - } - - if (!adr.getUser ().isEmpty ()) - { - TQString mail; - mail = adr.getUser (); - if (!mail.isEmpty () && !adr.getHost ().isEmpty ()) - mail += "@" + adr.getHost (); - if (!mail.isEmpty ()) - retVal = "" + retVal + ""; - } - return retVal; -} - -TQString -mailAddress::emailAddrAsAnchor (const TQPtrList < mailAddress > &list, bool value) -{ - TQString retVal; - TQPtrListIterator < mailAddress > it (list); - - while (it.current ()) - { - retVal += emailAddrAsAnchor ((*it.current ()), value) + "

\n"; - ++it; - } - - return retVal; -} - - -void mailAddress::clear() { - user.truncate(0); - host.truncate(0); - rawFullName.truncate(0); - rawComment.truncate(0); -} - diff --git a/tdeioslave/imap4/mailaddress.cpp b/tdeioslave/imap4/mailaddress.cpp new file mode 100644 index 000000000..8bb5831ec --- /dev/null +++ b/tdeioslave/imap4/mailaddress.cpp @@ -0,0 +1,324 @@ +/********************************************************************** + * + * mailaddress.cpp - 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 + +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 += TQCString (aCStr, advance + 1); + break; + case '(': + advance = mimeHdrLine::parseQuoted ('(', ')', aCStr); + rawComment += TQCString (aCStr, advance + 1); + break; + case '<': + advance = mimeHdrLine::parseQuoted ('<', '>', aCStr); + user = TQCString (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 += TQCString (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 TQCString +mailAddress::getStr () const +{ + TQCString retVal(128); // Should be generally big enough + + if (!rawFullName.isEmpty ()) + { + TQCString tmpName( rawFullName ); + KMime::addQuotes( tmpName, false ); + retVal = tmpName + " "; + } + 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 TQString & _str) +{ + rawFullName = rfcDecoder::encodeRFC2047String (_str).latin1 (); +} +const TQString +mailAddress::getFullName () const +{ + return rfcDecoder::decodeRFC2047String (rawFullName); +} + +void +mailAddress::setCommentRaw (const TQCString & _str) +{ + rawComment = _str; +} + +void +mailAddress::setComment (const TQString & _str) +{ + rawComment = rfcDecoder::encodeRFC2047String (_str).latin1 (); +} +const TQString +mailAddress::getComment () const +{ + return rfcDecoder::decodeRFC2047String (rawComment); +} + +const TQCString & +mailAddress::getCommentRaw () const +{ + return rawComment; +} + +TQString +mailAddress::emailAddrAsAnchor (const mailAddress & adr, bool shortAdr) +{ + TQString retVal; + if (!adr.getFullName ().isEmpty ()) + { + // should do some umlaut escaping + retVal += adr.getFullName () + " "; + } + if (!adr.getUser ().isEmpty () && !shortAdr) + { + retVal += "<" + adr.getUser (); + if (!adr.getHost ().isEmpty ()) + retVal += "@" + adr.getHost (); + retVal += "> "; + } + if (!adr.getComment ().isEmpty ()) + { + // should do some umlaut escaping + retVal = '(' + adr.getComment () + ')'; + } + + if (!adr.getUser ().isEmpty ()) + { + TQString mail; + mail = adr.getUser (); + if (!mail.isEmpty () && !adr.getHost ().isEmpty ()) + mail += "@" + adr.getHost (); + if (!mail.isEmpty ()) + retVal = "" + retVal + ""; + } + return retVal; +} + +TQString +mailAddress::emailAddrAsAnchor (const TQPtrList < mailAddress > &list, bool value) +{ + TQString retVal; + TQPtrListIterator < mailAddress > it (list); + + while (it.current ()) + { + retVal += emailAddrAsAnchor ((*it.current ()), value) + "

\n"; + ++it; + } + + return retVal; +} + + +void mailAddress::clear() { + user.truncate(0); + host.truncate(0); + rawFullName.truncate(0); + rawComment.truncate(0); +} + diff --git a/tdeioslave/imap4/mailheader.cc b/tdeioslave/imap4/mailheader.cc deleted file mode 100644 index c520b9094..000000000 --- a/tdeioslave/imap4/mailheader.cc +++ /dev/null @@ -1,203 +0,0 @@ -/*************************************************************************** - 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 TQCString label(addLine->getLabel()); - TQCString value(addLine->getValue()); - - if (!tqstricmp (label, "Return-Path")) { - returnpathAdr.parseAddress (value.data ()); - goto out; - } - if (!tqstricmp (label, "Sender")) { - senderAdr.parseAddress (value.data ()); - goto out; - } - if (!tqstricmp (label, "From")) { - fromAdr.parseAddress (value.data ()); - goto out; - } - if (!tqstricmp (label, "Reply-To")) { - replytoAdr.parseAddress (value.data ()); - goto out; - } - if (!tqstricmp (label, "To")) { - mailHeader::parseAddressList (value, &toAdr); - goto out; - } - if (!tqstricmp (label, "CC")) { - mailHeader::parseAddressList (value, &ccAdr); - goto out; - } - if (!tqstricmp (label, "BCC")) { - mailHeader::parseAddressList (value, &bccAdr); - goto out; - } - if (!tqstricmp (label, "Subject")) { - _subject = value.simplifyWhiteSpace(); - goto out; - } - if (!tqstricmp (label.data (), "Date")) { - mDate = value; - goto out; - } - if (!tqstricmp (label.data (), "Message-ID")) { - int start = value.findRev ('<'); - int end = value.findRev ('>'); - if (start < end) - messageID = value.mid (start, end - start + 1); - else { - tqWarning("bad Message-ID"); - /* messageID = value; */ - } - goto out; - } - if (!tqstricmp (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 TQCString __returnPath("Return-Path: ", 14); - static const TQCString __from ("From: ", 7); - static const TQCString __sender ("Sender: ", 9); - static const TQCString __replyTo ("Reply-To: ", 11); - static const TQCString __to ("To: ", 5); - static const TQCString __cc ("CC: ", 5); - static const TQCString __bcc ("BCC: ", 6); - static const TQCString __subject ("Subject: ", 10); - static const TQCString __messageId ("Message-ID: ", 13); - static const TQCString __inReplyTo ("In-Reply-To: ", 14); - static const TQCString __references("References: ", 13); - static const TQCString __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, - TQPtrList < 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; -} - -TQCString -mailHeader::getAddressStr (TQPtrList < mailAddress > *aList) -{ - TQCString retVal; - - TQPtrListIterator < mailAddress > it = TQPtrListIterator < mailAddress > (*aList); - while (it.current ()) - { - retVal += it.current ()->getStr (); - ++it; - if (it.current ()) - retVal += ", "; - } - return retVal; -} diff --git a/tdeioslave/imap4/mailheader.cpp b/tdeioslave/imap4/mailheader.cpp new file mode 100644 index 000000000..28eec1f02 --- /dev/null +++ b/tdeioslave/imap4/mailheader.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + mailheader.cpp - 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 TQCString label(addLine->getLabel()); + TQCString value(addLine->getValue()); + + if (!tqstricmp (label, "Return-Path")) { + returnpathAdr.parseAddress (value.data ()); + goto out; + } + if (!tqstricmp (label, "Sender")) { + senderAdr.parseAddress (value.data ()); + goto out; + } + if (!tqstricmp (label, "From")) { + fromAdr.parseAddress (value.data ()); + goto out; + } + if (!tqstricmp (label, "Reply-To")) { + replytoAdr.parseAddress (value.data ()); + goto out; + } + if (!tqstricmp (label, "To")) { + mailHeader::parseAddressList (value, &toAdr); + goto out; + } + if (!tqstricmp (label, "CC")) { + mailHeader::parseAddressList (value, &ccAdr); + goto out; + } + if (!tqstricmp (label, "BCC")) { + mailHeader::parseAddressList (value, &bccAdr); + goto out; + } + if (!tqstricmp (label, "Subject")) { + _subject = value.simplifyWhiteSpace(); + goto out; + } + if (!tqstricmp (label.data (), "Date")) { + mDate = value; + goto out; + } + if (!tqstricmp (label.data (), "Message-ID")) { + int start = value.findRev ('<'); + int end = value.findRev ('>'); + if (start < end) + messageID = value.mid (start, end - start + 1); + else { + tqWarning("bad Message-ID"); + /* messageID = value; */ + } + goto out; + } + if (!tqstricmp (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 TQCString __returnPath("Return-Path: ", 14); + static const TQCString __from ("From: ", 7); + static const TQCString __sender ("Sender: ", 9); + static const TQCString __replyTo ("Reply-To: ", 11); + static const TQCString __to ("To: ", 5); + static const TQCString __cc ("CC: ", 5); + static const TQCString __bcc ("BCC: ", 6); + static const TQCString __subject ("Subject: ", 10); + static const TQCString __messageId ("Message-ID: ", 13); + static const TQCString __inReplyTo ("In-Reply-To: ", 14); + static const TQCString __references("References: ", 13); + static const TQCString __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, + TQPtrList < 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; +} + +TQCString +mailHeader::getAddressStr (TQPtrList < mailAddress > *aList) +{ + TQCString retVal; + + TQPtrListIterator < mailAddress > it = TQPtrListIterator < mailAddress > (*aList); + while (it.current ()) + { + retVal += it.current ()->getStr (); + ++it; + if (it.current ()) + retVal += ", "; + } + return retVal; +} diff --git a/tdeioslave/imap4/mimehdrline.cc b/tdeioslave/imap4/mimehdrline.cc deleted file mode 100644 index 9646ed26f..000000000 --- a/tdeioslave/imap4/mimehdrline.cc +++ /dev/null @@ -1,521 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -#include -#include - -#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 TQCString & aLabel, const TQCString & 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 += TQCString (aCStr, skip + 1); - aCStr += skip; - retVal += skip; - skip = parseFullLine (aCStr); - mimeValue += TQCString (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 = TQCString ((const char *) NULL); - mimeValue = TQCString ((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 = TQCString (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 = TQCString (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 TQCString& -mimeHdrLine::getLabel () -{ - return mimeLabel; -} - -/** return the value */ -const TQCString& -mimeHdrLine::getValue () -{ - return mimeValue; -} - - -// FIXME: very inefficient still -TQCString -mimeHdrLine::truncateLine(TQCString aLine, unsigned int truncate) -{ - int cutHere; - TQCString 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/tdeioslave/imap4/mimehdrline.cpp b/tdeioslave/imap4/mimehdrline.cpp new file mode 100644 index 000000000..591d33863 --- /dev/null +++ b/tdeioslave/imap4/mimehdrline.cpp @@ -0,0 +1,521 @@ +/*************************************************************************** + mimehdrline.cpp - 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 +#include +#include +#include +#include + +#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 TQCString & aLabel, const TQCString & 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 += TQCString (aCStr, skip + 1); + aCStr += skip; + retVal += skip; + skip = parseFullLine (aCStr); + mimeValue += TQCString (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 = TQCString ((const char *) NULL); + mimeValue = TQCString ((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 = TQCString (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 = TQCString (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 TQCString& +mimeHdrLine::getLabel () +{ + return mimeLabel; +} + +/** return the value */ +const TQCString& +mimeHdrLine::getValue () +{ + return mimeValue; +} + + +// FIXME: very inefficient still +TQCString +mimeHdrLine::truncateLine(TQCString aLine, unsigned int truncate) +{ + int cutHere; + TQCString 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/tdeioslave/imap4/mimeheader.cc b/tdeioslave/imap4/mimeheader.cc deleted file mode 100644 index d0916ba50..000000000 --- a/tdeioslave/imap4/mimeheader.cc +++ /dev/null @@ -1,745 +0,0 @@ -/*************************************************************************** - 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 - -// #include -#include -#include -#include -#include -#include -#include -#include - -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 () -{ -} - -/* -TQPtrList mimeHeader::getAllParts() -{ - TQPtrList 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 (tqstrnicmp (addLine->getLabel (), "Content-", 8)) - { - additionalHdrLines.append (addLine); - } - else - { - int skip; - const char *aCStr = addLine->getValue ().data (); - TQDict < TQString > *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++; - } - TQCString mimeValue = TQCString (aCStr, skip - cut + 1); // cutting of one because of 0x00 - - - if (!tqstricmp (addLine->getLabel (), "Content-Disposition")) - { - aList = &dispositionList; - _contentDisposition = mimeValue; - } - else if (!tqstricmp (addLine->getLabel (), "Content-Type")) - { - aList = &typeList; - contentType = mimeValue; - } - else - if (!tqstricmp (addLine->getLabel (), "Content-Transfer-Encoding")) - { - contentEncoding = mimeValue; - } - else if (!tqstricmp (addLine->getLabel (), "Content-ID")) - { - contentID = mimeValue; - } - else if (!tqstricmp (addLine->getLabel (), "Content-Description")) - { - _contentDescription = mimeValue; - } - else if (!tqstricmp (addLine->getLabel (), "Content-MD5")) - { - contentMD5 = mimeValue; - } - else if (!tqstricmp (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 (TQCString (aCStr, skip).simplifyWhiteSpace(), aList); -// cout << "-- '" << aParm.data() << "'" << endl; - mimeValue = TQCString (addLine->getValue ().data (), skip); - aCStr += skip; - } - else - break; - } - } - } - } -} - -void -mimeHeader::addParameter (const TQCString& aParameter, TQDict < TQString > *aList) -{ - if ( !aList ) - return; - - TQString *aValue; - TQCString aLabel; - int pos = aParameter.find ('='); -// cout << aParameter.left(pos).data(); - aValue = new TQString (); - 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; -} - -TQString -mimeHeader::getDispositionParm (const TQCString& aStr) -{ - return getParameter (aStr, &dispositionList); -} - -TQString -mimeHeader::getTypeParm (const TQCString& aStr) -{ - return getParameter (aStr, &typeList); -} - -void -mimeHeader::setDispositionParm (const TQCString& aLabel, const TQString& aValue) -{ - setParameter (aLabel, aValue, &dispositionList); - return; -} - -void -mimeHeader::setTypeParm (const TQCString& aLabel, const TQString& aValue) -{ - setParameter (aLabel, aValue, &typeList); -} - -TQDictIterator < TQString > mimeHeader::getDispositionIterator () -{ - return TQDictIterator < TQString > (dispositionList); -} - -TQDictIterator < TQString > mimeHeader::getTypeIterator () -{ - return TQDictIterator < TQString > (typeList); -} - -TQPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator () -{ - return TQPtrListIterator < mimeHdrLine > (originalHdrLines); -} - -TQPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator () -{ - return TQPtrListIterator < mimeHdrLine > (additionalHdrLines); -} - -void -mimeHeader::outputHeader (mimeIO & useIO) -{ - if (!getDisposition ().isEmpty ()) - { - useIO.outputMimeLine (TQCString ("Content-Disposition: ") - + getDisposition () - + outputParameter (&dispositionList)); - } - - if (!getType ().isEmpty ()) - { - useIO.outputMimeLine (TQCString ("Content-Type: ") - + getType () + outputParameter (&typeList)); - } - if (!getDescription ().isEmpty ()) - useIO.outputMimeLine (TQCString ("Content-Description: ") + - getDescription ()); - if (!getID ().isEmpty ()) - useIO.outputMimeLine (TQCString ("Content-ID: ") + getID ()); - if (!getMD5 ().isEmpty ()) - useIO.outputMimeLine (TQCString ("Content-MD5: ") + getMD5 ()); - if (!getEncoding ().isEmpty ()) - useIO.outputMimeLine (TQCString ("Content-Transfer-Encoding: ") + - getEncoding ()); - - TQPtrListIterator < mimeHdrLine > ait = getAdditionalIterator (); - while (ait.current ()) - { - useIO.outputMimeLine (ait.current ()->getLabel () + ": " + - ait.current ()->getValue ()); - ++ait; - } - useIO.outputMimeLine (TQCString ("")); -} - -TQString -mimeHeader::getParameter (const TQCString& aStr, TQDict < TQString > *aDict) -{ - TQString 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 - TQString decoded, encoded; - int part = 0; - - do - { - TQCString 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 (TQCString ("''") + - encoded.local8Bit ()); - } - } - else - { - //simple encoded parameter - retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ()); - } - } - else - { - retVal = *found; - } - } - return retVal; -} - -void -mimeHeader::setParameter (const TQCString& aLabel, const TQString& aValue, - TQDict < TQString > *aDict) -{ - bool encoded = true; - uint vlen, llen; - TQString 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; - TQString shortValue; - TQCString 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 TQString (shortValue)); - i++; - } - } - else - { - aDict->insert (aLabel, new TQString (val)); - } - } -} - -TQCString -mimeHeader::outputParameter (TQDict < TQString > *aDict) -{ - TQCString retVal; - if (aDict) - { - TQDictIterator < TQString > 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) -{ - TQPtrListIterator < mimeHeader > nestedParts = getNestedIterator (); - TQCString 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 TQString& boundary) -{ - int retVal = 0; - bool mbox = false; - TQCString preNested, postNested; - mbox = parseHeader (useIO); - - kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl; - if (!tqstrnicmp (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 (!tqstrnicmp (getType (), "Multipart/Digest", 16)) - aHeader->setType ("Message/RFC822"); - - localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary")); - addNestedPart (aHeader); - } - while (localRetVal); //get nested stuff - } - if (!tqstrnicmp (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, TQCString & messageBody, - const TQString& boundary, bool mbox) -{ - TQCString inputStr; - TQCString buffer; - TQString partBoundary; - TQString partEnd; - int retVal = 0; //default is last part - - if (!boundary.isEmpty ()) - { - partBoundary = TQString ("--") + boundary; - partEnd = TQString ("--") + boundary + "--"; - } - - while (useIO.inputLine (inputStr)) - { - //check for the end of all parts - if (!partEnd.isEmpty () - && !tqstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1)) - { - retVal = 0; //end of these parts - break; - } - else if (!partBoundary.isEmpty () - && !tqstrnicmp (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; - TQCString 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 TQString & _str) -{ - // see if it is nested a little deeper - int pt = _str.find('.'); - if (pt != -1) - { - TQString 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(TQDataStream& stream) -{ - int nestedcount = nestedParts.count(); - if (nestedParts.isEmpty() && nestedMessage) - nestedcount = 1; - stream << nestedcount << contentType << TQString (getTypeParm ("name")) << _contentDescription - << _contentDisposition << contentEncoding << contentLength << partSpecifier; - // serialize nested message - if (nestedMessage) - nestedMessage->serialize(stream); - - // serialize nested parts - if (!nestedParts.isEmpty()) - { - TQPtrListIterator < mimeHeader > it(nestedParts); - mimeHeader* part; - while ( (part = it.current()) != 0 ) - { - ++it; - part->serialize(stream); - } - } -} - -#ifdef KMAIL_COMPATIBLE -// compatibility subroutines -TQString -mimeHeader::bodyDecoded () -{ - kdDebug(7116) << "mimeHeader::bodyDecoded" << endl; - TQByteArray temp; - - temp = bodyDecodedBinary (); - return TQString::fromLatin1 (temp.data (), temp.count ()); -} - -TQByteArray -mimeHeader::bodyDecodedBinary () -{ - TQByteArray 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 TQByteArray & _arr) -{ - setBodyEncoded (_arr); -} - -void -mimeHeader::setBodyEncoded (const TQByteArray & _arr) -{ - TQByteArray 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; -} - -TQString -mimeHeader::iconName () -{ - TQString fileName; - - // FIXME: bug? Why throw away this data? - fileName = - KMimeType::mimeType (contentType.lower ())->icon (TQString(), false); - fileName = - TDEGlobal::instance ()->iconLoader ()->iconPath (fileName, TDEIcon::Desktop); -// if (fileName.isEmpty()) -// fileName = TDEGlobal::instance()->iconLoader()->iconPath( "unknown", TDEIcon::Desktop ); - return fileName; -} - -void -mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy) -{ -// if(nestedMessage && destroy) delete nestedMessage; - nestedMessage = inPart; -} - -TQString -mimeHeader::headerAsString () -{ - mimeIOTQString myIO; - - outputHeader (myIO); - return myIO.getString (); -} - -TQString -mimeHeader::magicSetType (bool aAutoDecode) -{ - TQString mimetype; - TQByteArray 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/tdeioslave/imap4/mimeheader.cpp b/tdeioslave/imap4/mimeheader.cpp new file mode 100644 index 000000000..d9d2c7d24 --- /dev/null +++ b/tdeioslave/imap4/mimeheader.cpp @@ -0,0 +1,745 @@ +/*************************************************************************** + mimeheader.cpp - 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 + +// #include +#include +#include +#include +#include +#include +#include +#include + +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 () +{ +} + +/* +TQPtrList mimeHeader::getAllParts() +{ + TQPtrList 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 (tqstrnicmp (addLine->getLabel (), "Content-", 8)) + { + additionalHdrLines.append (addLine); + } + else + { + int skip; + const char *aCStr = addLine->getValue ().data (); + TQDict < TQString > *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++; + } + TQCString mimeValue = TQCString (aCStr, skip - cut + 1); // cutting of one because of 0x00 + + + if (!tqstricmp (addLine->getLabel (), "Content-Disposition")) + { + aList = &dispositionList; + _contentDisposition = mimeValue; + } + else if (!tqstricmp (addLine->getLabel (), "Content-Type")) + { + aList = &typeList; + contentType = mimeValue; + } + else + if (!tqstricmp (addLine->getLabel (), "Content-Transfer-Encoding")) + { + contentEncoding = mimeValue; + } + else if (!tqstricmp (addLine->getLabel (), "Content-ID")) + { + contentID = mimeValue; + } + else if (!tqstricmp (addLine->getLabel (), "Content-Description")) + { + _contentDescription = mimeValue; + } + else if (!tqstricmp (addLine->getLabel (), "Content-MD5")) + { + contentMD5 = mimeValue; + } + else if (!tqstricmp (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 (TQCString (aCStr, skip).simplifyWhiteSpace(), aList); +// cout << "-- '" << aParm.data() << "'" << endl; + mimeValue = TQCString (addLine->getValue ().data (), skip); + aCStr += skip; + } + else + break; + } + } + } + } +} + +void +mimeHeader::addParameter (const TQCString& aParameter, TQDict < TQString > *aList) +{ + if ( !aList ) + return; + + TQString *aValue; + TQCString aLabel; + int pos = aParameter.find ('='); +// cout << aParameter.left(pos).data(); + aValue = new TQString (); + 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; +} + +TQString +mimeHeader::getDispositionParm (const TQCString& aStr) +{ + return getParameter (aStr, &dispositionList); +} + +TQString +mimeHeader::getTypeParm (const TQCString& aStr) +{ + return getParameter (aStr, &typeList); +} + +void +mimeHeader::setDispositionParm (const TQCString& aLabel, const TQString& aValue) +{ + setParameter (aLabel, aValue, &dispositionList); + return; +} + +void +mimeHeader::setTypeParm (const TQCString& aLabel, const TQString& aValue) +{ + setParameter (aLabel, aValue, &typeList); +} + +TQDictIterator < TQString > mimeHeader::getDispositionIterator () +{ + return TQDictIterator < TQString > (dispositionList); +} + +TQDictIterator < TQString > mimeHeader::getTypeIterator () +{ + return TQDictIterator < TQString > (typeList); +} + +TQPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator () +{ + return TQPtrListIterator < mimeHdrLine > (originalHdrLines); +} + +TQPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator () +{ + return TQPtrListIterator < mimeHdrLine > (additionalHdrLines); +} + +void +mimeHeader::outputHeader (mimeIO & useIO) +{ + if (!getDisposition ().isEmpty ()) + { + useIO.outputMimeLine (TQCString ("Content-Disposition: ") + + getDisposition () + + outputParameter (&dispositionList)); + } + + if (!getType ().isEmpty ()) + { + useIO.outputMimeLine (TQCString ("Content-Type: ") + + getType () + outputParameter (&typeList)); + } + if (!getDescription ().isEmpty ()) + useIO.outputMimeLine (TQCString ("Content-Description: ") + + getDescription ()); + if (!getID ().isEmpty ()) + useIO.outputMimeLine (TQCString ("Content-ID: ") + getID ()); + if (!getMD5 ().isEmpty ()) + useIO.outputMimeLine (TQCString ("Content-MD5: ") + getMD5 ()); + if (!getEncoding ().isEmpty ()) + useIO.outputMimeLine (TQCString ("Content-Transfer-Encoding: ") + + getEncoding ()); + + TQPtrListIterator < mimeHdrLine > ait = getAdditionalIterator (); + while (ait.current ()) + { + useIO.outputMimeLine (ait.current ()->getLabel () + ": " + + ait.current ()->getValue ()); + ++ait; + } + useIO.outputMimeLine (TQCString ("")); +} + +TQString +mimeHeader::getParameter (const TQCString& aStr, TQDict < TQString > *aDict) +{ + TQString 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 + TQString decoded, encoded; + int part = 0; + + do + { + TQCString 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 (TQCString ("''") + + encoded.local8Bit ()); + } + } + else + { + //simple encoded parameter + retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ()); + } + } + else + { + retVal = *found; + } + } + return retVal; +} + +void +mimeHeader::setParameter (const TQCString& aLabel, const TQString& aValue, + TQDict < TQString > *aDict) +{ + bool encoded = true; + uint vlen, llen; + TQString 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; + TQString shortValue; + TQCString 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 TQString (shortValue)); + i++; + } + } + else + { + aDict->insert (aLabel, new TQString (val)); + } + } +} + +TQCString +mimeHeader::outputParameter (TQDict < TQString > *aDict) +{ + TQCString retVal; + if (aDict) + { + TQDictIterator < TQString > 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) +{ + TQPtrListIterator < mimeHeader > nestedParts = getNestedIterator (); + TQCString 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 TQString& boundary) +{ + int retVal = 0; + bool mbox = false; + TQCString preNested, postNested; + mbox = parseHeader (useIO); + + kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl; + if (!tqstrnicmp (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 (!tqstrnicmp (getType (), "Multipart/Digest", 16)) + aHeader->setType ("Message/RFC822"); + + localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary")); + addNestedPart (aHeader); + } + while (localRetVal); //get nested stuff + } + if (!tqstrnicmp (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, TQCString & messageBody, + const TQString& boundary, bool mbox) +{ + TQCString inputStr; + TQCString buffer; + TQString partBoundary; + TQString partEnd; + int retVal = 0; //default is last part + + if (!boundary.isEmpty ()) + { + partBoundary = TQString ("--") + boundary; + partEnd = TQString ("--") + boundary + "--"; + } + + while (useIO.inputLine (inputStr)) + { + //check for the end of all parts + if (!partEnd.isEmpty () + && !tqstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1)) + { + retVal = 0; //end of these parts + break; + } + else if (!partBoundary.isEmpty () + && !tqstrnicmp (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; + TQCString 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 TQString & _str) +{ + // see if it is nested a little deeper + int pt = _str.find('.'); + if (pt != -1) + { + TQString 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(TQDataStream& stream) +{ + int nestedcount = nestedParts.count(); + if (nestedParts.isEmpty() && nestedMessage) + nestedcount = 1; + stream << nestedcount << contentType << TQString (getTypeParm ("name")) << _contentDescription + << _contentDisposition << contentEncoding << contentLength << partSpecifier; + // serialize nested message + if (nestedMessage) + nestedMessage->serialize(stream); + + // serialize nested parts + if (!nestedParts.isEmpty()) + { + TQPtrListIterator < mimeHeader > it(nestedParts); + mimeHeader* part; + while ( (part = it.current()) != 0 ) + { + ++it; + part->serialize(stream); + } + } +} + +#ifdef KMAIL_COMPATIBLE +// compatibility subroutines +TQString +mimeHeader::bodyDecoded () +{ + kdDebug(7116) << "mimeHeader::bodyDecoded" << endl; + TQByteArray temp; + + temp = bodyDecodedBinary (); + return TQString::fromLatin1 (temp.data (), temp.count ()); +} + +TQByteArray +mimeHeader::bodyDecodedBinary () +{ + TQByteArray 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 TQByteArray & _arr) +{ + setBodyEncoded (_arr); +} + +void +mimeHeader::setBodyEncoded (const TQByteArray & _arr) +{ + TQByteArray 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; +} + +TQString +mimeHeader::iconName () +{ + TQString fileName; + + // FIXME: bug? Why throw away this data? + fileName = + KMimeType::mimeType (contentType.lower ())->icon (TQString(), false); + fileName = + TDEGlobal::instance ()->iconLoader ()->iconPath (fileName, TDEIcon::Desktop); +// if (fileName.isEmpty()) +// fileName = TDEGlobal::instance()->iconLoader()->iconPath( "unknown", TDEIcon::Desktop ); + return fileName; +} + +void +mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy) +{ +// if(nestedMessage && destroy) delete nestedMessage; + nestedMessage = inPart; +} + +TQString +mimeHeader::headerAsString () +{ + mimeIOTQString myIO; + + outputHeader (myIO); + return myIO.getString (); +} + +TQString +mimeHeader::magicSetType (bool aAutoDecode) +{ + TQString mimetype; + TQByteArray 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/tdeioslave/imap4/mimeio.cc b/tdeioslave/imap4/mimeio.cc deleted file mode 100644 index 757c11a47..000000000 --- a/tdeioslave/imap4/mimeio.cc +++ /dev/null @@ -1,188 +0,0 @@ -/*************************************************************************** - 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 -using namespace std; - -#include "mimeio.h" - -mimeIO::mimeIO () -{ - theCRLF = "\r\n"; - crlfLen = 2; -} - -mimeIO::~mimeIO () -{ -} - -int -mimeIO::inputLine (TQCString & 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 TQCString & 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 TQCString & inLine) -{ - int retVal = 0; - TQCString 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); -} - -mimeIOTQFile::mimeIOTQFile (const TQString & aName): -mimeIO (), -myFile (aName) -{ - myFile.open (IO_ReadOnly); -} - -mimeIOTQFile::~mimeIOTQFile () -{ - myFile.close (); -} - -int -mimeIOTQFile::outputLine (const TQCString &, int) -{ - return 0; -} - -int -mimeIOTQFile::inputLine (TQCString & data) -{ - data.resize( 1024 ); - myFile.readLine (data.data(), 1024); - - return data.length (); -} - -mimeIOTQString::mimeIOTQString () -{ -} - -mimeIOTQString::~mimeIOTQString () -{ -} - -int -mimeIOTQString::outputLine (const TQCString & _str, int len) -{ - if (len == -1) { - len = _str.length(); - } - theString += _str; - return len; -} - -int -mimeIOTQString::inputLine (TQCString & _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/tdeioslave/imap4/mimeio.cpp b/tdeioslave/imap4/mimeio.cpp new file mode 100644 index 000000000..a3315c268 --- /dev/null +++ b/tdeioslave/imap4/mimeio.cpp @@ -0,0 +1,188 @@ +/*************************************************************************** + mimeio.cpp - 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 +using namespace std; + +#include "mimeio.h" + +mimeIO::mimeIO () +{ + theCRLF = "\r\n"; + crlfLen = 2; +} + +mimeIO::~mimeIO () +{ +} + +int +mimeIO::inputLine (TQCString & 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 TQCString & 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 TQCString & inLine) +{ + int retVal = 0; + TQCString 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); +} + +mimeIOTQFile::mimeIOTQFile (const TQString & aName): +mimeIO (), +myFile (aName) +{ + myFile.open (IO_ReadOnly); +} + +mimeIOTQFile::~mimeIOTQFile () +{ + myFile.close (); +} + +int +mimeIOTQFile::outputLine (const TQCString &, int) +{ + return 0; +} + +int +mimeIOTQFile::inputLine (TQCString & data) +{ + data.resize( 1024 ); + myFile.readLine (data.data(), 1024); + + return data.length (); +} + +mimeIOTQString::mimeIOTQString () +{ +} + +mimeIOTQString::~mimeIOTQString () +{ +} + +int +mimeIOTQString::outputLine (const TQCString & _str, int len) +{ + if (len == -1) { + len = _str.length(); + } + theString += _str; + return len; +} + +int +mimeIOTQString::inputLine (TQCString & _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/tdeioslave/imap4/rfcdecoder.cc b/tdeioslave/imap4/rfcdecoder.cc deleted file mode 100644 index 41a50ec10..000000000 --- a/tdeioslave/imap4/rfcdecoder.cc +++ /dev/null @@ -1,668 +0,0 @@ -/********************************************************************** - * - * 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 -#include - -#include -#include - -#include -#include -#include -#include - -// This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997. -// adapted to QT-Toolkit by Sven Carstens 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 - */ -TQString rfcDecoder::fromIMAP (const TQString & inSrc) -{ - unsigned char c, i, bitcount; - unsigned long ucs4, utf16, bitbuf; - unsigned char base64[256], utf8[6]; - unsigned long srcPtr = 0; - TQCString dst; - TQCString 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 TQString::fromUtf8 (dst.data ()); -} - -/* replace " with \" and \ with \\ " and \ characters */ -TQString rfcDecoder::quoteIMAP(const TQString &src) -{ - uint len = src.length(); - TQString 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 - */ -TQString rfcDecoder::toIMAP (const TQString & inSrc) -{ - unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag; - unsigned long ucs4, bitbuf; - TQCString src = inSrc.utf8 (); - TQString 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); -} - -//----------------------------------------------------------------------------- -TQString rfcDecoder::decodeQuoting(const TQString &aStr) -{ - TQString result; - unsigned int strLength(aStr.length()); - for (unsigned int i = 0; i < strLength ; i++) - { - if (aStr[i] == "\\") i++; - result += aStr[i]; - } - return result; -} - -//----------------------------------------------------------------------------- -TQTextCodec * -rfcDecoder::codecForName (const TQString & _str) -{ - if (_str.isEmpty ()) - return NULL; - return TQTextCodec::codecForName (_str.lower (). - replace ("windows", "cp").latin1 ()); -} - -//----------------------------------------------------------------------------- -const TQString -rfcDecoder::decodeRFC2047String (const TQString & _str) -{ - TQString throw_away; - - return decodeRFC2047String (_str, throw_away); -} - -//----------------------------------------------------------------------------- -const TQString -rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset) -{ - TQString throw_away; - - return decodeRFC2047String (_str, charset, throw_away); -} - -//----------------------------------------------------------------------------- -const TQString -rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset, - TQString & language) -{ - //do we have a rfc string - if (_str.find("=?") < 0) - return _str; - - TQCString aStr = _str.ascii (); // TQString.length() means Unicode chars - TQCString result; - char *pos, *beg, *end, *mid = NULL; - TQCString 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 = TQCString (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 = TQCString (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) (TQChar) 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 ()) - { - TQTextCodec *aCodec = codecForName (charset.ascii ()); - if (aCodec) - { -// kdDebug(7116) << "Codec is " << aCodec->name() << endl; - return aCodec->toUnicode (result); - } - } - return result; -} - - -//----------------------------------------------------------------------------- -const char especials[17] = "()<>@,;:\"/[]?.= "; - -const TQString -rfcDecoder::encodeRFC2047String (const TQString & _str) -{ - if (_str.isEmpty ()) - return _str; - const signed char *latin = reinterpret_cast(_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; - TQCString 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 "= 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 TQString -rfcDecoder::encodeRFC2231String (const TQString & _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; - } - TQCString 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 TQString -rfcDecoder::decodeRFC2231String (const TQString & _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 - TQString charset = _str.left (p); - TQString st = _str.mid (l + 1); - TQString 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/tdeioslave/imap4/rfcdecoder.cpp b/tdeioslave/imap4/rfcdecoder.cpp new file mode 100644 index 000000000..36111bf1f --- /dev/null +++ b/tdeioslave/imap4/rfcdecoder.cpp @@ -0,0 +1,668 @@ +/********************************************************************** + * + * rfcdecoder.cpp - 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 +#include + +#include +#include + +#include +#include +#include +#include + +// This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997. +// adapted to QT-Toolkit by Sven Carstens 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 + */ +TQString rfcDecoder::fromIMAP (const TQString & inSrc) +{ + unsigned char c, i, bitcount; + unsigned long ucs4, utf16, bitbuf; + unsigned char base64[256], utf8[6]; + unsigned long srcPtr = 0; + TQCString dst; + TQCString 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 TQString::fromUtf8 (dst.data ()); +} + +/* replace " with \" and \ with \\ " and \ characters */ +TQString rfcDecoder::quoteIMAP(const TQString &src) +{ + uint len = src.length(); + TQString 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 + */ +TQString rfcDecoder::toIMAP (const TQString & inSrc) +{ + unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag; + unsigned long ucs4, bitbuf; + TQCString src = inSrc.utf8 (); + TQString 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); +} + +//----------------------------------------------------------------------------- +TQString rfcDecoder::decodeQuoting(const TQString &aStr) +{ + TQString result; + unsigned int strLength(aStr.length()); + for (unsigned int i = 0; i < strLength ; i++) + { + if (aStr[i] == "\\") i++; + result += aStr[i]; + } + return result; +} + +//----------------------------------------------------------------------------- +TQTextCodec * +rfcDecoder::codecForName (const TQString & _str) +{ + if (_str.isEmpty ()) + return NULL; + return TQTextCodec::codecForName (_str.lower (). + replace ("windows", "cp").latin1 ()); +} + +//----------------------------------------------------------------------------- +const TQString +rfcDecoder::decodeRFC2047String (const TQString & _str) +{ + TQString throw_away; + + return decodeRFC2047String (_str, throw_away); +} + +//----------------------------------------------------------------------------- +const TQString +rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset) +{ + TQString throw_away; + + return decodeRFC2047String (_str, charset, throw_away); +} + +//----------------------------------------------------------------------------- +const TQString +rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset, + TQString & language) +{ + //do we have a rfc string + if (_str.find("=?") < 0) + return _str; + + TQCString aStr = _str.ascii (); // TQString.length() means Unicode chars + TQCString result; + char *pos, *beg, *end, *mid = NULL; + TQCString 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 = TQCString (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 = TQCString (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) (TQChar) 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 ()) + { + TQTextCodec *aCodec = codecForName (charset.ascii ()); + if (aCodec) + { +// kdDebug(7116) << "Codec is " << aCodec->name() << endl; + return aCodec->toUnicode (result); + } + } + return result; +} + + +//----------------------------------------------------------------------------- +const char especials[17] = "()<>@,;:\"/[]?.= "; + +const TQString +rfcDecoder::encodeRFC2047String (const TQString & _str) +{ + if (_str.isEmpty ()) + return _str; + const signed char *latin = reinterpret_cast(_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; + TQCString 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 "= 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 TQString +rfcDecoder::encodeRFC2231String (const TQString & _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; + } + TQCString 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 TQString +rfcDecoder::decodeRFC2231String (const TQString & _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 + TQString charset = _str.left (p); + TQString st = _str.mid (l + 1); + TQString 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; +} -- cgit v1.2.1