From c5d357ca286908bc60c76e5456bac96968ae9ae5 Mon Sep 17 00:00:00 2001 From: mdecimus <11444311+mdecimus@users.noreply.github.com> Date: Thu, 18 Dec 2025 16:04:06 +0100 Subject: [PATCH] IMAP: Fix shared folder double nesting (test suite credits to @ochnygosch) (fixes #2358 closes #2525) --- crates/imap/src/op/create.rs | 11 ++++++--- tests/src/imap/mailbox.rs | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/imap/src/op/create.rs b/crates/imap/src/op/create.rs index 79a32f09..272a4e07 100644 --- a/crates/imap/src/op/create.rs +++ b/crates/imap/src/op/create.rs @@ -177,7 +177,7 @@ impl SessionData { let mut parent_mailbox_name = None; let (account_id, path) = { let mailboxes = self.mailboxes.lock(); - let (account, full_path) = + let (account, full_path, prefix) = if path.first() == Some(&self.server.core.jmap.shared_folder.as_str()) { // Shared Folders// if path.len() < 3 { @@ -201,7 +201,7 @@ impl SessionData { .skip(1) .find(|account| account.prefix == prefix) { - (account, full_path) + (account, full_path, prefix) } else { #[allow(clippy::unnecessary_literal_unwrap)] return Err(trc::ImapEvent::Error.into_err().details(format!( @@ -215,7 +215,7 @@ impl SessionData { *root = "INBOX"; } - (account, path.join("/")) + (account, path.join("/"), None) } else { return Err(trc::ImapEvent::Error .into_err() @@ -241,6 +241,11 @@ impl SessionData { parent_mailbox_id = mailbox_id.into(); parent_mailbox_name = mailbox_name.into(); break; + } else if prefix + .as_ref() + .is_some_and(|prefix| prefix == &mailbox_name) + { + break; } else { create_path.push(path.pop().unwrap()); } diff --git a/tests/src/imap/mailbox.rs b/tests/src/imap/mailbox.rs index 06cf55de..1d732a21 100644 --- a/tests/src/imap/mailbox.rs +++ b/tests/src/imap/mailbox.rs @@ -324,6 +324,53 @@ pub async fn test(mut imap: &mut ImapConnection, mut imap_check: &mut ImapConnec // Restore Trash folder's original name imap.send("RENAME \"Recycle Bin\" \"Deleted Items\"").await; imap.assert_read(Type::Tagged, ResponseType::Ok).await; + + // Shared folder creation tests + let mut imap_jane = ImapConnection::connect(b"_z ").await; + imap_jane + .authenticate("jane.smith@example.com", "secret") + .await; + imap_jane + .send("CREATE \"Shared Folders/support@example.com/INBOX/Test\"") + .await; + imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await; + + imap_jane + .send("CREATE \"Shared Folders/support@example.com/Test\"") + .await; + imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await; + + imap_jane + .send("CREATE \"Shared Folders/support@example.com/Test/TestSubfolder\"") + .await; + imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await; + imap_jane.send("LIST \"\" \"*\"").await; + imap_jane + .assert_read(Type::Tagged, ResponseType::Ok) + .await + .assert_folders( + [ + ("INBOX", [""]), + ("Deleted Items", [""]), + ("Drafts", [""]), + ("Junk Mail", [""]), + ("Sent Items", [""]), + ("Shared Folders", [""]), + ("Shared Folders/support@example.com", [""]), + ("Shared Folders/support@example.com/Deleted Items", [""]), + ("Shared Folders/support@example.com/Drafts", [""]), + ("Shared Folders/support@example.com/INBOX", [""]), + ("Shared Folders/support@example.com/INBOX/Test", [""]), + ("Shared Folders/support@example.com/Junk Mail", [""]), + ("Shared Folders/support@example.com/Sent Items", [""]), + ("Shared Folders/support@example.com/Test", [""]), + ( + "Shared Folders/support@example.com/Test/TestSubfolder", + [""], + ), + ], + true, + ); } fn mailbox_matches_pattern() {