send messages via SMTP in configurable chunks

providers may have a maximum for the SMTP RCPT TO: header,
therefore, an outgoing message is split into several chunks.
the chunk size defaults to 50, however, if known to be larger or smaller
by a dedicated provider, this setting may be changed.

this does not affect the MIME To: headers,
if a provider has a maximum here, this is change would not help.
This commit is contained in:
B. Petersen
2020-10-29 22:15:18 +01:00
parent 04a4424664
commit 8232a148aa
3 changed files with 40 additions and 29 deletions

View File

@@ -353,6 +353,10 @@ char* dc_get_blobdir (const dc_context_t* context);
* - `fetch_existing_msgs` = 1=fetch most recent existing messages on configure (default),
* 0=do not fetch existing messages on configure.
* In both cases, existing recipients are added to the contact database.
* - `max_smtp_rcpt_to` = set the max. number of recipients that should be used for `RCPT TO:` smtp header.
* If a message needs to be sent to more recipients, multiple messages are sent out,
* each with exactly the same MIME-message (with `To:` mime headers that may contain all recipients)
* (defaults to 50)
*
* If you want to retrieve a value, use dc_get_config().
*

View File

@@ -69,6 +69,9 @@ pub enum Config {
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
MediaQuality,
#[strum(props(default = "50"))]
MaxSmtpRcptTo,
/// If set to "1", on the first time `start_io()` is called after configuring,
/// the newest existing messages are fetched.
/// Existing recipients are added to the contact database regardless of this setting.

View File

@@ -3,6 +3,7 @@
use super::Smtp;
use async_smtp::*;
use crate::config::Config;
use crate::context::Context;
use crate::events::EventType;
use itertools::Itertools;
@@ -33,38 +34,41 @@ impl Smtp {
job_id: u32,
) -> Result<()> {
let message_len_bytes = message.len();
let chunk_size = context.get_config_int(Config::MaxSmtpRcptTo).await as usize;
let recipients_display = recipients.iter().map(|x| x.to_string()).join(",");
for recipients_chunk in recipients.chunks(chunk_size).into_iter() {
let recipients = recipients_chunk.to_vec();
let recipients_display = recipients.iter().map(|x| x.to_string()).join(",");
let envelope =
Envelope::new(self.from.clone(), recipients).map_err(Error::EnvelopeError)?;
let mail = SendableEmail::new(
envelope,
format!("{}", job_id), // only used for internal logging
message,
);
if let Some(ref mut transport) = self.transport {
// The timeout is 1min + 3min per MB.
let timeout = 60 + (180 * message_len_bytes / 1_000_000) as u64;
transport
.send_with_timeout(mail, Some(&Duration::from_secs(timeout)))
.await
.map_err(Error::SendError)?;
context.emit_event(EventType::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len_bytes, recipients_display
)));
self.last_success = Some(std::time::SystemTime::now());
Ok(())
} else {
warn!(
context,
"uh? SMTP has no transport, failed to send to {}", recipients_display
let envelope =
Envelope::new(self.from.clone(), recipients).map_err(Error::EnvelopeError)?;
let mail = SendableEmail::new(
envelope,
format!("{}", job_id), // only used for internal logging
&message,
);
Err(Error::NoTransport)
if let Some(ref mut transport) = self.transport {
// The timeout is 1min + 3min per MB.
let timeout = 60 + (180 * message_len_bytes / 1_000_000) as u64;
transport
.send_with_timeout(mail, Some(&Duration::from_secs(timeout)))
.await
.map_err(Error::SendError)?;
context.emit_event(EventType::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len_bytes, recipients_display
)));
self.last_success = Some(std::time::SystemTime::now());
} else {
warn!(
context,
"uh? SMTP has no transport, failed to send to {}", recipients_display
);
return Err(Error::NoTransport);
}
}
Ok(())
}
}