diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index caf95e2c6..4ccb61d51 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -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(). * diff --git a/src/config.rs b/src/config.rs index 1ceff9a08..188e27fb9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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. diff --git a/src/smtp/send.rs b/src/smtp/send.rs index 4ffa64682..1767c70c4 100644 --- a/src/smtp/send.rs +++ b/src/smtp/send.rs @@ -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(()) } }