Update melib ref and add mailcrab test
parent
b4329b993c
commit
67a0e9a1f2
|
@ -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"
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue