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.
456 lines
17 KiB
Rust
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);
|
|
}
|
|
}
|