@@ -19,8 +19,8 @@ use crate::chatlist::Chatlist;
19
19
use crate :: color:: str_to_color;
20
20
use crate :: config:: Config ;
21
21
use crate :: constants:: {
22
- Blocked , Chattype , DC_CHAT_ID_ALLDONE_HINT , DC_CHAT_ID_ARCHIVED_LINK , DC_CHAT_ID_LAST_SPECIAL ,
23
- DC_CHAT_ID_TRASH , DC_RESEND_USER_AVATAR_DAYS ,
22
+ self , Blocked , Chattype , DC_CHAT_ID_ALLDONE_HINT , DC_CHAT_ID_ARCHIVED_LINK ,
23
+ DC_CHAT_ID_LAST_SPECIAL , DC_CHAT_ID_TRASH , DC_RESEND_USER_AVATAR_DAYS ,
24
24
} ;
25
25
use crate :: contact:: { self , Contact , ContactAddress , ContactId , Origin } ;
26
26
use crate :: context:: Context ;
@@ -2662,17 +2662,20 @@ pub async fn send_msg(context: &Context, chat_id: ChatId, msg: &mut Message) ->
2662
2662
2663
2663
/// Tries to send a message synchronously.
2664
2664
///
2665
- /// Creates a new message in `smtp` table, then drectly opens an SMTP connection and sends the
2666
- /// message. If this fails, the message remains in the database to be sent later.
2665
+ /// Creates jobs in the `smtp` table, then drectly opens an SMTP connection and sends the
2666
+ /// message. If this fails, the jobs remain in the database for later sending .
2667
2667
pub async fn send_msg_sync ( context : & Context , chat_id : ChatId , msg : & mut Message ) -> Result < MsgId > {
2668
- if let Some ( rowid) = prepare_send_msg ( context, chat_id, msg) . await ? {
2669
- let mut smtp = crate :: smtp:: Smtp :: new ( ) ;
2668
+ let rowids = prepare_send_msg ( context, chat_id, msg) . await ?;
2669
+ if rowids. is_empty ( ) {
2670
+ return Ok ( msg. id ) ;
2671
+ }
2672
+ let mut smtp = crate :: smtp:: Smtp :: new ( ) ;
2673
+ for rowid in rowids {
2670
2674
send_msg_to_smtp ( context, & mut smtp, rowid)
2671
2675
. await
2672
2676
. context ( "failed to send message, queued for later sending" ) ?;
2673
-
2674
- context. emit_msgs_changed ( msg. chat_id , msg. id ) ;
2675
2677
}
2678
+ context. emit_msgs_changed ( msg. chat_id , msg. id ) ;
2676
2679
Ok ( msg. id )
2677
2680
}
2678
2681
@@ -2682,7 +2685,7 @@ async fn send_msg_inner(context: &Context, chat_id: ChatId, msg: &mut Message) -
2682
2685
msg. text = strip_rtlo_characters ( & msg. text ) ;
2683
2686
}
2684
2687
2685
- if prepare_send_msg ( context, chat_id, msg) . await ?. is_some ( ) {
2688
+ if ! prepare_send_msg ( context, chat_id, msg) . await ?. is_empty ( ) {
2686
2689
context. emit_msgs_changed ( msg. chat_id , msg. id ) ;
2687
2690
2688
2691
if msg. param . exists ( Param :: SetLatitude ) {
@@ -2695,12 +2698,12 @@ async fn send_msg_inner(context: &Context, chat_id: ChatId, msg: &mut Message) -
2695
2698
Ok ( msg. id )
2696
2699
}
2697
2700
2698
- /// Returns rowid from `smtp` table.
2701
+ /// Returns row ids of the `smtp` table.
2699
2702
async fn prepare_send_msg (
2700
2703
context : & Context ,
2701
2704
chat_id : ChatId ,
2702
2705
msg : & mut Message ,
2703
- ) -> Result < Option < i64 > > {
2706
+ ) -> Result < Vec < i64 > > {
2704
2707
// prepare_msg() leaves the message state to OutPreparing, we
2705
2708
// only have to change the state to OutPending in this case.
2706
2709
// Otherwise we still have to prepare the message, which will set
@@ -2716,20 +2719,16 @@ async fn prepare_send_msg(
2716
2719
) ;
2717
2720
message:: update_msg_state ( context, msg. id , MessageState :: OutPending ) . await ?;
2718
2721
}
2719
- let row_id = create_send_msg_job ( context, msg) . await ?;
2720
- Ok ( row_id)
2722
+ create_send_msg_jobs ( context, msg) . await
2721
2723
}
2722
2724
2723
- /// Constructs a job for sending a message and inserts into `smtp` table.
2725
+ /// Constructs jobs for sending a message and inserts them into the `smtp` table.
2724
2726
///
2725
- /// Returns rowid if job was created or `None` if SMTP job is not needed , e.g. when sending to a
2727
+ /// Returns row ids if jobs were created or an empty `Vec` otherwise , e.g. when sending to a
2726
2728
/// group with only self and no BCC-to-self configured.
2727
2729
///
2728
- /// The caller has to interrupt SMTP loop or otherwise process a new row.
2729
- pub ( crate ) async fn create_send_msg_job (
2730
- context : & Context ,
2731
- msg : & mut Message ,
2732
- ) -> Result < Option < i64 > > {
2730
+ /// The caller has to interrupt SMTP loop or otherwise process new rows.
2731
+ pub ( crate ) async fn create_send_msg_jobs ( context : & Context , msg : & mut Message ) -> Result < Vec < i64 > > {
2733
2732
let needs_encryption = msg. param . get_bool ( Param :: GuaranteeE2ee ) . unwrap_or_default ( ) ;
2734
2733
2735
2734
let attach_selfavatar = match shall_attach_selfavatar ( context, msg. chat_id ) . await {
@@ -2748,7 +2747,7 @@ pub(crate) async fn create_send_msg_job(
2748
2747
let lowercase_from = from. to_lowercase ( ) ;
2749
2748
2750
2749
// Send BCC to self if it is enabled and we are not going to
2751
- // delete it immediately.
2750
+ // delete it immediately. `from` must be the last addr, see `receive_imf_inner()` why.
2752
2751
if context. get_config_bool ( Config :: BccSelf ) . await ?
2753
2752
&& context. get_config_delete_server_after ( ) . await ? != Some ( 0 )
2754
2753
&& !recipients
@@ -2766,7 +2765,7 @@ pub(crate) async fn create_send_msg_job(
2766
2765
) ;
2767
2766
msg. id . set_delivered ( context) . await ?;
2768
2767
msg. state = MessageState :: OutDelivered ;
2769
- return Ok ( None ) ;
2768
+ return Ok ( Vec :: new ( ) ) ;
2770
2769
}
2771
2770
2772
2771
let rendered_msg = match mimefactory. render ( context) . await {
@@ -2826,27 +2825,32 @@ pub(crate) async fn create_send_msg_job(
2826
2825
msg. update_param ( context) . await ?;
2827
2826
}
2828
2827
2829
- ensure ! ( !recipients. is_empty( ) , "no recipients for smtp job set" ) ;
2830
-
2831
- let recipients = recipients. join ( " " ) ;
2832
-
2833
2828
msg. subject = rendered_msg. subject . clone ( ) ;
2834
2829
msg. update_subject ( context) . await ?;
2835
-
2836
- let row_id = context
2837
- . sql
2838
- . insert (
2839
- "INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id)
2840
- VALUES (?1, ?2, ?3, ?4)" ,
2841
- (
2842
- & rendered_msg. rfc724_mid ,
2843
- recipients,
2844
- & rendered_msg. message ,
2845
- msg. id ,
2846
- ) ,
2847
- )
2848
- . await ?;
2849
- Ok ( Some ( row_id) )
2830
+ let chunk_size = context
2831
+ . get_configured_provider ( )
2832
+ . await ?
2833
+ . and_then ( |provider| provider. opt . max_smtp_rcpt_to )
2834
+ . map_or ( constants:: DEFAULT_MAX_SMTP_RCPT_TO , usize:: from) ;
2835
+ let trans_fn = |t : & mut rusqlite:: Transaction | {
2836
+ let mut row_ids = Vec :: < i64 > :: new ( ) ;
2837
+ for recipients_chunk in recipients. chunks ( chunk_size) {
2838
+ let recipients_chunk = recipients_chunk. join ( " " ) ;
2839
+ let row_id = t. execute (
2840
+ "INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id) \
2841
+ VALUES (?1, ?2, ?3, ?4)",
2842
+ (
2843
+ & rendered_msg. rfc724_mid ,
2844
+ recipients_chunk,
2845
+ & rendered_msg. message ,
2846
+ msg. id ,
2847
+ ) ,
2848
+ ) ?;
2849
+ row_ids. push ( row_id. try_into ( ) ?) ;
2850
+ }
2851
+ Ok ( row_ids)
2852
+ } ;
2853
+ context. sql . transaction ( trans_fn) . await
2850
2854
}
2851
2855
2852
2856
/// Sends a text message to the given chat.
@@ -4002,7 +4006,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
4002
4006
. prepare_msg_raw ( context, & mut msg, None , curr_timestamp)
4003
4007
. await ?;
4004
4008
curr_timestamp += 1 ;
4005
- if create_send_msg_job ( context, & mut msg) . await ?. is_some ( ) {
4009
+ if ! create_send_msg_jobs ( context, & mut msg) . await ?. is_empty ( ) {
4006
4010
context. scheduler . interrupt_smtp ( ) . await ;
4007
4011
}
4008
4012
}
@@ -4059,7 +4063,7 @@ pub async fn resend_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
4059
4063
chat_id : msg. chat_id ,
4060
4064
msg_id : msg. id ,
4061
4065
} ) ;
4062
- if create_send_msg_job ( context, & mut msg) . await ?. is_some ( ) {
4066
+ if ! create_send_msg_jobs ( context, & mut msg) . await ?. is_empty ( ) {
4063
4067
context. scheduler . interrupt_smtp ( ) . await ;
4064
4068
}
4065
4069
}
0 commit comments