Fixed parsing of IMAP astring.

This commit is contained in:
vincent-richard 2021-03-24 21:04:01 +01:00
parent 7d64105dee
commit e5186e6710
2 changed files with 105 additions and 88 deletions

View File

@ -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;

View File

@ -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