// // VMime library (http://vmime.sourceforge.net) // Copyright (C) 2002-2004 Vincent Richard // // 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., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED #define VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED #include "../base.hpp" #include "../dateTime.hpp" #include "../charset.hpp" #include "../exception.hpp" #include "../utility/smartPtr.hpp" #include "../utility/stringUtils.hpp" #include "../encoderB64.hpp" #include "../encoderQP.hpp" #include "../platformDependant.hpp" #include "progressionListener.hpp" #include "timeoutHandler.hpp" #include "socket.hpp" #include "IMAPTag.hpp" #include #include //#define DEBUG_RESPONSE 1 #if DEBUG_RESPONSE # include #endif namespace vmime { namespace messaging { #if DEBUG_RESPONSE static string DEBUG_RESPONSE_level; static std::vector DEBUG_RESPONSE_components; # define DEBUG_ENTER_COMPONENT(x) \ DEBUG_RESPONSE_components.push_back(x); \ std::cout << DEBUG_RESPONSE_level \ << "(" << DEBUG_RESPONSE_level.length() << ") " \ << (x) << std::endl; # define DEBUG_FOUND(x, y) \ std::cout << "FOUND: " << x << ": " << y << std::endl; #else # define DEBUG_ENTER_COMPONENT(x) # define DEBUG_FOUND(x, y) #endif class IMAPParser { public: IMAPParser(IMAPTag* tag, socket* sok, timeoutHandler* _timeoutHandler) : m_tag(tag), m_socket(sok), m_progress(NULL), m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler) { } const IMAPTag* tag() const { return (m_tag); } const string lastLine() const { // Remove blanks and new lines at the end of the line. string line(m_lastLine); string::const_iterator it = line.end(); int count = 0; while (it != line.begin()) { const unsigned char c = *(it - 1); if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) break; ++count; --it; } line.resize(line.length() - count); return (line); } // // literalHandler : literal content handler // class component; class literalHandler { public: virtual ~literalHandler() { } // Abstract target class class target { protected: target(class progressionListener* progress) : m_progress(progress) {} target(const target&) {} public: virtual ~target() { } class progressionListener* progressionListener() { return (m_progress); } virtual void putData(const string& chunk) = 0; private: class progressionListener* m_progress; }; // Target: put in a string class targetString : public target { public: targetString(class progressionListener* progress, vmime::string& str) : target(progress), m_string(str) { } const vmime::string& string() const { return (m_string); } vmime::string& string() { return (m_string); } void putData(const vmime::string& chunk) { m_string += chunk; } private: vmime::string& m_string; }; // Target: redirect to an output stream class targetStream : public target { public: targetStream(class progressionListener* progress, utility::outputStream& stream) : target(progress), m_stream(stream) { } const utility::outputStream& stream() const { return (m_stream); } utility::outputStream& stream() { return (m_stream); } void putData(const string& chunk) { m_stream.write(chunk.data(), chunk.length()); } private: utility::outputStream& m_stream; }; // Called when the parser needs to know what to do with a literal // . comp: the component in which we are at this moment // . data: data specific to the component (may not be used) // // Returns : // . == NULL to put the literal into the response // . != NULL to redirect the literal to the specified target virtual target* targetFor(const component& comp, const int data) = 0; }; // // Base class for a terminal or a non-terminal // class component { public: component() { } virtual ~component() { } virtual void go(IMAPParser& parser, string& line, string::size_type* currentPos) = 0; const string makeResponseLine(const string& comp, const string& line, const string::size_type pos) { #if DEBUG_RESPONSE if (pos > line.length()) std::cout << "WARNING: component::makeResponseLine(): pos > line.length()" << std::endl; #endif string result(line.substr(0, pos)); result += "[^]"; // indicates current parser position result += line.substr(pos, line.length()); if (!comp.empty()) result += " [" + comp + "]"; return (result); } }; // // Parse one character // template class one_char : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'"); const string::size_type pos = *currentPos; if (pos < line.length() && line[pos] == C) *currentPos = pos + 1; else throw exceptions::invalid_response("", makeResponseLine("", line, pos)); } }; // // SPACE ::= // class SPACE : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("SPACE"); string::size_type pos = *currentPos; while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) ++pos; if (pos > *currentPos) *currentPos = pos; else throw exceptions::invalid_response("", makeResponseLine("SPACE", line, pos)); } }; // // CR ::= // LF ::= // CRLF ::= CR LF // class CRLF : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("CRLF"); string::size_type pos = *currentPos; parser.check (line, &pos, true); if (pos + 1 < line.length() && line[pos] == 0x0d && line[pos + 1] == 0x0a) { *currentPos = pos + 2; } else { throw exceptions::invalid_response("", makeResponseLine("CRLF", line, pos)); } } }; // // SPACE ::= // CTL ::= // CHAR ::= // ATOM_CHAR ::= // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials // list_wildcards ::= "%" / "*" // quoted_specials ::= <"> / "\" // // tag ::= 1* (named "xtag") // class xtag : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("tag"); string::size_type pos = *currentPos; bool end = false; string tagString; tagString.reserve(10); while (!end && pos < line.length()) { const unsigned char c = line[pos]; switch (c) { case '+': case '(': case ')': case '{': case 0x20: // SPACE case '%': // list_wildcards case '*': // list_wildcards case '"': // quoted_specials case '\\': // quoted_specials end = true; break; default: if (c <= 0x1f || c >= 0x7f) end = true; else { tagString += c; ++pos; } break; } } if (tagString == (string) *(parser.tag())) { *currentPos = pos; } else { // Invalid tag throw exceptions::invalid_response("", makeResponseLine("tag", line, pos)); } } }; // // digit ::= "0" / digit_nz // digit_nz ::= "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" // // number ::= 1*digit // ;; Unsigned 32-bit integer // ;; (0 <= n < 4,294,967,296) // class number : public component { public: number(const bool nonZero = false) : m_nonZero(nonZero), m_value(0) { } void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("number"); string::size_type pos = *currentPos; bool valid = true; unsigned int val = 0; while (valid && pos < line.length()) { const char c = line[pos]; if (c >= '0' && c <= '9') { val = (val * 10) + (c - '0'); ++pos; } else { valid = false; } } // Check for non-null length (and for non-zero number) if (!(m_nonZero && val == 0) && pos != *currentPos) { m_value = val; *currentPos = pos; } else { throw exceptions::invalid_response("", makeResponseLine("number", line, pos)); } } private: const bool m_nonZero; unsigned int m_value; public: const unsigned int value() const { return (m_value); } }; // nz_number ::= digit_nz *digit // ;; Non-zero unsigned 32-bit integer // ;; (0 < n < 4,294,967,296) // class nz_number : public number { public: nz_number() : number(true) { } }; // // text ::= 1*TEXT_CHAR // // CHAR ::= // TEXT_CHAR ::= // class text : public component { public: text(bool allow8bits = false, const char except = 0) : m_allow8bits(allow8bits), m_except(except) { } void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("text"); string::size_type pos = *currentPos; string::size_type len = 0; if (m_allow8bits) { const unsigned char except = m_except; for (bool end = false ; !end && pos < line.length() ; ) { const unsigned char c = line[pos]; if (c == 0x00 || c == 0x0d || c == 0x0a || c == except) { end = true; } else { ++pos; ++len; } } } else { const unsigned char except = m_except; for (bool end = false ; !end && pos < line.length() ; ) { const unsigned char c = line[pos]; if (c < 0x01 || c > 0x7f || c == 0x0d || c == 0x0a || c == except) { end = true; } else { ++pos; ++len; } } } if (len != 0) { m_value.resize(len); std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); *currentPos = pos; } else { throw exceptions::invalid_response("", makeResponseLine("text", line, pos)); } } private: string m_value; const bool m_allow8bits; const char m_except; public: const string& value() const { return (m_value); } }; class text8 : public text { public: text8() : text(true) { } }; template class text_except : public text { public: text_except() : text(false, C) { } }; template class text8_except : public text { public: text8_except() : text(true, C) { } }; // // QUOTED_CHAR ::= / "\" quoted_specials // quoted_specials ::= <"> / "\" // TEXT_CHAR ::= // CHAR ::= // class QUOTED_CHAR : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("quoted_char"); string::size_type pos = *currentPos; const unsigned char c = (pos < line.length() ? line[pos] : 0); if (c >= 0x01 && c <= 0x7f && // 0x01 - 0x7f c != '"' && c != '\\' && // quoted_specials c != '\r' && c != '\n') // CR and LF { m_value = c; *currentPos = pos + 1; } else if (c == '\\' && pos + 1 < line.length() && (line[pos + 1] == '"' || line[pos + 1] == '\\')) { m_value = line[pos + 1]; *currentPos = pos + 2; } else { throw exceptions::invalid_response("", makeResponseLine("QUOTED_CHAR", line, pos)); } } private: char m_value; public: const char value() const { return (m_value); } }; // // quoted ::= <"> *QUOTED_CHAR <"> // QUOTED_CHAR ::= / "\" quoted_specials // quoted_specials ::= <"> / "\" // TEXT_CHAR ::= // CHAR ::= // class quoted_text : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("quoted_text"); string::size_type pos = *currentPos; string::size_type len = 0; bool valid = false; m_value.reserve(line.length() - pos); for (bool end = false, quoted = false ; !end && pos < line.length() ; ) { const unsigned char c = line[pos]; if (quoted) { if (c == '"' || c == '\\') m_value += c; else { m_value += '\\'; m_value += c; } quoted = false; ++pos; ++len; } else { if (c == '\\') { quoted = true; ++pos; ++len; } else if (c == '"') { valid = true; end = true; } else if (c >= 0x01 && c <= 0x7f && // CHAR c != 0x0a && c != 0x0d) // CR and LF { m_value += c; ++pos; ++len; } else { valid = false; end = true; } } } if (valid) { *currentPos = pos; } else { throw exceptions::invalid_response("", makeResponseLine("quoted_text", line, pos)); } } private: string m_value; public: const string& value() const { return (m_value); } }; // // nil ::= "NIL" // class NIL : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("NIL"); string::size_type pos = *currentPos; parser.checkWithArg (line, &pos, "nil"); *currentPos = pos; } }; // // string ::= quoted / literal ----> named 'xstring' // // nil ::= "NIL" // quoted ::= <"> *QUOTED_CHAR <"> // QUOTED_CHAR ::= / "\" quoted_specials // quoted_specials ::= <"> / "\" // TEXT_CHAR ::= // CHAR ::= // literal ::= "{" number "}" CRLF *CHAR8 // ;; Number represents the number of CHAR8 octets // CHAR8 ::= // class xstring : public component { public: xstring(const bool canBeNIL = false, component* comp = NULL, const int data = 0) : m_canBeNIL(canBeNIL), m_component(comp), m_data(data) { } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("string"); string::size_type pos = *currentPos; if (m_canBeNIL && parser.checkWithArg (line, &pos, "nil", true)) { // NIL } else { pos = *currentPos; // quoted ::= <"> *QUOTED_CHAR <"> if (parser.check >(line, &pos, true)) { utility::auto_ptr text(parser.get (line, &pos)); parser.check >(line, &pos); if (parser.m_literalHandler != NULL) { literalHandler::target* target = parser.m_literalHandler->targetFor(*m_component, m_data); if (target != NULL) { m_value = "[literal-handler]"; const string::size_type length = text->value().length(); progressionListener* progress = target->progressionListener(); if (progress) { progress->start(length); } target->putData(text->value()); if (progress) { progress->progress(length, length); progress->stop(length); } delete (target); } else { m_value = text->value(); } } else { m_value = text->value(); } DEBUG_FOUND("string[quoted]", ""); } // literal ::= "{" number "}" CRLF *CHAR8 else { parser.check >(line, &pos); number* num = parser.get (line, &pos); const string::size_type length = num->value(); delete (num); parser.check >(line, &pos); parser.check (line, &pos); if (parser.m_literalHandler != NULL) { literalHandler::target* target = parser.m_literalHandler->targetFor(*m_component, m_data); if (target != NULL) { m_value = "[literal-handler]"; parser.m_progress = target->progressionListener(); parser.readLiteral(*target, length); parser.m_progress = NULL; delete (target); } else { literalHandler::targetString target(NULL, m_value); parser.readLiteral(target, length); } } else { literalHandler::targetString target(NULL, m_value); parser.readLiteral(target, length); } line += parser.readLine(); DEBUG_FOUND("string[literal]", ""); } } *currentPos = pos; } private: bool m_canBeNIL; string m_value; component* m_component; const int m_data; public: const string& value() const { return (m_value); } }; // // nstring ::= string / nil // class nstring : public xstring { public: nstring(component* comp = NULL, const int data = 0) : xstring(true, comp, data) { } }; // // astring ::= atom / string // class astring : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("astring"); string::size_type pos = *currentPos; xstring* str = NULL; if ((str = parser.get (line, &pos, true))) { m_value = str->value(); delete (str); } else { atom* at = parser.get (line, &pos); m_value = at->value(); delete (at); } *currentPos = pos; } private: string m_value; public: const string& value() const { return (m_value); } }; // // atom ::= 1*ATOM_CHAR // // ATOM_CHAR ::= // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials // CHAR ::= // CTL ::= // list_wildcards ::= "%" / "*" // quoted_specials ::= <"> / "\" // SPACE ::= // class atom : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("atom"); string::size_type pos = *currentPos; string::size_type len = 0; for (bool end = false ; !end && pos < line.length() ; ) { const unsigned char c = line[pos]; switch (c) { case '(': case ')': case '{': case 0x20: // SPACE case '%': // list_wildcards case '*': // list_wildcards case '"': // quoted_specials case '\\': // quoted_specials case '[': case ']': // for "special_atom" end = true; break; default: if (c <= 0x1f || c >= 0x7f) end = true; else { ++pos; ++len; } } } if (len != 0) { m_value.resize(len); std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); *currentPos = pos; } else { throw exceptions::invalid_response("", makeResponseLine("atom", line, pos)); } } private: string m_value; public: const string& value() const { return (m_value); } }; // // special atom (eg. "CAPABILITY", "FLAGS", "STATUS"...) // // " Except as noted otherwise, all alphabetic characters are case- // insensitive. The use of upper or lower case characters to define // token strings is for editorial clarity only. Implementations MUST // accept these strings in a case-insensitive fashion. " // class special_atom : public atom { public: special_atom(const char* str) : m_string(str) // 'string' must be in lower-case { } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT(string("special_atom(") + m_string + ")"); string::size_type pos = *currentPos; atom::go(parser, line, &pos); const char* cmp = value().c_str(); const char* with = m_string; bool ok = true; while (ok && *cmp && *with) { ok = (std::tolower(*cmp, std::locale()) == *with); ++cmp; ++with; } if (!ok || *cmp || *with) { throw exceptions::invalid_response("", makeResponseLine(string("special_atom <") + m_string + ">", line, pos)); } else { *currentPos = pos; } } private: const char* m_string; }; // // text_mime2 ::= "=?" "?" "?" "?=" // ;; Syntax defined in [MIME-HDRS] // class text_mime2 : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("text_mime2"); string::size_type pos = *currentPos; atom* theCharset = NULL, *theEncoding = NULL; text* theText = NULL; try { parser.check >(line, &pos); theCharset = parser.get (line, &pos); parser.check >(line, &pos); theEncoding = parser.get (line, &pos); parser.check >(line, &pos); theText = parser.get >(line, &pos); parser.check >(line, &pos); parser.check value()[0] == 'Q') { // Quoted-printable theEncoder = new encoderQP; theEncoder->getProperties()["rfc2047"] = true; } else if (theEncoding->value()[0] == 'b' || theEncoding->value()[0] == 'B') { // Base64 theEncoder = new encoderB64; } if (theEncoder) { utility::inputStreamStringAdapter in(theText->value()); utility::outputStreamStringAdapter out(m_value); theEncoder->decode(in, out); delete (theEncoder); } // No decoder available else { m_value = theText->value(); } delete (theEncoding); delete (theText); *currentPos = pos; } private: vmime::charset m_charset; string m_value; public: const vmime::charset& charset() const { return (m_charset); } const string& value() const { return (m_value); } }; // // flag ::= "\Answered" / "\Flagged" / "\Deleted" / // "\Seen" / "\Draft" / flag_keyword / flag_extension // // flag_extension ::= "\" atom // ;; Future expansion. Client implementations // ;; MUST accept flag_extension flags. Server // ;; implementations MUST NOT generate // ;; flag_extension flags except as defined by // ;; future standard or standards-track // ;; revisions of this specification. // // flag_keyword ::= atom // class flag : public component { public: flag() : m_flag_keyword(NULL) { } ~flag() { delete (m_flag_keyword); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("flag_keyword"); string::size_type pos = *currentPos; if (parser.check >(line, &pos, true)) { if (parser.check >(line, &pos, true)) { m_type = STAR; } else { atom* at = parser.get (line, &pos); const string name = stringUtils::toLower(at->value()); delete (at); if (name == "answered") m_type = ANSWERED; else if (name == "flagged") m_type = FLAGGED; else if (name == "deleted") m_type = DELETED; else if (name == "seen") m_type = SEEN; else if (name == "draft") m_type = DRAFT; else { m_type = UNKNOWN; m_name = name; } } } else { m_flag_keyword = parser.get (line, &pos); } *currentPos = pos; } enum Type { UNKNOWN, ANSWERED, FLAGGED, DELETED, SEEN, DRAFT, STAR // * = custom flags allowed }; private: Type m_type; string m_name; IMAPParser::atom* m_flag_keyword; public: const Type type() const { return (m_type); } const string& name() const { return (m_name); } const IMAPParser::atom* flag_keyword() const { return (m_flag_keyword); } }; // // flag_list ::= "(" #flag ")" // class flag_list : public component { public: ~flag_list() { for (std::vector ::iterator it = m_flags.begin() ; it != m_flags.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("flag_list"); string::size_type pos = *currentPos; parser.check >(line, &pos); while (!parser.check >(line, &pos, true)) { m_flags.push_back(parser.get (line, &pos)); parser.check (line, &pos, true); } *currentPos = pos; } private: std::vector m_flags; public: const std::vector & flags() const { return (m_flags); } }; // // mailbox ::= "INBOX" / astring // ;; INBOX is case-insensitive. All case variants of // ;; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX // ;; not as an astring. Refer to section 5.1 for // ;; further semantic details of mailbox names. // class mailbox : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("mailbox"); string::size_type pos = *currentPos; if (parser.checkWithArg (line, &pos, "inbox", true)) { m_type = INBOX; m_name = "INBOX"; } else { m_type = OTHER; astring* astr = parser.get (line, &pos); m_name = astr->value(); delete (astr); } *currentPos = pos; } enum Type { INBOX, OTHER }; private: Type m_type; string m_name; public: const Type type() const { return (m_type); } const string& name() const { return (m_name); } }; // // mailbox_flag := "\Marked" / "\Noinferiors" / // "\Noselect" / "\Unmarked" / flag_extension // class mailbox_flag : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("mailbox_flag"); string::size_type pos = *currentPos; parser.check >(line, &pos); atom* at = parser.get (line, &pos); const string name = stringUtils::toLower(at->value()); delete (at); if (name == "marked") m_type = MARKED; else if (name == "noinferiors") m_type = NOINFERIORS; else if (name == "noselect") m_type = NOSELECT; else if (name == "unmarked") m_type = UNMARKED; else { m_type = UNKNOWN; m_name = name; } *currentPos = pos; } enum Type { UNKNOWN, MARKED, NOINFERIORS, NOSELECT, UNMARKED }; private: Type m_type; string m_name; public: const Type type() const { return (m_type); } const string& name() const { return (m_name); } }; // // mailbox_flag_list ::= "(" #(mailbox_flag) ")" // class mailbox_flag_list : public component { public: ~mailbox_flag_list() { for (std::vector ::iterator it = m_flags.begin() ; it != m_flags.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("mailbox_flag_list"); string::size_type pos = *currentPos; parser.check >(line, &pos); while (!parser.check >(line, &pos, true)) { m_flags.push_back(parser.get (line, &pos)); parser.check (line, &pos, true); } *currentPos = pos; } private: std::vector m_flags; public: const std::vector & flags() const { return (m_flags); } }; // // mailbox_list ::= mailbox_flag_list SPACE // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox // class mailbox_list : public component { public: mailbox_list() : m_mailbox_flag_list(NULL), m_mailbox(NULL), m_quoted_char('\0') { } ~mailbox_list() { delete (m_mailbox_flag_list); delete (m_mailbox); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("mailbox_list"); string::size_type pos = *currentPos; m_mailbox_flag_list = parser.get (line, &pos); parser.check (line, &pos); if (!parser.check (line, &pos, true)) { parser.check >(line, &pos); QUOTED_CHAR* qc = parser.get (line, &pos); m_quoted_char = qc->value(); delete (qc); parser.check >(line, &pos); } parser.check (line, &pos); m_mailbox = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::mailbox_flag_list* m_mailbox_flag_list; IMAPParser::mailbox* m_mailbox; char m_quoted_char; public: const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } const char quoted_char() const { return (m_quoted_char); } }; // // resp_text_code ::= "ALERT" / "PARSE" / // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / // "UIDVALIDITY" SPACE nz_number / // "UNSEEN" SPACE nz_number / // atom [SPACE 1*] class resp_text_code : public component { public: resp_text_code() : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), m_text(NULL) { } ~resp_text_code() { delete (m_nz_number); delete (m_atom); delete (m_flag_list); delete (m_text); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("resp_text_code"); string::size_type pos = *currentPos; // "ALERT" if (parser.checkWithArg (line, &pos, "alert", true)) { m_type = ALERT; } // "PARSE" else if (parser.checkWithArg (line, &pos, "parse", true)) { m_type = PARSE; } // "PERMANENTFLAGS" SPACE flag_list else if (parser.checkWithArg (line, &pos, "permanentflags", true)) { m_type = PERMANENTFLAGS; parser.check (line, &pos); m_flag_list = parser.get (line, &pos); } // "READ-ONLY" else if (parser.checkWithArg (line, &pos, "read-only", true)) { m_type = READ_ONLY; } // "READ-WRITE" else if (parser.checkWithArg (line, &pos, "read-write", true)) { m_type = READ_WRITE; } // "TRYCREATE" else if (parser.checkWithArg (line, &pos, "trycreate", true)) { m_type = TRYCREATE; } // "UIDVALIDITY" SPACE nz_number else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) { m_type = UIDVALIDITY; parser.check (line, &pos); m_nz_number = parser.get (line, &pos); } // "UNSEEN" SPACE nz_number else if (parser.checkWithArg (line, &pos, "unseen", true)) { m_type = UNSEEN; parser.check (line, &pos); m_nz_number = parser.get (line, &pos); } // atom [SPACE 1*] else { m_type = OTHER; m_atom = parser.get (line, &pos); if (parser.check (line, &pos, true)) m_text = parser.get >(line, &pos); } *currentPos = pos; } enum Type { ALERT, PARSE, PERMANENTFLAGS, READ_ONLY, READ_WRITE, TRYCREATE, UIDVALIDITY, UNSEEN, OTHER }; private: Type m_type; IMAPParser::nz_number* m_nz_number; IMAPParser::atom* m_atom; IMAPParser::flag_list* m_flag_list; IMAPParser::text* m_text; public: const Type type() const { return (m_type); } const IMAPParser::nz_number* nz_number() const { return (m_nz_number); } const IMAPParser::atom* atom() const { return (m_atom); } const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } const IMAPParser::text* text() const { return (m_text); } }; // // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) // ;; text SHOULD NOT begin with "[" or "=" class resp_text : public component { public: resp_text() : m_resp_text_code(NULL) { } ~resp_text() { delete (m_resp_text_code); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("resp_text"); string::size_type pos = *currentPos; if (parser.check >(line, &pos, true)) { m_resp_text_code = parser.get (line, &pos); parser.check >(line, &pos); parser.check (line, &pos); } text_mime2* text1 = parser.get (line, &pos, true); if (text1 != NULL) { m_text = text1->value(); delete (text1); } else { IMAPParser::text* text2 = parser.get (line, &pos); m_text = text2->value(); delete (text2); } *currentPos = pos; } private: IMAPParser::resp_text_code* m_resp_text_code; string m_text; public: const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); } const string& text() const { return (m_text); } }; // // continue_req ::= "+" SPACE (resp_text / base64) // class continue_req : public component { public: continue_req() : m_resp_text(NULL) { } ~continue_req() { delete (m_resp_text); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("continue_req"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.check (line, &pos); m_resp_text = parser.get (line, &pos); parser.check (line, &pos); *currentPos = pos; } private: IMAPParser::resp_text* m_resp_text; public: const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } }; // // auth_type ::= atom // ;; Defined by [IMAP-AUTH] // class auth_type : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("auth_type"); atom* at = parser.get (line, currentPos); m_name = stringUtils::toLower(at->value()); delete (at); if (m_name == "kerberos_v4") m_type = KERBEROS_V4; else if (m_name == "gssapi") m_type = GSSAPI; else if (m_name == "skey") m_type = SKEY; else m_type = UNKNOWN; } enum Type { UNKNOWN, // RFC 1731 - IMAP4 Authentication Mechanisms KERBEROS_V4, GSSAPI, SKEY }; private: Type m_type; string m_name; public: const Type type() const { return (m_type); } const string name() const { return (m_name); } }; // // status_att ::= "MESSAGES" / "RECENT" / "UIDNEXT" / // "UIDVALIDITY" / "UNSEEN" // class status_att : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("status_att"); string::size_type pos = *currentPos; if (parser.checkWithArg (line, &pos, "messages", true)) { m_type = MESSAGES; } else if (parser.checkWithArg (line, &pos, "recent", true)) { m_type = RECENT; } else if (parser.checkWithArg (line, &pos, "uidnext", true)) { m_type = UIDNEXT; } else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) { m_type = UIDVALIDITY; } else { parser.checkWithArg (line, &pos, "unseen"); m_type = UNSEEN; } *currentPos = pos; } enum Type { MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN }; private: Type m_type; public: const Type type() const { return (m_type); } }; // // capability ::= "AUTH=" auth_type / atom // ;; New capabilities MUST begin with "X" or be // ;; registered with IANA as standard or standards-track // class capability : public component { public: capability() : m_auth_type(NULL), m_atom(NULL) { } ~capability() { delete (m_auth_type); delete (m_atom); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("capability"); string::size_type pos = *currentPos; class atom* at = parser.get (line, &pos); string value = at->value(); const char* str = value.c_str(); if ((str[0] == 'a' || str[0] == 'A') && (str[1] == 'u' || str[1] == 'U') && (str[2] == 't' || str[2] == 'T') && (str[3] == 'h' || str[3] == 'H') && (str[4] == '=')) { string::size_type pos = 5; m_auth_type = parser.get (value, &pos); delete (at); } else { m_atom = at; } *currentPos = pos; } private: IMAPParser::auth_type* m_auth_type; IMAPParser::atom* m_atom; public: const IMAPParser::auth_type* auth_type() const { return (m_auth_type); } const IMAPParser::atom* atom() const { return (m_atom); } }; // // capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1" // [SPACE 1#capability] // ;; IMAP4rev1 servers which offer RFC 1730 // ;; compatibility MUST list "IMAP4" as the first // ;; capability. // class capability_data : public component { public: ~capability_data() { for (std::vector ::iterator it = m_capabilities.begin() ; it != m_capabilities.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("capability_data"); string::size_type pos = *currentPos; parser.checkWithArg (line, &pos, "capability"); parser.check (line, &pos); bool IMAP4rev1 = false; for (bool end = false ; !end && !IMAP4rev1 ; ) { if (parser.checkWithArg (line, &pos, "imap4rev1", true)) { IMAP4rev1 = true; } else { capability* cap = parser.get (line, &pos); end = (cap == NULL); if (cap) { m_capabilities.push_back(cap); } } parser.check (line, &pos); } if (parser.check (line, &pos, true)) { for (capability* cap = NULL ; (cap = parser.get (line, &pos)) != NULL ; ) { m_capabilities.push_back(cap); parser.check (line, &pos); } } *currentPos = pos; } private: std::vector m_capabilities; public: const std::vector & capabilities() const { return (m_capabilities); } }; // // date_day_fixed ::= (SPACE digit) / 2digit // ;; Fixed-format version of date_day // // date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" // // date_year ::= 4digit // // time ::= 2digit ":" 2digit ":" 2digit // ;; Hours minutes seconds // // zone ::= ("+" / "-") 4digit // ;; Signed four-digit value of hhmm representing // ;; hours and minutes west of Greenwich (that is, // ;; (the amount that the given time differs from // ;; Universal Time). Subtracting the timezone // ;; from the given time will give the UT form. // ;; The Universal Time zone is "+0000". // // date_time ::= <"> date_day_fixed "-" date_month "-" date_year // SPACE time SPACE zone <"> // class date_time : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("date_time"); string::size_type pos = *currentPos; // <"> date_day_fixed "-" date_month "-" date_year parser.check >(line, &pos); parser.check (line, &pos, true); utility::auto_ptr nd(parser.get (line, &pos)); parser.check >(line, &pos); utility::auto_ptr amo(parser.get (line, &pos)); parser.check >(line, &pos); utility::auto_ptr ny(parser.get (line, &pos)); parser.check (line, &pos, true); // 2digit ":" 2digit ":" 2digit utility::auto_ptr nh(parser.get (line, &pos)); parser.check >(line, &pos); utility::auto_ptr nmi(parser.get (line, &pos)); parser.check >(line, &pos); utility::auto_ptr ns(parser.get (line, &pos)); parser.check (line, &pos, true); // ("+" / "-") 4digit int sign = 1; if (!(parser.check >(line, &pos, true))) parser.check >(line, &pos); utility::auto_ptr nz(parser.get (line, &pos)); parser.check >(line, &pos); m_datetime.setHour(std::min(std::max(nh->value(), 0u), 23u)); m_datetime.setMinute(std::min(std::max(nmi->value(), 0u), 59u)); m_datetime.setSecond(std::min(std::max(ns->value(), 0u), 59u)); const int zone = static_cast (nz->value()); const int zh = zone / 100; // hour offset const int zm = zone % 100; // minute offset m_datetime.setZone(((zh * 60) + zm) * sign); m_datetime.setDay(std::min(std::max(nd->value(), 1u), 31u)); m_datetime.setYear(ny->value()); const string month(stringUtils::toLower(amo->value())); int mon = vmime::datetime::JANUARY; if (month.length() >= 3) { switch (month[0]) { case 'j': { switch (month[1]) { case 'a': mon = vmime::datetime::JANUARY; break; case 'u': { switch (month[2]) { case 'n': mon = vmime::datetime::JUNE; break; default: mon = vmime::datetime::JULY; break; } break; } } break; } case 'f': mon = vmime::datetime::FEBRUARY; break; case 'm': { switch (month[2]) { case 'r': mon = vmime::datetime::MARCH; break; default: mon = vmime::datetime::MAY; break; } break; } case 'a': { switch (month[1]) { case 'p': mon = vmime::datetime::APRIL; break; default: mon = vmime::datetime::AUGUST; break; } break; } case 's': mon = vmime::datetime::SEPTEMBER; break; case 'o': mon = vmime::datetime::OCTOBER; break; case 'n': mon = vmime::datetime::NOVEMBER; break; case 'd': mon = vmime::datetime::DECEMBER; break; } } m_datetime.setMonth(mon); *currentPos = pos; } private: vmime::datetime m_datetime; }; // // header_fld_name ::= astring // typedef astring header_fld_name; // // header_list ::= "(" 1#header_fld_name ")" // class header_list : public component { public: ~header_list() { for (std::vector ::iterator it = m_fld_names.begin() ; it != m_fld_names.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("header_list"); string::size_type pos = *currentPos; parser.check >(line, &pos); while (!parser.check >(line, &pos, true)) { m_fld_names.push_back(parser.get (line, &pos)); parser.check (line, &pos, true); } *currentPos = pos; } private: std::vector m_fld_names; public: const std::vector & fld_names() const { return (m_fld_names); } }; // // body_extension ::= nstring / number / "(" 1#body_extension ")" // ;; Future expansion. Client implementations // ;; MUST accept body_extension fields. Server // ;; implementations MUST NOT generate // ;; body_extension fields except as defined by // ;; future standard or standards-track // ;; revisions of this specification. // class body_extension : public component { public: body_extension() : m_nstring(NULL), m_number(NULL) { } ~body_extension() { delete (m_nstring); delete (m_number); for (std::vector ::iterator it = m_body_extensions.begin() ; it != m_body_extensions.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { string::size_type pos = *currentPos; if (parser.check >(line, &pos, true)) { m_body_extensions.push_back (parser.get (line, &pos)); while (!parser.check >(line, &pos, true)) m_body_extensions.push_back(parser.get (line, &pos, true)); } else { if (!(m_nstring = parser.get (line, &pos, true))) m_number = parser.get (line, &pos); } *currentPos = pos; } private: IMAPParser::nstring* m_nstring; IMAPParser::number* m_number; std::vector m_body_extensions; public: IMAPParser::nstring* nstring() const { return (m_nstring); } IMAPParser::number* number() const { return (m_number); } const std::vector & body_extensions() const { return (m_body_extensions); } }; // // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"] // SPACE header_list / "TEXT" / "MIME" // class section_text : public component { public: section_text() : m_header_list(NULL) { } ~section_text() { delete (m_header_list); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("section_text"); string::size_type pos = *currentPos; // "HEADER.FIELDS" [".NOT"] SPACE header_list const bool b1 = parser.checkWithArg (line, &pos, "header.fields.not", true); const bool b2 = (b1 ? false : parser.checkWithArg (line, &pos, "header.fields", true)); if (b1 || b2) { m_type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; parser.check (line, &pos); m_header_list = parser.get (line, &pos); } // "HEADER" else if (parser.checkWithArg (line, &pos, "header", true)) { m_type = HEADER; } // "MIME" else if (parser.checkWithArg (line, &pos, "mime", true)) { m_type = MIME; } // "TEXT" else { m_type = TEXT; parser.checkWithArg (line, &pos, "text"); } *currentPos = pos; } enum Type { HEADER, HEADER_FIELDS, HEADER_FIELDS_NOT, MIME, TEXT }; private: Type m_type; IMAPParser::header_list* m_header_list; public: const Type type() const { return (m_type); } const IMAPParser::header_list* header_list() const { return (m_header_list); } }; // // section ::= "[" [section_text / (nz_number *["." nz_number] // ["." (section_text / "MIME")])] "]" // class section : public component { public: section() : m_section_text1(NULL), m_section_text2(NULL) { } ~section() { delete (m_section_text1); delete (m_section_text2); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("section"); string::size_type pos = *currentPos; parser.check >(line, &pos); if (!parser.check >(line, &pos, true)) { if (!(m_section_text1 = parser.get (line, &pos, true))) { nz_number* num = parser.get (line, &pos); m_nz_numbers.push_back(num->value()); delete (num); while (parser.check >(line, &pos, true)) { if ((num = parser.get (line, &pos, true))) { m_nz_numbers.push_back(num->value()); delete (num); } else { m_section_text2 = parser.get (line, &pos); break; } } } parser.check >(line, &pos); } *currentPos = pos; } private: section_text* m_section_text1; section_text* m_section_text2; std::vector m_nz_numbers; public: const section_text* section_text1() const { return (m_section_text1); } const section_text* section_text2() const { return (m_section_text2); } const std::vector & nz_numbers() const { return (m_nz_numbers); } }; // // addr_adl ::= nstring // ;; Holds route from [RFC-822] route-addr if // ;; non-NIL // // addr_host ::= nstring // ;; NIL indicates [RFC-822] group syntax. // ;; Otherwise, holds [RFC-822] domain name // // addr_mailbox ::= nstring // ;; NIL indicates end of [RFC-822] group; if // ;; non-NIL and addr_host is NIL, holds // ;; [RFC-822] group name. // ;; Otherwise, holds [RFC-822] local-part // // addr_name ::= nstring // ;; Holds phrase from [RFC-822] mailbox if // ;; non-NIL // // address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox // SPACE addr_host ")" // class address : public component { public: address() : m_addr_name(NULL), m_addr_adl(NULL), m_addr_mailbox(NULL), m_addr_host(NULL) { } ~address() { delete (m_addr_name); delete (m_addr_adl); delete (m_addr_mailbox); delete (m_addr_host); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("address"); string::size_type pos = *currentPos; parser.check >(line, &pos); m_addr_name = parser.get (line, &pos); parser.check (line, &pos); m_addr_adl = parser.get (line, &pos); parser.check (line, &pos); m_addr_mailbox = parser.get (line, &pos); parser.check (line, &pos); m_addr_host = parser.get (line, &pos); parser.check >(line, &pos); *currentPos = pos; } private: nstring* m_addr_name; nstring* m_addr_adl; nstring* m_addr_mailbox; nstring* m_addr_host; public: nstring* addr_name() const { return (m_addr_name); } nstring* addr_adl() const { return (m_addr_adl); } nstring* addr_mailbox() const { return (m_addr_mailbox); } nstring* addr_host() const { return (m_addr_host); } }; // // address_list ::= "(" 1*address ")" / nil // class address_list : public component { public: ~address_list() { for (std::vector ::iterator it = m_addresses.begin() ; it != m_addresses.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("address_list"); string::size_type pos = *currentPos; if (!parser.check (line, &pos, true)) { parser.check >(line, &pos); while (!parser.check >(line, &pos, true)) { m_addresses.push_back(parser.get
(line, &pos)); parser.check (line, &pos, true); } } *currentPos = pos; } private: std::vector m_addresses; public: const std::vector & addresses() const { return (m_addresses); } }; // // env_bcc ::= "(" 1*address ")" / nil // typedef address_list env_bcc; // // env_cc ::= "(" 1*address ")" / nil // typedef address_list env_cc; // // env_date ::= nstring // typedef nstring env_date; // // env_from ::= "(" 1*address ")" / nil // typedef address_list env_from; // // env_in_reply_to ::= nstring // typedef nstring env_in_reply_to; // // env_message_id ::= nstring // typedef nstring env_message_id; // // env_reply_to ::= "(" 1*address ")" / nil // typedef address_list env_reply_to; // // env_sender ::= "(" 1*address ")" / nil // typedef address_list env_sender; // // env_subject ::= nstring // typedef nstring env_subject; // // env_to ::= "(" 1*address ")" / nil // typedef address_list env_to; // // envelope ::= "(" env_date SPACE env_subject SPACE env_from // SPACE env_sender SPACE env_reply_to SPACE env_to // SPACE env_cc SPACE env_bcc SPACE env_in_reply_to // SPACE env_message_id ")" // class envelope : public component { public: envelope() : m_env_date(NULL), m_env_subject(NULL), m_env_from(NULL), m_env_sender(NULL), m_env_reply_to(NULL), m_env_to(NULL), m_env_cc(NULL), m_env_bcc(NULL), m_env_in_reply_to(NULL), m_env_message_id(NULL) { } ~envelope() { delete (m_env_date); delete (m_env_subject); delete (m_env_from); delete (m_env_sender); delete (m_env_reply_to); delete (m_env_to); delete (m_env_cc); delete (m_env_bcc); delete (m_env_in_reply_to); delete (m_env_message_id); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("envelope"); string::size_type pos = *currentPos; parser.check >(line, &pos); m_env_date = parser.get (line, &pos); parser.check (line, &pos); m_env_subject = parser.get (line, &pos); parser.check (line, &pos); m_env_from = parser.get (line, &pos); parser.check (line, &pos); m_env_sender = parser.get (line, &pos); parser.check (line, &pos); m_env_reply_to = parser.get (line, &pos); parser.check (line, &pos); m_env_to = parser.get (line, &pos); parser.check (line, &pos); m_env_cc = parser.get (line, &pos); parser.check (line, &pos); m_env_bcc = parser.get (line, &pos); parser.check (line, &pos); m_env_in_reply_to = parser.get (line, &pos); parser.check (line, &pos); m_env_message_id = parser.get (line, &pos); parser.check >(line, &pos); *currentPos = pos; } private: IMAPParser::env_date* m_env_date; IMAPParser::env_subject* m_env_subject; IMAPParser::env_from* m_env_from; IMAPParser::env_sender* m_env_sender; IMAPParser::env_reply_to* m_env_reply_to; IMAPParser::env_to* m_env_to; IMAPParser::env_cc* m_env_cc; IMAPParser::env_bcc* m_env_bcc; IMAPParser::env_in_reply_to* m_env_in_reply_to; IMAPParser::env_message_id* m_env_message_id; public: const IMAPParser::env_date* env_date() const { return (m_env_date); } const IMAPParser::env_subject* env_subject() const { return (m_env_subject); } const IMAPParser::env_from* env_from() const { return (m_env_from); } const IMAPParser::env_sender* env_sender() const { return (m_env_sender); } const IMAPParser::env_reply_to* env_reply_to() const { return (m_env_reply_to); } const IMAPParser::env_to* env_to() const { return (m_env_to); } const IMAPParser::env_cc* env_cc() const { return (m_env_cc); } const IMAPParser::env_bcc* env_bcc() const { return (m_env_bcc); } const IMAPParser::env_in_reply_to* env_in_reply_to() const { return (m_env_in_reply_to); } const IMAPParser::env_message_id* env_message_id() const { return (m_env_message_id); } }; // // body_fld_desc ::= nstring // typedef nstring body_fld_desc; // // body_fld_id ::= nstring // typedef nstring body_fld_id; // // body_fld_md5 ::= nstring // typedef nstring body_fld_md5; // // body_fld_octets ::= number // typedef number body_fld_octets; // // body_fld_lines ::= number // typedef number body_fld_lines; // // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ // "QUOTED-PRINTABLE") <">) / string // typedef xstring body_fld_enc; // // body_fld_param_item ::= string SPACE string // class body_fld_param_item : public component { public: body_fld_param_item() : m_string1(NULL), m_string2(NULL) { } ~body_fld_param_item() { delete (m_string1); delete (m_string2); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_fld_param_item"); string::size_type pos = *currentPos; m_string1 = parser.get (line, &pos); parser.check (line, &pos); m_string2 = parser.get (line, &pos); DEBUG_FOUND("body_fld_param_item", "<" << m_string1->value() << ", " << m_string2->value() << ">"); *currentPos = pos; } private: xstring* m_string1; xstring* m_string2; public: const xstring* string1() const { return (m_string1); } const xstring* string2() const { return (m_string2); } }; // // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil // class body_fld_param : public component { public: ~body_fld_param() { for (std::vector ::iterator it = m_items.begin() ; it != m_items.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_fld_param"); string::size_type pos = *currentPos; if (!parser.check (line, &pos, true)) { parser.check >(line, &pos); m_items.push_back(parser.get (line, &pos)); while (!parser.check >(line, &pos, true)) { parser.check (line, &pos); m_items.push_back(parser.get (line, &pos)); } } *currentPos = pos; } private: std::vector m_items; public: const std::vector & items() const { return (m_items); } }; // // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil // class body_fld_dsp : public component { public: body_fld_dsp() : m_string(NULL), m_body_fld_param(NULL) { } ~body_fld_dsp() { delete (m_string); delete (m_body_fld_param); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_fld_dsp"); string::size_type pos = *currentPos; if (!parser.check (line, &pos, true)) { parser.check >(line, &pos); m_string = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_param = parser.get (line, &pos); parser.check >(line, &pos); } *currentPos = pos; } private: class xstring* m_string; class body_fld_param* m_body_fld_param; public: const class xstring* str() const { return (m_string); } const class body_fld_param* body_fld_param() const { return (m_body_fld_param); } }; // // body_fld_lang ::= nstring / "(" 1#string ")" // class body_fld_lang : public component { public: ~body_fld_lang() { for (std::vector ::iterator it = m_strings.begin() ; it != m_strings.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_fld_lang"); string::size_type pos = *currentPos; if (parser.check >(line, &pos, true)) { m_strings.push_back(parser.get (line, &pos)); while (!parser.check >(line, &pos, true)) m_strings.push_back(parser.get (line, &pos)); } else { m_strings.push_back(parser.get (line, &pos)); } *currentPos = pos; } private: std::vector m_strings; public: const std::vector & strings() const { return (m_strings); } }; // // body_fields ::= body_fld_param SPACE body_fld_id SPACE // body_fld_desc SPACE body_fld_enc SPACE // body_fld_octets // class body_fields : public component { public: body_fields() : m_body_fld_param(NULL), m_body_fld_id(NULL), m_body_fld_desc(NULL), m_body_fld_enc(NULL), m_body_fld_octets(NULL) { } ~body_fields() { delete (m_body_fld_param); delete (m_body_fld_id); delete (m_body_fld_desc); delete (m_body_fld_enc); delete (m_body_fld_octets); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_fields"); string::size_type pos = *currentPos; m_body_fld_param = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_id = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_desc = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_enc = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_octets = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::body_fld_param* m_body_fld_param; IMAPParser::body_fld_id* m_body_fld_id; IMAPParser::body_fld_desc* m_body_fld_desc; IMAPParser::body_fld_enc* m_body_fld_enc; IMAPParser::body_fld_octets* m_body_fld_octets; public: const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } const IMAPParser::body_fld_id* body_fld_id() const { return (m_body_fld_id); } const IMAPParser::body_fld_desc* body_fld_desc() const { return (m_body_fld_desc); } const IMAPParser::body_fld_enc* body_fld_enc() const { return (m_body_fld_enc); } const IMAPParser::body_fld_octets* body_fld_octets() const { return (m_body_fld_octets); } }; // // media_subtype ::= string // ;; Defined in [MIME-IMT] // typedef xstring media_subtype; // // media_text ::= <"> "TEXT" <"> SPACE media_subtype // ;; Defined in [MIME-IMT] // class media_text : public component { public: media_text() : m_media_subtype(NULL) { } ~media_text() { delete (m_media_subtype); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("media_text"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.checkWithArg (line, &pos, "text"); parser.check >(line, &pos); parser.check (line, &pos); m_media_subtype = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_subtype* m_media_subtype; public: const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } }; // // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <"> // ;; Defined in [MIME-IMT] // class media_message : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("media_message"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.checkWithArg (line, &pos, "message"); parser.check >(line, &pos); parser.check (line, &pos); //parser.check >(line, &pos); //parser.checkWithArg (line, &pos, "rfc822"); //parser.check >(line, &pos); m_media_subtype = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_subtype* m_media_subtype; public: const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } }; // // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" / // "MESSAGE" / "VIDEO") <">) / string) // SPACE media_subtype // ;; Defined in [MIME-IMT] class media_basic : public component { public: media_basic() : m_media_type(NULL), m_media_subtype(NULL) { } ~media_basic() { delete (m_media_type); delete (m_media_subtype); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("media_basic"); string::size_type pos = *currentPos; m_media_type = parser.get (line, &pos); parser.check (line, &pos); m_media_subtype = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::xstring* m_media_type; IMAPParser::media_subtype* m_media_subtype; public: const IMAPParser::xstring* media_type() const { return (m_media_type); } const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } }; // // body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp // [SPACE body_fld_lang // [SPACE 1#body_extension]]] // ;; MUST NOT be returned on non-extensible // ;; "BODY" fetch // class body_ext_1part : public component { public: body_ext_1part() : m_body_fld_md5(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) { } ~body_ext_1part() { delete (m_body_fld_md5); delete (m_body_fld_dsp); delete (m_body_fld_lang); for (std::vector ::iterator it = m_body_extensions.begin() ; it != m_body_extensions.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_ext_1part"); string::size_type pos = *currentPos; m_body_fld_md5 = parser.get (line, &pos); // [SPACE body_fld_dsp if (parser.check (line, &pos, true)) { m_body_fld_dsp = parser.get (line, &pos); // [SPACE body_fld_lang if (parser.check (line, &pos, true)) { m_body_fld_lang = parser.get (line, &pos); // [SPACE 1#body_extension] if (parser.check (line, &pos, true)) { m_body_extensions.push_back (parser.get (line, &pos)); body_extension* ext = NULL; while ((ext = parser.get (line, &pos, true)) != NULL) m_body_extensions.push_back(ext); } } } *currentPos = pos; } private: IMAPParser::body_fld_md5* m_body_fld_md5; IMAPParser::body_fld_dsp* m_body_fld_dsp; IMAPParser::body_fld_lang* m_body_fld_lang; std::vector m_body_extensions; public: const IMAPParser::body_fld_md5* body_fld_md5() const { return (m_body_fld_md5); } const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } const std::vector body_extensions() const { return (m_body_extensions); } }; // // body_ext_mpart ::= body_fld_param // [SPACE body_fld_dsp SPACE body_fld_lang // [SPACE 1#body_extension]] // ;; MUST NOT be returned on non-extensible // ;; "BODY" fetch class body_ext_mpart : public component { public: body_ext_mpart() : m_body_fld_param(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) { } ~body_ext_mpart() { delete (m_body_fld_param); delete (m_body_fld_dsp); delete (m_body_fld_lang); for (std::vector ::iterator it = m_body_extensions.begin() ; it != m_body_extensions.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_ext_mpart"); string::size_type pos = *currentPos; m_body_fld_param = parser.get (line, &pos); // [SPACE body_fld_dsp SPACE body_fld_lang [SPACE 1#body_extension]] if (parser.check (line, &pos, true)) { m_body_fld_dsp = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_lang = parser.get (line, &pos); // [SPACE 1#body_extension] if (parser.check (line, &pos, true)) { m_body_extensions.push_back (parser.get (line, &pos)); body_extension* ext = NULL; while ((ext = parser.get (line, &pos, true)) != NULL) m_body_extensions.push_back(ext); } } *currentPos = pos; } private: IMAPParser::body_fld_param* m_body_fld_param; IMAPParser::body_fld_dsp* m_body_fld_dsp; IMAPParser::body_fld_lang* m_body_fld_lang; std::vector m_body_extensions; public: const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } const std::vector body_extensions() const { return (m_body_extensions); } }; // // body_type_basic ::= media_basic SPACE body_fields // ;; MESSAGE subtype MUST NOT be "RFC822" // class body_type_basic : public component { public: body_type_basic() : m_media_basic(NULL), m_body_fields(NULL) { } ~body_type_basic() { delete (m_media_basic); delete (m_body_fields); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_type_basic"); string::size_type pos = *currentPos; m_media_basic = parser.get (line, &pos); parser.check (line, &pos); m_body_fields = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_basic* m_media_basic; IMAPParser::body_fields* m_body_fields; public: const IMAPParser::media_basic* media_basic() const { return (m_media_basic); } const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } }; // // body_type_msg ::= media_message SPACE body_fields SPACE envelope // SPACE body SPACE body_fld_lines // class xbody; typedef xbody body; class body_type_msg : public component { public: body_type_msg() : m_media_message(NULL), m_body_fields(NULL), m_envelope(NULL), m_body(NULL), m_body_fld_lines(NULL) { } ~body_type_msg() { delete (m_media_message); delete (m_body_fields); delete (m_envelope); delete (m_body); delete (m_body_fld_lines); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_type_msg"); string::size_type pos = *currentPos; m_media_message = parser.get (line, &pos); parser.check (line, &pos); m_body_fields = parser.get (line, &pos); parser.check (line, &pos); m_envelope = parser.get (line, &pos); parser.check (line, &pos); m_body = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_lines = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_message* m_media_message; IMAPParser::body_fields* m_body_fields; IMAPParser::envelope* m_envelope; IMAPParser::xbody* m_body; IMAPParser::body_fld_lines* m_body_fld_lines; public: const IMAPParser::media_message* media_message() const { return (m_media_message); } const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } const IMAPParser::envelope* envelope() const { return (m_envelope); } const IMAPParser::xbody* body() const { return (m_body); } const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } }; // // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines // class body_type_text : public component { public: body_type_text() : m_media_text(NULL), m_body_fields(NULL), m_body_fld_lines(NULL) { } ~body_type_text() { delete (m_media_text); delete (m_body_fields); delete (m_body_fld_lines); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_type_text"); string::size_type pos = *currentPos; m_media_text = parser.get (line, &pos); parser.check (line, &pos); m_body_fields = parser.get (line, &pos); parser.check (line, &pos); m_body_fld_lines = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_text* m_media_text; IMAPParser::body_fields* m_body_fields; IMAPParser::body_fld_lines* m_body_fld_lines; public: const IMAPParser::media_text* media_text() const { return (m_media_text); } const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } }; // // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text) // [SPACE body_ext_1part] // class body_type_1part : public component { public: body_type_1part() : m_body_type_basic(NULL), m_body_type_msg(NULL), m_body_type_text(NULL), m_body_ext_1part(NULL) { } ~body_type_1part() { delete (m_body_type_basic); delete (m_body_type_msg); delete (m_body_type_text); delete (m_body_ext_1part); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_type_1part"); string::size_type pos = *currentPos; if (!(m_body_type_text = parser.get (line, &pos, true))) if (!(m_body_type_msg = parser.get (line, &pos, true))) m_body_type_basic = parser.get (line, &pos); if (parser.check (line, &pos, true)) m_body_ext_1part = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::body_type_basic* m_body_type_basic; IMAPParser::body_type_msg* m_body_type_msg; IMAPParser::body_type_text* m_body_type_text; IMAPParser::body_ext_1part* m_body_ext_1part; public: const IMAPParser::body_type_basic* body_type_basic() const { return (m_body_type_basic); } const IMAPParser::body_type_msg* body_type_msg() const { return (m_body_type_msg); } const IMAPParser::body_type_text* body_type_text() const { return (m_body_type_text); } const IMAPParser::body_ext_1part* body_ext_1part() const { return (m_body_ext_1part); } }; // // body_type_mpart ::= 1*body SPACE media_subtype // [SPACE body_ext_mpart] // class body_type_mpart : public component { public: body_type_mpart() : m_media_subtype(NULL), m_body_ext_mpart(NULL) { } ~body_type_mpart() { delete (m_media_subtype); delete (m_body_ext_mpart); for (std::vector ::iterator it = m_list.begin() ; it != m_list.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body_type_mpart"); string::size_type pos = *currentPos; m_list.push_back(parser.get (line, &pos)); for (xbody* b ; (b = parser.get (line, &pos, true)) ; ) m_list.push_back(b); parser.check (line, &pos); m_media_subtype = parser.get (line, &pos); if (parser.check (line, &pos, true)) m_body_ext_mpart = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::media_subtype* m_media_subtype; IMAPParser::body_ext_mpart* m_body_ext_mpart; std::vector m_list; public: const std::vector & list() const { return (m_list); } const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } const IMAPParser::body_ext_mpart* body_ext_mpart() const { return (m_body_ext_mpart); } }; // // xbody ::= "(" body_type_1part / body_type_mpart ")" // class xbody : public component { public: xbody() : m_body_type_1part(NULL), m_body_type_mpart(NULL) { } ~xbody() { delete (m_body_type_1part); delete (m_body_type_mpart); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("body"); string::size_type pos = *currentPos; parser.check >(line, &pos); if (!(m_body_type_1part = parser.get (line, &pos, true))) m_body_type_mpart = parser.get (line, &pos); parser.check >(line, &pos); *currentPos = pos; } private: IMAPParser::body_type_1part* m_body_type_1part; IMAPParser::body_type_mpart* m_body_type_mpart; public: const IMAPParser::body_type_1part* body_type_1part() const { return (m_body_type_1part); } const IMAPParser::body_type_mpart* body_type_mpart() const { return (m_body_type_mpart); } }; // // uniqueid ::= nz_number // ;; Strictly ascending // // msg_att_item ::= "ENVELOPE" SPACE envelope / // "FLAGS" SPACE "(" #(flag / "\Recent") ")" / // "INTERNALDATE" SPACE date_time / // "RFC822" [".HEADER" / ".TEXT"] SPACE nstring / // "RFC822.SIZE" SPACE number / // "BODY" ["STRUCTURE"] SPACE body / // "BODY" section ["<" number ">"] SPACE nstring / // "UID" SPACE uniqueid // class msg_att_item : public component { public: msg_att_item() : m_date_time(NULL), m_number(NULL), m_envelope(NULL), m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL) { } ~msg_att_item() { delete (m_date_time); delete (m_number); delete (m_envelope); delete (m_uniqueid); delete (m_nstring); delete (m_body); delete (m_flag_list); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("msg_att_item"); string::size_type pos = *currentPos; // "ENVELOPE" SPACE envelope if (parser.checkWithArg (line, &pos, "envelope", true)) { m_type = ENVELOPE; parser.check (line, &pos); m_envelope = parser.get (line, &pos); } // "FLAGS" SPACE "(" #(flag / "\Recent") ")" else if (parser.checkWithArg (line, &pos, "flags", true)) { m_type = FLAGS; parser.check (line, &pos); m_flag_list = parser.get (line, &pos); } // "INTERNALDATE" SPACE date_time else if (parser.checkWithArg (line, &pos, "internaldate", true)) { m_type = INTERNALDATE; parser.check (line, &pos); m_date_time = parser.get (line, &pos); } // "RFC822" ".HEADER" SPACE nstring else if (parser.checkWithArg (line, &pos, "rfc822.header", true)) { m_type = RFC822_HEADER; parser.check (line, &pos); m_nstring = parser.get (line, &pos); } // "RFC822" ".TEXT" SPACE nstring else if (parser.checkWithArg (line, &pos, "rfc822.text", true)) { m_type = RFC822_TEXT; parser.check (line, &pos); m_nstring = parser.getWithArgs (line, &pos, this, RFC822_TEXT); } // "RFC822.SIZE" SPACE number else if (parser.checkWithArg (line, &pos, "rfc822.size", true)) { m_type = RFC822_SIZE; parser.check (line, &pos); m_number = parser.get (line, &pos); } // "RFC822" SPACE nstring else if (parser.checkWithArg (line, &pos, "rfc822", true)) { m_type = RFC822; parser.check (line, &pos); m_nstring = parser.get (line, &pos); } // "BODY" "STRUCTURE" SPACE body else if (parser.checkWithArg (line, &pos, "bodystructure", true)) { m_type = BODY_STRUCTURE; parser.check (line, &pos); m_body = parser.get (line, &pos); } // "BODY" section ["<" number ">"] SPACE nstring // "BODY" SPACE body else if (parser.checkWithArg (line, &pos, "body", true)) { m_section = parser.get (line, &pos, true); // "BODY" section ["<" number ">"] SPACE nstring if (m_section != NULL) { m_type = BODY_SECTION; if (parser.check >(line, &pos, true)) { m_number = parser.get (line, &pos); parser.check '> >(line, &pos); } parser.check (line, &pos); m_nstring = parser.getWithArgs (line, &pos, this, BODY_SECTION); } // "BODY" SPACE body else { m_type = BODY; parser.check (line, &pos); m_body = parser.get (line, &pos); } } // "UID" SPACE uniqueid else { m_type = UID; parser.checkWithArg (line, &pos, "uid"); parser.check (line, &pos); m_uniqueid = parser.get (line, &pos); } *currentPos = pos; } enum Type { ENVELOPE, FLAGS, INTERNALDATE, RFC822, RFC822_SIZE, RFC822_HEADER, RFC822_TEXT, BODY, BODY_SECTION, BODY_STRUCTURE, UID }; private: Type m_type; IMAPParser::date_time* m_date_time; IMAPParser::number* m_number; IMAPParser::envelope* m_envelope; IMAPParser::nz_number* m_uniqueid; IMAPParser::nstring* m_nstring; IMAPParser::xbody* m_body; IMAPParser::flag_list* m_flag_list; IMAPParser::section* m_section; public: const Type type() const { return (m_type); } const IMAPParser::date_time* date_time() const { return (m_date_time); } const IMAPParser::number* number() const { return (m_number); } const IMAPParser::envelope* envelope() const { return (m_envelope); } const IMAPParser::nz_number* unique_id() const { return (m_uniqueid); } const IMAPParser::nstring* nstring() const { return (m_nstring); } const IMAPParser::xbody* body() const { return (m_body); } const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } const IMAPParser::section* section() const { return (m_section); } }; // // msg_att ::= "(" 1#(msg_att_item) ")" // class msg_att : public component { public: ~msg_att() { for (std::vector ::iterator it = m_items.begin() ; it != m_items.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("msg_att"); string::size_type pos = *currentPos; parser.check >(line, &pos); m_items.push_back(parser.get (line, &pos)); while (!parser.check >(line, &pos, true)) { parser.check (line, &pos); m_items.push_back(parser.get (line, &pos)); } *currentPos = pos; } private: std::vector m_items; public: const std::vector & items() const { return (m_items); } }; // // message_data ::= nz_number SPACE ("EXPUNGE" / // ("FETCH" SPACE msg_att)) // class message_data : public component { public: message_data() : m_number(0), m_msg_att(NULL) { } ~message_data() { delete (m_msg_att); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("message_data"); string::size_type pos = *currentPos; nz_number* num = parser.get (line, &pos); m_number = num->value(); delete (num); parser.check (line, &pos); if (parser.checkWithArg (line, &pos, "expunge", true)) { m_type = EXPUNGE; } else { parser.checkWithArg (line, &pos, "fetch"); parser.check (line, &pos); m_type = FETCH; m_msg_att = parser.get (line, &pos); } *currentPos = pos; } enum Type { EXPUNGE, FETCH }; private: Type m_type; unsigned int m_number; IMAPParser::msg_att* m_msg_att; public: const Type type() const { return (m_type); } const unsigned int number() const { return (m_number); } const IMAPParser::msg_att* msg_att() const { return (m_msg_att); } }; // // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text // ;; Status condition // class resp_cond_state : public component { public: resp_cond_state() : m_resp_text(NULL), m_status(BAD) { } ~resp_cond_state() { delete (m_resp_text); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("resp_cond_state"); string::size_type pos = *currentPos; if (parser.checkWithArg (line, &pos, "ok", true)) { m_status = OK; } else if (parser.checkWithArg (line, &pos, "no", true)) { m_status = NO; } else { parser.checkWithArg (line, &pos, "bad"); m_status = BAD; } parser.check (line, &pos); m_resp_text = parser.get (line, &pos); *currentPos = pos; } enum Status { OK, NO, BAD }; private: IMAPParser::resp_text* m_resp_text; Status m_status; public: const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } const Status status() const { return (m_status); } }; // // resp_cond_bye ::= "BYE" SPACE resp_text // class resp_cond_bye : public component { public: resp_cond_bye() : m_resp_text(NULL) { } ~resp_cond_bye() { delete (m_resp_text); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("resp_cond_bye"); string::size_type pos = *currentPos; parser.checkWithArg (line, &pos, "bye"); parser.check (line, &pos); m_resp_text = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::resp_text* m_resp_text; public: const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } }; // // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text // ;; Authentication condition // class resp_cond_auth : public component { public: resp_cond_auth() : m_resp_text(NULL) { } ~resp_cond_auth() { delete (m_resp_text); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("resp_cond_auth"); string::size_type pos = *currentPos; if (parser.checkWithArg (line, &pos, "ok", true)) { m_cond = OK; } else { parser.checkWithArg (line, &pos, "preauth"); m_cond = PREAUTH; } parser.check (line, &pos); m_resp_text = parser.get (line, &pos); *currentPos = pos; } enum Condition { OK, PREAUTH }; private: Condition m_cond; IMAPParser::resp_text* m_resp_text; public: const Condition condition() const { return (m_cond); } const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } }; // // status_info ::= status_att SPACE number // class status_info : public component { public: status_info() : m_status_att(NULL), m_number(NULL) { } ~status_info() { delete (m_status_att); delete (m_number); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("status_info"); string::size_type pos = *currentPos; m_status_att = parser.get (line, &pos); parser.check (line, &pos); m_number = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::status_att* m_status_att; IMAPParser::number* m_number; public: const IMAPParser::status_att* status_att() const { return (m_status_att); } const IMAPParser::number* number() const { return (m_number); } }; // // mailbox_data ::= "FLAGS" SPACE mailbox_flag_list / // "LIST" SPACE mailbox_list / // "LSUB" SPACE mailbox_list / // "MAILBOX" SPACE text / // "SEARCH" [SPACE 1#nz_number] / // "STATUS" SPACE mailbox SPACE // "(" #::iterator it = m_search_nz_number_list.begin() ; it != m_search_nz_number_list.end() ; ++it) { delete (*it); } } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("mailbox_data"); string::size_type pos = *currentPos; m_number = parser.get (line, &pos, true); if (m_number) { parser.check (line, &pos); if (parser.checkWithArg (line, &pos, "exists", true)) { m_type = EXISTS; } else { parser.checkWithArg (line, &pos, "recent"); m_type = RECENT; } } else { // "FLAGS" SPACE mailbox_flag_list if (parser.checkWithArg (line, &pos, "flags", true)) { parser.check (line, &pos); m_mailbox_flag_list = parser.get (line, &pos); m_type = FLAGS; } // "LIST" SPACE mailbox_list else if (parser.checkWithArg (line, &pos, "list", true)) { parser.check (line, &pos); m_mailbox_list = parser.get (line, &pos); m_type = LIST; } // "LSUB" SPACE mailbox_list else if (parser.checkWithArg (line, &pos, "lsub", true)) { parser.check (line, &pos); m_mailbox_list = parser.get (line, &pos); m_type = LSUB; } // "MAILBOX" SPACE text else if (parser.checkWithArg (line, &pos, "mailbox", true)) { parser.check (line, &pos); m_text = parser.get (line, &pos); m_type = MAILBOX; } // "SEARCH" [SPACE 1#nz_number] else if (parser.checkWithArg (line, &pos, "search", true)) { if (parser.check (line, &pos, true)) { m_search_nz_number_list.push_back (parser.get (line, &pos)); while (parser.check (line, &pos, true)) { m_search_nz_number_list.push_back (parser.get (line, &pos)); } } m_type = SEARCH; } // "STATUS" SPACE mailbox SPACE // "(" #(line, &pos, "status"); parser.check (line, &pos); m_mailbox = parser.get (line, &pos); parser.check (line, &pos); parser.check >(line, &pos); m_status_info_list.push_back(parser.get (line, &pos)); while (!parser.check >(line, &pos, true)) m_status_info_list.push_back(parser.get (line, &pos)); m_type = STATUS; } } *currentPos = pos; } enum Type { FLAGS, LIST, LSUB, MAILBOX, SEARCH, STATUS, EXISTS, RECENT }; private: Type m_type; IMAPParser::number* m_number; IMAPParser::mailbox_flag_list* m_mailbox_flag_list; IMAPParser::mailbox_list* m_mailbox_list; IMAPParser::mailbox* m_mailbox; IMAPParser::text* m_text; std::vector m_search_nz_number_list; std::vector m_status_info_list; public: const Type type() const { return (m_type); } const IMAPParser::number* number() const { return (m_number); } const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } const IMAPParser::mailbox_list* mailbox_list() const { return (m_mailbox_list); } const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } const IMAPParser::text* text() const { return (m_text); } const std::vector & search_nz_number_list() const { return (m_search_nz_number_list); } const std::vector & status_info_list() const { return (m_status_info_list); } }; // // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / // mailbox_data / message_data / capability_data) CRLF // class response_data : public component { public: response_data() : m_resp_cond_state(NULL), m_resp_cond_bye(NULL), m_mailbox_data(NULL), m_message_data(NULL), m_capability_data(NULL) { } ~response_data() { delete (m_resp_cond_state); delete (m_resp_cond_bye); delete (m_mailbox_data); delete (m_message_data); delete (m_capability_data); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("response_data"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.check (line, &pos); if (!(m_resp_cond_state = parser.get (line, &pos, true))) if (!(m_resp_cond_bye = parser.get (line, &pos, true))) if (!(m_mailbox_data = parser.get (line, &pos, true))) if (!(m_message_data = parser.get (line, &pos, true))) m_capability_data = parser.get (line, &pos); parser.check (line, &pos); *currentPos = pos; } private: IMAPParser::resp_cond_state* m_resp_cond_state; IMAPParser::resp_cond_bye* m_resp_cond_bye; IMAPParser::mailbox_data* m_mailbox_data; IMAPParser::message_data* m_message_data; IMAPParser::capability_data* m_capability_data; public: const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } const IMAPParser::mailbox_data* mailbox_data() const { return (m_mailbox_data); } const IMAPParser::message_data* message_data() const { return (m_message_data); } const IMAPParser::capability_data* capability_data() const { return (m_capability_data); } }; class continue_req_or_response_data : public component { public: continue_req_or_response_data() : m_continue_req(NULL), m_response_data(NULL) { } ~continue_req_or_response_data() { delete (m_continue_req); delete (m_response_data); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("continue_req_or_response_data"); string::size_type pos = *currentPos; if (!(m_continue_req = parser.get (line, &pos, true))) m_response_data = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::continue_req* m_continue_req; IMAPParser::response_data* m_response_data; public: const IMAPParser::continue_req* continue_req() const { return (m_continue_req); } const IMAPParser::response_data* response_data() const { return (m_response_data); } }; // // response_fatal ::= "*" SPACE resp_cond_bye CRLF // ;; Server closes connection immediately // class response_fatal : public component { public: response_fatal() : m_resp_cond_bye(NULL) { } ~response_fatal() { delete (m_resp_cond_bye); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("response_fatal"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.check (line, &pos); m_resp_cond_bye = parser.get (line, &pos); parser.check (line, &pos); *currentPos = pos; } private: IMAPParser::resp_cond_bye* m_resp_cond_bye; public: const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } }; // // response_tagged ::= tag SPACE resp_cond_state CRLF // class response_tagged : public component { public: response_tagged() : m_resp_cond_state(NULL) { } ~response_tagged() { delete (m_resp_cond_state); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("response_tagged"); string::size_type pos = *currentPos; parser.check (line, &pos); parser.check (line, &pos); m_resp_cond_state = parser.get (line, &pos); parser.check (line, &pos); *currentPos = pos; } private: IMAPParser::resp_cond_state* m_resp_cond_state; public: const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } }; // // response_done ::= response_tagged / response_fatal // class response_done : public component { public: response_done() : m_response_tagged(NULL), m_response_fatal(NULL) { } ~response_done() { delete (m_response_tagged); delete (m_response_fatal); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("response_done"); string::size_type pos = *currentPos; if (!(m_response_tagged = parser.get (line, &pos, true))) m_response_fatal = parser.get (line, &pos); *currentPos = pos; } private: IMAPParser::response_tagged* m_response_tagged; IMAPParser::response_fatal* m_response_fatal; public: const IMAPParser::response_tagged* response_tagged() const { return (m_response_tagged); } const IMAPParser::response_fatal* response_fatal() const { return (m_response_fatal); } }; // // response ::= *(continue_req / response_data) response_done // class response : public component { public: response() : m_response_done(NULL) { } ~response() { for (std::vector ::iterator it = m_continue_req_or_response_data.begin() ; it != m_continue_req_or_response_data.end() ; ++it) { delete (*it); } delete (m_response_done); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("response"); string::size_type pos = *currentPos; string curLine = line; bool partial = false; // partial response IMAPParser::continue_req_or_response_data* resp = NULL; while ((resp = parser.get (curLine, &pos, true)) != NULL) { m_continue_req_or_response_data.push_back(resp); // Partial response (continue_req) if (resp->continue_req()) { partial = true; break; } // We have read a CRLF, read another line curLine = parser.readLine(); pos = 0; } if (!partial) m_response_done = parser.get (curLine, &pos); *currentPos = pos; } const bool isBad() const { if (!response_done()) // incomplete (partial) response return (true); if (response_done()->response_fatal()) return (true); if (response_done()->response_tagged()->resp_cond_state()-> status() == IMAPParser::resp_cond_state::BAD) { return (true); } return (false); } private: std::vector m_continue_req_or_response_data; IMAPParser::response_done* m_response_done; public: const std::vector & continue_req_or_response_data() const { return (m_continue_req_or_response_data); } const IMAPParser::response_done* response_done() const { return (m_response_done); } }; // // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF // class greeting : public component { public: greeting() : m_resp_cond_auth(NULL), m_resp_cond_bye(NULL) { } ~greeting() { delete (m_resp_cond_auth); delete (m_resp_cond_bye); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("greeting"); string::size_type pos = *currentPos; parser.check >(line, &pos); parser.check (line, &pos); if (!(m_resp_cond_auth = parser.get (line, &pos, true))) m_resp_cond_bye = parser.get (line, &pos); parser.check (line, &pos); *currentPos = pos; } private: IMAPParser::resp_cond_auth* m_resp_cond_auth; IMAPParser::resp_cond_bye* m_resp_cond_bye; public: const IMAPParser::resp_cond_auth* resp_cond_auth() const { return (m_resp_cond_auth); } const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } }; // // The main functions used to parse a response // response* readResponse(literalHandler* lh = NULL) { string::size_type pos = 0; string line = readLine(); m_literalHandler = lh; response* resp = get (line, &pos); m_literalHandler = NULL; return (resp); } greeting* readGreeting() { string::size_type pos = 0; string line = readLine(); return get (line, &pos); } // // Get a token and advance // template TYPE* get(string& line, string::size_type* currentPos, const bool noThrow = false) { component* resp = new TYPE; return internalGet (resp, line, currentPos, noThrow); } template TYPE* getWithArgs(string& line, string::size_type* currentPos, ARG1_TYPE arg1, ARG2_TYPE arg2, const bool noThrow = false) { component* resp = new TYPE(arg1, arg2); return internalGet (resp, line, currentPos, noThrow); } private: template TYPE* internalGet(component* resp, string& line, string::size_type* currentPos, const bool noThrow = false) { #if DEBUG_RESPONSE DEBUG_RESPONSE_level += " "; #endif try { resp->go(*this, line, currentPos); #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); DEBUG_RESPONSE_components.pop_back(); #endif } catch (...) { #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); DEBUG_RESPONSE_components.pop_back(); #endif delete (resp); if (!noThrow) throw; return (NULL); } return static_cast (resp); } public: // // Check a token and advance // template const bool check(string& line, string::size_type* currentPos, const bool noThrow = false) { try { TYPE term; term.go(*this, line, currentPos); #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_components.pop_back(); #endif } catch (...) { #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_components.pop_back(); #endif if (!noThrow) throw; return false; } return true; } template const bool checkWithArg(string& line, string::size_type* currentPos, const ARG_TYPE arg, const bool noThrow = false) { try { TYPE term(arg); term.go(*this, line, currentPos); #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_components.pop_back(); #endif } catch (...) { #if DEBUG_RESPONSE std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; DEBUG_RESPONSE_components.pop_back(); #endif if (!noThrow) throw; return false; } return true; } private: IMAPTag* m_tag; socket* m_socket; progressionListener* m_progress; literalHandler* m_literalHandler; timeoutHandler* m_timeoutHandler; string m_buffer; int m_pos; string m_lastLine; public: // // Read one line // const string readLine() { string::size_type pos; while ((pos = m_buffer.find('\n')) == string::npos) { read(); } string line; line.resize(pos + 1); std::copy(m_buffer.begin(), m_buffer.begin() + pos + 1, line.begin()); m_buffer.erase(m_buffer.begin(), m_buffer.begin() + pos + 1); m_lastLine = line; #if DEBUG_RESPONSE std::cout << std::endl << "Read line:" << std::endl << line << std::endl; #endif return (line); } // // Read available data from socket stream // void read() { string receiveBuffer; while (receiveBuffer.empty()) { // Check whether the time-out delay is elapsed if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { if (!m_timeoutHandler->handleTimeOut()) throw exceptions::operation_timed_out(); } // We have received data: reset the time-out counter m_socket->receive(receiveBuffer); if (receiveBuffer.empty()) // buffer is empty { platformDependant::getHandler()->wait(); continue; } // We have received data ... if (m_timeoutHandler) m_timeoutHandler->resetTimeOut(); } m_buffer += receiveBuffer; } void readLiteral(literalHandler::target& buffer, string::size_type count) { string::size_type len = 0; string receiveBuffer; if (m_progress) m_progress->start(count); if (m_timeoutHandler) m_timeoutHandler->resetTimeOut(); if (!m_buffer.empty()) { if (m_buffer.length() > count) { buffer.putData(string(m_buffer.begin(), m_buffer.begin() + count)); m_buffer.erase(m_buffer.begin(), m_buffer.begin() + count); len = count; } else { len += m_buffer.length(); buffer.putData(m_buffer); m_buffer.clear(); } } while (len < count) { // Check whether the time-out delay is elapsed if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { if (!m_timeoutHandler->handleTimeOut()) throw exceptions::operation_timed_out(); } // Receive data from the socket m_socket->receive(receiveBuffer); if (receiveBuffer.empty()) // buffer is empty { platformDependant::getHandler()->wait(); continue; } // We have received data: reset the time-out counter if (m_timeoutHandler) m_timeoutHandler->resetTimeOut(); if (len + receiveBuffer.length() > count) { const string::size_type remaining = count - len; // Get the needed amount of data buffer.putData(string(receiveBuffer.begin(), receiveBuffer.begin() + remaining)); // Put the remaining data into the internal response buffer receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + remaining); m_buffer += receiveBuffer; len = count; } else { buffer.putData(receiveBuffer); len += receiveBuffer.length(); } // Notify progression if (m_progress) m_progress->progress(len, count); } if (m_progress) m_progress->stop(count); } }; } // messaging } // vmime #endif // VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED