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.
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{AssertResult, Type};
|
||||
use crate::utils::server::TestServer;
|
||||
use imap_proto::ResponseType;
|
||||
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running OBJECTID+ tests...");
|
||||
|
||||
let account = test.account("[email protected]");
|
||||
let account_id = account.id_string().to_string();
|
||||
let mut imap = account.imap_client().await;
|
||||
|
||||
// OBJECTID+ is advertised
|
||||
imap.send("CAPABILITY").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("OBJECTID+")
|
||||
.assert_count("OBJECTID ", 0);
|
||||
|
||||
// Before activation no object identifiers are leaked
|
||||
imap.send("SELECT INBOX").await;
|
||||
let lines = imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
assert!(
|
||||
!lines
|
||||
.iter()
|
||||
.any(|l| l.contains("OBJECTID") || l.contains("MAILBOXID")),
|
||||
"Pre-activation SELECT leaked object identifiers: {lines:?}"
|
||||
);
|
||||
|
||||
// Explicit activation via ENABLE
|
||||
imap.send("ENABLE OBJECTID+").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("ENABLED OBJECTID+");
|
||||
|
||||
// SELECT now returns a compound OBJECTID with MAILBOXID and ACCOUNTID
|
||||
imap.send("SELECT INBOX").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("[OBJECTID (")
|
||||
.assert_contains("MAILBOXID ")
|
||||
.assert_contains(&format!("ACCOUNTID {account_id}"));
|
||||
|
||||
// STATUS OBJECTID returns the compound for the queried mailbox, and an
|
||||
// already-activated session is not sent a second ENABLED response
|
||||
imap.send("STATUS INBOX (OBJECTID)").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("OBJECTID (")
|
||||
.assert_contains("MAILBOXID ")
|
||||
.assert_contains(&format!("ACCOUNTID {account_id}"))
|
||||
.assert_not_contains("ENABLED OBJECTID+");
|
||||
|
||||
// FETCH OBJECTID returns EMAILID and THREADID but never ACCOUNTID
|
||||
imap.send("UID FETCH 1 (OBJECTID)").await;
|
||||
let lines = imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
.any(|l| l.contains("OBJECTID (") && l.contains("EMAILID ") && l.contains("THREADID ")),
|
||||
"FETCH OBJECTID must include EMAILID and THREADID: {lines:?}"
|
||||
);
|
||||
assert!(
|
||||
!lines.iter().any(|l| l.contains("ACCOUNTID")),
|
||||
"FETCH OBJECTID must not include ACCOUNTID: {lines:?}"
|
||||
);
|
||||
|
||||
// Commands that do not request the OBJECTID item never emit it, even once activated
|
||||
imap.send("UID FETCH 1 (FLAGS)").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_not_contains("OBJECTID");
|
||||
imap.send("STATUS INBOX (MESSAGES)").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_not_contains("OBJECTID");
|
||||
|
||||
// EXAMINE returns the compound OBJECTID response code just like SELECT
|
||||
imap.send("EXAMINE INBOX").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("[OBJECTID (")
|
||||
.assert_contains("MAILBOXID ")
|
||||
.assert_contains(&format!("ACCOUNTID {account_id}"));
|
||||
|
||||
// CREATE returns the compound OBJECTID response code
|
||||
imap.send("CREATE \"ObjIdTest\"").await;
|
||||
let lines = imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
let mailbox_id = extract_value(&lines, "MAILBOXID ");
|
||||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
.any(|l| l.contains(&format!("ACCOUNTID {account_id}"))),
|
||||
"CREATE OBJECTID missing ACCOUNTID: {lines:?}"
|
||||
);
|
||||
|
||||
// RENAME returns the compound OBJECTID response code and preserves the MAILBOXID
|
||||
imap.send("RENAME \"ObjIdTest\" \"ObjIdRenamed\"").await;
|
||||
let lines = imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
let renamed_id = extract_value(&lines, "MAILBOXID ");
|
||||
assert_eq!(
|
||||
mailbox_id, renamed_id,
|
||||
"RENAME must preserve the MAILBOXID: {lines:?}"
|
||||
);
|
||||
|
||||
// Identifier-based selection resolves the mailbox regardless of its current name
|
||||
imap.send(&format!(
|
||||
"SELECT \"DoesNotExist\" (OBJECTID (MAILBOXID {mailbox_id} ACCOUNTID {account_id}))"
|
||||
))
|
||||
.await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains(&format!("MAILBOXID {mailbox_id}"));
|
||||
|
||||
// An unknown MAILBOXID falls back to selecting by name
|
||||
imap.send("SELECT \"ObjIdRenamed\" (OBJECTID (MAILBOXID abcdefgh ACCOUNTID abcdefgh))")
|
||||
.await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains(&format!("MAILBOXID {mailbox_id}"));
|
||||
|
||||
// An undecodable identifier falls back to selecting by name instead of failing
|
||||
imap.send("SELECT \"ObjIdRenamed\" (OBJECTID (MAILBOXID 456))")
|
||||
.await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains(&format!("MAILBOXID {mailbox_id}"));
|
||||
|
||||
// Unrecognised keys in the OBJECTID parameter are ignored
|
||||
imap.send("SELECT \"ObjIdRenamed\" (OBJECTID (FOOBAR baz MAILBOXID 456))")
|
||||
.await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains(&format!("MAILBOXID {mailbox_id}"));
|
||||
|
||||
// Cleanup
|
||||
imap.send("UNSELECT").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
imap.send("DELETE \"ObjIdRenamed\"").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
|
||||
// Implicit activation via the STATUS attribute on a fresh session
|
||||
let mut imap2 = test.account("[email protected]").imap_client().await;
|
||||
imap2.send("STATUS INBOX (OBJECTID)").await;
|
||||
let lines = imap2.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
assert!(
|
||||
lines.iter().any(|l| l.contains("ENABLED OBJECTID+")),
|
||||
"STATUS did not implicitly activate OBJECTID+: {lines:?}"
|
||||
);
|
||||
|
||||
// Implicit activation via the SELECT OBJECTID parameter on a fresh session
|
||||
let mut imap3 = test.account("[email protected]").imap_client().await;
|
||||
imap3.send("SELECT INBOX (OBJECTID)").await;
|
||||
let lines = imap3.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
assert!(
|
||||
lines.iter().any(|l| l.contains("ENABLED OBJECTID+")),
|
||||
"SELECT (OBJECTID) did not implicitly activate OBJECTID+: {lines:?}"
|
||||
);
|
||||
assert!(
|
||||
lines.iter().any(|l| l.contains("[OBJECTID (")),
|
||||
"SELECT (OBJECTID) did not return a compound OBJECTID: {lines:?}"
|
||||
);
|
||||
|
||||
// Activation via FETCH then a plain SELECT/CREATE must still carry the compound code
|
||||
let mut imap4 = test.account("[email protected]").imap_client().await;
|
||||
imap4.send("SELECT INBOX").await;
|
||||
imap4.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
imap4.send("UID FETCH 1 (OBJECTID)").await;
|
||||
let lines = imap4.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
assert!(
|
||||
lines.iter().any(|l| l.contains("ENABLED OBJECTID+")),
|
||||
"FETCH (OBJECTID) did not implicitly activate OBJECTID+: {lines:?}"
|
||||
);
|
||||
imap4.send("SELECT INBOX").await;
|
||||
imap4
|
||||
.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("[OBJECTID (");
|
||||
imap4.send("CREATE \"ObjIdTest4\"").await;
|
||||
imap4
|
||||
.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("[OBJECTID (");
|
||||
imap4.send("DELETE \"ObjIdTest4\"").await;
|
||||
imap4.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
}
|
||||
|
||||
fn extract_value(lines: &[String], key: &str) -> String {
|
||||
for line in lines {
|
||||
if let Some((_, rest)) = line.split_once(key) {
|
||||
return rest
|
||||
.split([' ', ')'])
|
||||
.next()
|
||||
.expect("Missing value delimiter")
|
||||
.to_string();
|
||||
}
|
||||
}
|
||||
panic!("Key {key:?} not found in {lines:?}");
|
||||
}
|
||||
Reference in New Issue
Block a user