Add smtp server test

pull/1/head
Manos Pitsidianakis 2022-05-08 00:46:49 +03:00
parent cdae585ee6
commit b401a52130
14 changed files with 666 additions and 68 deletions

203
Cargo.lock generated
View File

@ -257,6 +257,18 @@ dependencies = [
"safemem",
]
[[package]]
name = "bufstream"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
[[package]]
name = "bumpalo"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -405,6 +417,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "encoding"
version = "0.2.33"
@ -843,6 +861,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -894,6 +921,34 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "mailin"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8512e26d39b4899440f1f1e7994cf64a4bf14238ce8de60c65a21ac85388f42"
dependencies = [
"base64",
"either",
"log",
"nom",
"ternop",
]
[[package]]
name = "mailin-embedded"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965b95d71694c0785ddad0855e59833f0eea6630d7c3b3c6fba4825f64162402"
dependencies = [
"bufstream",
"lazy_static",
"log",
"mailin",
"rustls",
"rustls-pemfile",
"scoped_threadpool",
]
[[package]]
name = "mailpot"
version = "0.1.0"
@ -901,10 +956,13 @@ dependencies = [
"chrono",
"error-chain",
"log",
"mailin-embedded",
"melib",
"rusqlite",
"serde",
"serde_json",
"stderrlog",
"tempfile",
"toml",
"xdg",
]
@ -913,6 +971,7 @@ dependencies = [
name = "mailpot-cli"
version = "0.1.0"
dependencies = [
"log",
"mailpot",
"stderrlog",
"structopt",
@ -1379,6 +1438,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rusqlite"
version = "0.27.0"
@ -1400,6 +1474,27 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustls"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921"
dependencies = [
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "rustls-pemfile"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
dependencies = [
"base64",
]
[[package]]
name = "ryu"
version = "1.0.9"
@ -1428,12 +1523,28 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "security-framework"
version = "2.6.1"
@ -1592,6 +1703,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stderrlog"
version = "0.5.1"
@ -1669,6 +1786,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "ternop"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d4ae32d0a4605a89c28534371b056919c12e7a070ee07505af75130ff030111"
[[package]]
name = "textwrap"
version = "0.11.0"
@ -1945,6 +2068,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.2.2"
@ -2050,6 +2179,80 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]]
name = "web-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "wepoll-ffi"
version = "0.1.2"

View File

@ -19,3 +19,4 @@ path = "src/main.rs"
mailpot = { version = "0.1.0", path = "../core" }
structopt = "0.3.16"
stderrlog = "^0.5"
log = "0.4"

View File

@ -17,6 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
extern crate log;
extern crate mailpot;
extern crate stderrlog;
@ -169,7 +170,12 @@ fn run_app(opt: Opt) -> Result<()> {
if opt.debug {
println!("DEBUG: {:?}", &opt);
}
Configuration::init()?;
if let Some(config_path) = opt.config.as_ref() {
let config = Configuration::from_file(&config_path)?;
config.init_with()?;
} else {
Configuration::init()?;
}
use Command::*;
match opt.cmd {
DbLocation => {
@ -417,7 +423,7 @@ fn run_app(opt: Opt) -> Result<()> {
archive_url,
} => {
let db = Database::open_or_create_db()?;
db.create_list(MailingList {
let new = db.create_list(MailingList {
pk: 0,
name,
id,
@ -425,6 +431,14 @@ fn run_app(opt: Opt) -> Result<()> {
address,
archive_url,
})?;
log::trace!("created new list {:#?}", new);
if !opt.quiet {
println!(
"Created new list {:?} with primary key {}",
new.id,
new.pk()
);
}
}
Post { dry_run } => {
if opt.debug {
@ -439,7 +453,7 @@ fn run_app(opt: Opt) -> Result<()> {
match Envelope::from_bytes(input.as_bytes(), None) {
Ok(env) => {
let db = Database::open_or_create_db()?;
db.post(env, input.as_bytes(), dry_run)?;
db.post(&env, input.as_bytes(), dry_run)?;
}
Err(err) => {
eprintln!("Could not parse message: {}", err);

View File

@ -21,3 +21,8 @@ serde_json = "^1"
toml = "^0.5"
log = "0.4"
xdg = "2.1.0"
[dev-dependencies]
mailin-embedded = { version = "0.7", features = ["rtls"] }
tempfile = "3.3.0"
stderrlog = "^0.5"

View File

@ -22,7 +22,7 @@ use chrono::prelude::*;
use std::cell::RefCell;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
thread_local!(pub static CONFIG: RefCell<Configuration> = RefCell::new(Configuration::new()));
@ -38,31 +38,50 @@ pub struct Configuration {
pub send_mail: SendMail,
#[serde(default = "default_storage_fn")]
pub storage: String,
#[serde(default)]
pub db_path: Option<PathBuf>,
}
impl Default for Configuration {
fn default() -> Self {
Self::new()
}
}
impl Configuration {
pub(crate) fn new() -> Self {
Configuration {
send_mail: SendMail::ShellCommand(String::new()),
send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()),
storage: "sqlite3".into(),
db_path: None,
}
}
pub fn init_with(self) -> Result<()> {
CONFIG.with(|f| {
*f.borrow_mut() = self;
});
Ok(())
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let mut s = String::new();
let mut file = std::fs::File::open(path)?;
file.read_to_string(&mut s)?;
let config: Configuration = toml::from_str(&s)?;
Ok(config)
}
pub fn init() -> Result<()> {
let path =
xdg::BaseDirectories::with_prefix("mailpot")?.place_config_file("config.toml")?;
if !path.exists() {
return Err(format!("Configuration file {} doesn't exist", path.display()).into());
}
let mut s = String::new();
let mut file = std::fs::File::open(&path)?;
file.read_to_string(&mut s)?;
let config: Configuration = toml::from_str(&s)?;
CONFIG.with(|f| {
*f.borrow_mut() = config;
});
Ok(())
let config: Configuration = Self::from_file(&path)?;
config.init_with()
}
pub fn data_directory() -> Result<PathBuf> {

View File

@ -22,7 +22,9 @@ use melib::Envelope;
use models::changesets::*;
use rusqlite::Connection as DbConnection;
use rusqlite::OptionalExtension;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
const DB_NAME: &str = "mpot.db";
@ -112,6 +114,34 @@ impl Database {
Ok(ret)
}
pub fn set_list_policy(&self, list_pk: i64, policy: PostPolicy) -> Result<DbVal<PostPolicy>> {
let mut stmt = self.connection.prepare("INSERT OR REPLACE INTO post_policy(list, announce_only, subscriber_only, approval_needed) VALUES (?, ?, ?, ?) RETURNING *;")?;
let ret = stmt.query_row(
rusqlite::params![
&list_pk,
&policy.announce_only,
&policy.subscriber_only,
&policy.approval_needed,
],
|row| {
let pk = row.get("pk")?;
Ok(DbVal(
PostPolicy {
pk,
list: row.get("list")?,
announce_only: row.get("announce_only")?,
subscriber_only: row.get("subscriber_only")?,
approval_needed: row.get("approval_needed")?,
},
pk,
))
},
)?;
trace!("set_list_policy {:?}.", &ret);
Ok(ret)
}
pub fn update_list(&self, _change_set: MailingListChangeset) -> Result<()> {
/*
diesel::update(mailing_lists::table)
@ -314,6 +344,13 @@ impl Database {
}
pub fn db_path() -> Result<PathBuf> {
let mut config_path = None;
crate::config::CONFIG.with(|c| {
config_path = c.borrow().db_path.clone();
});
if let Some(db_path) = config_path {
return Ok(db_path);
}
let name = DB_NAME;
let data_dir = xdg::BaseDirectories::with_prefix("mailpot")?;
Ok(data_dir.place_data_file(name)?)
@ -330,14 +367,28 @@ impl Database {
pub fn open_or_create_db() -> Result<Self> {
let db_path = Self::db_path()?;
let mut set_mode = false;
let mut create = false;
if !db_path.exists() {
info!("Creating {} database in {}", DB_NAME, db_path.display());
set_mode = true;
create = true;
}
let conn = DbConnection::open(&db_path.to_str().unwrap())?;
if set_mode {
if create {
use std::os::unix::fs::PermissionsExt;
let mut child = Command::new("sqlite3")
.arg(&db_path)
.stdin(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
std::thread::spawn(move || {
stdin
.write_all(include_bytes!("./schema.sql"))
.expect("failed to write to stdin");
});
let output = child.wait_with_output()?;
if !output.status.success() {
return Err(format!("Could not initialize sqlite3 database at {}: sqlite3 returned exit code {} and stderr {}", db_path.display(), String::from_utf8_lossy(&output.stderr), output.status.code().unwrap_or_default()).into());
}
let file = std::fs::File::open(&db_path)?;
let metadata = file.metadata()?;
let mut permissions = metadata.permissions();
@ -346,6 +397,8 @@ impl Database {
file.set_permissions(permissions)?;
}
let conn = DbConnection::open(&db_path.to_str().unwrap())?;
Ok(Database { connection: conn })
}
@ -386,7 +439,7 @@ impl Database {
Ok(pk)
}
pub fn post(&self, env: Envelope, raw: &[u8], _dry_run: bool) -> Result<()> {
pub fn post(&self, env: &Envelope, raw: &[u8], _dry_run: bool) -> Result<()> {
trace!("Received envelope to post: {:#?}", &env);
let tos = env.to().to_vec();
if tos.is_empty() {
@ -422,7 +475,7 @@ impl Database {
ListRequest::Other(subaddr.trim().to_string())
}
},
&env,
env,
raw,
) {
info!("Processing request returned error: {}", err);
@ -453,7 +506,6 @@ impl Database {
use crate::mail::{ListContext, Post, PostAction};
for mut list in lists {
trace!("Examining list {}", list.list_id());
let post_pk = self.insert_post(list.pk, raw, &env)?;
let filters = self.get_list_filters(&list);
let memberships = self.list_members(list.pk)?;
trace!("List members {:#?}", &memberships);
@ -465,7 +517,6 @@ impl Database {
scheduled_jobs: vec![],
};
let mut post = Post {
pk: post_pk,
from: env.from()[0].clone(),
bytes: raw.to_vec(),
to: env.to().to_vec(),
@ -490,29 +541,37 @@ impl Database {
))?;
match action {
PostAction::Accept => {
let _post_pk = self.insert_post(list_ctx.list.pk, raw, env)?;
for job in list_ctx.scheduled_jobs.iter() {
if let crate::mail::MailJob::Send {
message_pk: _,
recipients,
} = job
{
futures::executor::block_on(conn.mail_transaction(
&String::from_utf8_lossy(&bytes),
Some(recipients),
))?;
if let crate::mail::MailJob::Send { recipients } = job {
if !recipients.is_empty() {
trace!("recipients: {:?}", &recipients);
futures::executor::block_on(conn.mail_transaction(
&String::from_utf8_lossy(&bytes),
Some(recipients),
))?;
} else {
trace!("list has no recipients");
}
}
}
/* - Save digest metadata in database */
/* - FIXME Save digest metadata in database */
}
PostAction::Reject { reason: _ } => {
/* - Notify submitter */
PostAction::Reject { reason } => {
/* FIXME - Notify submitter */
trace!("PostAction::Reject {{ reason: {} }}", reason);
//futures::executor::block_on(conn.mail_transaction(&post.bytes, b)).unwrap();
return Err(crate::ErrorKind::PostRejected(reason).into());
}
PostAction::Defer { reason: _ } => {
/* - Notify submitter
* - Save in database */
PostAction::Defer { reason } => {
trace!("PostAction::Defer {{ reason: {} }}", reason);
/* - FIXME Notify submitter
* - FIXME Save in database */
}
PostAction::Hold => {
trace!("PostAction::Hold");
/* FIXME - Save in database */
}
PostAction::Hold => { /* - Save in database */ }
}
}
_ => {}

View File

@ -19,7 +19,13 @@
// Create the Error, ErrorKind, ResultExt, and Result types
error_chain! {
foreign_links {
errors {
PostRejected(reason: String) {
description("Post rejected")
display("Your post has been rejected: {}", reason)
}
}
foreign_links {
Sql(rusqlite::Error);
Io(::std::io::Error);
Xdg(xdg::BaseDirectoriesError);

View File

@ -36,3 +36,7 @@ use models::*;
pub mod errors;
use errors::*;
pub mod db;
pub use config::{Configuration, SendMail};
pub use db::Database;
pub use errors::*;

View File

@ -40,7 +40,6 @@ pub struct ListContext<'list> {
///Post to be considered by the list's `PostFilter` stack.
pub struct Post {
pub pk: i64,
pub from: Address,
pub bytes: Vec<u8>,
pub to: Vec<Address>,
@ -50,7 +49,6 @@ pub struct Post {
impl core::fmt::Debug for Post {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
fmt.debug_struct("Post")
.field("pk", &self.pk)
.field("from", &self.from)
.field("bytes", &format_args!("{} bytes", self.bytes.len()))
.field("to", &self.to.as_slice())
@ -61,25 +59,10 @@ impl core::fmt::Debug for Post {
#[derive(Debug)]
pub enum MailJob {
Send {
message_pk: i64,
recipients: Vec<Address>,
},
Relay {
message: Vec<u8>,
recipients: Vec<Address>,
},
Error {
description: String,
},
StoreDigest {
message_pk: i64,
recipients: Vec<Address>,
},
ConfirmSubscription {
recipient: Address,
},
ConfirmUnsubscription {
recipient: Address,
},
Send { recipients: Vec<Address> },
Relay { recipients: Vec<Address> },
Error { description: String },
StoreDigest { recipients: Vec<Address> },
ConfirmSubscription { recipient: Address },
ConfirmUnsubscription { recipient: Address },
}

View File

@ -190,13 +190,9 @@ impl PostFilter for FinalizeRecipients {
// - check for duplicates (To,Cc,Bcc)
// - send confirmation to submitter
}
ctx.scheduled_jobs.push(MailJob::Send {
message_pk: post.pk,
recipients,
});
ctx.scheduled_jobs.push(MailJob::Send { recipients });
if !digests.is_empty() {
ctx.scheduled_jobs.push(MailJob::StoreDigest {
message_pk: post.pk,
recipients: digests,
});
}

View File

@ -24,6 +24,13 @@ use melib::email::Address;
pub struct DbVal<T>(pub T, pub i64);
impl<T> DbVal<T> {
#[inline(always)]
pub fn pk(&self) -> i64 {
self.1
}
}
impl<T> std::ops::Deref for DbVal<T> {
type Target = T;
fn deref(&self) -> &T {
@ -61,7 +68,16 @@ where
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
impl<T> std::cmp::PartialEq for DbVal<T>
where
T: std::cmp::PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct MailingList {
pub pk: i64,
pub name: String,

View File

@ -0,0 +1,47 @@
use mailpot::{models::*, Configuration, Database};
use tempfile::TempDir;
#[test]
fn test_init_empty() {
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
let mut config = Configuration::default();
config.db_path = Some(db_path.clone());
config.init_with().unwrap();
assert_eq!(Database::db_path().unwrap(), db_path);
let db = Database::open_or_create_db().unwrap();
assert!(db.list_lists().unwrap().is_empty());
}
#[test]
fn test_list_creation() {
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
let mut config = Configuration::default();
config.db_path = Some(db_path.clone());
config.init_with().unwrap();
assert_eq!(Database::db_path().unwrap(), db_path);
let db = Database::open_or_create_db().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 lists = db.list_lists().unwrap();
assert_eq!(lists.len(), 1);
assert_eq!(lists[0], foo_chat);
}

122
core/tests/smtp.rs 100644
View File

@ -0,0 +1,122 @@
use mailin_embedded::{Handler, Server, SslConfig};
use mailpot::{melib, models::*, Configuration, Database, SendMail};
use std::thread;
use tempfile::TempDir;
const ADDRESS: &str = "127.0.0.1:8825";
#[derive(Clone)]
struct MyHandler {}
impl Handler for MyHandler {}
fn get_smtp_conf() -> melib::smtp::SmtpServerConf {
use melib::smtp::*;
SmtpServerConf {
hostname: "127.0.0.1".into(),
port: 8825,
envelope_from: "foo-chat@example.com".into(),
auth: SmtpAuth::None,
security: SmtpSecurity::None,
extensions: Default::default(),
}
}
#[test]
fn test_smtp() {
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
let tmp_dir = TempDir::new().unwrap();
let _smtp_handle = thread::spawn(move || {
let handler = MyHandler {};
let mut server = Server::new(handler);
server
.with_name("example.com")
.with_ssl(SslConfig::None)
.unwrap()
.with_addr(ADDRESS)
.unwrap();
eprintln!("Running smtp server at {}", ADDRESS);
server.serve().expect("Could not run server");
});
let db_path = tmp_dir.path().join("mpot.db");
let mut config = Configuration::default();
config.send_mail = SendMail::Smtp(get_smtp_conf());
config.db_path = Some(db_path.clone());
config.init_with().unwrap();
assert_eq!(Database::db_path().unwrap(), db_path);
let db = Database::open_or_create_db().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) => {
eprintln!("envelope {:?}", &envelope);
match db
.post(&envelope, input_bytes, /* dry_run */ false)
.unwrap_err()
.kind()
{
mailpot::ErrorKind::PostRejected(_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.post(&envelope, input_bytes, /* dry_run */ false)
.unwrap();
}
Err(err) => {
panic!("Could not parse message: {}", err);
}
}
}

View File

@ -0,0 +1,123 @@
Return-Path: <japoeunp@hotmail.com>
Delivered-To: jonnny@miami-dice.co.uk
Received: from violet.xenserver.co.uk
by violet.xenserver.co.uk with LMTP
id qBHcI7LKml9FxzIAYrQLqw
(envelope-from <japoeunp@hotmail.com>)
for <jonnny@miami-dice.co.uk>; Thu, 29 Oct 2020 13:59:14 +0000
Return-path: <japoeunp@hotmail.com>
Envelope-to: jonnny@miami-dice.co.uk
Delivery-date: Thu, 29 Oct 2020 13:59:14 +0000
Received: from mail-oln040092254105.outbound.protection.outlook.com ([40.92.254.105]:29481 helo=APC01-PU1-obe.outbound.protection.outlook.com)
by violet.xenserver.co.uk with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
(Exim 4.93)
(envelope-from <japoeunp@hotmail.com>)
id 1kY8SJ-00DxYw-WD
for jonnny@miami-dice.co.uk; Thu, 29 Oct 2020 13:59:14 +0000
ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;
b=KKU/kthPXLl8CnAmBXXsD1QQWr4evL4ymaLwgHgRi5eSnOe2d2sQxrhcZ1VvLSvW2DQEQoNAm6NUtTC5uRUnBDS0n+g1E5/t1z8oFbzdioCIT6rL77ta3MVcaQ/o+gRa6dIwiNfu8z5GxAujOOu57gCfnCw3/gLeOHH01KtP4ezEB/DvAU9bC8eyso1T7nv+HT0riTjZOywGwDHnVb1aIPPIUiOQrrEi+cfLQRiCer01d94U8Wp+FUECrVYbr4uZGl8mbTwU4oZL1rJ25ubYG54e1ktaPJRa2YEitgJEF5sS8Z503c3RjzzBvvHkc/Kl6ypXcovP9xxeoSrS7YIPKA==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;
s=arcselector9901;
h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
bh=NUSuxgSF4fNUuTts93/OAIsK9q9w8XhbybHWH/oRmXo=;
b=VU2clBW8reAfnfCef0DeEDlBzcCU2u288YCjTvB0ekvBkJGSdI657WyS8KR7JSy0KcPWRfGbN9GJaETaasoa7bLdfuB6K9foup+vSqlA1witS5JQXQM/vJCKx67DbT8/8emLrKi7yDD2qjtRsb6HfvbwAGGvmPyUeyfTvRv6js+4YUbe5eN6CCdJEploBXDrWjFXHpSCwVCL1oF6rgrJf0+Td+ufX0QEHbOz2uJWj4yz0A8hK2yV+2JDVW7GiBwZMrO4yLNXYck/0HQRyYFe8I86xUBJWp/0IITCTe96x5L/H3lqmGkh4uRt8IsXT/2jBEm5CmXLxJZAMR8RONG9BQ==
ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none;
dkim=none; arc=none
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com;
s=selector1;
h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
bh=NUSuxgSF4fNUuTts93/OAIsK9q9w8XhbybHWH/oRmXo=;
b=JRkih9HxwazdzH6MSzSetJMcRwvDr+e97VnoDCQYJf9qQqgtQvzMZR0Z+d2Gu74Ip3ebcvx5oYlOpV15yVZAqUmUeirpF2rdkmMWQiaDQMq9SLiF09eMDkDfEdGLD4V+C36QIISRamgyagIsC72/UB6OyxpXoAjP0SFxbyItvWVgB9EVVsSJLOKXWgRWiYSZxMLye3OQUqdWoiQ9Tw/o8uywLTvcojOizZaS2SrYWajYScBmMiCh58dUarKzrfXmR/WisfBepCf1ia7BKttjalhuJBcMyKfM923X5IbZ+Yw+gVpLtzwGUyPt2cobOAxKna11whmpWdtoBeXRR/hKOg==
Received: from PU1APC01FT013.eop-APC01.prod.protection.outlook.com
(2a01:111:e400:7ebe::45) by
PU1APC01HT068.eop-APC01.prod.protection.outlook.com (2a01:111:e400:7ebe::323)
with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3520.15; Thu, 29 Oct
2020 13:58:16 +0000
Received: from PS1PR0601MB3675.apcprd06.prod.outlook.com
(2a01:111:e400:7ebe::44) by PU1APC01FT013.mail.protection.outlook.com
(2a01:111:e400:7ebe::78) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3520.15 via Frontend
Transport; Thu, 29 Oct 2020 13:58:16 +0000
Received: from PS1PR0601MB3675.apcprd06.prod.outlook.com
([fe80::65ed:e320:1c31:1695]) by PS1PR0601MB3675.apcprd06.prod.outlook.com
([fe80::65ed:e320:1c31:1695%7]) with mapi id 15.20.3499.027; Thu, 29 Oct 2020
13:58:16 +0000
From: Jamaica Poe <japoeunp@hotmail.com>
To: <foo-chat@example.com>
Subject: thankful that I had the chance to written report, that I could learn
and let alone the chance $4454.32
Thread-Topic: thankful that I had the chance to written report, that I could
learn and let alone the chance $4454.32
Thread-Index: AQHWrfuHFQ6EC5DxDEG0hktDfP8BQg==
Date: Thu, 29 Oct 2020 13:58:16 +0000
Message-ID:
<PS1PR0601MB36750BD00EA89E1482FA98A2D5140@PS1PR0601MB3675.apcprd06.prod.outlook.com>
Accept-Language: en-US
Content-Language: en-US
X-MS-Has-Attach: yes
X-MS-TNEF-Correlator:
x-incomingtopheadermarker:
OriginalChecksum:F29893979CC0F318539CFBE7837D9AF24B3138F68568814557FCEFB9E2B29E15;UpperCasedChecksum:CB03C669F747A2BC407B2616111C19885C665247F058AE9AD47D6DC952AF24E9;SizeAsReceived:9522;Count:41
x-tmn: [HPDb9F9LlPS7iY6PEMh8e6LwL83jrjOM]
x-ms-publictraffictype: Email
x-incomingheadercount: 41
x-eopattributedmessage: 0
x-ms-office365-filtering-correlation-id: 0a5172b1-080b-416d-02f7-08d87c12af1f
x-ms-exchange-slblob-mailprops:
KvCwBtHRxSnRTX+Gpnu8+r+btrbXhe1bKafs2OYpwV7YwfwLb/Kzv7smCuCmdXmRrPzgGMsdYpxp7uj/VctABycvZi2vvqgq28FbUIjMoGSzyVRruEcuIfs/p5HNZHSknRDnAAcWyfC0VEBDrfm+s1P+je4N6M7rJOQHeYFM3PtZQ9ZL1GFMuISD/O4rxIj0BnUHlLsilauxhYsOQqJ0E9i4eZEKf8FgOeRP///nI7Umf8hLSSai2cpfufADqc5F0TZJPQnV6PcsxuMPv1LKsqSVRvFRkOaptmEJ3PbaivKUKj1ZYpkUe0wdg1ZgYEu9HxE85CYgg0S1t8tzHcQ6Z+uz9TByKEzsUIG6BotNgFwSfiMwuDQAH1XLrhcbpqa6/ECAekFN6X0p2LMj3sPumWYoBjlMronIBtjxSNLXu1/VypYME2g7Vamq7tRppJa6ZtjcjYZkKF3gGJUCKDVnN3dfeKSL6wE4D/aflJXISl8nT/f3dxxRoJAE4be8iiJcfyAmk01e4S8lJu3Ou0DMDoxqGiVpwzoExNa4Xwl9XmiTDH0vwzLLs2mBjq2UA1jTEirBuCLMw8KquRwqiti4q8ZwuhVRQSkRk7nyyHztPN0ratfQN77UffToJDFEE9Af9WxYlI6mjE+Vo//+bYGgT69xQrVIFe/Q
x-ms-traffictypediagnostic: PU1APC01HT068:
x-microsoft-antispam: BCL:0;
x-microsoft-antispam-message-info:
63swzVCqv2rAYXNqhYzT9vSzRPp+cAVYyOvT2Mj05ccFhXJCE9VoIsVCJ7P/x8Afurj/cz8TTgCf3+ftdCAETm9BR3YRv501qxwuEpr9Y8138ZtYktHFtqLJQD6gAEUS2xqFOAX8GcGRbv4ivUE3Zc79/zO8aAyhrH1ztmCN3K0ZjGrV7ladXaKbbZ2LWPZKFQ+oJr2TuSluO0Jb9mF6gg==
x-ms-exchange-antispam-messagedata:
++T5++BTJOqLy0e59PDE+TsDIv7T0HYmNmR7jarIfrS2AxJESyw+K6ssWdBC58WaXxw6w7E2gUNPe/NJWZKnKzAZ/Tq3AYzucFlqHOkFdQoxwWZoquUNffSRPAl8A2JyVME7XIVmlb5QSnawMNgM2Q==
x-ms-exchange-transport-forked: True
Content-Type: text/html
Content-Transfer-Encoding: base64
MIME-Version: 1.0
X-OriginatorOrg: hotmail.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-AuthSource: PU1APC01FT013.eop-APC01.prod.protection.outlook.com
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000
X-MS-Exchange-CrossTenant-Network-Message-Id: 0a5172b1-080b-416d-02f7-08d87c12af1f
X-MS-Exchange-CrossTenant-originalarrivaltime: 29 Oct 2020 13:58:16.0996
(UTC)
X-MS-Exchange-CrossTenant-fromentityheader: Internet
X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa
X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000
X-MS-Exchange-Transport-CrossTenantHeadersStamped: PU1APC01HT068
X-Spam-Status: No, score=1.1
X-Spam-Score: 11
X-Spam-Bar: +
X-Ham-Report: Spam detection software, running on the system "violet.xenserver.co.uk",
has NOT identified this incoming email as spam. The original
message has been attached to this so you can view it or label
similar future email. If you have any questions, see
root\@localhost for details.
Content preview: ðŸ¡in process of discussion !💡 / correspond displacement
- BZ07OPI7MTRFIW ðŸ¡in process of discussion !💡 /
Content analysis details: (1.1 points, 5.0 required)
pts rule name description
---- ---------------------- --------------------------------------------------
0.8 BAYES_50 BODY: Bayes spam probability is 40 to 60%
[score: 0.5004569]
0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail
provider
[japoeunp[at]hotmail.com]
0.0 HTML_MESSAGE BODY: HTML included in message
-0.1 DKIM_VALID Message has at least one valid DKIM or DK signature
0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily
valid
-0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from
author's domain
-0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from
envelope-from domain
0.0 LOTS_OF_MONEY Huge... sums of money
0.5 KAM_NUMSUBJECT Subject ends in numbers excluding current years
X-Spam-Flag: NO
PCFET0NUWVBFPjxodG1sPjxoZWFkPjx0aXRsZT5mb288L3RpdGxlPjwvaGVhZD48Ym9k
eT48dGFibGUgY2xhc3M9ImZvbyI+PHRoZWFkPjx0cj48dGQ+Zm9vPC90ZD48L3RoZWFk
Pjx0Ym9keT48dHI+PHRkPmZvbzE8L3RkPjwvdHI+PC90Ym9keT48L3RhYmxlPjwvYm9k
eT48L2h0bWw+