Update melib ref and add mailcrab test

newstuff
Manos Pitsidianakis 2023-03-31 19:59:43 +03:00
parent b4329b993c
commit 67a0e9a1f2
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
11 changed files with 299 additions and 15 deletions

89
Cargo.lock generated
View File

@ -185,6 +185,12 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "bincode"
version = "1.3.3"
@ -524,6 +530,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "encoding_rs"
version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "error-chain"
version = "0.12.4"
@ -836,7 +851,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d"
dependencies = [
"base64",
"base64 0.13.0",
"bitflags",
"bytes",
"headers-core",
@ -1012,6 +1027,12 @@ dependencies = [
"libc",
]
[[package]]
name = "ipnet"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
[[package]]
name = "itoa"
version = "1.0.2"
@ -1101,7 +1122,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d0411d6d3cf6baacae37461dc5b0a32b9c68ae99ddef61bcd88174b8da890a"
dependencies = [
"base64",
"base64 0.13.0",
"either",
"log",
"nom",
@ -1133,6 +1154,7 @@ dependencies = [
"log",
"mailin-embedded",
"melib",
"reqwest",
"rusqlite",
"serde",
"serde_json",
@ -1170,10 +1192,10 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "melib"
version = "0.7.2"
source = "git+https://github.com/meli/meli?rev=b87d54ea3f#b87d54ea3f3f077b6330e798263be6a3d33b3b9c"
source = "git+https://github.com/meli/meli?rev=2447a2c#2447a2cbfeaa8d6f7ec11a2a8a6f3be1ff2fea58"
dependencies = [
"async-stream",
"base64",
"base64 0.13.0",
"bincode",
"bitflags",
"data-encoding",
@ -1781,6 +1803,40 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "reqwest"
version = "0.11.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254"
dependencies = [
"base64 0.21.0",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"ipnet",
"js-sys",
"log",
"mime",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -1829,7 +1885,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
dependencies = [
"base64",
"base64 0.13.0",
]
[[package]]
@ -2381,7 +2437,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5"
dependencies = [
"base64",
"base64 0.13.0",
"byteorder",
"bytes",
"http",
@ -2663,6 +2719,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.81"
@ -2807,6 +2875,15 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"

View File

@ -642,7 +642,7 @@ fn run_app(opt: Opt) -> Result<()> {
fn get_file_hash(file: &std::path::Path) -> EnvelopeHash {
let mut hasher = DefaultHasher::default();
file.hash(&mut hasher);
hasher.finish()
EnvelopeHash(hasher.finish())
}
let mut buf = Vec::with_capacity(4096);
let files =

View File

@ -15,7 +15,7 @@ anyhow = "1.0.58"
chrono = { version = "^0.4", features = ["serde", ] }
error-chain = { version = "0.12.4", default-features = false }
log = "0.4"
melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms", "maildir_backend"], git = "https://github.com/meli/meli", rev = "b87d54ea3f" }
melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms", "maildir_backend"], git = "https://github.com/meli/meli", rev = "2447a2c" }
rusqlite = { version = "^0.27", features = ["bundled"] }
serde = { version = "^1", features = ["derive", ] }
serde_json = "^1"
@ -24,5 +24,6 @@ xdg = "2.4.1"
[dev-dependencies]
mailin-embedded = { version = "0.7", features = ["rtls"] }
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking"] }
stderrlog = "^0.5"
tempfile = "3.3.0"

View File

@ -5,3 +5,13 @@ Initialize `sqlite3` database
```shell
sqlite3 mpot.db < ./src/schema.sql
```
## Tests
`test_smtp_mailcrab` requires a running mailcrab instance.
You must set the environment variable `MAILCRAB_IP` to run this.
Example:
```shell
MAILCRAB_IP="127.0.0.1" cargo test mailcrab
```

View File

@ -49,7 +49,7 @@ impl Database {
}
Ok(Database {
conf: conf.clone(),
connection: DbConnection::open(&conf.db_path.to_str().unwrap())?,
connection: DbConnection::open(conf.db_path.to_str().unwrap())?,
})
}
@ -95,7 +95,7 @@ impl Database {
.canonicalize()
.context("Could not canonicalize db path")?;
let conn = DbConnection::open(&db_path.to_str().unwrap())?;
let conn = DbConnection::open(db_path.to_str().unwrap())?;
Ok(Database {
conf: conf.clone(),

View File

@ -147,15 +147,17 @@ impl Database {
trace!("result {:#?}", result);
let Post { bytes, action, .. } = post;
trace!("Action is {:#?}", action);
let post_env = melib::Envelope::from_bytes(&bytes, None)?;
match action {
PostAction::Accept => {
let _post_pk = self.insert_post(list_ctx.list.pk, &bytes, &post_env)?;
trace!("post_pk is {:#?}", _post_pk);
for job in list_ctx.scheduled_jobs.iter() {
trace!("job is {:#?}", &job);
if let crate::mail::MailJob::Send { recipients } = job {
trace!("recipients: {:?}", &recipients);
if !recipients.is_empty() {
trace!("recipients: {:?}", &recipients);
if let crate::config::SendMail::Smtp(ref smtp_conf) =
&self.conf.send_mail
{

View File

@ -48,7 +48,7 @@ error_chain! {
Sql(rusqlite::Error);
Io(::std::io::Error);
Xdg(xdg::BaseDirectoriesError);
Melib(melib::error::MeliError);
Melib(melib::error::Error);
Configuration(toml::de::Error);
SerdeJson(serde_json::Error);
}

View File

@ -175,7 +175,7 @@ impl PostFilter for FinalizeRecipients {
let email_from = post.from.get_email();
for member in ctx.memberships {
trace!("examining member {:?}", &member);
if member.address != email_from {
if member.address == email_from {
trace!("member is submitter");
}
if member.digest {

View File

@ -94,6 +94,39 @@ CREATE TABLE IF NOT EXISTS error_queue (
datetime TEXT NOT NULL DEFAULT (datetime())
);
-- # Queues
--
-- ## The "maildrop" queue
--
-- Messages that have been submitted but not yet processed, await processing in
-- the "maildrop" queue. Messages can be added to the "maildrop" queue even when
-- mailpot is not running.
--
-- ## The "deferred" queue
--
-- When all the deliverable recipients for a message are delivered, and for some
-- recipients delivery failed for a transient reason (it might succeed later), the
-- message is placed in the "deferred" queue.
--
-- ## The "hold" queue
--
-- List administrators may introduce rules for emails to be placed indefinitely in
-- the "hold" queue. Messages placed in the "hold" queue stay there until the
-- administrator intervenes. No periodic delivery attempts are made for messages
-- in the "hold" queue.
CREATE TABLE IF NOT EXISTS queue (
pk INTEGER PRIMARY KEY NOT NULL,
kind TEXT CHECK (kind IN ('maildrop', 'hold', 'deferred', 'corrupt')) NOT NULL,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime())
);
CREATE INDEX IF NOT EXISTS post_listpk_idx ON post(list);
CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id);

View File

@ -98,6 +98,39 @@ CREATE TABLE IF NOT EXISTS error_queue (
datetime TEXT NOT NULL DEFAULT (datetime())
);
-- # Queues
--
-- ## The "maildrop" queue
--
-- Messages that have been submitted but not yet processed, await processing in
-- the "maildrop" queue. Messages can be added to the "maildrop" queue even when
-- mailpot is not running.
--
-- ## The "deferred" queue
--
-- When all the deliverable recipients for a message are delivered, and for some
-- recipients delivery failed for a transient reason (it might succeed later), the
-- message is placed in the "deferred" queue.
--
-- ## The "hold" queue
--
-- List administrators may introduce rules for emails to be placed indefinitely in
-- the "hold" queue. Messages placed in the "hold" queue stay there until the
-- administrator intervenes. No periodic delivery attempts are made for messages
-- in the "hold" queue.
CREATE TABLE IF NOT EXISTS queue (
pk INTEGER PRIMARY KEY NOT NULL,
kind TEXT CHECK (kind IN ('maildrop', 'hold', 'deferred', 'corrupt')) NOT NULL,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime())
);
CREATE INDEX IF NOT EXISTS post_listpk_idx ON post(list);
CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id);

View File

@ -1,3 +1,4 @@
use log::{trace, warn};
use mailin_embedded::{Handler, Response, Server, SslConfig};
use mailpot::{melib, models::*, Configuration, Database, SendMail};
use std::net::IpAddr; //, Ipv4Addr, Ipv6Addr};
@ -229,7 +230,9 @@ fn test_smtp() {
.unwrap_err()
.kind()
{
mailpot::ErrorKind::PostRejected(_reason) => {}
mailpot::ErrorKind::PostRejected(reason) => {
trace!("Non-member post succesfully rejected: '{reason}'");
}
other => panic!("Got unexpected error: {}", other),
}
@ -274,3 +277,128 @@ fn test_smtp() {
}
assert_eq!(handler.stored.lock().unwrap().len(), 2);
}
#[test]
fn test_smtp_mailcrab() {
use std::env;
fn get_smtp_conf() -> melib::smtp::SmtpServerConf {
use melib::smtp::*;
SmtpServerConf {
hostname: "127.0.0.1".into(),
port: 1025,
envelope_from: "foo-chat@example.com".into(),
auth: SmtpAuth::None,
security: SmtpSecurity::None,
extensions: Default::default(),
}
}
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
let Ok(mailcrab_ip) = env::var("MAILCRAB_IP") else {
warn!("MAILCRAB_IP env var not set, is mailcrab server running?");
return;
};
let mailcrab_port = env::var("MAILCRAB_PORT").unwrap_or("1080".to_string());
let api_uri = format!("http://{mailcrab_ip}:{mailcrab_port}/api/messages");
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
let config = Configuration {
send_mail: SendMail::Smtp(get_smtp_conf()),
db_path: db_path.clone(),
storage: "sqlite3".to_string(),
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {
pk: 0,
name: "foobar chat".into(),
id: "foo-chat".into(),
address: "foo-chat@example.com".into(),
description: None,
archive_url: None,
})
.unwrap();
assert_eq!(foo_chat.pk(), 1);
let post_policy = db
.set_list_policy(
foo_chat.pk(),
PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
},
)
.unwrap();
assert_eq!(post_policy.pk(), 1);
let input_bytes = include_bytes!("./test_sample_longmessage.eml");
match melib::Envelope::from_bytes(input_bytes, None) {
Ok(envelope) => {
match db
.post(&envelope, input_bytes, /* dry_run */ false)
.unwrap_err()
.kind()
{
mailpot::ErrorKind::PostRejected(reason) => {
trace!("Non-member post succesfully rejected: '{reason}'");
}
other => panic!("Got unexpected error: {}", other),
}
db.add_member(
foo_chat.pk(),
ListMembership {
pk: 0,
list: foo_chat.pk(),
address: "japoeunp@hotmail.com".into(),
name: Some("Jamaica Poe".into()),
digest: false,
hide_address: false,
receive_duplicates: true,
receive_own_posts: true,
receive_confirmation: true,
enabled: true,
},
)
.unwrap();
db.add_member(
foo_chat.pk(),
ListMembership {
pk: 0,
list: foo_chat.pk(),
address: "manos@example.com".into(),
name: Some("Manos Hands".into()),
digest: false,
hide_address: false,
receive_duplicates: true,
receive_own_posts: true,
receive_confirmation: true,
enabled: true,
},
)
.unwrap();
db.post(&envelope, input_bytes, /* dry_run */ false)
.unwrap();
}
Err(err) => {
panic!("Could not parse message: {}", err);
}
}
let mails: String = reqwest::blocking::get(&api_uri).unwrap().text().unwrap();
trace!("mails: {}", mails);
}