Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions imap-codec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ quirk = [
"quirk_trailing_space_id",
"quirk_trailing_space_search",
"quirk_spaces_between_addresses",
"quirk_spaces_between_body_parts",
"quirk_empty_continue_req",
"quirk_body_fld_enc_nil_to_empty",
"quirk_always_normalize_sequence_sets",
Expand All @@ -49,6 +50,8 @@ quirk_rectify_numbers = []
quirk_excessive_space_quota_resource = []
# Accept spaces between envelope addresses in `FETCH` data response.
quirk_spaces_between_addresses = []
# Accept spaces between body parts in multipart BODYSTRUCTURE responses.
quirk_spaces_between_body_parts = []
# Accept a trailing space in `STATUS` data response.
quirk_trailing_space_status = []
# Accept a trailing space in `CAPABILITY` data response.
Expand Down
33 changes: 32 additions & 1 deletion imap-codec/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,13 @@ fn body_type_mpart_limited(
}));
}

let body_parser = body(remaining_recursion);

#[cfg(feature = "quirk_spaces_between_body_parts")]
let body_parser = preceded(many0(sp), body_parser);

let mut parser = tuple((
many1(body(remaining_recursion)),
many1(body_parser),
sp,
media_subtype,
opt(preceded(sp, body_ext_mpart)),
Expand Down Expand Up @@ -790,4 +795,30 @@ mod tests {
assert_eq!(body_fld_octets(b"-999999)").unwrap().1, 0);
}
}

#[test]
fn test_spaces_between_body_parts_quirk() {
use super::body_type_mpart_limited;

// Trailing ")" simulates the enclosing body delimiter needed by the streaming parser.

// Adjacent bodies (no space between parts) should always parse.
let adjacent = b"(\"TEXT\" \"PLAIN\" NIL NIL NIL \"7BIT\" 0 0)(\"TEXT\" \"HTML\" NIL NIL NIL \"7BIT\" 0 0) \"alternative\")";
assert!(body_type_mpart_limited(adjacent, 8).is_ok());

// Bodies with spaces between parts (common server quirk).
let spaced = b"(\"TEXT\" \"PLAIN\" NIL NIL NIL \"7BIT\" 0 0) (\"TEXT\" \"HTML\" NIL NIL NIL \"7BIT\" 0 0) \"alternative\")";

#[cfg(not(feature = "quirk_spaces_between_body_parts"))]
{
// Without quirk, the parser stops after the first body and fails
// because it can't parse the space-separated second body.
assert!(body_type_mpart_limited(spaced, 8).is_err());
}

#[cfg(feature = "quirk_spaces_between_body_parts")]
{
assert!(body_type_mpart_limited(spaced, 8).is_ok());
}
}
}
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ cargo_hack mode: install_cargo_hack
quirk_trailing_space_search,\
quirk_trailing_space_status,\
quirk_spaces_between_addresses,\
quirk_spaces_between_body_parts,\
quirk_empty_continue_req,\
quirk_body_fld_enc_nil_to_empty\
{{ mode }}
Expand Down
Loading