diff options
Diffstat (limited to 'src/messaging/IMAPParser.hpp')
-rw-r--r-- | src/messaging/IMAPParser.hpp | 5075 |
1 files changed, 5075 insertions, 0 deletions
diff --git a/src/messaging/IMAPParser.hpp b/src/messaging/IMAPParser.hpp new file mode 100644 index 00000000..2e4a56ab --- /dev/null +++ b/src/messaging/IMAPParser.hpp @@ -0,0 +1,5075 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 "../encoderB64.hpp" +#include "../encoderQP.hpp" + +#include "../platformDependant.hpp" + +#include "progressionListener.hpp" +#include "timeoutHandler.hpp" +#include "socket.hpp" + +#include "IMAPTag.hpp" + +#include <vector> +#include <stdexcept> + + +//#define DEBUG_RESPONSE 1 + + +#if DEBUG_RESPONSE +# include <iostream> +#endif + + +namespace vmime { +namespace messaging { + + +#if DEBUG_RESPONSE + static string DEBUG_RESPONSE_level; + static std::vector <string> 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 <char C> + 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 ::= <ASCII SP, space, 0x20> + // + + 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 ::= <ASCII CR, carriage return, 0x0D> + // LF ::= <ASCII LF, line feed, 0x0A> + // 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 <SPACE>(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 ::= <ASCII SP, space, 0x20> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // ATOM_CHAR ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // + // tag ::= 1*<any ATOM_CHAR except "+"> (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 ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // TEXT_CHAR ::= <any CHAR except CR and LF> + // + + 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 <char C> + class text_except : public text + { + public: + + text_except() : text(false, C) + { + } + }; + + + template <char C> + class text8_except : public text + { + public: + + text8_except() : text(true, C) + { + } + }; + + + // + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + 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 ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + 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 <special_atom>(line, &pos, "nil"); + + *currentPos = pos; + } + }; + + + // + // string ::= quoted / literal ----> named 'xstring' + // + // nil ::= "NIL" + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // literal ::= "{" number "}" CRLF *CHAR8 + // ;; Number represents the number of CHAR8 octets + // CHAR8 ::= <any 8-bit octet except NUL, 0x01 - 0xff> + // + + 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 <special_atom>(line, &pos, "nil", true)) + { + // NIL + } + else + { + pos = *currentPos; + + // quoted ::= <"> *QUOTED_CHAR <"> + if (parser.check <one_char <'"'> >(line, &pos, true)) + { + utility::auto_ptr <quoted_text> text(parser.get <quoted_text>(line, &pos)); + parser.check <one_char <'"'> >(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]", "<length=" << m_value.length() << ", value='" << m_value << "'>"); + } + // literal ::= "{" number "}" CRLF *CHAR8 + else + { + parser.check <one_char <'{'> >(line, &pos); + + number* num = parser.get <number>(line, &pos); + + const string::size_type length = num->value(); + delete (num); + + parser.check <one_char <'}'> >(line, &pos); + + parser.check <CRLF>(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]", "<length=" << length << ", value='" << m_value << "'>"); + } + } + + *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 <xstring>(line, &pos, true))) + { + m_value = str->value(); + delete (str); + } + else + { + atom* at = parser.get <atom>(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 ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // SPACE ::= <ASCII SP, space, 0x20> + // + + 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 ::= "=?" <charset> "?" <encoding> "?" <encoded-text> "?=" + // ;; 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 <one_char <'='> >(line, &pos); + parser.check <one_char <'?'> >(line, &pos); + + theCharset = parser.get <atom>(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + + theEncoding = parser.get <atom>(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + + theText = parser.get <text8_except <'?'> >(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + parser.check <one_char <'='> >(line, &pos); + } + catch (std::exception& e) + { + delete (theCharset); + delete (theEncoding); + delete (theText); + + throw; + } + + m_charset = theCharset->value(); + delete (theCharset); + + // Decode text + encoder* theEncoder = NULL; + + if (theEncoding->value()[0] == 'q' || theEncoding->value()[0] == 'Q') + { + // Quoted-printable + theEncoder = new encoderQP; + theEncoder->properties()["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 <one_char <'\\'> >(line, &pos, true)) + { + if (parser.check <one_char <'*'> >(line, &pos, true)) + { + m_type = STAR; + } + else + { + atom* at = parser.get <atom>(line, &pos); + const string name = 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 <atom>(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 <flag*>::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 <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_flags.push_back(parser.get <flag>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <flag*> m_flags; + + public: + + const std::vector <flag*>& 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 <special_atom>(line, &pos, "inbox", true)) + { + m_type = INBOX; + m_name = "INBOX"; + } + else + { + m_type = OTHER; + + astring* astr = parser.get <astring>(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 <one_char <'\\'> >(line, &pos); + + atom* at = parser.get <atom>(line, &pos); + const string name = 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 <mailbox_flag*>::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 <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_flags.push_back(parser.get <mailbox_flag>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <mailbox_flag*> m_flags; + + public: + + const std::vector <mailbox_flag*>& 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 <IMAPParser::mailbox_flag_list>(line, &pos); + + parser.check <SPACE>(line, &pos); + + if (!parser.check <NIL>(line, &pos, true)) + { + parser.check <one_char <'"'> >(line, &pos); + + QUOTED_CHAR* qc = parser.get <QUOTED_CHAR>(line, &pos); + m_quoted_char = qc->value(); + delete (qc); + + parser.check <one_char <'"'> >(line, &pos); + } + + parser.check <SPACE>(line, &pos); + + m_mailbox = parser.get <IMAPParser::mailbox>(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*<any TEXT_CHAR except "]">] + + 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 <special_atom>(line, &pos, "alert", true)) + { + m_type = ALERT; + } + // "PARSE" + else if (parser.checkWithArg <special_atom>(line, &pos, "parse", true)) + { + m_type = PARSE; + } + // "PERMANENTFLAGS" SPACE flag_list + else if (parser.checkWithArg <special_atom>(line, &pos, "permanentflags", true)) + { + m_type = PERMANENTFLAGS; + + parser.check <SPACE>(line, &pos); + + m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos); + } + // "READ-ONLY" + else if (parser.checkWithArg <special_atom>(line, &pos, "read-only", true)) + { + m_type = READ_ONLY; + } + // "READ-WRITE" + else if (parser.checkWithArg <special_atom>(line, &pos, "read-write", true)) + { + m_type = READ_WRITE; + } + // "TRYCREATE" + else if (parser.checkWithArg <special_atom>(line, &pos, "trycreate", true)) + { + m_type = TRYCREATE; + } + // "UIDVALIDITY" SPACE nz_number + else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + + parser.check <SPACE>(line, &pos); + m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos); + } + // "UNSEEN" SPACE nz_number + else if (parser.checkWithArg <special_atom>(line, &pos, "unseen", true)) + { + m_type = UNSEEN; + + parser.check <SPACE>(line, &pos); + m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos); + } + // atom [SPACE 1*<any TEXT_CHAR except "]">] + else + { + m_type = OTHER; + + m_atom = parser.get <IMAPParser::atom>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_text = parser.get <text_except <']'> >(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 <one_char <'['> >(line, &pos, true)) + { + m_resp_text_code = parser.get <IMAPParser::resp_text_code>(line, &pos); + + parser.check <one_char <']'> >(line, &pos); + parser.check <SPACE>(line, &pos); + } + + text_mime2* text1 = parser.get <text_mime2>(line, &pos, true); + + if (text1 != NULL) + { + m_text = text1->value(); + delete (text1); + } + else + { + IMAPParser::text* text2 = + parser.get <IMAPParser::text>(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 <one_char <'+'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos); + + parser.check <CRLF>(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 <atom>(line, currentPos); + m_name = 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 <special_atom>(line, &pos, "messages", true)) + { + m_type = MESSAGES; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "recent", true)) + { + m_type = RECENT; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "uidnext", true)) + { + m_type = UIDNEXT; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + } + else + { + parser.checkWithArg <special_atom>(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 <IMAPParser::atom>(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 <IMAPParser::auth_type>(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 <capability*>::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 <special_atom>(line, &pos, "capability"); + parser.check <SPACE>(line, &pos); + + bool IMAP4rev1 = false; + + for (bool end = false ; !end && !IMAP4rev1 ; ) + { + if (parser.checkWithArg <special_atom>(line, &pos, "imap4rev1", true)) + { + IMAP4rev1 = true; + } + else + { + capability* cap = parser.get <capability>(line, &pos); + end = (cap == NULL); + + if (cap) + { + m_capabilities.push_back(cap); + } + } + + parser.check <SPACE>(line, &pos); + } + + + if (parser.check <SPACE>(line, &pos, true)) + { + for (capability* cap = NULL ; + (cap = parser.get <capability>(line, &pos)) != NULL ; ) + { + m_capabilities.push_back(cap); + + parser.check <SPACE>(line, &pos); + } + } + + *currentPos = pos; + } + + private: + + std::vector <capability*> m_capabilities; + + public: + + const std::vector <capability*>& 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 <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos, true); + + utility::auto_ptr <number> nd(parser.get <number>(line, &pos)); + + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <atom> amo(parser.get <atom>(line, &pos)); + + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <number> ny(parser.get <number>(line, &pos)); + + parser.check <SPACE>(line, &pos, true); + + // 2digit ":" 2digit ":" 2digit + utility::auto_ptr <number> nh(parser.get <number>(line, &pos)); + + parser.check <one_char <':'> >(line, &pos); + + utility::auto_ptr <number> nmi(parser.get <number>(line, &pos)); + + parser.check <one_char <':'> >(line, &pos); + + utility::auto_ptr <number> ns(parser.get <number>(line, &pos)); + + parser.check <SPACE>(line, &pos, true); + + // ("+" / "-") 4digit + int sign = 1; + + if (!(parser.check <one_char <'+'> >(line, &pos, true))) + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <number> nz(parser.get <number>(line, &pos)); + + parser.check <one_char <'"'> >(line, &pos); + + + m_datetime.hour() = std::min(std::max(nh->value(), 0u), 23u); + m_datetime.minute() = std::min(std::max(nmi->value(), 0u), 59u); + m_datetime.second() = std::min(std::max(ns->value(), 0u), 59u); + + const int zone = static_cast <int>(nz->value()); + const int zh = zone / 100; // hour offset + const int zm = zone % 100; // minute offset + + m_datetime.zone() = ((zh * 60) + zm) * sign; + + m_datetime.day() = std::min(std::max(nd->value(), 1u), 31u); + m_datetime.year() = ny->value(); + + const string month(vmime::toLower(amo->value())); + vmime::datetime::comp_t 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.month() = 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 <header_fld_name*>::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 <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_fld_names.push_back(parser.get <header_fld_name>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <header_fld_name*> m_fld_names; + + public: + + const std::vector <header_fld_name*>& 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 <body_extension*>::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 <one_char <'('> >(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_body_extensions.push_back(parser.get <body_extension>(line, &pos, true)); + } + else + { + if (!(m_nstring = parser.get <IMAPParser::nstring>(line, &pos, true))) + m_number = parser.get <IMAPParser::number>(line, &pos); + } + + *currentPos = pos; + } + + private: + + IMAPParser::nstring* m_nstring; + IMAPParser::number* m_number; + + std::vector <body_extension*> m_body_extensions; + + public: + + IMAPParser::nstring* nstring() const { return (m_nstring); } + IMAPParser::number* number() const { return (m_number); } + + const std::vector <body_extension*>& 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 <special_atom>(line, &pos, "header.fields.not", true); + const bool b2 = (b1 ? false : parser.checkWithArg <special_atom>(line, &pos, "header.fields", true)); + + if (b1 || b2) + { + m_type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; + + parser.check <SPACE>(line, &pos); + m_header_list = parser.get <IMAPParser::header_list>(line, &pos); + } + // "HEADER" + else if (parser.checkWithArg <special_atom>(line, &pos, "header", true)) + { + m_type = HEADER; + } + // "MIME" + else if (parser.checkWithArg <special_atom>(line, &pos, "mime", true)) + { + m_type = MIME; + } + // "TEXT" + else + { + m_type = TEXT; + + parser.checkWithArg <special_atom>(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 <one_char <'['> >(line, &pos); + + if (!parser.check <one_char <']'> >(line, &pos, true)) + { + if (!(m_section_text1 = parser.get <section_text>(line, &pos, true))) + { + nz_number* num = parser.get <nz_number>(line, &pos); + m_nz_numbers.push_back(num->value()); + delete (num); + + while (parser.check <one_char <'.'> >(line, &pos, true)) + { + if ((num = parser.get <nz_number>(line, &pos, true))) + { + m_nz_numbers.push_back(num->value()); + delete (num); + } + else + { + m_section_text2 = parser.get <section_text>(line, &pos); + break; + } + } + } + + parser.check <one_char <']'> >(line, &pos); + } + + *currentPos = pos; + } + + private: + + section_text* m_section_text1; + section_text* m_section_text2; + std::vector <unsigned int> 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 <unsigned int>& 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 <one_char <'('> >(line, &pos); + m_addr_name = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_adl = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_mailbox = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_host = parser.get <nstring>(line, &pos); + parser.check <one_char <')'> >(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 <address*>::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 <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_addresses.push_back(parser.get <address>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + } + + *currentPos = pos; + } + + private: + + std::vector <address*> m_addresses; + + public: + + const std::vector <address*>& 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 <one_char <'('> >(line, &pos); + + m_env_date = parser.get <IMAPParser::env_date>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_subject = parser.get <IMAPParser::env_subject>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_from = parser.get <IMAPParser::env_from>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_sender = parser.get <IMAPParser::env_sender>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_reply_to = parser.get <IMAPParser::env_reply_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_to = parser.get <IMAPParser::env_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_cc = parser.get <IMAPParser::env_cc>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_bcc = parser.get <IMAPParser::env_bcc>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_in_reply_to = parser.get <IMAPParser::env_in_reply_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_message_id = parser.get <IMAPParser::env_message_id>(line, &pos); + + parser.check <one_char <')'> >(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 <xstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_string2 = parser.get <xstring>(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 <body_fld_param_item*>::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 <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + + m_items.push_back(parser.get <body_fld_param_item>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + parser.check <SPACE>(line, &pos); + m_items.push_back(parser.get <body_fld_param_item>(line, &pos)); + } + } + + *currentPos = pos; + } + + private: + + std::vector <body_fld_param_item*> m_items; + + public: + + const std::vector <body_fld_param_item*>& 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 <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + m_string = parser.get <xstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_param = parser.get <class body_fld_param>(line, &pos); + parser.check <one_char <')'> >(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 <xstring*>::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 <one_char <'('> >(line, &pos, true)) + { + m_strings.push_back(parser.get <class xstring>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_strings.push_back(parser.get <class xstring>(line, &pos)); + } + else + { + m_strings.push_back(parser.get <class nstring>(line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector <xstring*> m_strings; + + public: + + const std::vector <xstring*>& 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 <IMAPParser::body_fld_param>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_id = parser.get <IMAPParser::body_fld_id>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_desc = parser.get <IMAPParser::body_fld_desc>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_enc = parser.get <IMAPParser::body_fld_enc>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_octets = parser.get <IMAPParser::body_fld_octets>(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 <one_char <'"'> >(line, &pos); + parser.checkWithArg <special_atom>(line, &pos, "text"); + parser.check <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(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 <one_char <'"'> >(line, &pos); + parser.checkWithArg <special_atom>(line, &pos, "message"); + parser.check <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + //parser.check <one_char <'"'> >(line, &pos); + //parser.checkWithArg <special_atom>(line, &pos, "rfc822"); + //parser.check <one_char <'"'> >(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(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 <xstring>(line, &pos); + + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(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 <body_extension*>::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 <IMAPParser::body_fld_md5>(line, &pos); + + // [SPACE body_fld_dsp + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos); + + // [SPACE body_fld_lang + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos); + + // [SPACE 1#body_extension] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get <body_extension>(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 <body_extension*> 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_extension*> 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 <body_extension*>::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 <IMAPParser::body_fld_param>(line, &pos); + + // [SPACE body_fld_dsp SPACE body_fld_lang [SPACE 1#body_extension]] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos); + + // [SPACE 1#body_extension] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get <body_extension>(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 <body_extension*> 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_extension*> 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 <IMAPParser::media_basic>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(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 <IMAPParser::media_message>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos); + parser.check <SPACE>(line, &pos); + m_envelope = parser.get <IMAPParser::envelope>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body = parser.get <IMAPParser::xbody>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(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 <IMAPParser::media_text>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(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 <IMAPParser::body_type_text>(line, &pos, true))) + if (!(m_body_type_msg = parser.get <IMAPParser::body_type_msg>(line, &pos, true))) + m_body_type_basic = parser.get <IMAPParser::body_type_basic>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_body_ext_1part = parser.get <IMAPParser::body_ext_1part>(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 <xbody*>::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 <xbody>(line, &pos)); + + for (xbody* b ; (b = parser.get <xbody>(line, &pos, true)) ; ) + m_list.push_back(b); + + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_body_ext_mpart = parser.get <IMAPParser::body_ext_mpart>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + IMAPParser::body_ext_mpart* m_body_ext_mpart; + + std::vector <xbody*> m_list; + + public: + + const std::vector <IMAPParser::xbody*>& 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 <one_char <'('> >(line, &pos); + + if (!(m_body_type_1part = parser.get <IMAPParser::body_type_1part>(line, &pos, true))) + m_body_type_mpart = parser.get <IMAPParser::body_type_mpart>(line, &pos); + + parser.check <one_char <')'> >(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 <special_atom>(line, &pos, "envelope", true)) + { + m_type = ENVELOPE; + + parser.check <SPACE>(line, &pos); + m_envelope = parser.get <IMAPParser::envelope>(line, &pos); + } + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" + else if (parser.checkWithArg <special_atom>(line, &pos, "flags", true)) + { + m_type = FLAGS; + + parser.check <SPACE>(line, &pos); + + m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos); + } + // "INTERNALDATE" SPACE date_time + else if (parser.checkWithArg <special_atom>(line, &pos, "internaldate", true)) + { + m_type = INTERNALDATE; + + parser.check <SPACE>(line, &pos); + m_date_time = parser.get <IMAPParser::date_time>(line, &pos); + } + // "RFC822" ".HEADER" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.header", true)) + { + m_type = RFC822_HEADER; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.get <IMAPParser::nstring>(line, &pos); + } + // "RFC822" ".TEXT" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.text", true)) + { + m_type = RFC822_TEXT; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.getWithArgs <IMAPParser::nstring> + (line, &pos, this, RFC822_TEXT); + } + // "RFC822.SIZE" SPACE number + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.size", true)) + { + m_type = RFC822_SIZE; + + parser.check <SPACE>(line, &pos); + m_number = parser.get <IMAPParser::number>(line, &pos); + } + // "RFC822" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822", true)) + { + m_type = RFC822; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.get <IMAPParser::nstring>(line, &pos); + } + // "BODY" "STRUCTURE" SPACE body + else if (parser.checkWithArg <special_atom>(line, &pos, "bodystructure", true)) + { + m_type = BODY_STRUCTURE; + + parser.check <SPACE>(line, &pos); + + m_body = parser.get <IMAPParser::body>(line, &pos); + } + // "BODY" section ["<" number ">"] SPACE nstring + // "BODY" SPACE body + else if (parser.checkWithArg <special_atom>(line, &pos, "body", true)) + { + m_section = parser.get <IMAPParser::section>(line, &pos, true); + + // "BODY" section ["<" number ">"] SPACE nstring + if (m_section != NULL) + { + m_type = BODY_SECTION; + + if (parser.check <one_char <'<'> >(line, &pos, true)) + { + m_number = parser.get <IMAPParser::number>(line, &pos); + parser.check <one_char <'>'> >(line, &pos); + } + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.getWithArgs <IMAPParser::nstring> + (line, &pos, this, BODY_SECTION); + } + // "BODY" SPACE body + else + { + m_type = BODY; + + parser.check <SPACE>(line, &pos); + + m_body = parser.get <IMAPParser::body>(line, &pos); + } + } + // "UID" SPACE uniqueid + else + { + m_type = UID; + + parser.checkWithArg <special_atom>(line, &pos, "uid"); + parser.check <SPACE>(line, &pos); + + m_uniqueid = parser.get <nz_number>(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 <msg_att_item*>::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 <one_char <'('> >(line, &pos); + + m_items.push_back(parser.get <msg_att_item>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + parser.check <SPACE>(line, &pos); + m_items.push_back(parser.get <msg_att_item>(line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector <msg_att_item*> m_items; + + public: + + const std::vector <msg_att_item*>& 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 <nz_number>(line, &pos); + m_number = num->value(); + delete (num); + + parser.check <SPACE>(line, &pos); + + if (parser.checkWithArg <special_atom>(line, &pos, "expunge", true)) + { + m_type = EXPUNGE; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "fetch"); + + parser.check <SPACE>(line, &pos); + + m_type = FETCH; + m_msg_att = parser.get <IMAPParser::msg_att>(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 <special_atom>(line, &pos, "ok", true)) + { + m_status = OK; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "no", true)) + { + m_status = NO; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "bad"); + m_status = BAD; + } + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(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 <special_atom>(line, &pos, "bye"); + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(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 <special_atom>(line, &pos, "ok", true)) + { + m_cond = OK; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "preauth"); + + m_cond = PREAUTH; + } + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(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 <IMAPParser::status_att>(line, &pos); + parser.check <SPACE>(line, &pos); + m_number = parser.get <IMAPParser::number>(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 + // "(" #<status_att number ")" / + // number SPACE "EXISTS" / + // number SPACE "RECENT" + // + + class mailbox_data : public component + { + public: + + mailbox_data() + : m_number(NULL), m_mailbox_flag_list(NULL), m_mailbox_list(NULL), + m_mailbox(NULL), m_text(NULL) + { + } + + ~mailbox_data() + { + delete (m_number); + delete (m_mailbox_flag_list); + delete (m_mailbox_list); + delete (m_mailbox); + delete (m_text); + + for (std::vector <nz_number*>::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 <IMAPParser::number>(line, &pos, true); + + if (m_number) + { + parser.check <SPACE>(line, &pos); + + if (parser.checkWithArg <special_atom>(line, &pos, "exists", true)) + { + m_type = EXISTS; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "recent"); + + m_type = RECENT; + } + } + else + { + // "FLAGS" SPACE mailbox_flag_list + if (parser.checkWithArg <special_atom>(line, &pos, "flags", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_flag_list = parser.get <IMAPParser::mailbox_flag_list>(line, &pos); + + m_type = FLAGS; + } + // "LIST" SPACE mailbox_list + else if (parser.checkWithArg <special_atom>(line, &pos, "list", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos); + + m_type = LIST; + } + // "LSUB" SPACE mailbox_list + else if (parser.checkWithArg <special_atom>(line, &pos, "lsub", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos); + + m_type = LSUB; + } + // "MAILBOX" SPACE text + else if (parser.checkWithArg <special_atom>(line, &pos, "mailbox", true)) + { + parser.check <SPACE>(line, &pos); + + m_text = parser.get <IMAPParser::text>(line, &pos); + + m_type = MAILBOX; + } + // "SEARCH" [SPACE 1#nz_number] + else if (parser.checkWithArg <special_atom>(line, &pos, "search", true)) + { + if (parser.check <SPACE>(line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get <nz_number>(line, &pos)); + + while (parser.check <SPACE>(line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get <nz_number>(line, &pos)); + } + } + + m_type = SEARCH; + } + // "STATUS" SPACE mailbox SPACE + // "(" #<status_att number)] ")" + // + // "(" [status_att SPACE number *(SPACE status_att SPACE number)] ")" + else + { + parser.checkWithArg <special_atom>(line, &pos, "status"); + parser.check <SPACE>(line, &pos); + + m_mailbox = parser.get <IMAPParser::mailbox>(line, &pos); + + parser.check <SPACE>(line, &pos); + parser.check <one_char <'('> >(line, &pos); + + m_status_info_list.push_back(parser.get <status_info>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_status_info_list.push_back(parser.get <status_info>(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 <nz_number*> m_search_nz_number_list; + std::vector <status_info*> 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 <nz_number*>& search_nz_number_list() const { return (m_search_nz_number_list); } + const std::vector <status_info*>& 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 <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + if (!(m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos, true))) + if (!(m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos, true))) + if (!(m_mailbox_data = parser.get <IMAPParser::mailbox_data>(line, &pos, true))) + if (!(m_message_data = parser.get <IMAPParser::message_data>(line, &pos, true))) + m_capability_data = parser.get <IMAPParser::capability_data>(line, &pos); + + parser.check <CRLF>(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 <IMAPParser::continue_req>(line, &pos, true))) + m_response_data = parser.get <IMAPParser::response_data>(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 <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos); + + parser.check <CRLF>(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 <IMAPParser::xtag>(line, &pos); + parser.check <SPACE>(line, &pos); + m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos); + parser.check <CRLF>(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 <IMAPParser::response_tagged>(line, &pos, true))) + m_response_fatal = parser.get <IMAPParser::response_fatal>(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 <IMAPParser::continue_req_or_response_data*>::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 <IMAPParser::continue_req_or_response_data>(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 <IMAPParser::response_done>(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 <IMAPParser::continue_req_or_response_data*> m_continue_req_or_response_data; + IMAPParser::response_done* m_response_done; + + public: + + const std::vector <IMAPParser::continue_req_or_response_data*>& 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 <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + if (!(m_resp_cond_auth = parser.get <IMAPParser::resp_cond_auth>(line, &pos, true))) + m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos); + + parser.check <CRLF>(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 <response>(line, &pos); + m_literalHandler = NULL; + + return (resp); + } + + + greeting* readGreeting() + { + string::size_type pos = 0; + string line = readLine(); + + return get <greeting>(line, &pos); + } + + + // + // Get a token and advance + // + + template <class TYPE> + TYPE* get(string& line, string::size_type* currentPos, + const bool noThrow = false) + { + component* resp = new TYPE; + return internalGet <TYPE>(resp, line, currentPos, noThrow); + } + + + template <class TYPE, class ARG1_TYPE, class ARG2_TYPE> + 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 <TYPE>(resp, line, currentPos, noThrow); + } + + +private: + + template <class TYPE> + 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 <TYPE*>(resp); + } + + +public: + + // + // Check a token and advance + // + + template <class TYPE> + 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 <class TYPE, class ARG_TYPE> + 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 |