diff --git a/core/src/db/error_queue.rs b/core/src/db/error_queue.rs index de79838..b49a0f9 100644 --- a/core/src/db/error_queue.rs +++ b/core/src/db/error_queue.rs @@ -21,6 +21,26 @@ use super::*; use serde_json::{json, Value}; impl Database { + pub fn insert_to_error_queue(&self, env: &Envelope, raw: &[u8]) -> Result { + let mut stmt = self.connection.prepare("INSERT INTO error_queue(to_address, from_address, subject, message_id, message, timestamp, datetime) VALUES(?, ?, ?, ?, ?, ?, ?) RETURNING pk;")?; + let pk = stmt.query_row( + rusqlite::params![ + &env.field_to_to_string(), + &env.field_from_to_string(), + &env.subject(), + &env.message_id().to_string(), + raw, + &env.timestamp, + &env.date, + ], + |row| { + let pk: i64 = row.get("pk")?; + Ok(pk) + }, + )?; + Ok(pk) + } + pub fn error_queue(&self) -> Result>> { let mut stmt = self.connection.prepare("SELECT * FROM error_queue;")?; let error_iter = stmt.query_map([], |row| { @@ -32,8 +52,8 @@ impl Database { "from_address": row.get::<_, String>("from_address")?, "subject": row.get::<_, String>("subject")?, "message_id": row.get::<_, String>("message_id")?, - "message": row.get::<_, String>("message")?, - "timestamp": row.get::<_, String>("timestamp")?, + "message": row.get::<_, Vec>("message")?, + "timestamp": row.get::<_, u64>("timestamp")?, "datetime": row.get::<_, String>("datetime")?, }), pk, diff --git a/core/src/db/posts.rs b/core/src/db/posts.rs index ec30d53..09d16a6 100644 --- a/core/src/db/posts.rs +++ b/core/src/db/posts.rs @@ -52,6 +52,21 @@ impl Database { } pub fn post(&self, env: &Envelope, raw: &[u8], _dry_run: bool) -> Result<()> { + let result = self.inner_post(env, raw, _dry_run); + if let Err(err) = result { + return match self.insert_to_error_queue(env, raw) { + Ok(idx) => Err(Error::from_kind(Information(format!( + "Inserted into error_queue at index {}", + idx + ))) + .chain_err(|| err)), + Err(err2) => Err(err.chain_err(|| err2)), + }; + } + result + } + + fn inner_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() { diff --git a/core/src/errors.rs b/core/src/errors.rs index a11879e..8bc022d 100644 --- a/core/src/errors.rs +++ b/core/src/errors.rs @@ -34,6 +34,11 @@ error_chain! { description("List request is invalid") display("Your list request has been found invalid: {}.", reason) } + + Information(reason: String) { + description("") + display("{}.", reason) + } } foreign_links { Sql(rusqlite::Error); diff --git a/core/tests/error_queue.rs b/core/tests/error_queue.rs new file mode 100644 index 0000000..abdcbc9 --- /dev/null +++ b/core/tests/error_queue.rs @@ -0,0 +1,82 @@ +use mailpot::{melib, models::*, Configuration, Database, SendMail}; +use tempfile::TempDir; + +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_error_queue() { + stderrlog::new() + .quiet(false) + .verbosity(15) + .show_module_names(true) + .timestamp(stderrlog::Timestamp::Millisecond) + .init() + .unwrap(); + let tmp_dir = TempDir::new().unwrap(); + + 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), + } + assert_eq!(db.error_queue().unwrap().len(), 1) + } + Err(err) => { + panic!("Could not parse message: {}", err); + } + } +}