aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/net/imap/IMAPParser.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/net/imap/IMAPParser.hpp')
-rw-r--r--vmime-master/src/vmime/net/imap/IMAPParser.hpp4986
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