diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c70a3614..f57253647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ - allow removing quotes on existing drafts (#2950) - update provider database (11 Jan 2022) #2959 - python: allow timeout for internal configure tracker API #2967 +- Replace `SendMsgToSmtp` jobs which stored outgoing messages in blobdir with `smtp` SQL table #2939 #2966 ### Fixed - Fix: Make `add_parts()` not early-exit #2879 diff --git a/src/job.rs b/src/job.rs index 64902e582..4fe35d33c 100644 --- a/src/job.rs +++ b/src/job.rs @@ -318,7 +318,7 @@ impl Job { return Status::RetryLater; } - let status = smtp_send(context, recipients, body, smtp, msg_id, 0).await; + let status = smtp_send(context, &recipients, &body, smtp, msg_id, 0).await; if matches!(status, Status::Finished(Ok(_))) { // Remove additional SendMdn jobs we have aggregated into this one. job_try!(kill_ids(context, &additional_job_ids).await); diff --git a/src/smtp.rs b/src/smtp.rs index 25bc90add..e80650cf8 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -225,10 +225,16 @@ impl Smtp { } } +/// Tries to send a message. +/// +/// Returns Status::Finished if sending the message should not be retried anymore, +/// Status::RetryLater if sending should be postponed and Status::RetryNow if it is suspected that +/// temporary failure is caused by stale connection, in which case a second attempt to send the +/// same message may be done immediately. pub(crate) async fn smtp_send( context: &Context, - recipients: Vec, - message: String, + recipients: &[async_smtp::EmailAddress], + message: &str, smtp: &mut Smtp, msg_id: MsgId, rowid: i64, @@ -241,7 +247,7 @@ pub(crate) async fn smtp_send( smtp.connectivity.set_working(context).await; let send_result = smtp - .send(context, recipients, message.into_bytes(), rowid) + .send(context, recipients, message.as_bytes(), rowid) .await; smtp.last_send_error = send_result.as_ref().err().map(|e| e.to_string()); @@ -362,7 +368,7 @@ pub(crate) async fn send_msg_to_smtp( .await .context("SMTP connection failure") { - smtp.last_send_error = Some(format!("SMTP connection failure: {:#}", err)); + smtp.last_send_error = Some(format!("{:#}", err)); return Err(err); } @@ -392,7 +398,43 @@ pub(crate) async fn send_msg_to_smtp( ) .collect::>(); - let status = smtp_send(context, recipients_list, body, smtp, msg_id, rowid).await; + let status = match smtp_send( + context, + &recipients_list, + body.as_str(), + smtp, + msg_id, + rowid, + ) + .await + { + Status::RetryNow => { + // Do a single retry immediately without increasing retry counter in case of stale + // connection. + info!(context, "Doing immediate retry to send message."); + + // smtp_send just closed stale SMTP connection, reconnect and try again. + if let Err(err) = smtp + .connect_configured(context) + .await + .context("failed to reopen stale SMTP connection") + { + smtp.last_send_error = Some(format!("{:#}", err)); + return Err(err); + } + + smtp_send( + context, + &recipients_list, + body.as_str(), + smtp, + msg_id, + rowid, + ) + .await + } + status => status, + }; match status { Status::Finished(res) => { if res.is_ok() { diff --git a/src/smtp/send.rs b/src/smtp/send.rs index 716e11362..50ecce745 100644 --- a/src/smtp/send.rs +++ b/src/smtp/send.rs @@ -28,8 +28,8 @@ impl Smtp { pub async fn send( &mut self, context: &Context, - recipients: Vec, - message: Vec, + recipients: &[EmailAddress], + message: &[u8], rowid: i64, ) -> Result<()> { let message_len_bytes = message.len(); @@ -53,7 +53,7 @@ impl Smtp { let mail = SendableEmail::new( envelope, rowid.to_string(), // only used for internal logging - &message, + message, ); if let Some(ref mut transport) = self.transport {