From d6d36768f9cfbc1f9df01d9596403bc181ba4740 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sat, 2 May 2020 17:13:54 +1000 Subject: [PATCH] Geary.Imap.Deserialiser: Handle reserved chars in response-text RFC 3501 allows any kind of char except CRLF in `resp-text` after an optional response code, so handle that. Addresses another issue in #711 --- .../imap/transport/imap-deserializer.vala | 30 ++++++++++++++++--- .../transport/imap-deserializer-test.vala | 18 +++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/engine/imap/transport/imap-deserializer.vala b/src/engine/imap/transport/imap-deserializer.vala index 72e739eb..70079e09 100644 --- a/src/engine/imap/transport/imap-deserializer.vala +++ b/src/engine/imap/transport/imap-deserializer.vala @@ -30,6 +30,10 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { private const size_t MAX_BLOCK_READ_SIZE = 4096; + private const string[] RESPONSE_ATOMS = { + "OK", "NO", "BAD", "BYE", "PREAUTH" + }; + private enum Mode { LINE, BLOCK, @@ -49,6 +53,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { LITERAL, LITERAL_DATA_BEGIN, LITERAL_DATA, + RESPONSE_TEXT, FAILED, CLOSED, COUNT @@ -102,6 +107,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { private Geary.Memory.GrowableBuffer? block_buffer = null; private unowned uint8[]? current_buffer = null; private int ins_priority = Priority.DEFAULT; + private bool is_parsing_flags = false; @@ -179,12 +185,12 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { new Geary.State.Mapping(State.START_PARAM, Event.ERROR, on_error), new Geary.State.Mapping(State.ATOM, Event.CHAR, on_atom_char), - new Geary.State.Mapping(State.ATOM, Event.EOL, on_atom_eol), + new Geary.State.Mapping(State.ATOM, Event.EOL, on_param_eol), new Geary.State.Mapping(State.ATOM, Event.EOS, on_eos), new Geary.State.Mapping(State.ATOM, Event.ERROR, on_error), new Geary.State.Mapping(State.FLAG, Event.CHAR, on_flag_char), - new Geary.State.Mapping(State.FLAG, Event.EOL, on_atom_eol), + new Geary.State.Mapping(State.FLAG, Event.EOL, on_param_eol), new Geary.State.Mapping(State.FLAG, Event.EOS, on_eos), new Geary.State.Mapping(State.FLAG, Event.ERROR, on_error), @@ -217,6 +223,11 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { new Geary.State.Mapping(State.LITERAL_DATA, Event.EOS, on_eos), new Geary.State.Mapping(State.LITERAL_DATA, Event.ERROR, on_error), + new Geary.State.Mapping(State.RESPONSE_TEXT, Event.CHAR, on_response_text_char), + new Geary.State.Mapping(State.RESPONSE_TEXT, Event.EOL, on_param_eol), + new Geary.State.Mapping(State.RESPONSE_TEXT, Event.EOS, on_eos), + new Geary.State.Mapping(State.RESPONSE_TEXT, Event.ERROR, on_error), + new Geary.State.Mapping(State.FAILED, Event.EOL, on_failed_eol), new Geary.State.Mapping(State.FAILED, Event.EOS, Geary.State.nop), new Geary.State.Mapping(State.FAILED, Event.ERROR, Geary.State.nop), @@ -590,7 +601,12 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { return State.START_PARAM; default: - if (!this.is_parsing_flags) { + if (this.context_stack.size == 1 && + this.context.size >= 2 && + this.context.get(1).to_string().ascii_up() in RESPONSE_ATOMS) { + append_to_string(ch); + return State.RESPONSE_TEXT; + } else if (!this.is_parsing_flags) { if (DataFormat.is_atom_special(ch)) { warning("Received an invalid atom-char: %c", ch); return State.FAILED; @@ -697,7 +713,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { return State.TAG; } - private uint on_atom_eol(uint state, uint event, void *user) { + private uint on_param_eol(uint state, uint event, void *user) { // clean up final atom save_string_parameter(false); flush_params(); @@ -837,6 +853,12 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source { return State.START_PARAM; } + private uint on_response_text_char(uint state, uint event, void *user) { + char ch = *((char *) user); + append_to_string(ch); + return State.RESPONSE_TEXT; + } + private uint on_eos() { debug("EOS"); diff --git a/test/engine/imap/transport/imap-deserializer-test.vala b/test/engine/imap/transport/imap-deserializer-test.vala index 2f3a2d4a..b3a61712 100644 --- a/test/engine/imap/transport/imap-deserializer-test.vala +++ b/test/engine/imap/transport/imap-deserializer-test.vala @@ -47,6 +47,9 @@ class Geary.Imap.DeserializerTest : TestCase { // fail, disable for the moment add_test("invalid_flag_prefix", invalid_flag_prefix); + add_test("reserved_in_response_text", reserved_in_response_text); + + add_test("instant_eos", instant_eos); add_test("bye_eos", bye_eos); } @@ -220,14 +223,13 @@ class Geary.Imap.DeserializerTest : TestCase { public void aliyun_greeting() throws Error { string greeting = "* OK AliYun IMAP Server Ready(10.147.40.164)"; - string parsed = "* OK AliYun IMAP Server Ready (10.147.40.164)"; this.stream.add_data(greeting.data); this.stream.add_data(EOL.data); this.process.begin(Expect.MESSAGE, this.async_completion); RootParameters? message = this.process.end(async_result()); - assert(message.to_string() == parsed); + assert(message.to_string() == greeting); } public void invalid_atom_prefix() throws Error { @@ -320,6 +322,18 @@ class Geary.Imap.DeserializerTest : TestCase { //this.process.end(async_result()); } + public void reserved_in_response_text() throws Error { + // As seen in #711 + string line = """a008 BAD Missing ] in: header.fields"""; + this.stream.add_data(line.data); + this.stream.add_data(EOL.data); + + this.process.begin(Expect.MESSAGE, this.async_completion); + RootParameters? message = this.process.end(async_result()); + + assert(message.to_string() == line); + } + public void instant_eos() throws Error { this.process.begin(Expect.EOS, this.async_completion); this.process.end(async_result());