📄 imapparser.hpp
字号:
//// VMime library (http://www.vmime.org)// Copyright (C) 2002-2008 Vincent Richard <vincent@vincent-richard.net>//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License as// published by the Free Software Foundation; either version 2 of// the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU// General Public License for more details.//// You should have received a copy of the GNU General Public License along// with this program; if not, write to the Free Software Foundation, Inc.,// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.//// 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/base.hpp"#include "vmime/dateTime.hpp"#include "vmime/charset.hpp"#include "vmime/exception.hpp"#include "vmime/utility/smartPtr.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/platform.hpp"#include "vmime/net/timeoutHandler.hpp"#include "vmime/net/socket.hpp"#include "vmime/net/imap/IMAPTag.hpp"#include <vector>#include <stdexcept>//#define DEBUG_RESPONSE 1#if DEBUG_RESPONSE# include <iostream>#endifnamespace 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 string::size_type currentPos) : m_name(name), m_line(line), m_pos(currentPos) { ++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 (string::size_type 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 << (std::uncaught_exception() ? "FALSE" : "TRUE") << ", pos=" << m_pos; std::cout << std::endl; --IMAPParserDebugResponse_level; IMAPParserDebugResponse_stack.pop_back(); } private: const string& m_name; string& m_line; string::size_type m_pos; }; #define DEBUG_ENTER_COMPONENT(x) \ IMAPParserDebugResponse dbg(x, line, *currentPos) #define DEBUG_FOUND(x, y) \ std::cout << "FOUND: " << x << ": " << y << std::endl;#else #define DEBUG_ENTER_COMPONENT(x) #define DEBUG_FOUND(x, y)#endifclass IMAPParser : public object{public: IMAPParser(weak_ref <IMAPTag> tag, weak_ref <socket> sok, weak_ref <timeoutHandler> _timeoutHandler) : m_tag(tag), m_socket(sok), m_progress(NULL), m_strict(false), m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler) { } ref <const IMAPTag> getTag() const { return m_tag.acquire(); } void setSocket(ref <socket> sok) { m_socket = sok; } /** 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; } const string lastLine() const { // Remove blanks and new lines at the end of the line. string line(m_lastLine); string::const_iterator it = line.end(); int count = 0; while (it != line.begin()) { const unsigned char c = *(it - 1); if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) break; ++count; --it; } line.resize(line.length() - count); return (line); } // // literalHandler : literal content handler // class component; class literalHandler { public: virtual ~literalHandler() { } // Abstract target class class target { protected: target(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; 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) { } const vmime::string& string() const { return (m_string); } vmime::string& string() { return (m_string); } void putData(const vmime::string& chunk) { m_string += chunk; } private: vmime::string& m_string; }; // Target: redirect to an output stream class targetStream : public target { public: targetStream(utility::progressListener* progress, utility::outputStream& stream) : target(progress), m_stream(stream) { } const utility::outputStream& stream() const { return (m_stream); } utility::outputStream& stream() { return (m_stream); } void putData(const string& chunk) { m_stream.write(chunk.data(), chunk.length()); } private: utility::outputStream& m_stream; }; // Called when the parser needs to know what to do with a literal // . comp: the component in which we are at this moment // . data: data specific to the component (may not be used) // // Returns : // . == NULL to put the literal into the response // . != NULL to redirect the literal to the specified target virtual target* targetFor(const component& comp, const int data) = 0; }; // // Base class for a terminal or a non-terminal // class component { public: component() { } virtual ~component() { } virtual void go(IMAPParser& parser, string& line, string::size_type* currentPos) = 0; const string makeResponseLine(const string& comp, const string& line, const string::size_type pos) {#if DEBUG_RESPONSE if (pos > line.length()) std::cout << "WARNING: component::makeResponseLine(): pos > line.length()" << std::endl;#endif string result(line.substr(0, pos)); result += "[^]"; // indicates current parser position result += line.substr(pos, line.length()); if (!comp.empty()) result += " [" + comp + "]"; return (result); } };#define COMPONENT_ALIAS(parent, name) \ class name : public parent \ { \ void go(IMAPParser& parser, string& line, string::size_type* currentPos) \ { \ DEBUG_ENTER_COMPONENT(#name); \ parent::go(parser, line, currentPos); \ } \ } // // Parse one character // template <char C> class one_char : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'"); const string::size_type pos = *currentPos; if (pos < line.length() && line[pos] == C) *currentPos = pos + 1; else throw exceptions::invalid_response("", makeResponseLine("", line, pos)); } }; // // SPACE ::= <ASCII SP, space, 0x20> // class SPACE : public component { public: void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("SPACE"); string::size_type pos = *currentPos; while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) ++pos; if (pos > *currentPos) *currentPos = pos; else throw exceptions::invalid_response("", makeResponseLine("SPACE", line, pos)); } }; // // CR ::= <ASCII CR, carriage return, 0x0D> // LF ::= <ASCII LF, line feed, 0x0A> // CRLF ::= CR LF // class CRLF : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("CRLF"); string::size_type pos = *currentPos; parser.check <SPACE>(line, &pos, true); if (pos + 1 < line.length() && line[pos] == 0x0d && line[pos + 1] == 0x0a) { *currentPos = pos + 2; } else { throw exceptions::invalid_response("", makeResponseLine("CRLF", line, pos)); } } }; // // SPACE ::= <ASCII SP, space, 0x20> // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> // ATOM_CHAR ::= <any CHAR except atom_specials> // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials // list_wildcards ::= "%" / "*" // quoted_specials ::= <"> / "\" // // tag ::= 1*<any ATOM_CHAR except "+"> (named "xtag") // class xtag : public component { public: void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("tag"); string::size_type pos = *currentPos; bool end = false; string tagString; tagString.reserve(10); while (!end && pos < line.length()) { const unsigned char c = line[pos]; switch (c) { case '+': case '(': case ')': case '{': case 0x20: // SPACE case '%': // list_wildcards case '*': // list_wildcards case '"': // quoted_specials case '\\': // quoted_specials end = true; break; default: if (c <= 0x1f || c >= 0x7f) end = true; else { tagString += c; ++pos; } break; } } if (tagString == string(*parser.getTag())) { *currentPos = pos; } else { // Invalid tag throw exceptions::invalid_response("", makeResponseLine("tag", line, pos)); } } }; // // digit ::= "0" / digit_nz // digit_nz ::= "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" // // number ::= 1*digit // ;; Unsigned 32-bit integer // ;; (0 <= n < 4,294,967,296) // class number : public component { public: number(const bool nonZero = false) : m_nonZero(nonZero), m_value(0) { } void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("number"); string::size_type pos = *currentPos; bool valid = true; unsigned int val = 0; while (valid && pos < line.length()) { const char c = line[pos]; if (c >= '0' && c <= '9') { val = (val * 10) + (c - '0'); ++pos; } else { valid = false; } } // Check for non-null length (and for non-zero number) if (!(m_nonZero && val == 0) && pos != *currentPos) { m_value = val; *currentPos = pos; } else { throw exceptions::invalid_response("", makeResponseLine("number", line, pos)); } } private: const bool m_nonZero; unsigned int m_value; public: unsigned int value() const { return (m_value); } }; // nz_number ::= digit_nz *digit // ;; Non-zero unsigned 32-bit integer // ;; (0 < n < 4,294,967,296) // class nz_number : public number { public: nz_number() : number(true) { } }; // // text ::= 1*TEXT_CHAR // // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> // TEXT_CHAR ::= <any CHAR except CR and LF> // class text : public component { public: text(bool allow8bits = false, const char except = 0) : m_allow8bits(allow8bits), m_except(except) { } void go(IMAPParser& parser, string& line, string::size_type* currentPos) { DEBUG_ENTER_COMPONENT("text"); string::size_type pos = *currentPos; string::size_type len = 0; if (m_allow8bits || !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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -