|
| 1 | +use std::{io::Write, str::from_utf8}; |
| 2 | + |
| 3 | +use abnf_core::streaming::dquote; |
| 4 | +use imap_types::{ |
| 5 | + extensions::utf8::QuotedUtf8, |
| 6 | + utils::{escape_quoted, indicators::is_quoted_specials, unescape_quoted}, |
| 7 | +}; |
| 8 | +use nom::{ |
| 9 | + bytes::streaming::{escaped, take_while1}, |
| 10 | + character::streaming::one_of, |
| 11 | + combinator::map_res, |
| 12 | + sequence::tuple, |
| 13 | +}; |
| 14 | + |
| 15 | +use crate::{ |
| 16 | + decode::IMAPResult, |
| 17 | + encode::{EncodeContext, EncodeIntoContext}, |
| 18 | +}; |
| 19 | + |
| 20 | +impl EncodeIntoContext for QuotedUtf8<'_> { |
| 21 | + fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { |
| 22 | + write!(ctx, "\"{}\"", escape_quoted(self.0.as_ref())) |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +/// ```abnf |
| 27 | +/// ; QUOTED-CHAR is not modified, as it will affect other RFC 3501 ABNF non-terminals. |
| 28 | +/// quoted =/ DQUOTE *uQUOTED-CHAR DQUOTE |
| 29 | +/// |
| 30 | +/// uQUOTED-CHAR = QUOTED-CHAR / UTF8-2 / UTF8-3 / UTF8-4 |
| 31 | +/// UTF8-2 = <Defined in Section 4 of RFC 3629> |
| 32 | +/// UTF8-3 = <Defined in Section 4 of RFC 3629> |
| 33 | +/// UTF8-4 = <Defined in Section 4 of RFC 3629> |
| 34 | +/// ``` |
| 35 | +/// |
| 36 | +/// This function only allocates a new String, when needed, i.e. when |
| 37 | +/// quoted chars need to be replaced. |
| 38 | +pub(crate) fn quoted_utf8(input: &[u8]) -> IMAPResult<&[u8], QuotedUtf8> { |
| 39 | + let mut parser = tuple(( |
| 40 | + dquote, |
| 41 | + map_res( |
| 42 | + escaped( |
| 43 | + take_while1(|c| !is_quoted_specials(c)), |
| 44 | + '\\', |
| 45 | + one_of("\\\""), |
| 46 | + ), |
| 47 | + from_utf8, |
| 48 | + ), |
| 49 | + dquote, |
| 50 | + )); |
| 51 | + |
| 52 | + let (remaining, (_, quoted_utf8, _)) = parser(input)?; |
| 53 | + |
| 54 | + Ok((remaining, QuotedUtf8(unescape_quoted(quoted_utf8)))) |
| 55 | +} |
| 56 | + |
| 57 | +#[cfg(test)] |
| 58 | +mod test { |
| 59 | + use imap_types::extensions::utf8::QuotedUtf8; |
| 60 | + |
| 61 | + use super::quoted_utf8; |
| 62 | + |
| 63 | + #[test] |
| 64 | + fn test_quoted_utf8() { |
| 65 | + assert_eq!( |
| 66 | + (b"".as_ref(), QuotedUtf8("äö¹".into())), |
| 67 | + quoted_utf8("\"äö¹\"".as_bytes()).unwrap() |
| 68 | + ); |
| 69 | + } |
| 70 | +} |
0 commit comments