aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvincent-richard <[email protected]>2021-03-24 20:04:01 +0000
committervincent-richard <[email protected]>2021-03-24 20:04:01 +0000
commite5186e6710ebad3cb70f59fe81d82e9d1b1fba06 (patch)
tree245c58afa110d3f6156262fc51a920ba1c700b38
parentMerge pull request #257 from jacadcaps/defaultCertificateVerifier (diff)
downloadvmime-e5186e6710ebad3cb70f59fe81d82e9d1b1fba06.tar.gz
vmime-e5186e6710ebad3cb70f59fe81d82e9d1b1fba06.zip
Fixed parsing of IMAP astring.
-rw-r--r--src/vmime/net/imap/IMAPParser.hpp137
-rw-r--r--tests/net/imap/IMAPParserTest.cpp56
2 files changed, 105 insertions, 88 deletions
diff --git a/src/vmime/net/imap/IMAPParser.hpp b/src/vmime/net/imap/IMAPParser.hpp
index 2a37b9cd..59bab777 100644
--- a/src/vmime/net/imap/IMAPParser.hpp
+++ b/src/vmime/net/imap/IMAPParser.hpp
@@ -1022,48 +1022,6 @@ public:
string value;
};
- //
- // unquoted_text_nonstrict: this is an alternate version of quoted_text,
- // for use in non-strict mode. We stop at the first space character.
- //
-
- DECLARE_COMPONENT(unquoted_text_nonstrict)
-
- bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) {
-
- size_t pos = *currentPos;
- size_t len = 0;
-
- value.reserve(line.length() - pos);
-
- for (bool end = false ; !end && pos < line.length() ; ) {
-
- const unsigned char c = line[pos];
-
- if (c >= 0x01 && c <= 0x7f && // CHAR
- c != 0x0a && c != 0x0d && // CR and LF
- c != 0x20 && // SPACE
- c != 0x09) { // TAB
-
- value += c;
-
- ++pos;
- ++len;
-
- } else {
-
- end = true;
- }
- }
-
- *currentPos = pos;
-
- return true;
- }
-
-
- string value;
- };
//
// nil ::= "NIL"
@@ -1212,47 +1170,6 @@ public:
DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << value << "'>");
- // In non-strict mode, accept unquoted strings, but stop at next SPACE
- } else if (!parser.isStrict()) {
-
- shared_ptr <unquoted_text_nonstrict> text;
- VIMAP_PARSER_GET(unquoted_text_nonstrict, text);
-
- 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[non-strict]", "<length=" << value.length() << ", value='" << value << "'>");
-
} else {
VIMAP_PARSER_FAIL();
@@ -1298,9 +1215,21 @@ public:
//
- // astring ::= atom / string
+ // astring = 1*ASTRING-CHAR / string
+ //
+ // ASTRING-CHAR = ATOM-CHAR / resp-specials
+ //
+ // ATOM-CHAR = <any CHAR except atom-specials>
+ //
+ // atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards /
+ // quoted-specials / resp-specials
+ //
+ // list-wildcards = "%" / "*"
+ //
+ // quoted-specials = DQUOTE / "\"
+ //
+ // resp-specials = "]"
//
-
DECLARE_COMPONENT(astring)
bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) {
@@ -1311,11 +1240,43 @@ public:
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;
+
+ value.reserve(line.length() - pos);
+
+ for (bool end = false ; !end && pos < line.length() ; ) {
+
+ const unsigned char c = line[pos];
+
+ if (!parser.isStrict() || (c >= 0x01 && c <= 0x7f)) { // CHAR or any byte in non-strict mode
+
+ if (c == '(' ||
+ c == ')' ||
+ c == '{' ||
+ c == 0x20 || // SP
+ c == 0x0a || c == 0x0d || // CR and LF
+ c <= 0x1f || c == 0x7f || // CTL
+ c == '%' || c == '*' || // list-wildcards
+ c == '"' || c == '\\' || // quoted-specials
+ (parser.isStrict() && c == ']')) { // resp-specials
+
+ end = true;
+
+ } else {
+
+ value += c;
+
+ ++pos;
+ }
+
+ } else {
+
+ end = true;
+ }
+ }
}
*currentPos = pos;
diff --git a/tests/net/imap/IMAPParserTest.cpp b/tests/net/imap/IMAPParserTest.cpp
index 387e4c66..47880a06 100644
--- a/tests/net/imap/IMAPParserTest.cpp
+++ b/tests/net/imap/IMAPParserTest.cpp
@@ -39,6 +39,7 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest)
VMIME_TEST(testPipelining)
VMIME_TEST(testStarFlagWithoutBackslash)
VMIME_TEST(testUnquotedMailboxName)
+ VMIME_TEST(testInvalidCharsInAstring)
VMIME_TEST_LIST_END
@@ -427,4 +428,59 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest)
}
}
+ // Some broken IMAP servers return non-ASCII chars in astring.
+ // Server returns UTF-8 instead of modified UTF-7.
+ void testInvalidCharsInAstring() {
+
+ const char* respText =
+ R"END(* STATUS Segregator/Społeczności (MESSAGES 3 UIDNEXT 4 UIDVALIDITY 1519867193 UNSEEN 0))END"
+ "\r\n"
+ R"END(a001 OK Completed.)END"
+ "\r\n";
+
+ // Strict mode
+ {
+ auto socket = vmime::make_shared <testSocket>();
+ auto toh = vmime::make_shared <testTimeoutHandler>();
+
+ auto tag = vmime::make_shared <vmime::net::imap::IMAPTag>();
+
+ socket->localSend(respText);
+
+ auto parser = vmime::make_shared <vmime::net::imap::IMAPParser>();
+
+ parser->setSocket(socket);
+ parser->setTimeoutHandler(toh);
+ parser->setStrict(true);
+
+ VASSERT_THROW("strict mode", parser->readResponse(*tag), vmime::exceptions::invalid_response);
+ }
+
+ // Non-strict mode
+ {
+ auto socket = vmime::make_shared <testSocket>();
+ auto toh = vmime::make_shared <testTimeoutHandler>();
+
+ auto tag = vmime::make_shared <vmime::net::imap::IMAPTag>();
+
+ socket->localSend(respText);
+
+ auto parser = vmime::make_shared <vmime::net::imap::IMAPParser>();
+
+ parser->setSocket(socket);
+ parser->setTimeoutHandler(toh);
+ parser->setStrict(false);
+
+ std::unique_ptr <vmime::net::imap::IMAPParser::response> resp;
+
+ VASSERT_NO_THROW("non-strict mode", resp.reset(parser->readResponse(*tag)));
+
+ VASSERT_EQ("resp size", 1, resp->continue_req_or_response_data.size());
+ VASSERT("resp data", resp->continue_req_or_response_data[0]->response_data);
+ VASSERT("mbox data", resp->continue_req_or_response_data[0]->response_data->mailbox_data);
+ VASSERT("mbox", resp->continue_req_or_response_data[0]->response_data->mailbox_data->mailbox);
+ VASSERT_EQ("mbox name", "Segregator/Społeczności", resp->continue_req_or_response_data[0]->response_data->mailbox_data->mailbox->name);
+ }
+ }
+
VMIME_TEST_SUITE_END