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.
324 lines
8.9 KiB
Rust
324 lines
8.9 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
pub mod acl;
|
|
pub mod antispam;
|
|
pub mod append;
|
|
pub mod basic;
|
|
pub mod body_structure;
|
|
pub mod condstore;
|
|
pub mod copy_move;
|
|
pub mod fetch;
|
|
pub mod idle;
|
|
pub mod mailbox;
|
|
pub mod managesieve;
|
|
pub mod messagelimit;
|
|
pub mod objectid;
|
|
pub mod pop;
|
|
pub mod search;
|
|
pub mod store;
|
|
pub mod thread;
|
|
pub mod uidbatches;
|
|
pub mod uidonly;
|
|
|
|
use crate::utils::{
|
|
imap::{AssertResult, ImapConnection, Type},
|
|
server::TestServerBuilder,
|
|
};
|
|
use ahash::AHashSet;
|
|
use imap_proto::ResponseType;
|
|
use registry::{
|
|
schema::{
|
|
enums::{Permission, SpecialUse},
|
|
prelude::ObjectType,
|
|
structs::{
|
|
Email, EmailFolder, Expression, Imap, MemoryLookupKey, MtaStageAuth, MtaStageData,
|
|
SpamClassifier, SpamTag, SpamTagScore,
|
|
},
|
|
},
|
|
types::float::Float,
|
|
};
|
|
use serde_json::json;
|
|
use std::{path::PathBuf, time::Instant};
|
|
use utils::map::vec_map::VecMap;
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
pub async fn imap_tests() {
|
|
let mut test = TestServerBuilder::new("imap_tests")
|
|
.await
|
|
.with_default_listeners()
|
|
.await
|
|
.build()
|
|
.await;
|
|
|
|
// Create admin account
|
|
let admin = test.create_admin_account("[email protected]").await;
|
|
|
|
// Create test users
|
|
for (name, secret, description, aliases) in [
|
|
(
|
|
"[email protected]",
|
|
"12345 + extra safety",
|
|
"John Doe",
|
|
&["[email protected]"][..],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"abcde + extra safety",
|
|
"Jane Smith",
|
|
&["[email protected]"][..],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"098765 + extra safety",
|
|
"Bill Foobar",
|
|
&["[email protected]"][..],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"a_pop3_safe_secret_with_extra_safety",
|
|
"Karl Popper",
|
|
&["[email protected]"][..],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"secret2 + extra safety",
|
|
"Sigmund Gudmund Dudmundsson",
|
|
&[][..],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"secret3 + extra safety",
|
|
"Spam Trap",
|
|
&[][..],
|
|
),
|
|
] {
|
|
let account = admin
|
|
.create_user_account(
|
|
name,
|
|
secret,
|
|
description,
|
|
aliases,
|
|
vec![Permission::UnlimitedRequests, Permission::UnlimitedUploads],
|
|
)
|
|
.await;
|
|
test.insert_account(account);
|
|
}
|
|
|
|
// Create test group
|
|
test.insert_account(
|
|
admin
|
|
.create_group_account("[email protected]", "Support Group", &[])
|
|
.await,
|
|
);
|
|
|
|
// Add Jane to the Support group
|
|
let support_id = test.account("[email protected]").id();
|
|
admin
|
|
.registry_update_object(
|
|
ObjectType::Account,
|
|
test.account("[email protected]").id(),
|
|
json!({
|
|
"memberGroupIds": { support_id: true },
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
// Add test settings
|
|
admin
|
|
.registry_create_object(Imap {
|
|
allow_plain_text_auth: true,
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaStageAuth {
|
|
require: Expression {
|
|
else_: "false".to_string(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(SpamClassifier {
|
|
min_ham_samples: 10,
|
|
min_spam_samples: 10,
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(Email {
|
|
default_folders: VecMap::from_iter(
|
|
[
|
|
(SpecialUse::Inbox, "Inbox"),
|
|
(SpecialUse::Sent, "Sent Items"),
|
|
(SpecialUse::Trash, "Deleted Items"),
|
|
(SpecialUse::Junk, "Junk Mail"),
|
|
(SpecialUse::Drafts, "Drafts"),
|
|
]
|
|
.into_iter()
|
|
.map(|(use_, name)| {
|
|
(
|
|
use_,
|
|
EmailFolder {
|
|
name: name.into(),
|
|
subscribe: false,
|
|
..Default::default()
|
|
},
|
|
)
|
|
}),
|
|
),
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaStageData {
|
|
add_delivered_to_header: false,
|
|
enable_spam_filter: Expression {
|
|
else_: "recipients[0] != '[email protected]'".into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(SpamTag::Score(SpamTagScore {
|
|
score: Float::new(10.0),
|
|
tag: "PROB_SPAM_LOW".into(),
|
|
}))
|
|
.await;
|
|
admin
|
|
.registry_create_object(SpamTag::Score(SpamTagScore {
|
|
score: Float::new(10.0),
|
|
tag: "PROB_SPAM_HIGH".into(),
|
|
}))
|
|
.await;
|
|
admin
|
|
.registry_create_object(SpamTag::Score(SpamTagScore {
|
|
score: Float::new(100.0),
|
|
tag: "SPAM_TRAP".into(),
|
|
}))
|
|
.await;
|
|
admin
|
|
.registry_create_object(MemoryLookupKey {
|
|
is_glob_pattern: true,
|
|
key: "spamtrap@*".into(),
|
|
namespace: "spam-traps".into(),
|
|
})
|
|
.await;
|
|
admin.reload_settings().await;
|
|
admin.reload_lookup_stores().await;
|
|
|
|
test.insert_account(admin);
|
|
|
|
let start_time = Instant::now();
|
|
|
|
// Body structure tests
|
|
body_structure::test();
|
|
|
|
// Connect to IMAP server
|
|
let mut imap_check = ImapConnection::connect(b"_y ").await;
|
|
let mut imap = ImapConnection::connect(b"_x ").await;
|
|
for imap in [&mut imap, &mut imap_check] {
|
|
imap.assert_read(Type::Untagged, ResponseType::Ok).await;
|
|
}
|
|
|
|
// Unauthenticated tests
|
|
basic::test(&mut imap, &mut imap_check).await;
|
|
|
|
// Login
|
|
let account = test.account("[email protected]");
|
|
for imap in [&mut imap, &mut imap_check] {
|
|
imap.authenticate(account.name(), account.secret()).await;
|
|
}
|
|
|
|
// Test GETJMAPACCESS (RFC 9698)
|
|
imap.send("GETJMAPACCESS").await;
|
|
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
|
.await
|
|
.assert_contains("* JMAPACCESS \"")
|
|
.assert_contains("/.well-known/jmap\"");
|
|
|
|
// Delete folders
|
|
for mailbox in ["Drafts", "Junk Mail", "Sent Items"] {
|
|
imap.send(&format!("DELETE \"{}\"", mailbox)).await;
|
|
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
|
}
|
|
|
|
mailbox::test(&mut imap, &mut imap_check, &test).await;
|
|
append::test(&mut imap, &mut imap_check, &test).await;
|
|
search::test(&mut imap, &mut imap_check, &test).await;
|
|
fetch::test(&mut imap, &mut imap_check).await;
|
|
objectid::test(&test).await;
|
|
store::test(&mut imap, &mut imap_check, &test).await;
|
|
copy_move::test(&mut imap, &mut imap_check).await;
|
|
thread::test(&mut imap, &mut imap_check, &test).await;
|
|
idle::test(&mut imap, &mut imap_check, false).await;
|
|
condstore::test(&mut imap, &mut imap_check).await;
|
|
acl::test(&mut imap, &mut imap_check, &test).await;
|
|
uidbatches::test(&mut imap, &mut imap_check).await;
|
|
messagelimit::test(&mut imap, &mut imap_check).await;
|
|
|
|
// UIDONLY cannot be disabled once enabled, so it uses its own connection
|
|
uidonly::test(&test).await;
|
|
|
|
// Logout
|
|
for imap in [&mut imap, &mut imap_check] {
|
|
imap.send("UNAUTHENTICATE").await;
|
|
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
|
|
|
imap.send("LOGOUT").await;
|
|
imap.assert_read(Type::Untagged, ResponseType::Bye).await;
|
|
}
|
|
|
|
// Antispam training
|
|
antispam::test(&test).await;
|
|
|
|
// Run ManageSieve tests
|
|
managesieve::test(&test).await;
|
|
|
|
// Run POP3 tests
|
|
pop::test(&test).await;
|
|
|
|
// Print elapsed time
|
|
let elapsed = start_time.elapsed();
|
|
println!(
|
|
"Elapsed: {}.{:03}s",
|
|
elapsed.as_secs(),
|
|
elapsed.subsec_millis()
|
|
);
|
|
|
|
// Remove test data
|
|
if test.is_reset() {
|
|
test.temp_dir.delete();
|
|
}
|
|
}
|
|
|
|
pub fn expand_uid_list(list: &str) -> AHashSet<u32> {
|
|
let mut items = AHashSet::new();
|
|
for uid in list.split(',') {
|
|
if let Some((start, end)) = uid.split_once(':') {
|
|
let start = start.parse::<u32>().unwrap();
|
|
let end = end.parse::<u32>().unwrap();
|
|
for uid in start..=end {
|
|
items.insert(uid);
|
|
}
|
|
} else {
|
|
items.insert(uid.parse::<u32>().unwrap());
|
|
}
|
|
}
|
|
|
|
items
|
|
}
|
|
|
|
fn resources_dir() -> PathBuf {
|
|
let mut resources = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
resources.push("resources");
|
|
resources.push("imap");
|
|
resources
|
|
}
|