diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/imap/IMAPParser.hpp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/imap/IMAPParser.hpp')
-rw-r--r-- | vmime-master/src/vmime/net/imap/IMAPParser.hpp | 4986 |
1 files changed, 4986 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/imap/IMAPParser.hpp b/vmime-master/src/vmime/net/imap/IMAPParser.hpp new file mode 100644 index 0000000..281fcb6 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPParser.hpp @@ -0,0 +1,4986 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/base.hpp" +#include "vmime/dateTime.hpp" +#include "vmime/charset.hpp" +#include "vmime/exception.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/progressListener.hpp" + +#include "vmime/utility/encoder/b64Encoder.hpp" +#include "vmime/utility/encoder/qpEncoder.hpp" + +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/net/imap/IMAPTag.hpp" + +#include <vector> +#include <stdexcept> +#include <memory> + + +//#define DEBUG_RESPONSE 1 + + +#if DEBUG_RESPONSE +# include <iostream> +#endif + + +/** Make the parsing of a component fail. + */ +#define VIMAP_PARSER_FAIL() \ + { \ + parser.m_errorResponseLine = makeResponseLine(getComponentName(), line, pos); \ + return false; \ + } + +/** Make the parsing of a component fail if a condition is not matched. + * If the specified expression does not resolve to "true", the parsing + * will fail. + * + * @param cond condition expression + */ +#define VIMAP_PARSER_FAIL_UNLESS(cond) \ + if (!(cond)) \ + { \ + VIMAP_PARSER_FAIL(); \ + } + +/** Check for a token and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + */ +#define VIMAP_PARSER_CHECK(type) \ + VIMAP_PARSER_FAIL_UNLESS(parser.check <type>(line, &pos)) + +/** Check for an optional token and advance. + * If the token is not matched, parsing will continue anyway. + * + * @param type token class + */ +#define VIMAP_PARSER_TRY_CHECK(type) \ + (parser.check <type>(line, &pos)) + +/** Get a token and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + * @param variable variable which will receive pointer to the retrieved token + */ +#define VIMAP_PARSER_GET(type, variable) \ + { \ + variable.reset(parser.get <type>(line, &pos)); \ + VIMAP_PARSER_FAIL_UNLESS(variable.get()); \ + } + +/** Get an optional token and advance. + * If the token is not matched, parsing will continue anyway. + */ +#define VIMAP_PARSER_TRY_GET(type, variable) \ + (variable.reset(parser.get <type>(line, &pos)), variable.get()) + +/** Get an optional token and advance. If found, token will be pushed back + * to a vector. If the token is not matched, stopInstr will be executed. + * + * @param type token class + * @param variable variable of type std::vector<> to which the retrieved + * token will be pushed + * @param stopInstr instruction to execute if token is not found + */ +#define VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(type, variable, stopInstr) \ + { \ + std::unique_ptr <type> v; \ + try \ + { \ + v.reset(parser.get <type>(line, &pos)); \ + if (!v) \ + { \ + stopInstr; \ + } \ + variable.push_back(std::move(v)); \ + } \ + catch (...) \ + { \ + throw; \ + } \ + } + +/** Get a token and advance. Token will be pushed back to a vector. + * If the token is not matched, parsing will fail. + * + * @param type token class + * @param variable variable of type std::vector<> to which the retrieved + * token will be pushed + */ +#define VIMAP_PARSER_GET_PUSHBACK(type, variable) \ + VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(type, variable, VIMAP_PARSER_FAIL()) + + +/** Check for a token which takes an argument and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + */ +#define VIMAP_PARSER_CHECK_WITHARG(type, arg) \ + VIMAP_PARSER_FAIL_UNLESS(parser.checkWithArg <type>(line, &pos, arg)) + +/** Check for an optional token which takes an argument and advance. + * If the token is not matched, parsing will continue anyway. + * + * @param type token class + */ +#define VIMAP_PARSER_TRY_CHECK_WITHARG(type, arg) \ + (parser.checkWithArg <type>(line, &pos, arg)) + + + +namespace vmime { +namespace net { +namespace imap { + + +#if DEBUG_RESPONSE + static int IMAPParserDebugResponse_level = 0; + static std::vector <string> IMAPParserDebugResponse_stack; + + class IMAPParserDebugResponse { + + public: + + IMAPParserDebugResponse( + const string& name, + string& line, + const size_t currentPos, + const bool &result + ) + : m_name(name), + m_line(line), + m_pos(currentPos), + m_result(result) { + + ++IMAPParserDebugResponse_level; + IMAPParserDebugResponse_stack.push_back(name); + + for (int i = 0 ; i < IMAPParserDebugResponse_level ; ++i) { + std::cout << " "; + } + + std::cout << "ENTER(" << m_name << "), pos=" << m_pos; + std::cout << std::endl; + + for (std::vector <string>::iterator it = IMAPParserDebugResponse_stack.begin() ; + it != IMAPParserDebugResponse_stack.end() ; ++it) { + + std::cout << "> " << *it << " "; + } + + std::cout << std::endl; + std::cout << string(m_line.begin() + (m_pos < 30 ? 0U : m_pos - 30), + m_line.begin() + std::min(m_line.length(), m_pos + 30)) << std::endl; + + for (size_t i = (m_pos < 30 ? m_pos : (m_pos - (m_pos - 30))) ; i != 0 ; --i) { + std::cout << " "; + } + + std::cout << "^" << std::endl; + } + + ~IMAPParserDebugResponse() { + + for (int i = 0 ; i < IMAPParserDebugResponse_level ; ++i) { + std::cout << " "; + } + + std::cout << "LEAVE(" << m_name << "), result="; + std::cout << (m_result ? "TRUE" : "FALSE") << ", pos=" << m_pos; + std::cout << std::endl; + + --IMAPParserDebugResponse_level; + IMAPParserDebugResponse_stack.pop_back(); + } + + private: + + const string m_name; + string m_line; + size_t m_pos; + const bool& m_result; + }; + + + #define DEBUG_ENTER_COMPONENT(x, result) \ + IMAPParserDebugResponse dbg(x, line, *currentPos, result) + + #define DEBUG_FOUND(x, y) \ + std::cout << "FOUND: " << x << ": " << y << std::endl; +#else + #define DEBUG_ENTER_COMPONENT(x, result) + #define DEBUG_FOUND(x, y) +#endif + + +class VMIME_EXPORT IMAPParser : public object { + +public: + + IMAPParser() + : m_progress(NULL), + m_strict(false), + m_literalHandler(NULL) { + + } + + ~IMAPParser() { + + for (auto it = m_pendingResponses.begin() ; it != m_pendingResponses.end() ; ++it) { + delete it->second; + } + } + + + /** Set the socket currently used by this parser to receive data + * from server. + * + * @param sok socket + */ + void setSocket(const shared_ptr <socket>& sok) { + + m_socket = sok; + } + + /** Set the timeout handler currently used by this parser. + * + * @param toh timeout handler + */ + void setTimeoutHandler(const shared_ptr <timeoutHandler>& toh) { + + m_timeoutHandler = toh; + } + + /** Set the tracer currently used by this parser. + * + * @param tr tracer + */ + void setTracer(const shared_ptr <tracer>& tr) { + + m_tracer = tr; + } + + /** Set whether we operate in strict mode (this may not work + * with some servers which are not fully standard-compliant). + * + * @param strict true to operate in strict mode, or false + * to operate in default, relaxed mode + */ + void setStrict(const bool strict) { + + m_strict = strict; + } + + /** Return true if the parser operates in strict mode, or + * false otherwise. + * + * @return true if we are in strict mode, false otherwise + */ + bool isStrict() const { + + return m_strict; + } + + + + // + // literalHandler : literal content handler + // + + class component; + + class literalHandler { + + public: + + virtual ~literalHandler() { } + + + // Abstract target class + class target { + + protected: + + target(utility::progressListener* progress) : m_progress(progress) {} + target(const target&) {} + + public: + + virtual ~target() { } + + + utility::progressListener* progressListener() { return (m_progress); } + + virtual void putData(const string& chunk) = 0; + + virtual size_t getBytesWritten() const = 0; + + private: + + utility::progressListener* m_progress; + }; + + + // Target: put in a string + class targetString : public target { + + public: + + targetString(utility::progressListener* progress, vmime::string& str) + : target(progress), m_string(str), m_bytesWritten(0) { } + + const vmime::string& string() const { return (m_string); } + vmime::string& string() { return (m_string); } + + + void putData(const vmime::string& chunk) { + + m_string += chunk; + m_bytesWritten += chunk.length(); + } + + size_t getBytesWritten() const { + + return m_bytesWritten; + } + + private: + + vmime::string& m_string; + size_t m_bytesWritten; + }; + + + // Target: redirect to an output stream + class targetStream : public target { + + public: + + targetStream( + utility::progressListener* progress, + utility::outputStream& stream + ) + : target(progress), + m_stream(stream), + m_bytesWritten(0) { + + } + + 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()); + m_bytesWritten += chunk.length(); + } + + size_t getBytesWritten() const { + + return m_bytesWritten; + } + + private: + + utility::outputStream& m_stream; + size_t m_bytesWritten; + }; + + + // 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 shared_ptr <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 const string getComponentName() const = 0; + + bool parse(IMAPParser& parser, string& line, size_t* currentPos) { + + bool res = false; + DEBUG_ENTER_COMPONENT(getComponentName(), res); + res = parseImpl(parser, line, currentPos); + return res; + } + + virtual bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) = 0; + + + const string makeResponseLine( + const string& comp, + const string& line, + const size_t 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); + } + }; + + +#define COMPONENT_ALIAS(parent, name) \ + class name : public parent { \ + virtual const string getComponentName() const { return #name; } \ + public: \ + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { \ + return parent::parseImpl(parser, line, currentPos); \ + } \ + } + +#define DECLARE_COMPONENT(name) \ + class name : public component { \ + virtual const string getComponentName() const { return #name; } \ + public: + + + // + // Parse one character + // + + template <char C> + class one_char : public component { + + public: + + const string getComponentName() const { + + return string("one_char <") + C + ">"; + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + const size_t pos = *currentPos; + + if (pos < line.length() && line[pos] == C) { + *currentPos = pos + 1; + return true; + } else { + return false; + } + } + }; + + + // + // SPACE ::= <ASCII SP, space, 0x20> + // + + DECLARE_COMPONENT(SPACE) + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) { + ++pos; + } + + if (pos > *currentPos) { + *currentPos = pos; + return true; + } else { + return false; + } + } + }; + + + // + // CR ::= <ASCII CR, carriage return, 0x0D> + // LF ::= <ASCII LF, line feed, 0x0A> + // CRLF ::= CR LF + // + + DECLARE_COMPONENT(CRLF) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_TRY_CHECK(SPACE); + + if (pos + 1 < line.length() && + line[pos] == 0x0d && line[pos + 1] == 0x0a) { + + *currentPos = pos + 2; + return true; + + } else { + + return false; + } + } + }; + + + // + // 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") + // + + DECLARE_COMPONENT(xtag) + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + bool end = false; + + 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; + } + } + + *currentPos = pos; + return true; + } + + string tagString; + }; + + + // + // 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) + // + + DECLARE_COMPONENT(number) + + number(const bool nonZero = false) + : value(0), + m_nonZero(nonZero) { + + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t 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) { + value = val; + *currentPos = pos; + return true; + } else { + return false; + } + } + + + unsigned long value; + + private: + + const bool m_nonZero; + }; + + + // 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) { + + } + }; + + + // + // uniqueid ::= nz_number + // ;; Strictly ascending + // + + class uniqueid : public nz_number { + + public: + + uniqueid() + : nz_number() { + + } + }; + + + // uid-range = (uniqueid ":" uniqueid) + // ; two uniqueid values and all values + // ; between these two regards of order. + // ; Example: 2:4 and 4:2 are equivalent. + + DECLARE_COMPONENT(uid_range) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(uniqueid, uniqueid1); + VIMAP_PARSER_CHECK(one_char <':'>); + VIMAP_PARSER_GET(uniqueid, uniqueid2); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <uniqueid> uniqueid1; + std::unique_ptr <uniqueid> uniqueid2; + }; + + + // + // uid-set = (uniqueid / uid-range) *("," uid-set) + // + + DECLARE_COMPONENT(uid_set) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // We have either a 'uid_range' or a 'uniqueid' + if (!VIMAP_PARSER_TRY_GET(IMAPParser::uid_range, uid_range)) { + VIMAP_PARSER_GET(IMAPParser::uniqueid, uniqueid); + } + + // And maybe another 'uid-set' following + if (VIMAP_PARSER_TRY_CHECK(one_char <','>)) { + VIMAP_PARSER_GET(IMAPParser::uid_set, next_uid_set); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::uniqueid> uniqueid; + std::unique_ptr <IMAPParser::uid_range> uid_range; + + std::unique_ptr <IMAPParser::uid_set> next_uid_set; + }; + + + // + // text ::= 1*TEXT_CHAR + // + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // TEXT_CHAR ::= <any CHAR except CR and LF> + // + + DECLARE_COMPONENT(text) + + text(bool allow8bits = false, const char except = 0) + : m_allow8bits(allow8bits), + m_except(except) { + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t len = 0; + + if (m_allow8bits || !parser.isStrict()) { + + 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) { + VIMAP_PARSER_FAIL(); + } + + value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, value.begin()); + + *currentPos = pos; + + return true; + } + + + string value; + + private: + + const bool m_allow8bits; + const char m_except; + }; + + + 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> + // + + DECLARE_COMPONENT(QUOTED_CHAR) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + const unsigned char c = static_cast <unsigned char>(pos < line.length() ? line[pos] : 0); + + if (c >= 0x01 && c <= 0x7f && // 0x01 - 0x7f + c != '"' && c != '\\' && // quoted_specials + c != '\r' && c != '\n') { // CR and LF + + value = c; + *currentPos = pos + 1; + + } else if (c == '\\' && pos + 1 < line.length() && + (line[pos + 1] == '"' || line[pos + 1] == '\\')) { + + value = line[pos + 1]; + *currentPos = pos + 2; + + } else { + + VIMAP_PARSER_FAIL(); + } + + return true; + } + + + char 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> + // + + DECLARE_COMPONENT(quoted_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t len = 0; + bool valid = false; + + 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 == '\\') { + value += c; + } else { + value += '\\'; + 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 + + value += c; + + ++pos; + ++len; + + } else { + + valid = false; + end = true; + } + } + } + + if (!valid) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + + + string value; + }; + + + // + // nil ::= "NIL" + // + + DECLARE_COMPONENT(NIL) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "nil"); + + *currentPos = pos; + + return true; + } + }; + + + // + // 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> + // + + DECLARE_COMPONENT(xstring) + + xstring( + const bool canBeNIL = false, + component* comp = NULL, + const int data = 0 + ) + : isNIL(true), + m_canBeNIL(canBeNIL), + m_component(comp), + m_data(data) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (m_canBeNIL && + VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "nil")) { + + // NIL + isNIL = true; + + } else { + + pos = *currentPos; + + isNIL = false; + + // quoted ::= <"> *QUOTED_CHAR <"> + if (VIMAP_PARSER_TRY_CHECK(one_char <'"'>)) { + + shared_ptr <quoted_text> text; + VIMAP_PARSER_GET(quoted_text, text); + VIMAP_PARSER_CHECK(one_char <'"'>); + + if (parser.m_literalHandler != NULL) { + + shared_ptr <literalHandler::target> target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) { + + value = "[literal-handler]"; + + const size_t length = text->value.length(); + utility::progressListener* progress = target->progressListener(); + + if (progress) { + progress->start(length); + } + + target->putData(text->value); + + if (progress) { + progress->progress(length, length); + progress->stop(length); + } + + } else { + + value = text->value; + } + + } else { + + value = text->value; + } + + DEBUG_FOUND("string[quoted]", "<length=" << value.length() << ", value='" << value << "'>"); + + // literal ::= "{" number "}" CRLF *CHAR8 + } else { + + VIMAP_PARSER_CHECK(one_char <'{'>); + + shared_ptr <number> num; + VIMAP_PARSER_GET(number, num); + + const size_t length = num->value; + + VIMAP_PARSER_CHECK(one_char <'}'> ); + + VIMAP_PARSER_CHECK(CRLF); + + + if (parser.m_literalHandler != NULL) { + + shared_ptr <literalHandler::target> target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) { + + value = "[literal-handler]"; + + parser.m_progress = target->progressListener(); + parser.readLiteral(*target, length); + parser.m_progress = NULL; + + } else { + + literalHandler::targetString target(NULL, value); + parser.readLiteral(target, length); + } + + } else { + + literalHandler::targetString target(NULL, value); + parser.readLiteral(target, length); + } + + line += parser.readLine(); + + DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << value << "'>"); + } + } + + *currentPos = pos; + + return true; + } + + + bool isNIL; + string value; + + private: + + bool m_canBeNIL; + + component* m_component; + const int m_data; + }; + + + // + // nstring ::= string / nil + // + + class nstring : public xstring { + + public: + + const string getComponentName() const { + + return "nstring"; + } + + nstring(component* comp = NULL, const int data = 0) + : xstring(true, comp, data) { + + } + }; + + + // + // astring ::= atom / string + // + + DECLARE_COMPONENT(astring) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <xstring> str; + VIMAP_PARSER_TRY_GET(xstring, str); + + if (str) { + value = str->value; + } else { + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + value = at->value; + } + + *currentPos = pos; + + return true; + } + + + string 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> + // + + DECLARE_COMPONENT(atom) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t 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) { + + value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, value.begin()); + + *currentPos = pos; + + } else { + + VIMAP_PARSER_FAIL(); + } + + return true; + } + + + string 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: + + const std::string getComponentName() const { + + return string("special_atom <") + m_string + ">"; + } + + special_atom(const char* str) + : m_string(str) { // 'string' must be in lower-case + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!atom::parseImpl(parser, line, &pos)) { + return false; + } + + 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) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + + private: + + const char* m_string; + }; + + + // + // text_mime2 ::= "=?" <charset> "?" <encoding> "?" <encoded-text> "?=" + // ;; Syntax defined in [MIME-HDRS] + // + + DECLARE_COMPONENT(text_mime2) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <atom> theCharset, theEncoding; + std::unique_ptr <text> theText; + + VIMAP_PARSER_CHECK(one_char <'='> ); + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(atom, theCharset); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(atom, theEncoding); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(text8_except <'?'> , theText); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + VIMAP_PARSER_CHECK(one_char <'='> ); + + charset = theCharset->value; + + // Decode text + scoped_ptr <utility::encoder::encoder> theEncoder; + + if (theEncoding->value[0] == 'q' || theEncoding->value[0] == 'Q') { + + // Quoted-printable + theEncoder.reset(new utility::encoder::qpEncoder()); + theEncoder->getProperties()["rfc2047"] = true; + + } else if (theEncoding->value[0] == 'b' || theEncoding->value[0] == 'B') { + + // Base64 + theEncoder.reset(new utility::encoder::b64Encoder()); + } + + if (theEncoder.get()) { + + utility::inputStreamStringAdapter in(theText->value); + utility::outputStreamStringAdapter out(value); + + theEncoder->decode(in, out); + + // No decoder available + } else { + + value = theText->value; + } + + *currentPos = pos; + + return true; + } + + + vmime::charset charset; + string value; + }; + + + // seq-number = nz-number / "*" + // ; message sequence number (COPY, FETCH, STORE + // ; commands) or unique identifier (UID COPY, + // ; UID FETCH, UID STORE commands). + + DECLARE_COMPONENT(seq_number) + + seq_number() + : star(false) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + + star = true; + number.reset(); + + } else { + + star = false; + VIMAP_PARSER_GET(IMAPParser::number, number); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::number> number; + bool star; + }; + + + // seq-range = seq-number ":" seq-number + // ; two seq-number values and all values between + // ; these two regardless of order. + // ; Example: 2:4 and 4:2 are equivalent and indicate + // ; values 2, 3, and 4. + + DECLARE_COMPONENT(seq_range) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(seq_number, first); + + VIMAP_PARSER_CHECK(one_char <'*'> ); + + VIMAP_PARSER_GET(seq_number, last); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::seq_number> first; + std::unique_ptr <IMAPParser::seq_number> last; + }; + + + // sequence-set = (seq-number / seq-range) *("," sequence-set) + // ; set of seq-number values, regardless of order. + // ; Servers MAY coalesce overlaps and/or execute the + // ; sequence in any order. + // ; Example: a message sequence number set of + // ; 2,4:7,9,12:* for a mailbox with 15 messages is + // ; equivalent to 2,4,5,6,7,9,12,13,14,15 + + DECLARE_COMPONENT(sequence_set) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::seq_range, range)) { + VIMAP_PARSER_GET(IMAPParser::seq_number, number); + } + + if (VIMAP_PARSER_TRY_CHECK(one_char <','> )) { + VIMAP_PARSER_GET(sequence_set, nextSet); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::seq_number> number; + std::unique_ptr <IMAPParser::seq_range> range; + std::unique_ptr <IMAPParser::sequence_set> nextSet; + }; + + + // mod-sequence-value = 1*DIGIT + // ;; Positive unsigned 64-bit integer + // ;; (mod-sequence) + // ;; (1 <= n < 18,446,744,073,709,551,615) + + DECLARE_COMPONENT(mod_sequence_value) + + mod_sequence_value() + : value(0) { + + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + bool valid = true; + vmime_uint64 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; + } + } + + value = val; + + *currentPos = pos; + + return true; + } + + + vmime_uint64 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 + // + + DECLARE_COMPONENT(flag) + + flag() + : type(UNKNOWN) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'\\'> )) { + + if (VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + + type = STAR; + + } else { + + shared_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + const string tname = utility::stringUtils::toLower(at->value); + + if (tname == "answered") { + type = ANSWERED; + } else if (tname == "flagged") { + type = FLAGGED; + } else if (tname == "deleted") { + type = DELETED; + } else if (tname == "seen") { + type = SEEN; + } else if (tname == "draft") { + type = DRAFT; + } else { + type = UNKNOWN; + name = tname; + } + } + + } else { + + if (!parser.isStrict() && VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + type = STAR; // map "*" to "\*" + } else { + type = KEYWORD_OR_EXTENSION; + VIMAP_PARSER_GET(atom, flag_keyword); + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + UNKNOWN, + ANSWERED, + FLAGGED, + DELETED, + SEEN, + DRAFT, + KEYWORD_OR_EXTENSION, + STAR // * = custom flags allowed + }; + + + Type type; + string name; + + std::unique_ptr <IMAPParser::atom> flag_keyword; + }; + + + // + // flag_list ::= "(" #flag ")" + // + + DECLARE_COMPONENT(flag_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(flag, flags); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <flag>> 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. + // + + DECLARE_COMPONENT(mailbox) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "inbox")) { + + type = INBOX; + name = "INBOX"; + + } else { + + type = OTHER; + + std::unique_ptr <astring> astr; + VIMAP_PARSER_GET(astring, astr); + name = astr->value; + } + + *currentPos = pos; + + return true; + } + + + enum Type { + INBOX, + OTHER + }; + + + Type type; + string name; + }; + + + // + // mailbox_flag := "\Marked" / "\Noinferiors" / + // "\Noselect" / "\Unmarked" / flag_extension + // + + DECLARE_COMPONENT(mailbox_flag) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'\\'> )) { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + const string tname = utility::stringUtils::toLower(at->value); + + type = UNKNOWN; // default + + switch (tname[0]) { + + case 'a': + + if (tname == "all") { + type = SPECIALUSE_ALL; + } else if (tname == "archive") { + type = SPECIALUSE_ARCHIVE; + } + + break; + + case 'd': + + if (tname == "drafts") { + type = SPECIALUSE_DRAFTS; + } + + break; + + case 'f': + + if (tname == "flagged") { + type = SPECIALUSE_FLAGGED; + } + + break; + + case 'h': + + if (tname == "haschildren") { + type = HASCHILDREN; + } else if (tname == "hasnochildren") { + type = HASNOCHILDREN; + } + + break; + + case 'i': + + if (tname == "important") { + type = SPECIALUSE_IMPORTANT; + } + + break; + + case 'j': + + if (tname == "junk") { + type = SPECIALUSE_JUNK; + } + + break; + + case 'm': + + if (tname == "marked") { + type = MARKED; + } + + break; + + case 'n': + + if (tname == "noinferiors") { + type = NOINFERIORS; + } else if (tname == "noselect") { + type = NOSELECT; + } + + break; + + case 's': + + if (tname == "sent") { + type = SPECIALUSE_SENT; + } + + break; + + case 't': + + if (tname == "trash") { + type = SPECIALUSE_TRASH; + } + + break; + + case 'u': + + if (tname == "unmarked") { + type = UNMARKED; + } + + break; + } + + if (type == UNKNOWN) { + name = "\\" + tname; + } + + } else { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + const string tname = utility::stringUtils::toLower(at->value); + + type = UNKNOWN; + name = tname; + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // RFC-3348 - Child Mailbox Extension + HASCHILDREN, + HASNOCHILDREN, + + // RFC-6154 - Special-Use Mailboxes + SPECIALUSE_ALL, + SPECIALUSE_ARCHIVE, + SPECIALUSE_DRAFTS, + SPECIALUSE_FLAGGED, + SPECIALUSE_JUNK, + SPECIALUSE_SENT, + SPECIALUSE_TRASH, + SPECIALUSE_IMPORTANT, // draft + + // Standard mailbox flags + UNKNOWN, + MARKED, + NOINFERIORS, + NOSELECT, + UNMARKED + }; + + + Type type; + string name; + }; + + + // + // mailbox_flag_list ::= "(" #(mailbox_flag) ")" + // + + DECLARE_COMPONENT(mailbox_flag_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(mailbox_flag, flags); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <mailbox_flag>> flags; + }; + + + // + // mailbox_list ::= mailbox_flag_list SPACE + // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + // + + DECLARE_COMPONENT(mailbox_list) + + mailbox_list() + : quoted_char('\0') { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::mailbox_flag_list, mailbox_flag_list); + + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_CHECK(NIL)) { + + VIMAP_PARSER_CHECK(one_char <'"'> ); + + std::unique_ptr <QUOTED_CHAR> qc; + VIMAP_PARSER_GET(QUOTED_CHAR, qc); + quoted_char = qc->value; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox, mailbox); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::mailbox_flag_list> mailbox_flag_list; + std::unique_ptr <IMAPParser::mailbox> mailbox; + + char quoted_char; + }; + + + // + // auth_type ::= atom + // ;; Defined by [IMAP-AUTH] + // + + DECLARE_COMPONENT(auth_type) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + name = utility::stringUtils::toLower(at->value); + + if (name == "kerberos_v4") { + type = KERBEROS_V4; + } else if (name == "gssapi") { + type = GSSAPI; + } else if (name == "skey") { + type = SKEY; + } else { + type = UNKNOWN; + } + + return true; + } + + + enum Type { + UNKNOWN, + + // RFC 1731 - IMAP4 Authentication Mechanisms + KERBEROS_V4, + GSSAPI, + SKEY + }; + + + Type type; + string name; + }; + + + // + // status-att-val = ("MESSAGES" SP number) / + // ("RECENT" SP number) / + // ("UIDNEXT" SP nz-number) / + // ("UIDVALIDITY" SP nz-number) / + // ("UNSEEN" SP number) + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer + // ;; extends non-terminal defined in [IMAPABNF]. + // ;; Value 0 denotes that the mailbox doesn't + // ;; support persistent mod-sequences + // + + DECLARE_COMPONENT(status_att_val) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "HIGHESTMODSEQ" SP mod-sequence-valzer + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "highestmodseq")) { + + type = HIGHESTMODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, value); + + } else { + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "messages")) { + type = MESSAGES; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "recent")) { + type = RECENT; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnext")) { + type = UIDNEXT; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidvalidity")) { + type = UIDVALIDITY; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "unseen"); + type = UNSEEN; + } + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::number, value); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // Extensions + HIGHESTMODSEQ, + + // Standard IMAP + MESSAGES, + RECENT, + UIDNEXT, + UIDVALIDITY, + UNSEEN + }; + + + Type type; + std::unique_ptr <IMAPParser::component> value; + + + const IMAPParser::number* value_as_number() const { + return dynamic_cast <IMAPParser::number *>(value.get()); + } + + const IMAPParser::mod_sequence_value* value_as_mod_sequence_value() const { + return dynamic_cast <IMAPParser::mod_sequence_value *>(value.get()); + } + }; + + + // status-att-list = status-att-val *(SP status-att-val) + + DECLARE_COMPONENT(status_att_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET_PUSHBACK(IMAPParser::status_att_val, values); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(IMAPParser::status_att_val, values); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <status_att_val>> values; + }; + + + // + // capability ::= "AUTH=" auth_type / atom + // ;; New capabilities MUST begin with "X" or be + // ;; registered with IANA as standard or standards-track + // + + DECLARE_COMPONENT(capability) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::atom, atom); + + string value = atom->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] == '=')) { + + size_t pos = 5; + auth_type.reset(parser.get <IMAPParser::auth_type>(value, &pos)); + + atom.reset(); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::auth_type> auth_type; + std::unique_ptr <IMAPParser::atom> 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. + // + + DECLARE_COMPONENT(capability_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "capability"); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + std::unique_ptr <capability> cap; + + if (parser.isStrict() || capabilities.empty()) { + VIMAP_PARSER_GET(capability, cap); + } else { + VIMAP_PARSER_TRY_GET(capability, cap); // allow SPACE at end of line (Apple iCloud IMAP server) + } + + if (!cap) { + break; + } + + capabilities.push_back(std::move(cap)); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <capability>> 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 <"> + // + + DECLARE_COMPONENT(date_time) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // <"> date_day_fixed "-" date_month "-" date_year + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_TRY_CHECK(SPACE); + + shared_ptr <number> nd; + VIMAP_PARSER_GET(number, nd); + + VIMAP_PARSER_CHECK(one_char <'-'> ); + + shared_ptr <atom> amo; + VIMAP_PARSER_GET(atom, amo); + + VIMAP_PARSER_CHECK(one_char <'-'> ); + + shared_ptr <number> ny; + VIMAP_PARSER_GET(number, ny); + + VIMAP_PARSER_TRY_CHECK(SPACE); + + // 2digit ":" 2digit ":" 2digit + shared_ptr <number> nh; + VIMAP_PARSER_GET(number, nh); + + VIMAP_PARSER_CHECK(one_char <':'> ); + + shared_ptr <number> nmi; + VIMAP_PARSER_GET(number, nmi); + + VIMAP_PARSER_CHECK(one_char <':'> ); + + shared_ptr <number> ns; + VIMAP_PARSER_GET(number, ns); + + VIMAP_PARSER_TRY_CHECK(SPACE); + + // ("+" / "-") 4digit + int sign = 1; + + if (!(VIMAP_PARSER_TRY_CHECK(one_char <'+'> ))) { + VIMAP_PARSER_CHECK(one_char <'-'> ); + } + + shared_ptr <number> nz; + VIMAP_PARSER_GET(number, nz); + + VIMAP_PARSER_CHECK(one_char <'"'> ); + + + m_datetime.setHour(static_cast <int>(std::min(std::max(nh->value, 0ul), 23ul))); + m_datetime.setMinute(static_cast <int>(std::min(std::max(nmi->value, 0ul), 59ul))); + m_datetime.setSecond(static_cast <int>(std::min(std::max(ns->value, 0ul), 59ul))); + + const int zone = static_cast <int>(nz->value); + const int zh = zone / 100; // hour offset + const int zm = zone % 100; // minute offset + + m_datetime.setZone(((zh * 60) + zm) * sign); + + m_datetime.setDay(static_cast <int>(std::min(std::max(nd->value, 1ul), 31ul))); + m_datetime.setYear(static_cast <int>(ny->value)); + + const string month(utility::stringUtils::toLower(amo->value)); + int mon = vmime::datetime::JANUARY; + + if (month.length() >= 3) { + + switch (month[0]) { + + case 'j': { + + switch (month[1]) { + + case 'a': mon = vmime::datetime::JANUARY; break; + case 'u': { + + switch (month[2]) { + + case 'n': mon = vmime::datetime::JUNE; break; + default: mon = vmime::datetime::JULY; break; + } + + break; + } + + } + + break; + } + case 'f': mon = vmime::datetime::FEBRUARY; break; + case 'm': { + + switch (month[2]) { + case 'r': mon = vmime::datetime::MARCH; break; + default: mon = vmime::datetime::MAY; break; + } + + break; + } + case 'a': + { + switch (month[1]) { + case 'p': mon = vmime::datetime::APRIL; break; + default: mon = vmime::datetime::AUGUST; break; + } + + break; + } + case 's': mon = vmime::datetime::SEPTEMBER; break; + case 'o': mon = vmime::datetime::OCTOBER; break; + case 'n': mon = vmime::datetime::NOVEMBER; break; + case 'd': mon = vmime::datetime::DECEMBER; break; + } + } + + m_datetime.setMonth(mon); + + *currentPos = pos; + + return true; + } + + private: + + vmime::datetime m_datetime; + }; + + + // + // header_fld_name ::= astring + // + + COMPONENT_ALIAS(astring, header_fld_name); + + + // + // header_list ::= "(" 1#header_fld_name ")" + // + + DECLARE_COMPONENT(header_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(header_fld_name, fld_names); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <header_fld_name>> 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. + // + + DECLARE_COMPONENT(body_extension) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + } else { + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::nstring, nstring)) { + VIMAP_PARSER_GET(IMAPParser::number, number); + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::nstring> nstring; + std::unique_ptr <IMAPParser::number> number; + + std::vector <std::unique_ptr <body_extension>> body_extensions; + }; + + + // + // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"] + // SPACE header_list / "TEXT" / "MIME" + // + + DECLARE_COMPONENT(section_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "HEADER.FIELDS" [".NOT"] SPACE header_list + const bool b1 = VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header.fields.not"); + const bool b2 = (b1 ? false : VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header.fields")); + + if (b1 || b2) { + + type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::header_list, header_list); + + // "HEADER" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header")) { + + type = HEADER; + + // "MIME" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "mime")) { + + type = MIME; + + // "TEXT" + } else { + + type = TEXT; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "text"); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + HEADER, + HEADER_FIELDS, + HEADER_FIELDS_NOT, + MIME, + TEXT + }; + + + Type type; + std::unique_ptr <IMAPParser::header_list> header_list; + }; + + + // + // section ::= "[" [section_text / (nz_number *["." nz_number] + // ["." (section_text / "MIME")])] "]" + // + + DECLARE_COMPONENT(section) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'['> ); + + if (!VIMAP_PARSER_TRY_CHECK(one_char <']'> )) { + + if (!VIMAP_PARSER_TRY_GET(section_text, section_text1)) { + + std::unique_ptr <nz_number> num; + VIMAP_PARSER_GET(nz_number, num); + nz_numbers.push_back(static_cast <unsigned int>(num->value)); + + while (VIMAP_PARSER_TRY_CHECK(one_char <'.'> )) { + + if (VIMAP_PARSER_TRY_GET(nz_number, num)) { + nz_numbers.push_back(static_cast <unsigned int>(num->value)); + } else { + VIMAP_PARSER_GET(section_text, section_text2); + break; + } + } + } + + VIMAP_PARSER_CHECK(one_char <']'> ); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <section_text> section_text1; + std::unique_ptr <section_text> section_text2; + std::vector <unsigned int> 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 ")" + // + + DECLARE_COMPONENT(address) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + VIMAP_PARSER_GET(nstring, addr_name); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_adl); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_mailbox); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_host); + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <nstring> addr_name; + std::unique_ptr <nstring> addr_adl; + std::unique_ptr <nstring> addr_mailbox; + std::unique_ptr <nstring> addr_host; + }; + + + // + // address_list ::= "(" 1*address ")" / nil + // + + DECLARE_COMPONENT(address_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_CHECK(NIL)) { + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(address, addresses); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <address>> addresses; + }; + + + // + // env_bcc ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_bcc); + + + // + // env_cc ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_cc); + + + // + // env_date ::= nstring + // + + COMPONENT_ALIAS(nstring, env_date); + + + // + // env_from ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_from); + + + // + // env_in_reply_to ::= nstring + // + + COMPONENT_ALIAS(nstring, env_in_reply_to); + + + // + // env_message_id ::= nstring + // + + COMPONENT_ALIAS(nstring, env_message_id); + + + // + // env_reply_to ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_reply_to); + + + // + // env_sender ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_sender); + + + // + // env_subject ::= nstring + // + + COMPONENT_ALIAS(nstring, env_subject); + + + // + // env_to ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(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 ")" + // + + DECLARE_COMPONENT(envelope) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + VIMAP_PARSER_GET(IMAPParser::env_date, env_date); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_subject, env_subject); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_from, env_from); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_sender, env_sender); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_reply_to, env_reply_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_to, env_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_cc, env_cc); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_bcc, env_bcc); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_in_reply_to, env_in_reply_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_message_id, env_message_id); + + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::env_date> env_date; + std::unique_ptr <IMAPParser::env_subject> env_subject; + std::unique_ptr <IMAPParser::env_from> env_from; + std::unique_ptr <IMAPParser::env_sender> env_sender; + std::unique_ptr <IMAPParser::env_reply_to> env_reply_to; + std::unique_ptr <IMAPParser::env_to> env_to; + std::unique_ptr <IMAPParser::env_cc> env_cc; + std::unique_ptr <IMAPParser::env_bcc> env_bcc; + std::unique_ptr <IMAPParser::env_in_reply_to> env_in_reply_to; + std::unique_ptr <IMAPParser::env_message_id> env_message_id; + }; + + + // + // body_fld_desc ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_desc); + + + // + // body_fld_id ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_id); + + + // + // body_fld_md5 ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_md5); + + + // + // body_fld_octets ::= number + // + + COMPONENT_ALIAS(number, body_fld_octets); + + + // + // body_fld_lines ::= number + // + + COMPONENT_ALIAS(number, body_fld_lines); + + + // + // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ + // "QUOTED-PRINTABLE") <">) / string + // + + class body_fld_enc : public nstring { + + public: + + const string getComponentName() const { + + return "body_fld_enc"; + } + + body_fld_enc() { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!xstring::parseImpl(parser, line, &pos)) { + return false; + } + + // " When an IMAP4 client sends a FETCH (bodystructure) request + // to a server that is running the Exchange Server 2007 IMAP4 + // service, a corrupted response is sent as a reply " + // (see http://support.microsoft.com/kb/975918/en-us) + // + // Fail in strict mode + if (isNIL && parser.isStrict()) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + }; + + + // + // body_fld_param_item ::= string SPACE string + // + + DECLARE_COMPONENT(body_fld_param_item) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!parser.isStrict()) { + + // Some servers send an <atom> instead of a <string> here: + // eg. ... (CHARSET "X-UNKNOWN") ... + if (!VIMAP_PARSER_TRY_GET(xstring, string1)) { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + string1.reset(new xstring()); + string1->value = at->value; + } + + } else { + + VIMAP_PARSER_GET(xstring, string1); + } + + VIMAP_PARSER_CHECK(SPACE); + + if (!parser.isStrict()) { + + // In non-strict mode, allow NIL in value + std::unique_ptr <nstring> nstr; + VIMAP_PARSER_GET(nstring, nstr); + + string2.reset(new xstring()); + string2->value = nstr->value; + + } else { + + VIMAP_PARSER_GET(xstring, string2); + } + + DEBUG_FOUND("body_fld_param_item", "<" << string1->value << ", " << string2->value << ">"); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <xstring> string1; + std::unique_ptr <xstring> string2; + }; + + + // + // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil + // + + DECLARE_COMPONENT(body_fld_param) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + bool isNIL = false; + + if (!parser.isStrict()) { + + // In non-strict mode, allow "()" instead of "NIL" + if (VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + isNIL = true; + } + } + + if (!isNIL) { + + VIMAP_PARSER_GET_PUSHBACK(body_fld_param_item, items); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(body_fld_param_item, items); + } + } + + } else { + + VIMAP_PARSER_CHECK(NIL); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <body_fld_param_item>> items; + }; + + + // + // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil + // + + DECLARE_COMPONENT(body_fld_dsp) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET(xstring, m_string); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(class body_fld_param, m_body_fld_param); + VIMAP_PARSER_CHECK(one_char <')'> ); + + } else { + + VIMAP_PARSER_CHECK(NIL); + } + + *currentPos = pos; + + return true; + } + + private: + + std::unique_ptr <class xstring> m_string; + std::unique_ptr <class body_fld_param> m_body_fld_param; + + public: + + const class xstring* str() const { return m_string.get(); } + const class body_fld_param* body_fld_param() const { return m_body_fld_param.get(); } + }; + + + // + // body_fld_lang ::= nstring / "(" 1#string ")" + // + + DECLARE_COMPONENT(body_fld_lang) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET_PUSHBACK(xstring, strings); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(xstring, strings); + } + + } else { + + VIMAP_PARSER_GET_PUSHBACK(nstring, strings); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <xstring>> strings; + }; + + + // + // body_fields ::= body_fld_param SPACE body_fld_id SPACE + // body_fld_desc SPACE body_fld_enc SPACE + // body_fld_octets + // + + DECLARE_COMPONENT(body_fields) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_param, body_fld_param); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_id, body_fld_id); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_desc, body_fld_desc); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_enc, body_fld_enc); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_octets, body_fld_octets); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_param> body_fld_param; + std::unique_ptr <IMAPParser::body_fld_id> body_fld_id; + std::unique_ptr <IMAPParser::body_fld_desc> body_fld_desc; + std::unique_ptr <IMAPParser::body_fld_enc> body_fld_enc; + std::unique_ptr <IMAPParser::body_fld_octets> body_fld_octets; + }; + + + // + // media_subtype ::= string + // ;; Defined in [MIME-IMT] + // + + COMPONENT_ALIAS(xstring, media_subtype); + + + // + // media_text ::= <"> "TEXT" <"> SPACE media_subtype + // ;; Defined in [MIME-IMT] + // + + DECLARE_COMPONENT(media_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK_WITHARG(special_atom, "text"); + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + }; + + + // + // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <"> + // ;; Defined in [MIME-IMT] + // + + DECLARE_COMPONENT(media_message) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK_WITHARG(special_atom, "message"); + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK(SPACE); + + //VIMAP_PARSER_CHECK(one_char <'"'> ); + //VIMAP_PARSER_CHECK_WITHARG(special_atom, "rfc822"); + //VIMAP_PARSER_CHECK(one_char <'"'> ); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + }; + + + // + // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" / + // "MESSAGE" / "VIDEO") <">) / string) + // SPACE media_subtype + // ;; Defined in [MIME-IMT] + + DECLARE_COMPONENT(media_basic) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(xstring, media_type); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::xstring> media_type; + std::unique_ptr <IMAPParser::media_subtype> 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 + // + + DECLARE_COMPONENT(body_ext_1part) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) + { + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_md5, body_fld_md5); + + // [SPACE body_fld_dsp + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_dsp, body_fld_dsp); + + // [SPACE body_fld_lang + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_lang, body_fld_lang); + + // [SPACE 1#body_extension] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + } + } + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_md5> body_fld_md5; + std::unique_ptr <IMAPParser::body_fld_dsp> body_fld_dsp; + std::unique_ptr <IMAPParser::body_fld_lang> body_fld_lang; + + std::vector <std::unique_ptr <body_extension>> 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 + + DECLARE_COMPONENT(body_ext_mpart) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_param, body_fld_param); + + // [SPACE body_fld_dsp [SPACE body_fld_lang [SPACE 1#body_extension]]] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_dsp, body_fld_dsp); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_lang, body_fld_lang); + + // [SPACE 1#body_extension] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + } + } + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_param> body_fld_param; + std::unique_ptr <IMAPParser::body_fld_dsp> body_fld_dsp; + std::unique_ptr <IMAPParser::body_fld_lang> body_fld_lang; + + std::vector <std::unique_ptr <body_extension>> body_extensions; + }; + + + // + // body_type_basic ::= media_basic SPACE body_fields + // ;; MESSAGE subtype MUST NOT be "RFC822" + // + + DECLARE_COMPONENT(body_type_basic) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_basic, media_basic); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_basic> media_basic; + std::unique_ptr <IMAPParser::body_fields> body_fields; + }; + + + // + // body_type_msg ::= media_message SPACE body_fields SPACE envelope + // SPACE body SPACE body_fld_lines + // + + class xbody; + typedef xbody body; + + DECLARE_COMPONENT(body_type_msg) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_message, media_message); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + VIMAP_PARSER_CHECK(SPACE); + + // BUGFIX: made SPACE optional. This is not standard, but some servers + // seem to return responses like that... + VIMAP_PARSER_GET(IMAPParser::envelope, envelope); + VIMAP_PARSER_TRY_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::xbody, body); + VIMAP_PARSER_TRY_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_lines, body_fld_lines); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_message> media_message; + std::unique_ptr <IMAPParser::body_fields> body_fields; + std::unique_ptr <IMAPParser::envelope> envelope; + std::unique_ptr <IMAPParser::xbody> body; + std::unique_ptr <IMAPParser::body_fld_lines> body_fld_lines; + }; + + + // + // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines + // + + DECLARE_COMPONENT(body_type_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_text, media_text); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_lines, body_fld_lines); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_text> media_text; + std::unique_ptr <IMAPParser::body_fields> body_fields; + std::unique_ptr <IMAPParser::body_fld_lines> body_fld_lines; + }; + + + // + // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text) + // [SPACE body_ext_1part] + // + + DECLARE_COMPONENT(body_type_1part) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_text, body_type_text)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_msg, body_type_msg)) { + VIMAP_PARSER_GET(IMAPParser::body_type_basic, body_type_basic); + } + } + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_ext_1part, body_ext_1part)) { + --pos; + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_type_basic> body_type_basic; + std::unique_ptr <IMAPParser::body_type_msg> body_type_msg; + std::unique_ptr <IMAPParser::body_type_text> body_type_text; + + std::unique_ptr <IMAPParser::body_ext_1part> body_ext_1part; + }; + + + // + // body_type_mpart ::= 1*body SPACE media_subtype + // [SPACE body_ext_mpart] + // + + DECLARE_COMPONENT(body_type_mpart) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET_PUSHBACK(xbody, list); + + while (true) { + VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(xbody, list, break); + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(IMAPParser::body_ext_mpart, body_ext_mpart); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + std::unique_ptr <IMAPParser::body_ext_mpart> body_ext_mpart; + + std::vector <std::unique_ptr <xbody>> list; + }; + + + // + // xbody ::= "(" body_type_1part / body_type_mpart ")" + // + + DECLARE_COMPONENT(xbody) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_mpart, body_type_mpart)) { + VIMAP_PARSER_GET(IMAPParser::body_type_1part, body_type_1part); + } + + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_type_1part> body_type_1part; + std::unique_ptr <IMAPParser::body_type_mpart> body_type_mpart; + }; + + + // 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 + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // msg_att_item /= "MODSEQ" SP "(" mod_sequence_value ")" + + DECLARE_COMPONENT(msg_att_item) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "ENVELOPE" SPACE envelope + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "envelope")) { + + type = ENVELOPE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::envelope, envelope); + + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "flags")) { + + type = FLAGS; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::flag_list, flag_list); + + // "INTERNALDATE" SPACE date_time + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "internaldate")) { + + type = INTERNALDATE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::date_time, date_time); + + // "RFC822" ".HEADER" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.header")) { + + type = RFC822_HEADER; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::nstring, nstring); + + // "RFC822" ".TEXT" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.text")) { + + type = RFC822_TEXT; + + VIMAP_PARSER_CHECK(SPACE); + + nstring.reset(parser.getWithArgs <IMAPParser::nstring>(line, &pos, this, RFC822_TEXT)); + + VIMAP_PARSER_FAIL_UNLESS(nstring); + + // "RFC822.SIZE" SPACE number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.size")) { + + type = RFC822_SIZE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::number, number); + + // "RFC822" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822")) { + + type = RFC822; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::nstring, nstring); + + // "BODY" "STRUCTURE" SPACE body + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "bodystructure")) { + + type = BODY_STRUCTURE; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::body, body); + + // "BODY" section ["<" number ">"] SPACE nstring + // "BODY" SPACE body + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "body")) { + + VIMAP_PARSER_TRY_GET(IMAPParser::section, section); + + // "BODY" section ["<" number ">"] SPACE nstring + if (section != NULL) { + + type = BODY_SECTION; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'<'> )) { + VIMAP_PARSER_GET(IMAPParser::number, number); + VIMAP_PARSER_CHECK(one_char <'>'> ); + } + + VIMAP_PARSER_CHECK(SPACE); + + nstring.reset(parser.getWithArgs <IMAPParser::nstring>(line, &pos, this, BODY_SECTION)); + + VIMAP_PARSER_FAIL_UNLESS(nstring); + + // "BODY" SPACE body + } else { + + type = BODY; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::body, body); + } + + // "MODSEQ" SP "(" mod_sequence_value ")" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "modseq")) { + + type = MODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_CHECK(one_char <'('> ); + + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, mod_sequence_value); + + VIMAP_PARSER_CHECK(one_char <')'> ); + + // "UID" SPACE uniqueid + } else { + + type = UID; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "uid"); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::uniqueid, uniqueid); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + ENVELOPE, + FLAGS, + INTERNALDATE, + RFC822, + RFC822_SIZE, + RFC822_HEADER, + RFC822_TEXT, + BODY, + BODY_SECTION, + BODY_STRUCTURE, + UID, + MODSEQ + }; + + + Type type; + + std::unique_ptr <IMAPParser::date_time> date_time; + std::unique_ptr <IMAPParser::number> number; + std::unique_ptr <IMAPParser::envelope> envelope; + std::unique_ptr <IMAPParser::uniqueid> uniqueid; + std::unique_ptr <IMAPParser::nstring> nstring; + std::unique_ptr <IMAPParser::xbody> body; + std::unique_ptr <IMAPParser::flag_list> flag_list; + std::unique_ptr <IMAPParser::section> section; + std::unique_ptr <IMAPParser::mod_sequence_value> mod_sequence_value; + }; + + + // + // msg_att ::= "(" 1#(msg_att_item) ")" + // + + DECLARE_COMPONENT(msg_att) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + items.push_back(std::move(std::unique_ptr <msg_att_item>(parser.get <msg_att_item>(line, &pos)))); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(msg_att_item, items); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <msg_att_item>> items; + }; + + + // + // message_data ::= nz_number SPACE ("EXPUNGE" / + // ("FETCH" SPACE msg_att)) + // + + DECLARE_COMPONENT(message_data) + + message_data() + : number(0) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + scoped_ptr <nz_number> num; + VIMAP_PARSER_GET(nz_number, num); + number = static_cast <unsigned int>(num->value); + + VIMAP_PARSER_CHECK(SPACE); + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "expunge")) { + + type = EXPUNGE; + + } else { + + type = FETCH; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "fetch"); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::msg_att, msg_att); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + EXPUNGE, + FETCH + }; + + + Type type; + unsigned int number; + std::unique_ptr <IMAPParser::msg_att> msg_att; + }; + + + // + // resp_text_code ::= "ALERT" / "PARSE" / + // capability-data / + // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + // "UIDVALIDITY" SPACE nz_number / + // "UNSEEN" SPACE nz_number / + // "UIDNEXT" SPACE nz-number / + // atom [SPACE 1*<any TEXT_CHAR except "]">] + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value / + // "NOMODSEQ" / + // "MODIFIED" SP set + // + // IMAP UIDPLUS Extension (RFC-4315): + // + // resp-text-code =/ "APPENDUID" SP nz-number SP append-uid / + // "COPYUID" SP nz-number SP uid-set SP uid-set / + // "UIDNOTSTICKY" + + DECLARE_COMPONENT(resp_text_code) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "ALERT" + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "alert")) { + + type = ALERT; + + // "PARSE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "parse")) { + + type = PARSE; + + // capability_data + } else if (VIMAP_PARSER_TRY_GET(IMAPParser::capability_data, capability_data)) { + + type = CAPABILITY; + + // "PERMANENTFLAGS" SPACE flag_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "permanentflags")) { + + type = PERMANENTFLAGS; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::flag_list, flag_list); + + // "READ-ONLY" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "read-only")) { + + type = READ_ONLY; + + // "READ-WRITE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "read-write")) { + + type = READ_WRITE; + + // "TRYCREATE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "trycreate")) { + + type = TRYCREATE; + + // "UIDVALIDITY" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidvalidity")) { + + type = UIDVALIDITY; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "UIDNEXT" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnext")) { + + type = UIDNEXT; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "UNSEEN" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "unseen")) { + + type = UNSEEN; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "HIGHESTMODSEQ" SP mod-sequence-value + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "highestmodseq")) { + + type = HIGHESTMODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, mod_sequence_value); + + // "NOMODSEQ" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "nomodseq")) { + + type = NOMODSEQ; + + // "MODIFIED" SP sequence-set + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "modified")) { + + type = MODIFIED; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::sequence_set, sequence_set); + + // "APPENDUID" SP nz-number SP append-uid + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "appenduid")) { + + type = APPENDUID; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set); + + // "COPYUID" SP nz-number SP uid-set SP uid-set + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "copyuid")) { + + type = COPYUID; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set2); + + // "UIDNOTSTICKY" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnotsticky")) { + + type = UIDNOTSTICKY; + + // atom [SPACE 1*<any TEXT_CHAR except "]">] + } else { + + type = OTHER; + + VIMAP_PARSER_GET(IMAPParser::atom, atom); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(text_except <']'> , text); + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // Extensions + HIGHESTMODSEQ, + NOMODSEQ, + MODIFIED, + APPENDUID, + COPYUID, + UIDNOTSTICKY, + + // Standard IMAP + ALERT, + PARSE, + CAPABILITY, + PERMANENTFLAGS, + READ_ONLY, + READ_WRITE, + TRYCREATE, + UIDVALIDITY, + UIDNEXT, + UNSEEN, + OTHER + }; + + + Type type; + + std::unique_ptr <IMAPParser::nz_number> nz_number; + std::unique_ptr <IMAPParser::atom> atom; + std::unique_ptr <IMAPParser::flag_list> flag_list; + std::unique_ptr <IMAPParser::text> text; + std::unique_ptr <IMAPParser::mod_sequence_value> mod_sequence_value; + std::unique_ptr <IMAPParser::sequence_set> sequence_set; + std::unique_ptr <IMAPParser::capability_data> capability_data; + std::unique_ptr <IMAPParser::uid_set> uid_set; + std::unique_ptr <IMAPParser::uid_set> uid_set2; + }; + + + // + // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + // ;; text SHOULD NOT begin with "[" or "=" + + DECLARE_COMPONENT(resp_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'['> )) { + + VIMAP_PARSER_GET(IMAPParser::resp_text_code, resp_text_code); + + VIMAP_PARSER_CHECK(one_char <']'> ); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + std::unique_ptr <text_mime2> text1; + VIMAP_PARSER_TRY_GET(text_mime2, text1); + + if (text1.get()) { + + text = text1->value; + + } else { + + std::unique_ptr <IMAPParser::text> text2; + VIMAP_PARSER_TRY_GET(IMAPParser::text, text2); + + if (text2.get()) { + text = text2->value; + } else { + // Empty response text + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text_code> resp_text_code; + string text; + }; + + + // + // continue_req ::= "+" SPACE (resp_text / base64) + // + + DECLARE_COMPONENT(continue_req) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'+'> ); + + if (!parser.isStrict()) { + + // Some servers do not send SPACE when response text is empty + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + } else { + resp_text.reset(new IMAPParser::resp_text()); // empty + } + + } else { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + // ;; Status condition + // + + DECLARE_COMPONENT(resp_cond_state) + + resp_cond_state() + : status(BAD) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "ok")) { + status = OK; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "no")) { + status = NO; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "bad"); + status = BAD; + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + enum Status { + OK, + NO, + BAD + }; + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + Status status; + }; + + + // + // resp_cond_bye ::= "BYE" SPACE resp_text + // + + DECLARE_COMPONENT(resp_cond_bye) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "bye"); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text + // ;; Authentication condition + // + + DECLARE_COMPONENT(resp_cond_auth) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "ok")) { + condition = OK; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "preauth"); + condition = PREAUTH; + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + enum Condition { + OK, + PREAUTH + }; + + + Condition condition; + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // 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-list] ")" / + // number SPACE "EXISTS" / + // number SPACE "RECENT" + // + + DECLARE_COMPONENT(mailbox_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_GET(IMAPParser::number, number)) { + + VIMAP_PARSER_CHECK(SPACE); + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "exists")) { + type = EXISTS; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "recent"); + type = RECENT; + } + + } else { + + // "FLAGS" SPACE mailbox_flag_list + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "flags")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_flag_list, mailbox_flag_list); + + type = FLAGS; + + // "LIST" SPACE mailbox_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "list")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_list, mailbox_list); + + type = LIST; + + // "LSUB" SPACE mailbox_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "lsub")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_list, mailbox_list); + + type = LSUB; + + // "MAILBOX" SPACE text + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "mailbox")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::text, text); + + type = MAILBOX; + + // "SEARCH" [SPACE 1#nz_number] + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "search")) { + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(nz_number, search_nz_number_list); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(nz_number, search_nz_number_list); + } + } + + type = SEARCH; + + // "STATUS" SPACE mailbox SPACE + // "(" [status_att_list] ")" + } else { + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "status"); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox, mailbox); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_CHECK(one_char <'('> ); + VIMAP_PARSER_TRY_GET(IMAPParser::status_att_list, status_att_list); + VIMAP_PARSER_CHECK(one_char <')'> ); + + type = STATUS; + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + + FLAGS, + LIST, + LSUB, + MAILBOX, + SEARCH, + STATUS, + EXISTS, + RECENT + }; + + + Type type; + + std::unique_ptr <IMAPParser::number> number; + std::unique_ptr <IMAPParser::mailbox_flag_list> mailbox_flag_list; + std::unique_ptr <IMAPParser::mailbox_list> mailbox_list; + std::unique_ptr <IMAPParser::mailbox> mailbox; + std::unique_ptr <IMAPParser::text> text; + std::vector <std::unique_ptr <nz_number>> search_nz_number_list; + std::unique_ptr <IMAPParser::status_att_list> status_att_list; + }; + + + // + // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + // mailbox_data / message_data / capability_data) CRLF + // + + DECLARE_COMPONENT(response_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_state, resp_cond_state)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_bye, resp_cond_bye)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::mailbox_data, mailbox_data)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::message_data, message_data)) { + VIMAP_PARSER_GET(IMAPParser::capability_data, capability_data); + } + } + } + } + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_cond_state> resp_cond_state; + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + std::unique_ptr <IMAPParser::mailbox_data> mailbox_data; + std::unique_ptr <IMAPParser::message_data> message_data; + std::unique_ptr <IMAPParser::capability_data> capability_data; + }; + + + DECLARE_COMPONENT(continue_req_or_response_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::continue_req, continue_req)) { + VIMAP_PARSER_GET(IMAPParser::response_data, response_data); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::continue_req> continue_req; + std::unique_ptr <IMAPParser::response_data> response_data; + }; + + + // + // response_fatal ::= "*" SPACE resp_cond_bye CRLF + // ;; Server closes connection immediately + // + + DECLARE_COMPONENT(response_fatal) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_cond_bye, resp_cond_bye); + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + }; + + + // + // response_tagged ::= tag SPACE resp_cond_state CRLF + // + + DECLARE_COMPONENT(response_tagged) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::xtag, tag); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::resp_cond_state, resp_cond_state); + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::xtag> tag; + std::unique_ptr <IMAPParser::resp_cond_state> resp_cond_state; + }; + + + // + // response_done ::= response_tagged / response_fatal + // + + DECLARE_COMPONENT(response_done) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::response_tagged, response_tagged)) { + VIMAP_PARSER_GET(IMAPParser::response_fatal, response_fatal); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::response_tagged> response_tagged; + std::unique_ptr <IMAPParser::response_fatal> response_fatal; + }; + + + // + // response ::= *(continue_req / response_data) response_done + // + + DECLARE_COMPONENT(response) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t 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))) { + + continue_req_or_response_data.push_back( + std::move( + std::unique_ptr <IMAPParser::continue_req_or_response_data>(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) { + response_done.reset(parser.get <IMAPParser::response_done>(curLine, &pos)); + VIMAP_PARSER_FAIL_UNLESS(response_done); + } + + *currentPos = pos; + + return true; + } + + + 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; + } + + void setErrorLog(const string& errorLog) { + + m_errorLog = errorLog; + } + + const string& getErrorLog() const { + + return m_errorLog; + } + + + std::vector <std::unique_ptr <IMAPParser::continue_req_or_response_data>> continue_req_or_response_data; + std::unique_ptr <IMAPParser::response_done> response_done; + + private: + + string m_errorLog; + }; + + + // + // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF + // + + DECLARE_COMPONENT(greeting) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_auth, resp_cond_auth)) { + VIMAP_PARSER_GET(IMAPParser::resp_cond_bye, resp_cond_bye); + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + void setErrorLog(const string& errorLog) { + + m_errorLog = errorLog; + } + + const string& getErrorLog() const { + + return m_errorLog; + } + + + std::unique_ptr <IMAPParser::resp_cond_auth> resp_cond_auth; + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + + private: + + string m_errorLog; + }; + + + + // + // The main functions used to parse a response + // + + response* readResponse(const IMAPTag& tag, literalHandler* lh = NULL) { + + while (true) { + + auto it = m_pendingResponses.find(std::string(tag)); + + if (it != m_pendingResponses.end()) { + auto* resp = it->second; + m_pendingResponses.erase(it); + return resp; + } + + size_t pos = 0; + string line = readLine(); + + m_literalHandler = lh; + response* resp = get <response>(line, &pos); + m_literalHandler = NULL; + + if (!resp) { + throw exceptions::invalid_response("", m_errorResponseLine); + } + + resp->setErrorLog(lastLine()); + + // If there is a continue_req, return the response immediately + for (auto &respData : resp->continue_req_or_response_data) { + if (respData->continue_req) { + return resp; + } + } + + // Else, return response if the tag is the one we expect + if (resp->response_done && resp->response_done->response_tagged && + resp->response_done->response_tagged->tag) { + + if (tag == resp->response_done->response_tagged->tag->tagString) { + return resp; + } else { + // Not our response tag, cache it for later + m_pendingResponses[resp->response_done->response_tagged->tag->tagString] = resp; + } + } else { + // Untagged response + return resp; + } + } + } + + + greeting* readGreeting() { + + size_t pos = 0; + string line = readLine(); + + greeting* greet = get <greeting>(line, &pos); + + if (!greet) { + throw exceptions::invalid_response("", m_errorResponseLine); + } + + greet->setErrorLog(lastLine()); + + return greet; + } + + + /** Parse a token and advance. + * If the token has been parsed successfully, a raw pointer to it + * will be returned. The caller is responsible to free the memory. + * + * @param TYPE token type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @return a raw pointer to the parsed token, or NULL otherwise + */ + template <class TYPE> + TYPE* get(string& line, size_t* currentPos) { + + component* resp = new TYPE; + return internalGet <TYPE>(resp, line, currentPos); + } + + /** Parse a token which takes 2 arguments and advance. + * If the token has been parsed successfully, a raw pointer to it + * will be returned. The caller is responsible to free the memory. + * + * @param TYPE token type (class) + * @param ARG1_TYPE type of argument #1 (class) + * @param ARG2_TYPE type of argument #2 (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @param arg1 value of argument 1 to be passed to the token + * @param arg2 value of argument 2 to be passed to the token + * @return a raw pointer to the parsed token, or NULL otherwise + */ + template <class TYPE, class ARG1_TYPE, class ARG2_TYPE> + TYPE* getWithArgs(string& line, size_t* currentPos, ARG1_TYPE arg1, ARG2_TYPE arg2) { + + component* resp = new TYPE(arg1, arg2); + return internalGet <TYPE>(resp, line, currentPos); + } + +private: + + template <class TYPE> + TYPE* internalGet(component* resp, string& line, size_t* currentPos) { + + const size_t oldPos = *currentPos; + + if (!resp->parse(*this, line, currentPos)) { + + *currentPos = oldPos; + + delete resp; + + return NULL; + } + + return static_cast <TYPE*>(resp); + } + + 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); + } + +public: + + /** Check for a token and advance. + * + * @param TYPE token type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @return true if the token has been parsed, or false otherwise + */ + template <class TYPE> + bool check(string& line, size_t* currentPos) { + + const size_t oldPos = *currentPos; + + TYPE term; + + if (!term.parse(*this, line, currentPos)) { + *currentPos = oldPos; + return false; + } else { + return true; + } + } + + /** Check for a token which takes an argument and advance. + * + * @param TYPE token type (class) + * @param ARG_TYPE argument type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @param arg argument to be passed to the token + * @return true if the token has been parsed, or false otherwise + */ + template <class TYPE, class ARG_TYPE> + bool checkWithArg(string& line, size_t* currentPos, const ARG_TYPE arg) { + + const size_t oldPos = *currentPos; + + TYPE term(arg); + + if (!term.parse(*this, line, currentPos)) { + *currentPos = oldPos; + return false; + } else { + return true; + } + } + + +private: + + weak_ptr <socket> m_socket; + shared_ptr <tracer> m_tracer; + + utility::progressListener* m_progress; + + bool m_strict; + + literalHandler* m_literalHandler; + + weak_ptr <timeoutHandler> m_timeoutHandler; + + + string m_buffer; + + string m_lastLine; + string m_errorResponseLine; + + std::map <std::string, response*> m_pendingResponses; + +public: + + /** Read a line from the input buffer. The function blocks until a + * complete line is read from the buffer. Position in input buffer + * will be updated. + * + * @return next line + */ + const string readLine() { + + size_t 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 + + if (m_tracer) { + string::size_type len = line.length(); + while (len != 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) --len; + m_tracer->traceReceive(line.substr(0, len)); + } + + return (line); + } + + /** Fill in the input buffer with data available from the socket stream. + * The function blocks until some data is available. + */ + void read() { + + string receiveBuffer; + + shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); + shared_ptr <socket> sok = m_socket.lock(); + + if (toh) { + toh->resetTimeOut(); + } + + while (receiveBuffer.empty()) { + + // Check whether the time-out delay is elapsed + if (toh && toh->isTimeOut()) { + if (!toh->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + } + + // We have received data: reset the time-out counter + sok->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + + if (sok->getStatus() & socket::STATUS_WANT_WRITE) { + sok->waitForWrite(); + } else { + sok->waitForRead(); + } + + continue; + } + + // We have received data ... + if (toh) { + toh->resetTimeOut(); + } + } + + m_buffer += receiveBuffer; + } + + + void readLiteral(literalHandler::target& buffer, size_t count) { + + size_t len = 0; + string receiveBuffer; + + shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); + shared_ptr <socket> sok = m_socket.lock(); + + if (m_progress) { + m_progress->start(count); + } + + if (toh) { + toh->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 (toh && toh->isTimeOut()) { + + if (!toh->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + toh->resetTimeOut(); + } + + // Receive data from the socket + sok->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + + if (sok->getStatus() & socket::STATUS_WANT_WRITE) { + sok->waitForWrite(); + } else { + sok->waitForRead(); + } + + continue; + } + + // We have received data: reset the time-out counter + if (toh) { + toh->resetTimeOut(); + } + + if (len + receiveBuffer.length() > count) { + + const size_t 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 progress + if (m_progress) { + m_progress->progress(len, count); + } + } + + if (m_tracer) { + m_tracer->traceReceiveBytes(count); + } + + if (m_progress) { + m_progress->stop(count); + } + } +}; + + +} // imap +} // net +} // vmime + + +#undef VIMAP_PARSER_FAIL +#undef VIMAP_PARSER_FAIL_UNLESS +#undef VIMAP_PARSER_CHECK +#undef VIMAP_PARSER_TRY_CHECK +#undef VIMAP_PARSER_GET +#undef VIMAP_PARSER_GET +#undef VIMAP_PARSER_TRY_GET +#undef VIMAP_PARSER_TRY_GET +#undef VIMAP_PARSER_GET_PUSHBACK +#undef VIMAP_PARSER_CHECK_WITHARG +#undef VIMAP_PARSER_TRY_CHECK_WITHARG + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED |