core: notify submitter if they are already subscribed

grcov
Manos Pitsidianakis 2023-04-20 00:20:18 +03:00
parent 0e4f2a85b3
commit 1beac75e37
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
5 changed files with 140 additions and 18 deletions

View File

@ -206,21 +206,65 @@ impl Connection {
}
}
PostAction::Reject { reason } => {
/* FIXME - Notify submitter */
trace!("PostAction::Reject {{ reason: {} }}", reason);
//futures::executor::block_on(conn.mail_transaction(&post.bytes, b)).unwrap();
log::info!("PostAction::Reject {{ reason: {} }}", reason);
for f in env.from() {
/* send error notice to e-mail sender */
self.send_reply_with_list_template(
TemplateRenderContext {
template: Template::GENERIC_FAILURE,
default_fn: Some(Template::default_generic_failure),
list: &list,
context: minijinja::context! {
list => &list,
subject => format!("Your post to {} was rejected.", list.id),
details => &reason,
},
queue: Queue::Out,
comment: format!("PostAction::Reject {{ reason: {} }}", reason),
},
std::iter::once(Cow::Borrowed(f)),
)?;
}
return Err(PostRejected(reason).into());
}
PostAction::Defer { reason } => {
trace!("PostAction::Defer {{ reason: {} }}", reason);
/*
* - FIXME Notify submitter
* - FIXME Save in database */
for f in env.from() {
/* send error notice to e-mail sender */
self.send_reply_with_list_template(
TemplateRenderContext {
template: Template::GENERIC_FAILURE,
default_fn: Some(Template::default_generic_failure),
list: &list,
context: minijinja::context! {
list => &list,
subject => format!("Your post to {} was deferred.", list.id),
details => &reason,
},
queue: Queue::Out,
comment: format!("PostAction::Defer {{ reason: {} }}", reason),
},
std::iter::once(Cow::Borrowed(f)),
)?;
}
self.insert_to_queue(QueueEntry::new(
Queue::Deferred,
Some(list.pk),
Some(Cow::Borrowed(&post_env)),
&bytes,
Some(format!("PostAction::Defer {{ reason: {} }}", reason)),
)?)?;
return Err(PostRejected(reason).into());
}
PostAction::Hold => {
trace!("PostAction::Hold");
/* FIXME - Save in database */
self.insert_to_queue(QueueEntry::new(
Queue::Hold,
Some(list.pk),
Some(Cow::Borrowed(&post_env)),
&bytes,
Some("PostAction::Hold".to_string()),
)?)?;
return Err(PostRejected("Hold".into()).into());
}
}
@ -245,12 +289,35 @@ impl Connection {
env.from(),
list
);
let approval_needed = post_policy
.as_ref()
.map(|p| p.approval_needed)
.unwrap_or(false);
for f in env.from() {
let email_from = f.get_email();
if self
.list_subscription_by_address(list.pk, &email_from)
.is_ok()
{
/* send error notice to e-mail sender */
self.send_reply_with_list_template(
TemplateRenderContext {
template: Template::GENERIC_FAILURE,
default_fn: Some(Template::default_generic_failure),
list,
context: minijinja::context! {
list => &list,
subject => format!("You are already subscribed to {}.", list.id),
details => "No action has been taken since you are already subscribed to the list.",
},
queue: Queue::Out,
comment: format!("Address {} is already subscribed to list {}", f, list.id),
},
std::iter::once(Cow::Borrowed(f)),
)?;
continue;
}
let subscription = ListSubscription {
pk: 0,
list: list.pk,
@ -368,6 +435,10 @@ impl Connection {
list_owners.iter().map(|owner| Cow::Owned(owner.address())),
)?;
} else {
log::trace!(
"Added subscription to list {list:?} for address {f:?}, sending \
confirmation."
);
self.send_reply_with_list_template(
TemplateRenderContext {
template: Template::SUBSCRIPTION_CONFIRMATION,

View File

@ -61,7 +61,7 @@ impl Queue {
}
/// A queue entry.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct QueueEntry {
/// Database primary key.
pub pk: i64,
@ -93,6 +93,28 @@ impl std::fmt::Display for QueueEntry {
}
}
impl std::fmt::Debug for QueueEntry {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(stringify!(QueueEntry))
.field("pk", &self.pk)
.field("queue", &self.queue)
.field("list", &self.list)
.field("comment", &self.comment)
.field("to_addresses", &self.to_addresses)
.field("from_address", &self.from_address)
.field("subject", &self.subject)
.field("message_id", &self.message_id)
.field("message length", &self.message.len())
.field(
"message",
&format!("{:.15}", String::from_utf8_lossy(&self.message)),
)
.field("timestamp", &self.timestamp)
.field("datetime", &self.datetime)
.finish()
}
}
impl QueueEntry {
/// Create new entry.
pub fn new(
@ -125,6 +147,7 @@ impl QueueEntry {
impl Connection {
/// Insert a received email into a queue.
pub fn insert_to_queue(&self, mut entry: QueueEntry) -> Result<DbVal<QueueEntry>> {
log::trace!("Inserting to queue: {entry}");
let mut stmt = self.connection.prepare(
"INSERT INTO queue(which, list, comment, to_addresses, from_address, subject, \
message_id, message, timestamp, datetime) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) \

View File

@ -144,6 +144,16 @@ impl PostFilter for AddListHeaders {
let (mut headers, body) = melib::email::parser::mail(&post.bytes).unwrap();
let sender = format!("<{}>", ctx.list.address);
headers.push((&b"Sender"[..], sender.as_bytes()));
let mut subject = format!("[{}] ", ctx.list.id).into_bytes();
if let Some((_, subj_val)) = headers
.iter_mut()
.find(|(k, _)| k.eq_ignore_ascii_case(b"Subject"))
{
subject.extend(subj_val.iter().cloned());
*subj_val = subject.as_slice();
} else {
headers.push((&b"Subject"[..], subject.as_slice()));
}
let list_id = Some(ctx.list.id_header());
let list_help = ctx.list.help_header();
@ -233,9 +243,6 @@ impl PostFilter for FinalizeRecipients {
trace!("Subscription gets copy");
recipients.push(subscription.address());
}
// TODO:
// - check for duplicates (To,Cc,Bcc)
// - send confirmation to submitter
}
ctx.scheduled_jobs.push(MailJob::Send { recipients });
if !digests.is_empty() {

View File

@ -86,7 +86,10 @@ impl Template {
pk: -1,
name: Self::GENERIC_FAILURE.to_string(),
list: None,
subject: Some("Your e-mail was not processed successfully.".to_string()),
subject: Some(
"{{ subject if subject else \"Your e-mail was not processed successfully.\" }}"
.to_string(),
),
headers_json: None,
body: "{{ details|safe if details else \"The list owners and administrators have been \
notified.\" }}"
@ -100,7 +103,10 @@ impl Template {
pk: -1,
name: Self::GENERIC_SUCCESS.to_string(),
list: None,
subject: Some("Your e-mail was processed successfully.".to_string()),
subject: Some(
"{{ subject if subject else \"Your e-mail was processed successfully.\" }}"
.to_string(),
),
headers_json: None,
body: "{{ details|safe if details else \"\" }}".to_string(),
}

View File

@ -158,7 +158,7 @@ impl Handler for MyHandler {
self.stored.lock().unwrap().push((to.clone(), env));
}
Err(err) => {
eprintln!("envelope parse error {}", err);
panic!("envelope parse error {}", err);
}
}
}
@ -168,8 +168,7 @@ impl Handler for MyHandler {
.push(((ip, domain), Message::Helo));
return OK;
}
log::error!("last self.mails item was not Message::Data: {last:?}");
INTERNAL_ERROR
panic!("last self.mails item was not Message::Data: {last:?}"); //INTERNAL_ERROR
}
}
@ -313,7 +312,23 @@ fn test_smtp() {
.unwrap();
}
}));
assert_eq!(handler.stored.lock().unwrap().len(), 2);
let stored = handler.stored.lock().unwrap();
assert_eq!(stored.len(), 3);
assert_eq!(&stored[0].0, "japoeunp@example.com");
assert_eq!(
&stored[0].1.subject(),
"Your post to foo-chat was rejected."
);
assert_eq!(
&stored[1].1.subject(),
"[foo-chat] thankful that I had the chance to written report, that I could learn and let \
alone the chance $4454.32"
);
assert_eq!(
&stored[2].1.subject(),
"[foo-chat] thankful that I had the chance to written report, that I could learn and let \
alone the chance $4454.32"
);
}
#[test]