From 369c68231ff2da867d4ed25e0951509d1fc5da12 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 20 Jan 2024 11:45:32 +0100 Subject: [PATCH] test UIDPLUS --- src/imap/capability.rs | 5 ++++ tests/behavior.rs | 50 +++++++++++++++++++++++++++++++++++++-- tests/common/fragments.rs | 30 ++++++++++++++++++----- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/imap/capability.rs b/src/imap/capability.rs index 7959832..525b3ef 100644 --- a/src/imap/capability.rs +++ b/src/imap/capability.rs @@ -14,6 +14,10 @@ fn capability_condstore() -> Capability<'static> { Capability::try_from("CONDSTORE").unwrap() } +fn capability_uidplus() -> Capability<'static> { + Capability::try_from("UIDPLUS").unwrap() +} + /* fn capability_qresync() -> Capability<'static> { Capability::try_from("QRESYNC").unwrap() @@ -33,6 +37,7 @@ impl Default for ServerCapability { Capability::Idle, capability_unselect(), capability_condstore(), + capability_uidplus(), //capability_qresync(), ])) } diff --git a/tests/behavior.rs b/tests/behavior.rs index a6dc78d..0447d80 100644 --- a/tests/behavior.rs +++ b/tests/behavior.rs @@ -12,6 +12,7 @@ fn main() { rfc7888_imapext_literal(); rfc4551_imapext_condstore(); rfc2177_imapext_idle(); + rfc4315_imapext_uidplus(); println!("โœ… SUCCESS ๐ŸŒŸ๐Ÿš€๐Ÿฅณ๐Ÿ™๐Ÿฅน"); } @@ -45,7 +46,7 @@ fn rfc3501_imap4rev1_base() { copy(imap_socket, Selection::FirstId, Mailbox::Archive) .context("copy message to the archive mailbox")?; - append_email(imap_socket, Email::Basic).context("insert email in INBOX")?; + append(imap_socket, Email::Basic).context("insert email in INBOX")?; noop_exists(imap_socket, 2).context("noop loop must detect a new email")?; search(imap_socket, SearchKind::Text("OoOoO")).expect("search should return something"); store( @@ -244,7 +245,7 @@ fn rfc4551_imapext_condstore() { fn rfc2177_imapext_idle() { println!("๐Ÿงช rfc2177_imapext_idle"); common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| { - // Test setup + // Test setup, check capability connect(imap_socket).context("server says hello")?; capability(imap_socket, Extension::Idle).context("check server capabilities")?; login(imap_socket, Account::Alice).context("login test")?; @@ -261,3 +262,48 @@ fn rfc2177_imapext_idle() { }) .expect("test fully run"); } + +fn rfc4315_imapext_uidplus() { + println!("๐Ÿงช rfc4315_imapext_uidplus"); + common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| { + // Test setup, check capability, insert 2 emails + connect(imap_socket).context("server says hello")?; + capability(imap_socket, Extension::UidPlus).context("check server capabilities")?; + login(imap_socket, Account::Alice).context("login test")?; + select(imap_socket, Mailbox::Inbox, SelectMod::None).context("select inbox")?; + lmtp_handshake(lmtp_socket).context("handshake lmtp done")?; + lmtp_deliver_email(lmtp_socket, Email::Basic).context("mail delivered successfully")?; + lmtp_deliver_email(lmtp_socket, Email::Multipart).context("mail delivered successfully")?; + noop_exists(imap_socket, 2).context("noop loop must detect a new email")?; + + // Check UID EXPUNGE seqset + store( + imap_socket, + Selection::All, + Flag::Deleted, + StoreAction::AddFlags, + StoreMod::None, + )?; + let res = uid_expunge(imap_socket, Selection::FirstId)?; + assert_eq!(res.lines().count(), 2); + assert!(res.contains("* 1 EXPUNGE")); + + // APPENDUID check UID + UID VALIDITY + // Note: 4 and not 3, as we update the UID counter when we delete an email + // it's part of our UID proof + let res = append(imap_socket, Email::Multipart)?; + assert!(res.contains("[APPENDUID 1 4]")); + + // COPYUID, check + create_mailbox(imap_socket, Mailbox::Archive).context("created mailbox archive")?; + let res = copy(imap_socket, Selection::FirstId, Mailbox::Archive)?; + assert!(res.contains("[COPYUID 1 2 1]")); + + // MOVEUID, check + let res = r#move(imap_socket, Selection::FirstId, Mailbox::Archive)?; + assert!(res.contains("[COPYUID 1 2 2]")); + + Ok(()) + }) + .expect("test fully run"); +} diff --git a/tests/common/fragments.rs b/tests/common/fragments.rs index 11e0bb7..147c8f0 100644 --- a/tests/common/fragments.rs +++ b/tests/common/fragments.rs @@ -37,6 +37,7 @@ pub enum Extension { Condstore, LiteralPlus, Idle, + UidPlus, } pub enum Enable { @@ -116,6 +117,7 @@ pub fn capability(imap: &mut TcpStream, ext: Extension) -> Result<()> { Extension::Condstore => Some("CONDSTORE"), Extension::LiteralPlus => Some("LITERAL+"), Extension::Idle => Some("IDLE"), + Extension::UidPlus => Some("UIDPLUS"), }; let mut buffer: [u8; 6000] = [0; 6000]; @@ -320,7 +322,7 @@ pub fn fetch( Ok(srv_msg.to_string()) } -pub fn copy(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result<()> { +pub fn copy(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result { let mut buffer: [u8; 65535] = [0; 65535]; assert!(matches!(selection, Selection::FirstId)); assert!(matches!(to, Mailbox::Archive)); @@ -328,11 +330,12 @@ pub fn copy(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result<( imap.write(&b"45 copy 1 ArchiveCustom\r\n"[..])?; let read = read_lines(imap, &mut buffer, None)?; assert_eq!(&read[..5], &b"45 OK"[..]); + let srv_msg = std::str::from_utf8(read)?; - Ok(()) + Ok(srv_msg.to_string()) } -pub fn append_email(imap: &mut TcpStream, content: Email) -> Result<()> { +pub fn append(imap: &mut TcpStream, content: Email) -> Result { let mut buffer: [u8; 6000] = [0; 6000]; let ref_mail = match content { @@ -353,8 +356,9 @@ pub fn append_email(imap: &mut TcpStream, content: Email) -> Result<()> { imap.write(&b"\r\n"[..])?; let read = read_lines(imap, &mut buffer, None)?; assert_eq!(&read[..5], &b"47 OK"[..]); + let srv_msg = std::str::from_utf8(read)?; - Ok(()) + Ok(srv_msg.to_string()) } pub fn search(imap: &mut TcpStream, sk: SearchKind) -> Result { @@ -417,6 +421,20 @@ pub fn expunge(imap: &mut TcpStream) -> Result<()> { Ok(()) } +pub fn uid_expunge(imap: &mut TcpStream, sel: Selection) -> Result { + use Selection::*; + let mut buffer: [u8; 6000] = [0; 6000]; + let selstr = match sel { + FirstId => "1", + SecondId => "2", + All => "1:*", + }; + imap.write(format!("61 UID EXPUNGE {}\r\n", selstr).as_bytes())?; + let read = read_lines(imap, &mut buffer, Some(&b"61 OK"[..]))?; + let srv_msg = std::str::from_utf8(read)?; + Ok(srv_msg.to_string()) +} + pub fn rename_mailbox(imap: &mut TcpStream, from: Mailbox, to: Mailbox) -> Result<()> { assert!(matches!(from, Mailbox::Archive)); assert!(matches!(to, Mailbox::Drafts)); @@ -466,7 +484,7 @@ pub fn close(imap: &mut TcpStream) -> Result<()> { Ok(()) } -pub fn r#move(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result<()> { +pub fn r#move(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result { let mut buffer: [u8; 1500] = [0; 1500]; assert!(matches!(to, Mailbox::Archive)); assert!(matches!(selection, Selection::FirstId)); @@ -476,7 +494,7 @@ pub fn r#move(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result let srv_msg = std::str::from_utf8(read)?; assert!(srv_msg.contains("* 1 EXPUNGE")); - Ok(()) + Ok(srv_msg.to_string()) } pub fn enable(imap: &mut TcpStream, ask: Enable, done: Option) -> Result<()> {