use super::{BytesMut, LiteralMode, LITERAL_MINUS_MAX};
pub(crate) fn encode_quoted_or_literal(buf: &mut BytesMut, data: &[u8], literal_mode: LiteralMode) {
let data = strip_nul_bytes(data);
let quotable = data.iter().all(|&b| (0x20..0x7F).contains(&b));
emit_quoted_or_literal(buf, &data, quotable, literal_mode);
}
pub(crate) fn encode_quoted_or_literal_utf8(
buf: &mut BytesMut,
data: &[u8],
utf8_mode: bool,
literal_mode: LiteralMode,
) {
if !utf8_mode {
encode_quoted_or_literal(buf, data, literal_mode);
return;
}
let data = strip_nul_bytes(data);
let quotable = std::str::from_utf8(&data).is_ok()
&& data.iter().all(|&b| (b >= 0x20 && b != 0x7F) || b >= 0x80);
emit_quoted_or_literal(buf, &data, quotable, literal_mode);
}
fn strip_nul_bytes(data: &[u8]) -> std::borrow::Cow<'_, [u8]> {
if data.contains(&0x00) {
tracing::warn!(
"Stripped NUL bytes from IMAP string data — RFC 3501 Section 9 forbids %x00"
);
std::borrow::Cow::Owned(data.iter().copied().filter(|&b| b != 0x00).collect())
} else {
std::borrow::Cow::Borrowed(data)
}
}
pub(super) fn emit_quoted_string(buf: &mut BytesMut, data: &[u8]) {
buf.extend_from_slice(b"\"");
for &byte in data {
if byte == b'\\' || byte == b'"' {
buf.extend_from_slice(b"\\");
}
buf.extend_from_slice(&[byte]);
}
buf.extend_from_slice(b"\"");
}
pub(super) fn emit_quoted_or_literal(
buf: &mut BytesMut,
data: &[u8],
quotable: bool,
literal_mode: LiteralMode,
) {
if quotable {
emit_quoted_string(buf, data);
} else {
buf.extend_from_slice(b"{");
buf.extend_from_slice(data.len().to_string().as_bytes());
let use_non_sync = match literal_mode {
LiteralMode::LiteralPlus => true,
LiteralMode::LiteralMinus => data.len() <= LITERAL_MINUS_MAX,
LiteralMode::Synchronizing => false,
};
if use_non_sync {
buf.extend_from_slice(b"+}\r\n");
} else {
buf.extend_from_slice(b"}\r\n");
}
buf.extend_from_slice(data);
}
}
pub(super) fn encode_literal8(buf: &mut BytesMut, data: &[u8]) {
buf.extend_from_slice(b"~{");
buf.extend_from_slice(data.len().to_string().as_bytes());
buf.extend_from_slice(b"}\r\n");
buf.extend_from_slice(data);
}
pub(super) fn encode_metadata_value(buf: &mut BytesMut, data: &[u8], literal_mode: LiteralMode) {
let quotable = data.iter().all(|&b| (0x20..0x7F).contains(&b));
if data.contains(&0) {
encode_literal8(buf, data);
} else {
emit_quoted_or_literal(buf, data, quotable, literal_mode);
}
}