Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement BINARY (incomplete) #445

Merged
merged 2 commits into from
Feb 14, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
--workspace \
--exclude tokio-client --exclude tokio-server --exclude tokio-support \
--feature-powerset \
--group-features starttls,ext_condstore_qresync,ext_login_referrals,ext_mailbox_referrals \
--group-features starttls,ext_condstore_qresync,ext_login_referrals,ext_mailbox_referrals,ext_id,ext_sort_thread,ext_binary \
--exclude-features ext,split

test:
Expand Down
1 change: 1 addition & 0 deletions imap-codec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ext_login_referrals = ["imap-types/ext_login_referrals"]
ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"]
ext_id = ["imap-types/ext_id"]
ext_sort_thread = ["imap-types/ext_sort_thread"]
ext_binary = ["imap-types/ext_binary"]
# </Forward to imap-types>

# IMAP quirks
Expand Down
2 changes: 2 additions & 0 deletions imap-codec/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ext_login_referrals = ["imap-codec/ext_login_referrals"]
ext_mailbox_referrals = ["imap-codec/ext_mailbox_referrals"]
ext_id = ["imap-codec/ext_id"]
ext_sort_thread = ["imap-codec/ext_sort_thread"]
ext_binary = ["imap-codec/ext_binary"]

# IMAP quirks
quirk_crlf_relaxed = ["imap-codec/quirk_crlf_relaxed"]
Expand All @@ -33,6 +34,7 @@ ext = [
#"ext_mailbox_referrals",
"ext_id",
"ext_sort_thread",
"ext_binary",
]
# Enable `Debug`-printing during parsing. This is useful to analyze crashes.
debug = []
Expand Down
2 changes: 2 additions & 0 deletions imap-codec/src/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "ext_binary")]
pub mod binary;
pub mod compress;
pub mod enable;
#[cfg(feature = "ext_id")]
Expand Down
66 changes: 66 additions & 0 deletions imap-codec/src/extensions/binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::borrow::Cow;

#[cfg(not(feature = "quirk_crlf_relaxed"))]
use abnf_core::streaming::crlf;
#[cfg(feature = "quirk_crlf_relaxed")]
use abnf_core::streaming::crlf_relaxed as crlf;
use imap_types::{core::LiteralMode, extensions::binary::Literal8};
use nom::{
bytes::streaming::{tag, take},
character::streaming::char,
combinator::{map, opt},
sequence::{delimited, terminated, tuple},
};

use crate::{
core::number,
decode::{IMAPErrorKind, IMAPParseError, IMAPResult},
};

#[allow(unused)] // TODO(444)
/// See https://datatracker.ietf.org/doc/html/rfc3516 and https://datatracker.ietf.org/doc/html/rfc4466
///
/// ```abnf
/// literal8 = "~{" number ["+"] "}" CRLF *OCTET
/// ;; <number> represents the number of OCTETs in the response string.
/// ;; The "+" is only allowed when both LITERAL+ and BINARY extensions are supported by the server.
/// ```
pub(crate) fn literal8(input: &[u8]) -> IMAPResult<&[u8], Literal8> {
let (remaining, (length, mode)) = terminated(
delimited(
tag(b"~{"),
tuple((
number,
map(opt(char('+')), |i| {
i.map(|_| LiteralMode::NonSync).unwrap_or(LiteralMode::Sync)
}),
)),
tag(b"}"),
),
crlf,
)(input)?;

// Signal that an continuation request could be required.
// Note: This doesn't trigger when there is data following the literal prefix.
if remaining.is_empty() {
return Err(nom::Err::Failure(IMAPParseError {
input,
kind: IMAPErrorKind::Literal {
// We don't know the tag here and rely on an upper parser, e.g., `command` to fill this in.
tag: None,
length,
mode,
},
}));
}

let (remaining, data) = take(length)(remaining)?;

Ok((
remaining,
Literal8 {
data: Cow::Borrowed(data),
mode,
},
))
}
1 change: 1 addition & 0 deletions imap-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ext_login_referrals = []
ext_mailbox_referrals = []
ext_id = []
ext_sort_thread = []
ext_binary = []

