SieveScripts and VacationResponse implementation

This commit is contained in:
Mauro D
2023-05-19 15:16:48 +00:00
parent 4e4632571c
commit bc40256ac4
45 changed files with 2188 additions and 325 deletions

View File

@@ -25,12 +25,12 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
let account_id_3 = test_account_create(&server, "bill@example.com", "12345", "Bill Foobar")
.await
.to_string();
test_alias_create(&server, "jdoe@example.com", "john.doe@example.com").await;
test_alias_create(&server, "jdoe@example.com", "john.doe@example.com", false).await;
// Create a mailing list
test_alias_create(&server, "jdoe@example.com", "members@example.com").await;
test_alias_create(&server, "jane@example.com", "members@example.com").await;
test_alias_create(&server, "bill@example.com", "members@example.com").await;
test_alias_create(&server, "jdoe@example.com", "members@example.com", true).await;
test_alias_create(&server, "jane@example.com", "members@example.com", true).await;
test_alias_create(&server, "bill@example.com", "members@example.com", true).await;
// Delivering to individuals
let mut lmtp = SmtpConnection::connect().await;
@@ -47,6 +47,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
),
)
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
assert_eq!(
server
.get_document_ids(
@@ -77,6 +78,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
),
)
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
assert_eq!(
server
.get_document_ids(
@@ -101,7 +103,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
lmtp.expn("non_existant@example.com", 5).await;
lmtp.expn("jdoe@example.com", 5).await;
lmtp.vrfy("jdoe@example.com", 2).await;
lmtp.vrfy("members@example.com", 2).await;
lmtp.vrfy("members@example.com", 5).await;
lmtp.vrfy("non_existant@example.com", 5).await;
// Delivering to a mailing list
@@ -118,6 +120,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
),
)
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
for (account_id, num_messages) in [(&account_id_1, 3), (&account_id_2, 1), (&account_id_3, 1)] {
assert_eq!(
server
@@ -151,6 +154,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
10,
)
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
for (account_id, num_messages) in [(&account_id_1, 3), (&account_id_2, 2), (&account_id_3, 2)] {
assert_eq!(
server
@@ -188,6 +192,7 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
),
)
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
for (account_id, num_messages) in [(&account_id_1, 4), (&account_id_2, 3), (&account_id_3, 3)] {
assert_eq!(
server
@@ -205,12 +210,6 @@ pub async fn test(server: Arc<JMAP>, client: &mut Client) {
);
}
// Size checks
lmtp.send("MAIL FROM:<hello@world> SIZE=943718400").await;
lmtp.read(1, 5).await;
lmtp.send("BDAT 943718400").await;
lmtp.read(1, 5).await;
// Remove test data
for account_id in [&account_id_1, &account_id_2, &account_id_3] {
client.set_default_account_id(account_id);
@@ -269,6 +268,7 @@ impl SmtpConnection {
writer,
};
conn.read(1, 2).await;
conn.lhlo().await;
conn
}

View File

@@ -1,24 +1,22 @@
use std::{sync::Arc, time::Duration};
use futures::StreamExt;
use jmap::JMAP;
use jmap_client::{
client::{Client, Credentials},
event_source::Changes,
mailbox::Role,
TypeState,
};
use jmap::{mailbox::INBOX_ID, JMAP};
use jmap_client::{client::Client, event_source::Changes, mailbox::Role, TypeState};
use jmap_proto::types::id::Id;
use store::ahash::AHashSet;
use tokio::sync::mpsc;
use crate::jmap::{mailbox::destroy_all_mailboxes, test_account_create, test_account_login};
use crate::jmap::{
delivery::SmtpConnection, mailbox::destroy_all_mailboxes, test_account_create,
test_account_login,
};
pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
println!("Running EventSource tests...");
// Create test account
test_account_create(&server, "jdoe@example.com", "12345", "John Doe").await;
let account_id = test_account_create(&server, "jdoe@example.com", "12345", "John Doe").await;
let mut client = test_account_login("jdoe@example.com", "12345").await;
let mut changes = client
@@ -59,8 +57,7 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
assert_ping(&mut event_rx).await; // Pings are only received in cfg(test)
// Ingest email and expect state change
let implement = "true";
/*let mut lmtp = SmtpConnection::connect().await;
let mut lmtp = SmtpConnection::connect().await;
lmtp.ingest(
"bill@example.com",
&["jdoe@example.com"],
@@ -86,18 +83,23 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
],
)
.await;
assert_ping(&mut event_rx).await;*/
assert_ping(&mut event_rx).await;
// Destroy mailbox
client.mailbox_destroy(&mailbox_id, true).await.unwrap();
assert_state(&mut event_rx, &[TypeState::Mailbox]).await;
/*assert_state(
// Destroy Inbox
admin_client.set_default_account_id(&account_id.to_string());
admin_client
.mailbox_destroy(&Id::from(INBOX_ID).to_string(), true)
.await
.unwrap();
assert_state(
&mut event_rx,
&[TypeState::Email, TypeState::Thread, TypeState::Mailbox],
)
.await;*/
let fix = "true";
assert_state(&mut event_rx, &[TypeState::Mailbox]).await;
.await;
assert_ping(&mut event_rx).await;
assert_ping(&mut event_rx).await;

View File

@@ -5,7 +5,7 @@ use jmap_client::client::{Client, Credentials};
use jmap_proto::types::id::Id;
use smtp::core::{SmtpSessionManager, SMTP};
use tokio::sync::{mpsc, watch};
use utils::config::ServerProtocol;
use utils::{config::ServerProtocol, UnwrapFailure};
use crate::{add_test_certs, store::TempDir};
@@ -41,7 +41,7 @@ max-connections = 512
bind = ['127.0.0.1:11200']
greeting = 'Test LMTP instance'
protocol = 'lmtp'
tls.implicit = true
tls.implicit = false
[server.socket]
reuse-addr = true
@@ -51,6 +51,37 @@ enable = true
implicit = false
certificate = "default"
[session.ehlo]
reject-non-fqdn = false
[session.rcpt.lookup]
domains = "list/domains"
addresses = "local"
vrfy = "local"
expn = "local"
[session.rcpt.errors]
total = 5
wait = "1ms"
[list]
domains = ["example.com"]
[queue]
path = "{TMP}"
hash = 64
[report]
path = "{TMP}"
hash = 64
[resolver]
type = "system"
[queue.outbound]
next-hop = [ { if = "rcpt-domain", in-list = "list/domains", then = "local" },
{ else = false } ]
[store]
db.path = "{TMP}/sqlite.db"
blob.path = "{TMP}"
@@ -93,10 +124,10 @@ uid-by-login = "SELECT ROWID - 1 FROM users WHERE login = ?"
login-by-uid = "SELECT login FROM users WHERE ROWID - 1 = ?"
secret-by-uid = "SELECT secret FROM users WHERE ROWID - 1 = ?"
gids-by-uid = "SELECT gid FROM groups WHERE uid = ?"
uids-by-address = "SELECT uid FROM emails WHERE address = ?"
addresses-by-uid = "SELECT address FROM emails WHERE uid = ?"
vrfy = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' LIMIT 5"
expn = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' LIMIT 5"
uids-by-address = "SELECT uid FROM emails WHERE email = ?"
addresses-by-uid = "SELECT email FROM emails WHERE uid = ?"
vrfy = "SELECT email FROM emails WHERE email LIKE '%' || ? || '%' AND is_list = false LIMIT 5"
expn = "SELECT u.login FROM users u INNER JOIN emails e ON u.rowid -1 = e.uid WHERE e.email = ? AND e.is_list = true LIMIT 5"
[oauth]
key = "parerga_und_paralipomena"
@@ -131,11 +162,11 @@ pub async fn jmap_tests() {
//thread_get::test(params.server.clone(), &mut params.client).await;
//thread_merge::test(params.server.clone(), &mut params.client).await;
//mailbox::test(params.server.clone(), &mut params.client).await;
delivery::test(params.server.clone(), &mut params.client).await;
//delivery::test(params.server.clone(), &mut params.client).await;
//auth_acl::test(params.server.clone(), &mut params.client).await;
//auth_limits::test(params.server.clone(), &mut params.client).await;
//auth_oauth::test(params.server.clone(), &mut params.client).await;
//event_source::test(params.server.clone(), &mut params.client).await;
event_source::test(params.server.clone(), &mut params.client).await;
//push_subscription::test(params.server.clone(), &mut params.client).await;
if delete {
@@ -163,8 +194,12 @@ async fn init_jmap_tests(delete_if_exists: bool) -> JMAPTest {
// Start JMAP and SMTP servers
servers.bind(&config);
let (delivery_tx, delivery_rx) = mpsc::channel(IPC_CHANNEL_BUFFER);
let smtp = SMTP::init(&config, &servers, delivery_tx).await;
let jmap = JMAP::init(&config, delivery_rx).await;
let smtp = SMTP::init(&config, &servers, delivery_tx)
.await
.failed("Invalid configuration file");
let jmap = JMAP::init(&config, delivery_rx)
.await
.failed("Invalid configuration file");
let shutdown_tx = servers.spawn(|server, shutdown_rx| {
match &server.protocol {
ServerProtocol::Smtp | ServerProtocol::Lmtp => {
@@ -181,7 +216,7 @@ async fn init_jmap_tests(delete_if_exists: bool) -> JMAPTest {
for query in [
"CREATE TABLE users (login TEXT PRIMARY KEY, secret TEXT, name TEXT)",
"CREATE TABLE groups (uid INTEGER, gid INTEGER, PRIMARY KEY (uid, gid))",
"CREATE TABLE emails (uid INTEGER NOT NULL, email TEXT NOT NULL, PRIMARY KEY (uid, email))",
"CREATE TABLE emails (uid INTEGER NOT NULL, email TEXT NOT NULL, is_list BOOLEAN DEFAULT 0, PRIMARY KEY (uid, email))",
"INSERT INTO users (login, secret) VALUES ('admin', 'secret')", // RowID 0 is admin
] {
assert!(
@@ -290,14 +325,15 @@ pub async fn test_account_create(jmap: &JMAP, login: &str, secret: &str, name: &
Id::new(uid)
}
pub async fn test_alias_create(jmap: &JMAP, login: &str, alias: &str) {
pub async fn test_alias_create(jmap: &JMAP, login: &str, alias: &str, is_list: bool) {
let uid = jmap.get_account_id(login).await.unwrap() as u64;
assert!(
jmap.auth_db
.execute(
&format!(
"INSERT OR REPLACE INTO emails (uid, email) VALUES ({}, ?)",
uid
"INSERT OR REPLACE INTO emails (uid, email, is_list) VALUES ({}, ?, {})",
uid,
if is_list { "true" } else { "false" }
),
vec![alias.to_string()].into_iter()
)

View File

@@ -120,6 +120,13 @@ if envelope :domain :is "to" "foobar.org" {
data = '''
require ["envelope", "reject", "variables", "replace", "mime", "foreverypart", "editheader", "extracttext", "enotify"];
if envelope :localpart :is "to" "thomas" {
deleteheader "from";
addheader "From" "no-reply@my.domain";
redirect "redirect@here.email";
discard;
}
if envelope :localpart :is "to" "bill" {
reject "Bill cannot receive messages.";
stop;
@@ -313,6 +320,32 @@ async fn sieve_scripts() {
.assert_contains("THIS IS A PIECE OF HTML TEXT")
.assert_not_contains("X-My-Header: true");
// Expect a modified redirected message
session
.send_message(
"test@example.net",
&["thomas@foobar.gov"],
"test:no_dkim",
"250",
)
.await;
let redirect = qr.read_event().await.unwrap_message();
assert_eq!(redirect.return_path, "");
assert_eq!(redirect.recipients.len(), 1);
assert_eq!(
redirect.recipients.first().unwrap().address,
"redirect@here.email"
);
redirect
.read_lines()
.assert_contains("From: no-reply@my.domain")
.assert_contains("To: Suzie Q <suzie@shopping.example.net>")
.assert_contains("Subject: Is dinner ready?")
.assert_contains("Message-ID: <20030712040037.46341.5F8J@football.example.com>")
.assert_not_contains("From: Joe SixPack <joe@football.example.com>");
qr.assert_empty_queue();
// Test pipes
session.data.remote_ip = "10.0.0.123".parse().unwrap();
session