Fixed parsing of IMAP astring.
This commit is contained in:
parent
7d64105dee
commit
e5186e6710
@ -1022,48 +1022,6 @@ public:
|
|||||||
string value;
|
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"
|
// nil ::= "NIL"
|
||||||
@ -1212,47 +1170,6 @@ public:
|
|||||||
|
|
||||||
DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << value << "'>");
|
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 {
|
} else {
|
||||||
|
|
||||||
VIMAP_PARSER_FAIL();
|
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)
|
DECLARE_COMPONENT(astring)
|
||||||
|
|
||||||
bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) {
|
bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) {
|
||||||
@ -1311,11 +1240,43 @@ public:
|
|||||||
VIMAP_PARSER_TRY_GET(xstring, str);
|
VIMAP_PARSER_TRY_GET(xstring, str);
|
||||||
|
|
||||||
if (str) {
|
if (str) {
|
||||||
|
|
||||||
value = str->value;
|
value = str->value;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
std::unique_ptr <atom> at;
|
|
||||||
VIMAP_PARSER_GET(atom, at);
|
value.reserve(line.length() - pos);
|
||||||
value = at->value;
|
|
||||||
|
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;
|
*currentPos = pos;
|
||||||
|
@ -39,6 +39,7 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest)
|
|||||||
VMIME_TEST(testPipelining)
|
VMIME_TEST(testPipelining)
|
||||||
VMIME_TEST(testStarFlagWithoutBackslash)
|
VMIME_TEST(testStarFlagWithoutBackslash)
|
||||||
VMIME_TEST(testUnquotedMailboxName)
|
VMIME_TEST(testUnquotedMailboxName)
|
||||||
|
VMIME_TEST(testInvalidCharsInAstring)
|
||||||
VMIME_TEST_LIST_END
|
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
|
VMIME_TEST_SUITE_END
|
||||||
|
Loading…
x
Reference in New Issue
Block a user