# Unlock `unvalidated` constructors.
unvalidated = []
Expand Down
2 changes: 2 additions & 0 deletions imap-types/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ext_login_referrals = ["imap-types/ext_login_referrals"]
ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"]
ext_id = ["imap-types/ext_id"]
ext_sort_thread = ["imap-types/ext_sort_thread"]
ext_binary = ["imap-types/ext_binary"]
# </Forward to imap-types>

# Use (most) IMAP extensions.
Expand All @@ -30,6 +31,7 @@ ext = [
#"ext_mailbox_referrals",
"ext_id",
"ext_sort_thread",
"ext_binary",
]
# Enable `Debug`-printing during parsing. This is useful to analyze crashes.
debug = []
Expand Down
2 changes: 2 additions & 0 deletions imap-types/src/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! IMAP extensions.

#[cfg(feature = "ext_binary")]
pub mod binary;
pub mod compress;
pub mod enable;
pub mod idle;
Expand Down
54 changes: 54 additions & 0 deletions imap-types/src/extensions/binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! IMAP4 Binary Content Extension

use std::{
borrow::Cow,
fmt::{Debug, Formatter},
};

#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[cfg(feature = "bounded-static")]
use bounded_static::ToStatic;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::core::LiteralMode;

/// String that might contain NULs.
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Literal8<'a> {
pub data: Cow<'a, [u8]>,
/// Specifies whether this is a synchronizing or non-synchronizing literal.
///
/// `true` (default) denotes a synchronizing literal, e.g., `~{3}\r\nfoo`.
/// `false` denotes a non-synchronizing literal, e.g., `~{3+}\r\nfoo`.
///
/// Note: In the special case that a server advertised a `LITERAL-` capability, AND the literal
/// has more than 4096 bytes a non-synchronizing literal must still be treated as synchronizing.
pub mode: LiteralMode,
}

// We want a more readable `Debug` implementation.
impl<'a> Debug for Literal8<'a> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
struct BStr<'a>(&'a Cow<'a, [u8]>);

impl<'a> Debug for BStr<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"b\"{}\"",
crate::utils::escape_byte_string(self.0.as_ref())
)
}
}

f.debug_struct("Literal8")
.field("data", &BStr(&self.data))
.field("mode", &self.mode)
.finish()
}
}
2 changes: 2 additions & 0 deletions imap-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
//! |ext_condstore_qresync|Quick Flag Changes Resynchronization and Quick Mailbox Resynchronization ([RFC 7162]) |Unfinished|
//! |ext_login_referrals |IMAP4 Login Referrals ([RFC 2221]) |Unfinished|
//! |ext_mailbox_referrals|IMAP4 Mailbox Referrals ([RFC 2193]) |Unfinished|
//! |ext_binary |IMAP4 Binary Content Extension ([RFC 3516]) |Unfinished|
//! |starttls |IMAP4rev1 ([RFC 3501]; section 6.2.1) | |
//!
//! STARTTLS is not an IMAP extension but feature-gated because it [should be avoided](https://nostarttls.secvuln.info/).
Expand Down Expand Up @@ -151,6 +152,7 @@
//! [RFC 2221]: https://datatracker.ietf.org/doc/html/rfc2221
//! [RFC 2971]: https://datatracker.ietf.org/doc/html/rfc2971
//! [RFC 3501]: https://datatracker.ietf.org/doc/html/rfc3501
//! [RFC 3516]: https://datatracker.ietf.org/doc/html/rfc3516
//! [RFC 3691]: https://datatracker.ietf.org/doc/html/rfc3691
//! [RFC 4959]: https://datatracker.ietf.org/doc/html/rfc4959
//! [RFC 4978]: https://datatracker.ietf.org/doc/html/rfc4978
Expand Down
Loading