Add smtp server test
parent
cdae585ee6
commit
b401a52130
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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> {
|
||||
|
|
107
core/src/db.rs
107
core/src/db.rs
|
@ -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 */ }
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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+
|
Loading…
Reference in New Issue