#ifndef _IMAPPARSER_H #define _IMAPPARSER_H /********************************************************************** * * imapparser.h - 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 * *********************************************************************/ #include #include #include #include #include #include #include "imaplist.h" #include "imapcommand.h" #include "imapinfo.h" #include "mailheader.h" class KURL; class TQString; class mailAddress; class mimeHeader; /** @brief a string used during parsing * the string allows you to move the effective start of the string using * str.pos++ and str.pos--. * @bug it is possible to move past the beginning and end of the string */ class parseString { public: parseString() { pos = 0; } char operator[](uint i) const { return data[i + pos]; } bool isEmpty() const { return pos >= data.size(); } TQCString cstr() const { if (pos >= data.size()) return TQCString(); return TQCString(data.data() + pos, data.size() - pos + 1); } int find(char c, int index = 0) { int res = data.find(c, index + pos); return (res == -1) ? res : (res - pos); } // Warning: does not check for going past end of "data" void takeLeft(TQCString& dest, uint len) const { dest.resize(len + 1); tqmemmove(dest.data(), data.data() + pos, len); } // Warning: does not check for going past end of "data" void takeLeftNoResize(TQCString& dest, uint len) const { tqmemmove(dest.data(), data.data() + pos, len); } // Warning: does not check for going past end of "data" void takeMid(TQCString& dest, uint start, uint len) const { dest.resize(len + 1); tqmemmove(dest.data(), data.data() + pos + start, len); } // Warning: does not check for going past end of "data" void takeMidNoResize(TQCString& dest, uint start, uint len) const { tqmemmove(dest.data(), data.data() + pos + start, len); } void clear() { data.resize(0); pos = 0; } uint length() { if( pos < data.size() ) { return data.size() - pos; } else { return 0; } } void fromString(const TQString &s) { clear(); data.duplicate(s.latin1(), s.length()); } TQByteArray data; uint pos; }; class imapCache { public: imapCache () { myHeader = NULL; mySize = 0; myFlags = 0; myUid = 0; } ~imapCache () { if (myHeader) delete myHeader; } mailHeader *getHeader () { return myHeader; } void setHeader (mailHeader * inHeader) { myHeader = inHeader; } ulong getSize () { return mySize; } void setSize (ulong inSize) { mySize = inSize; } ulong getUid () { return myUid; } void setUid (ulong inUid) { myUid = inUid; } ulong getFlags () { return myFlags; } void setFlags (ulong inFlags) { myFlags = inFlags; } TQCString getDate () { return myDate; } void setDate (const TQCString & _str) { myDate = _str; } void clear() { if (myHeader) delete myHeader; myHeader = NULL; mySize = 0; myFlags = 0; myDate = TQCString(); myUid = 0; } protected: mailHeader * myHeader; ulong mySize; ulong myFlags; ulong myUid; TQCString myDate; }; class imapParser { public: /** the different states the client can be in */ enum IMAP_STATE { ISTATE_NO, /**< Not connected */ ISTATE_CONNECT, /**< Connected but not logged in */ ISTATE_LOGIN, /**< Logged in */ ISTATE_SELECT /**< A folder is currently selected */ }; public: imapParser (); virtual ~ imapParser (); /** @brief Get the current state */ enum IMAP_STATE getState () { return currentState; } /** @brief Set the current state */ void setState(enum IMAP_STATE state) { currentState = state; } /* @brief return the currently selected mailbox */ const TQString getCurrentBox () { return rfcDecoder::fromIMAP(currentBox); }; /** * @brief do setup and send the command to parseWriteLine * @param aCmd The command to perform * @return The completed command */ imapCommand *sendCommand (imapCommand * aCmd); /** * @brief perform a command and wait to parse the result * @param aCmd The command to perform * @return The completed command */ imapCommand *doCommand (imapCommand * aCmd); /** * @brief plaintext login * @param aUser Username * @param aPass Password * @param resultInfo The resultinfo from the command * @return success or failure */ bool clientLogin (const TQString & aUser, const TQString & aPass, TQString & resultInfo); /** * @brief non-plaintext login * @param aUser Username * @param aPass Password * @param aAuth authentication method * @param isSSL are we using SSL * @param resultInfo The resultinfo from the command * @return success or failure */ bool clientAuthenticate (KIO::SlaveBase *slave, KIO::AuthInfo &ai, const TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & resultInfo); /** * main loop for the parser * reads one line and dispatches it to the appropriate sub parser */ int parseLoop (); /** * @brief parses all untagged responses and passes them on to the * following parsers */ void parseUntagged (parseString & result); /** @brief parse a RECENT line */ void parseRecent (ulong value, parseString & result); /** @brief parse a RESULT line */ void parseResult (TQByteArray & result, parseString & rest, const TQString & command = TQString()); /** @brief parse a CAPABILITY line */ void parseCapability (parseString & result); /** @brief parse a FLAGS line */ void parseFlags (parseString & result); /** @brief parse a LIST line */ void parseList (parseString & result); /** @brief parse a LSUB line */ void parseLsub (parseString & result); /** @brief parse a LISTRIGHTS line */ void parseListRights (parseString & result); /** @brief parse a MYRIGHTS line */ void parseMyRights (parseString & result); /** @brief parse a SEARCH line */ void parseSearch (parseString & result); /** @brief parse a STATUS line */ void parsetStatus (parseString & result); /** @brief parse a EXISTS line */ void parseExists (ulong value, parseString & result); /** @brief parse a EXPUNGE line */ void parseExpunge (ulong value, parseString & result); /** @brief parse a ACL line */ void parseAcl (parseString & result); /** @brief parse a ANNOTATION line */ void parseAnnotation (parseString & result); /** @brief parse a NAMESPACE line */ void parseNamespace (parseString & result); /** @brief parse a QUOTAROOT line */ void parseQuotaRoot (parseString & result); /** @brief parse a QUOTA line */ void parseQuota (parseString & result); /** @brief parse a custom command line */ void parseCustom (parseString & result); /** @brief parse a OTHER-USER line */ void parseOtherUser (parseString & result); /** @brief parse a DELEGATE line */ void parseDelegate (parseString & result); /** @brief parse a OUT-OF-OFFICE line */ void parseOutOfOffice (parseString & result); /** * parses the results of a fetch command * processes it with the following sub parsers */ void parseFetch (ulong value, parseString & inWords); /** read a envelope from imap and parse the addresses */ mailHeader *parseEnvelope (parseString & inWords); /** @brief parse an address list and return a list of addresses */ void parseAddressList (parseString & inWords, TQPtrList& list); /** @brief parse an address and return the ref again */ const mailAddress& parseAddress (parseString & inWords, mailAddress& buffer); /** parse the result of the body command */ void parseBody (parseString & inWords); /** parse the body structure recursively */ mimeHeader *parseBodyStructure (parseString & inWords, TQString & section, mimeHeader * inHeader = 0); /** parse only one not nested part */ mimeHeader *parseSimplePart (parseString & inWords, TQString & section, mimeHeader * localPart = 0); /** parse a parameter list (name value pairs) */ TQAsciiDict < TQString > parseParameters (parseString & inWords); /** * parse the disposition list (disposition (name value pairs)) * the disposition has the key 'content-disposition' */ TQAsciiDict < TQString > parseDisposition (parseString & inWords); // reimplement these /** relay hook to send the fetched data directly to an upper level */ virtual void parseRelay (const TQByteArray & buffer); /** relay hook to announce the fetched data directly to an upper level */ virtual void parseRelay (ulong); /** read at least len bytes */ virtual bool parseRead (TQByteArray & buffer, ulong len, ulong relay = 0); /** read at least a line (up to CRLF) */ virtual bool parseReadLine (TQByteArray & buffer, ulong relay = 0); /** write argument to server */ virtual void parseWriteLine (const TQString &); // generic parser routines /** parse a parenthesized list */ void parseSentence (parseString & inWords); /** parse a literal or word, may require more data */ TQCString parseLiteralC(parseString & inWords, bool relay = false, bool stopAtBracket = false, int *outlen = 0); inline TQByteArray parseLiteral (parseString & inWords, bool relay = false, bool stopAtBracket = false) { int len = 0; // string size // Choice: we can create an extra TQCString, or we can get the buffer in // the wrong size to start. Let's try option b. TQCString tmp = parseLiteralC(inWords, relay, stopAtBracket, &len); return TQByteArray().duplicate(tmp.data(), len); } // static parser routines, can be used elsewhere static TQCString b2c(const TQByteArray &ba) { return TQCString(ba.data(), ba.size() + 1); } /** parse one word (maybe quoted) upto next space " ) ] } */ static TQCString parseOneWordC (parseString & inWords, bool stopAtBracket = FALSE, int *len = 0); /** parse one number using parseOneWord */ static bool parseOneNumber (parseString & inWords, ulong & num); /** extract the box,section,list type, uid, uidvalidity,info from an url */ static void parseURL (const KURL & _url, TQString & _box, TQString & _section, TQString & _type, TQString & _uid, TQString & _validity, TQString & _info); /** @brief return the last handled foo * @todo work out what a foo is */ imapCache *getLastHandled () { return lastHandled; }; /** @brief return the last results */ const TQStringList & getResults () { return lastResults; }; /** @brief return the last status code */ const imapInfo & geStatus () { return lasStatus; }; /** return the select info */ const imapInfo & getSelected () { return selectInfo; }; const TQByteArray & getContinuation () { return continuation; }; /** @brief see if server has a capability */ bool hasCapability (const TQString &); void removeCapability (const TQString & cap); static inline void skipWS (parseString & inWords) { char c; while (!inWords.isEmpty() && ((c = inWords[0]) == ' ' || c == '\t' || c == '\r' || c == '\n')) { inWords.pos++; } } /** @brief find the namespace for the given box */ TQString namespaceForBox( const TQString & box ); protected: /** the current state we're in */ enum IMAP_STATE currentState; /** the box selected */ TQString currentBox; /** @brief here we store the result from select/examine and unsolicited updates */ imapInfo selectInfo; /** @brief the results from the last status command */ imapInfo lasStatus; /** @brief the results from the capabilities, split at ' ' */ TQStringList imapCapabilities; /** @brief the results from list/lsub/listrights commands */ TQValueList < imapList > listResponses; /** @brief queues handling the running commands */ TQPtrList < imapCommand > sentQueue; // no autodelete TQPtrList < imapCommand > completeQueue; // autodelete !! /** * everything we didn't handle, everything but the greeting is bogus */ TQStringList unhandled; /** the last continuation request (there MUST not be more than one pending) */ TQByteArray continuation; /** the last uid seen while a fetch */ TQString seenUid; imapCache *lastHandled; ulong commandCounter; /** @brief the results from search/acl commands */ TQStringList lastResults; /** * @brief namespace prefix - delimiter association * The namespace is cleaned before so that it does not contain the delimiter */ TQMap namespaceToDelimiter; /** * @brief list of namespaces in the form: section=namespace=delimiter * section is 0 (personal), 1 (other users) or 2 (shared) */ TQStringList imapNamespaces; private: /** we don't want to be able to copy this object */ imapParser & operator = (const imapParser &); // hide the copy ctor }; #endif