Files
inbuxa-server/tests/src/imap/mailbox.rs
T
jcoffey-dev 7dae9b29fd Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
2026-09-18 10:21:56 -07:00

456 lines
17 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use imap::op::list::matches_pattern;
use imap_proto::ResponseType;
use crate::utils::server::TestServer;
use super::{AssertResult, ImapConnection, Type};
pub async fn test(
mut imap: &mut ImapConnection,
mut imap_check: &mut ImapConnection,
test: &TestServer,
) {
println!("Running mailbox tests...");
// Pattern matching tests
mailbox_matches_pattern();
// Create third connection for testing
let mut other_conn = test.account("[email protected]").imap_client().await;
// List folders
imap.send("LIST \"\" \"*\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders([("INBOX", [""]), ("Deleted Items", [""])], true);
// Create folders
imap.send("CREATE \"Tofu\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("CREATE \"Fruit\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("CREATE \"Fruit/Apple\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("CREATE \"Fruit/Apple/Green\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("CREATE \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
// Select folder from another connection
other_conn.send("SELECT \"Tofu\"").await;
other_conn.assert_read(Type::Tagged, ResponseType::Ok).await;
other_conn.send("SELECT \"L&APg-bende opgaver\"").await;
other_conn.assert_read(Type::Tagged, ResponseType::Ok).await;
// Make sure folders are visible
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST \"\" \"*\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", [""]),
("Deleted Items", [""]),
("Fruit", [""]),
("Fruit/Apple", [""]),
("Fruit/Apple/Green", [""]),
("Tofu", [""]),
("L&APg-bende opgaver", [""]),
],
true,
);
}
imap.send("DELETE \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
// Special use folders that already exist should not be allowed
imap.send("CREATE \"Second trash\" (USE (\\Trash))").await;
imap.assert_read(Type::Tagged, ResponseType::No).await;
// Every command in a pipelined batch is answered, even after a failure
imap.send_raw(concat!(
"_p1 STATUS \"Tofu\" (MESSAGES)\r\n",
"_p2 STATUS \"Does Not Exist\" (MESSAGES)\r\n",
"_x STATUS \"Fruit/Apple\" (MESSAGES)\r\n"
))
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_contains("_p1 OK")
.assert_contains("_p2 NO [NONEXISTENT]")
.assert_contains("* STATUS \"Fruit/Apple\"");
// Enable IMAP4rev2
imap.send("ENABLE IMAP4rev2").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
// Create and delete using IMAP4rev2
imap.send("CREATE \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("SELECT \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("UNSELECT \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("DELETE \"L&APg-bende opgaver\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
// Create missing parent folders
imap.send("CREATE \"/Vegetable/Broccoli\" (USE (\\Important))")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("CREATE \" Cars/Electric /4 doors/ Red/\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST \"\" \"*\" RETURN (CHILDREN SPECIAL-USE)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["HasNoChildren", ""]),
("Deleted Items", ["HasNoChildren", "Trash"]),
("Cars/Electric/4 doors/Red", ["HasNoChildren", ""]),
("Cars/Electric/4 doors", ["HasChildren", ""]),
("Cars/Electric", ["HasChildren", ""]),
("Cars", ["HasChildren", ""]),
("Fruit", ["HasChildren", ""]),
("Fruit/Apple", ["HasChildren", ""]),
("Fruit/Apple/Green", ["HasNoChildren", ""]),
("Vegetable", ["HasChildren", ""]),
("Vegetable/Broccoli", ["HasNoChildren", "\\Important"]),
("Tofu", ["HasNoChildren", ""]),
],
true,
);
}
// Rename folders
imap.send("RENAME \"Fruit/Apple/Green\" \"Fruit/Apple/Red\"")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("RENAME \"Cars\" \"Vehicles\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("RENAME \"Vegetable/Broccoli\" \"Veggies/Green/Broccoli\"")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("RENAME \"Tofu\" \"INBOX\"").await;
imap.assert_read(Type::Tagged, ResponseType::No).await;
imap.send("RENAME \"Tofu\" \"Inbox/Tofu\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("RENAME \"Deleted Items\" \"Recycle Bin\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST \"\" \"*\" RETURN (CHILDREN SPECIAL-USE)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["HasChildren", ""]),
("INBOX/Tofu", ["HasNoChildren", ""]),
("Recycle Bin", ["HasNoChildren", "Trash"]),
("Vehicles/Electric/4 doors/Red", ["HasNoChildren", ""]),
("Vehicles/Electric/4 doors", ["HasChildren", ""]),
("Vehicles/Electric", ["HasChildren", ""]),
("Vehicles", ["HasChildren", ""]),
("Fruit", ["HasChildren", ""]),
("Fruit/Apple", ["HasChildren", ""]),
("Fruit/Apple/Red", ["HasNoChildren", ""]),
("Vegetable", ["HasNoChildren", ""]),
("Veggies", ["HasChildren", ""]),
("Veggies/Green", ["HasChildren", ""]),
("Veggies/Green/Broccoli", ["HasNoChildren", ""]),
],
true,
);
}
// Delete folders
imap.send("DELETE \"INBOX/Tofu\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("DELETE \"Vegetable\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("DELETE \"Vehicles\"").await;
imap.assert_read(Type::Tagged, ResponseType::No).await;
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST \"\" \"*\" RETURN (CHILDREN SPECIAL-USE)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["HasNoChildren", ""]),
("Recycle Bin", ["HasNoChildren", "Trash"]),
("Vehicles/Electric/4 doors/Red", ["HasNoChildren", ""]),
("Vehicles/Electric/4 doors", ["HasChildren", ""]),
("Vehicles/Electric", ["HasChildren", ""]),
("Vehicles", ["HasChildren", ""]),
("Fruit", ["HasChildren", ""]),
("Fruit/Apple", ["HasChildren", ""]),
("Fruit/Apple/Red", ["HasNoChildren", ""]),
("Veggies", ["HasChildren", ""]),
("Veggies/Green", ["HasChildren", ""]),
("Veggies/Green/Broccoli", ["HasNoChildren", ""]),
],
true,
);
}
// Subscribe
imap.send("SUBSCRIBE \"INBOX\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.send("SUBSCRIBE \"Vehicles/Electric/4 doors/Red\"")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST \"\" \"*\" RETURN (SUBSCRIBED SPECIAL-USE)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["Subscribed", ""]),
("Recycle Bin", ["", "Trash"]),
("Vehicles/Electric/4 doors/Red", ["Subscribed", ""]),
("Vehicles/Electric/4 doors", ["", ""]),
("Vehicles/Electric", ["", ""]),
("Vehicles", ["", ""]),
("Fruit", ["", ""]),
("Fruit/Apple", ["", ""]),
("Fruit/Apple/Red", ["", ""]),
("Veggies", ["", ""]),
("Veggies/Green", ["", ""]),
("Veggies/Green/Broccoli", ["", ""]),
],
true,
);
}
// Filter by subscribed including children
imap.send("LIST (SUBSCRIBED) \"\" \"*\" RETURN (CHILDREN)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["Subscribed", "HasNoChildren"]),
(
"Vehicles/Electric/4 doors/Red",
["Subscribed", "HasNoChildren"],
),
],
true,
);
// Recursive match including children
imap.send("LIST (SUBSCRIBED RECURSIVEMATCH) \"\" \"*\" RETURN (CHILDREN)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", ["Subscribed", "HasNoChildren"]),
(
"Vehicles/Electric/4 doors/Red",
["Subscribed", "HasNoChildren"],
),
(
"Vehicles/Electric/4 doors",
["\"CHILDINFO\" (\"SUBSCRIBED\")", "HasChildren"],
),
(
"Vehicles/Electric",
["\"CHILDINFO\" (\"SUBSCRIBED\")", "HasChildren"],
),
(
"Vehicles",
["\"CHILDINFO\" (\"SUBSCRIBED\")", "HasChildren"],
),
],
true,
);
// Imap4rev1 LSUB
imap.send("LSUB \"\" \"*\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[("INBOX", [""]), ("Vehicles/Electric/4 doors/Red", [""])],
true,
);
// Unsubscribe
imap.send("UNSUBSCRIBE \"Vehicles/Electric/4 doors/Red\"")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
for imap in [&mut imap, &mut imap_check] {
imap.send("LIST (SUBSCRIBED RECURSIVEMATCH) \"\" \"*\" RETURN (CHILDREN)")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders([("INBOX", ["Subscribed", "HasNoChildren"])], true);
}
// LIST Filters
imap.send("LIST \"\" \"%\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("INBOX", [""]),
("Recycle Bin", [""]),
("Vehicles", [""]),
("Fruit", [""]),
("Veggies", [""]),
],
true,
);
imap.send("LIST \"\" \"*/Red\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders(
[
("Vehicles/Electric/4 doors/Red", [""]),
("Fruit/Apple/Red", [""]),
],
true,
);
imap.send("LIST \"\" \"Fruit/*\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders([("Fruit/Apple/Red", [""]), ("Fruit/Apple", [""])], true);
imap.send("LIST \"\" \"Fruit/%\"").await;
imap.assert_read(Type::Tagged, ResponseType::Ok)
.await
.assert_folders([("Fruit/Apple", [""])], true);
// 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 = test.account("[email protected]").imap_client().await;
imap_jane
.send("CREATE \"Shared Folders/[email protected]/INBOX/Test\"")
.await;
imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await;
imap_jane
.send("CREATE \"Shared Folders/[email protected]/Test\"")
.await;
imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await;
imap_jane
.send("CREATE \"Shared Folders/[email protected]/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/[email protected]", [""]),
("Shared Folders/[email protected]/Deleted Items", [""]),
("Shared Folders/[email protected]/Drafts", [""]),
("Shared Folders/[email protected]/INBOX", [""]),
("Shared Folders/[email protected]/INBOX/Test", [""]),
("Shared Folders/[email protected]/Junk Mail", [""]),
("Shared Folders/[email protected]/Sent Items", [""]),
("Shared Folders/[email protected]/Test", [""]),
(
"Shared Folders/[email protected]/Test/TestSubfolder",
[""],
),
],
true,
);
}
fn mailbox_matches_pattern() {
let mailboxes = [
"imaptest",
"imaptest/test",
"imaptest/test2",
"imaptest/test3",
"imaptest/test3/test4",
"imaptest/test3/test4/test5",
"foobar/test",
"foobar/test/test",
"foobar/test1/test1",
];
for (pattern, expected_match) in [
(
"imaptest/%",
vec!["imaptest/test", "imaptest/test2", "imaptest/test3"],
),
("imaptest/%/%", vec!["imaptest/test3/test4"]),
(
"imaptest/*",
vec![
"imaptest/test",
"imaptest/test2",
"imaptest/test3",
"imaptest/test3/test4",
"imaptest/test3/test4/test5",
],
),
("imaptest/*test4", vec!["imaptest/test3/test4"]),
(
"imaptest/*test*",
vec![
"imaptest/test",
"imaptest/test2",
"imaptest/test3",
"imaptest/test3/test4",
"imaptest/test3/test4/test5",
],
),
("imaptest/%3/%", vec!["imaptest/test3/test4"]),
("imaptest/%3/%4", vec!["imaptest/test3/test4"]),
("imaptest/%t*4", vec!["imaptest/test3/test4"]),
("*st/%3/%4/%5", vec!["imaptest/test3/test4/test5"]),
(
"*%*%*%",
vec![
"imaptest",
"imaptest/test",
"imaptest/test2",
"imaptest/test3",
"imaptest/test3/test4",
"imaptest/test3/test4/test5",
"foobar/test",
"foobar/test/test",
"foobar/test1/test1",
],
),
("foobar*test", vec!["foobar/test", "foobar/test/test"]),
] {
let patterns = vec![pattern.into()];
let mut matched_mailboxes = Vec::new();
for mailbox in mailboxes {
if matches_pattern(&patterns, mailbox) {
matched_mailboxes.push(mailbox);
}
}
assert_eq!(matched_mailboxes, expected_match, "for pattern {}", pattern);
}
}