Skip to content
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
1 change: 1 addition & 0 deletions imap-codec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ ext_login_referrals = ["imap-types/ext_login_referrals"]
ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"]
ext_metadata = ["imap-types/ext_metadata"]
ext_namespace = ["imap-types/ext_namespace"]
ext_status_size = ["imap-types/ext_status_size"]
ext_utf8 = ["imap-types/ext_utf8"]
# </Forward to imap-types>

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 @@ -25,6 +25,7 @@ ext_login_referrals = ["imap-codec/ext_login_referrals"]
ext_mailbox_referrals = ["imap-codec/ext_mailbox_referrals"]
ext_metadata = ["imap-codec/ext_metadata"]
ext_namespace = ["imap-codec/ext_namespace"]
ext_status_size = ["imap-codec/ext_status_size"]
ext_utf8 = ["imap-codec/ext_utf8"]

# IMAP quirks
Expand All @@ -41,6 +42,7 @@ ext = [
#"ext_mailbox_referrals",
"ext_metadata",
"ext_namespace",
"ext_status_size",
"ext_utf8",
]
# Enable `Debug`-printing during parsing. This is useful to analyze crashes.
Expand Down
7 changes: 7 additions & 0 deletions imap-codec/src/codec/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ impl EncodeIntoContext for StatusDataItemName {
Self::Unseen => ctx.write_all(b"UNSEEN"),
Self::Deleted => ctx.write_all(b"DELETED"),
Self::DeletedStorage => ctx.write_all(b"DELETED-STORAGE"),
#[cfg(feature = "ext_status_size")]
Self::Size => ctx.write_all(b"SIZE"),
#[cfg(feature = "ext_condstore_qresync")]
Self::HighestModSeq => ctx.write_all(b"HIGHESTMODSEQ"),
}
Expand Down Expand Up @@ -1700,6 +1702,11 @@ impl EncodeIntoContext for StatusDataItem {
ctx.write_all(b"DELETED-STORAGE ")?;
count.encode_ctx(ctx)
}
#[cfg(feature = "ext_status_size")]
Self::Size(size) => {
ctx.write_all(b"SIZE ")?;
size.encode_ctx(ctx)
}
#[cfg(feature = "ext_condstore_qresync")]
Self::HighestModSeq(value) => {
ctx.write_all(b"HIGHESTMODSEQ ")?;
Expand Down
8 changes: 8 additions & 0 deletions imap-codec/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,14 @@ mod tests {
b"".as_ref(),
Response::Data(Data::Capability(Vec1::from(Capability::Imap4Rev1))),
),
#[cfg(feature = "ext_status_size")]
(
b"* CAPABILITY IMAP4REV1 STATUS=SIZE\r\n".as_ref(),
b"".as_ref(),
Response::Data(
Data::capability(vec![Capability::Imap4Rev1, Capability::StatusSize]).unwrap(),
),
),
(
b"* LIST (\\Noselect) \"/\" bbb\r\n",
b"",
Expand Down
54 changes: 54 additions & 0 deletions imap-codec/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub(crate) fn status_att(input: &[u8]) -> IMAPResult<&[u8], StatusDataItemName>
tag_no_case(b"DELETED-STORAGE"),
),
value(StatusDataItemName::Deleted, tag_no_case(b"DELETED")),
#[cfg(feature = "ext_status_size")]
value(StatusDataItemName::Size, tag_no_case(b"SIZE")),
#[cfg(feature = "ext_condstore_qresync")]
value(
StatusDataItemName::HighestModSeq,
Expand Down Expand Up @@ -87,6 +89,11 @@ fn status_att_val(input: &[u8]) -> IMAPResult<&[u8], StatusDataItem> {
preceded(tag_no_case(b"DELETED "), number),
StatusDataItem::Deleted,
),
#[cfg(feature = "ext_status_size")]
map(
preceded(tag_no_case(b"SIZE "), number64),
StatusDataItem::Size,
),
#[cfg(feature = "ext_condstore_qresync")]
map(
preceded(tag_no_case(b"HIGHESTMODSEQ "), mod_sequence_valzer),
Expand All @@ -99,8 +106,51 @@ fn status_att_val(input: &[u8]) -> IMAPResult<&[u8], StatusDataItem> {
mod tests {
use std::num::NonZeroU32;

#[cfg(feature = "ext_status_size")]
use imap_types::{
command::CommandBody,
mailbox::Mailbox,
response::{Data, Response},
};

use super::*;
use crate::testing::known_answer_test_encode;
#[cfg(feature = "ext_status_size")]
use crate::testing::{kat_inverse_command, kat_inverse_response};

#[cfg(feature = "ext_status_size")]
#[test]
fn test_kat_inverse_status_size() {
kat_inverse_command(&[(
b"A01 STATUS frop (MESSAGES SIZE UIDNEXT)\r\n",
b"",
CommandBody::status(
"frop",
vec![
StatusDataItemName::Messages,
StatusDataItemName::Size,
StatusDataItemName::UidNext,
],
)
.unwrap()
.tag("A01")
.unwrap(),
)]);

kat_inverse_response(&[(
b"* STATUS frop (MESSAGES 8 SIZE 44421 UIDNEXT 242344)\r\n",
b"",
Response::Data(Data::Status {
mailbox: Mailbox::try_from("frop").unwrap(),
items: vec![
StatusDataItem::Messages(8),
StatusDataItem::Size(44421),
StatusDataItem::UidNext(NonZeroU32::new(242344).unwrap()),
]
.into(),
}),
)]);
}

#[test]
fn test_encode_status_data_item_name() {
Expand All @@ -112,6 +162,8 @@ mod tests {
(StatusDataItemName::Unseen, b"UNSEEN"),
(StatusDataItemName::Deleted, b"DELETED"),
(StatusDataItemName::DeletedStorage, b"DELETED-STORAGE"),
#[cfg(feature = "ext_status_size")]
(StatusDataItemName::Size, b"SIZE"),
];

for test in tests {
Expand All @@ -138,6 +190,8 @@ mod tests {
StatusDataItem::DeletedStorage(u64::MAX),
b"DELETED-STORAGE 18446744073709551615",
),
#[cfg(feature = "ext_status_size")]
(StatusDataItem::Size(u64::MAX), b"SIZE 18446744073709551615"),
];

for test in tests {
Expand Down
1 change: 1 addition & 0 deletions imap-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ext_login_referrals = []
ext_mailbox_referrals = []
ext_metadata = []
ext_namespace = []
ext_status_size = []
ext_utf8 = []

[dependencies]
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 @@ -21,6 +21,7 @@ ext_login_referrals = ["imap-types/ext_login_referrals"]
ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"]
ext_metadata = ["imap-types/ext_metadata"]
ext_namespace = ["imap-types/ext_namespace"]
ext_status_size = ["imap-types/ext_status_size"]
ext_utf8 = ["imap-types/ext_utf8"]
# </Forward to imap-types>

Expand All @@ -33,6 +34,7 @@ ext = [
#"ext_mailbox_referrals",
"ext_metadata",
"ext_namespace",
"ext_status_size",
"ext_utf8",
]
# Enable `Debug`-printing during parsing. This is useful to analyze crashes.
Expand Down
17 changes: 17 additions & 0 deletions imap-types/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,9 @@ pub enum Capability<'a> {
/// NAMESPACE extension (RFC 2342)
#[cfg(feature = "ext_namespace")]
Namespace,
/// STATUS=SIZE extension (RFC 8438)
#[cfg(feature = "ext_status_size")]
StatusSize,
#[cfg(feature = "ext_utf8")]
Utf8(Utf8Kind),
/// Other/Unknown
Expand Down Expand Up @@ -1153,6 +1156,8 @@ impl Display for Capability<'_> {
Self::QResync => write!(f, "QRESYNC"),
#[cfg(feature = "ext_namespace")]
Self::Namespace => write!(f, "NAMESPACE"),
#[cfg(feature = "ext_status_size")]
Self::StatusSize => write!(f, "STATUS=SIZE"),
#[cfg(feature = "ext_utf8")]
Self::Utf8(kind) => write!(f, "UTF8={kind}"),
Self::Other(other) => write!(f, "{}", other.0),
Expand Down Expand Up @@ -1223,6 +1228,8 @@ impl<'a> From<Atom<'a>> for Capability<'a> {
"condstore" => Self::CondStore,
#[cfg(feature = "ext_condstore_qresync")]
"qresync" => Self::QResync,
#[cfg(feature = "ext_status_size")]
"status=size" => Self::StatusSize,
"uidplus" => Self::UidPlus,
#[cfg(feature = "ext_utf8")]
"utf8=accept" => Self::Utf8(Utf8Kind::Accept),
Expand Down Expand Up @@ -1321,6 +1328,16 @@ mod tests {
assert_eq!(Capability::ListExtended.to_string(), "LIST-EXTENDED");
}

#[cfg(feature = "ext_status_size")]
#[test]
fn test_capability_status_size() {
assert_eq!(
Capability::from(Atom::try_from("status=size").unwrap()),
Capability::StatusSize,
);
assert_eq!(Capability::StatusSize.to_string(), "STATUS=SIZE");
}

#[test]
fn test_conversion_continue_failing() {
let tests = [
Expand Down
9 changes: 9 additions & 0 deletions imap-types/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ pub enum StatusDataItemName {
/// The amount of storage space that can be reclaimed by performing EXPUNGE on the mailbox.
DeletedStorage,

#[cfg(feature = "ext_status_size")]
/// The total size of the mailbox in octets.
Size,

#[cfg(feature = "ext_condstore_qresync")]
HighestModSeq,
}
Expand Down Expand Up @@ -67,6 +71,11 @@ pub enum StatusDataItem {
/// The amount of storage space that can be reclaimed by performing EXPUNGE on the mailbox.
DeletedStorage(u64),

#[cfg(feature = "ext_status_size")]
/// The total size of the mailbox in octets. Refer to RFC 8438 and RFC 9051,
/// Section 6.3.11 for more information.
Size(u64),

#[cfg(feature = "ext_condstore_qresync")]
/// The highest mod-sequence value of all messages in the mailbox.
/// This is the same value that is returned by the server in the HIGHESTMODSEQ response code in
Expand Down
2 changes: 2 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ cargo_hack mode: install_cargo_hack
ext_mailbox_referrals,\
ext_metadata,\
ext_namespace,\
ext_status_size,\
ext_utf8 \
--group-features \
quirk_crlf_relaxed,\
Expand Down Expand Up @@ -96,6 +97,7 @@ cargo_hack mode: install_cargo_hack
ext_mailbox_referrals,\
ext_metadata,\
ext_namespace,\
ext_status_size,\
ext_utf8\
{{ mode }}

Expand Down
Loading