diff options
author | vincent-richard <[email protected]> | 2021-03-24 20:04:01 +0000 |
---|---|---|
committer | vincent-richard <[email protected]> | 2021-03-24 20:04:01 +0000 |
commit | e5186e6710ebad3cb70f59fe81d82e9d1b1fba06 (patch) | |
tree | 245c58afa110d3f6156262fc51a920ba1c700b38 | |
parent | Merge pull request #257 from jacadcaps/defaultCertificateVerifier (diff) | |
download | vmime-e5186e6710ebad3cb70f59fe81d82e9d1b1fba06.tar.gz vmime-e5186e6710ebad3cb70f59fe81d82e9d1b1fba06.zip |
Fixed parsing of IMAP astring.
-rw-r--r-- | src/vmime/net/imap/IMAPParser.hpp | 137 | ||||
-rw-r--r-- | tests/net/imap/IMAPParserTest.cpp | 56 |
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 |