mirror of
https://github.com/chatmail/core.git
synced 2026-08-13 04:42:05 +03:00
Compare commits
2 Commits
v2.57.0
...
iequidoo/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e14f01f866 | ||
|
|
3a36b0beb9 |
@@ -251,12 +251,7 @@ def test_realtime_large_webxdc(acfactory, path_to_large_webxdc):
|
||||
ac1_ac2_chat = ac1.create_chat(ac2)
|
||||
ac1_webxdc_msg = ac1_ac2_chat.send_message(text="realtime check", file=path_to_large_webxdc)
|
||||
|
||||
# Receive pre-message.
|
||||
ac2_webxdc_msg = ac2.wait_for_incoming_msg()
|
||||
|
||||
# Receive post-message.
|
||||
ac2_webxdc_msg = ac2.wait_for_msg(EventType.MSGS_CHANGED)
|
||||
|
||||
ac2_webxdc_msg.send_webxdc_realtime_advertisement()
|
||||
event = ac1.wait_for_event(EventType.WEBXDC_REALTIME_ADVERTISEMENT_RECEIVED)
|
||||
assert event.msg_id == ac1_webxdc_msg.id
|
||||
|
||||
@@ -926,10 +926,6 @@ def test_delete_fully_downloaded_msg(acfactory, tmp_path, direct_imap):
|
||||
|
||||
msg = bob.wait_for_incoming_msg()
|
||||
msg_snapshot = msg.get_snapshot()
|
||||
assert msg_snapshot.download_state == DownloadState.AVAILABLE
|
||||
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
||||
assert msgs_changed_event.msg_id == msg.id
|
||||
msg_snapshot = msg.get_snapshot()
|
||||
assert msg_snapshot.download_state == DownloadState.DONE
|
||||
|
||||
bob_direct_imap = direct_imap(bob)
|
||||
@@ -960,10 +956,6 @@ def test_imap_autodelete_fully_downloaded_msg(acfactory, tmp_path, direct_imap):
|
||||
|
||||
msg = bob.wait_for_incoming_msg()
|
||||
msg_snapshot = msg.get_snapshot()
|
||||
assert msg_snapshot.download_state == DownloadState.AVAILABLE
|
||||
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
||||
assert msgs_changed_event.msg_id == msg.id
|
||||
msg_snapshot = msg.get_snapshot()
|
||||
assert msg_snapshot.download_state == DownloadState.DONE
|
||||
|
||||
bob_direct_imap = direct_imap(bob)
|
||||
@@ -1443,7 +1435,5 @@ def test_large_message(acfactory) -> None:
|
||||
)
|
||||
|
||||
msg = bob.wait_for_incoming_msg()
|
||||
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
||||
assert msg.id == msgs_changed_event.msg_id
|
||||
snapshot = msg.get_snapshot()
|
||||
assert snapshot.text == "Hello World, this message is bigger than 5 bytes"
|
||||
|
||||
22
src/chat.rs
22
src/chat.rs
@@ -704,8 +704,7 @@ SELECT id, rfc724_mid, pre_rfc724_mid, timestamp, ?, 1 FROM msgs WHERE chat_id=?
|
||||
context
|
||||
.set_config_internal(Config::LastHousekeeping, None)
|
||||
.await?;
|
||||
context.scheduler.interrupt_smtp().await;
|
||||
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2987,15 +2986,6 @@ WHERE id=?
|
||||
)?;
|
||||
for recipients_chunk in recipients.chunks(chunk_size) {
|
||||
let recipients_chunk = recipients_chunk.join(" ");
|
||||
if let Some(pre_msg) = &rendered_pre_msg {
|
||||
let row_id = stmt.execute((
|
||||
&pre_msg.rfc724_mid,
|
||||
&recipients_chunk,
|
||||
&pre_msg.message,
|
||||
msg.id,
|
||||
))?;
|
||||
row_ids.push(row_id.try_into()?);
|
||||
}
|
||||
let row_id = stmt.execute((
|
||||
&rendered_msg.rfc724_mid,
|
||||
&recipients_chunk,
|
||||
@@ -3003,6 +2993,16 @@ WHERE id=?
|
||||
msg.id,
|
||||
))?;
|
||||
row_ids.push(row_id.try_into()?);
|
||||
let Some(pre_msg) = &rendered_pre_msg else {
|
||||
continue;
|
||||
};
|
||||
let row_id = stmt.execute((
|
||||
&pre_msg.rfc724_mid,
|
||||
&recipients_chunk,
|
||||
&pre_msg.message,
|
||||
msg.id,
|
||||
))?;
|
||||
row_ids.push(row_id.try_into()?);
|
||||
}
|
||||
Ok(row_ids)
|
||||
};
|
||||
|
||||
@@ -160,7 +160,6 @@ pub(crate) async fn download_msg(
|
||||
|
||||
let Some((server_uid, server_folder, msg_transport_id)) = row else {
|
||||
// No IMAP record found, we don't know the UID and folder.
|
||||
delete_from_available_post_msgs(context, &rfc724_mid).await?;
|
||||
return Err(anyhow!(
|
||||
"IMAP location for {rfc724_mid:?} post-message is unknown"
|
||||
));
|
||||
@@ -234,31 +233,6 @@ async fn set_state_to_failure(context: &Context, rfc724_mid: &str) -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn available_post_msgs_contains_rfc724_mid(
|
||||
context: &Context,
|
||||
rfc724_mid: &str,
|
||||
) -> Result<bool> {
|
||||
Ok(context
|
||||
.sql
|
||||
.query_get_value::<String>(
|
||||
"SELECT rfc724_mid FROM available_post_msgs WHERE rfc724_mid=?",
|
||||
(&rfc724_mid,),
|
||||
)
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
async fn delete_from_available_post_msgs(context: &Context, rfc724_mid: &str) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"DELETE FROM available_post_msgs WHERE rfc724_mid=?",
|
||||
(&rfc724_mid,),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_from_downloads(context: &Context, rfc724_mid: &str) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
@@ -286,7 +260,6 @@ pub(crate) async fn download_msgs(context: &Context, session: &mut Session) -> R
|
||||
let res = download_msg(context, rfc724_mid.clone(), session).await;
|
||||
if let Ok(Some(())) = res {
|
||||
delete_from_downloads(context, rfc724_mid).await?;
|
||||
delete_from_available_post_msgs(context, rfc724_mid).await?;
|
||||
}
|
||||
if let Err(err) = res {
|
||||
warn!(
|
||||
@@ -300,18 +273,13 @@ pub(crate) async fn download_msgs(context: &Context, session: &mut Session) -> R
|
||||
"{rfc724_mid} download failed and there is no downloaded pre-message."
|
||||
);
|
||||
delete_from_downloads(context, rfc724_mid).await?;
|
||||
} else if available_post_msgs_contains_rfc724_mid(context, rfc724_mid).await? {
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"{rfc724_mid} is in available_post_msgs table but we failed to fetch it,
|
||||
so set the message to DownloadState::Failure - probably it was deleted on the server in the meantime"
|
||||
"Failed to fetch {rfc724_mid}, setting DownloadState to Failure."
|
||||
);
|
||||
set_state_to_failure(context, rfc724_mid).await?;
|
||||
delete_from_downloads(context, rfc724_mid).await?;
|
||||
delete_from_available_post_msgs(context, rfc724_mid).await?;
|
||||
} else {
|
||||
// leave the message in DownloadState::InProgress;
|
||||
// it will be downloaded once it arrives.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,44 +287,6 @@ pub(crate) async fn download_msgs(context: &Context, session: &mut Session) -> R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downloads known post-messages without pre-messages
|
||||
/// in order to guard against lost pre-messages.
|
||||
pub(crate) async fn download_known_post_messages_without_pre_message(
|
||||
context: &Context,
|
||||
session: &mut Session,
|
||||
) -> Result<()> {
|
||||
let rfc724_mids = context
|
||||
.sql
|
||||
.query_map_vec("SELECT rfc724_mid FROM available_post_msgs", (), |row| {
|
||||
let rfc724_mid: String = row.get(0)?;
|
||||
Ok(rfc724_mid)
|
||||
})
|
||||
.await?;
|
||||
for rfc724_mid in &rfc724_mids {
|
||||
if msg_is_downloaded_for(context, rfc724_mid).await? {
|
||||
delete_from_available_post_msgs(context, rfc724_mid).await?;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download the Post-Message unconditionally,
|
||||
// because the Pre-Message got lost.
|
||||
// The message may be in the wrong order,
|
||||
// but at least we have it at all.
|
||||
let res = download_msg(context, rfc724_mid.clone(), session).await;
|
||||
if let Ok(Some(())) = res {
|
||||
delete_from_available_post_msgs(context, rfc724_mid).await?;
|
||||
}
|
||||
if let Err(err) = res {
|
||||
warn!(
|
||||
context,
|
||||
"download_known_post_messages_without_pre_message: Failed to download message rfc724_mid={rfc724_mid}: {:#}.",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
15
src/imap.rs
15
src/imap.rs
@@ -607,7 +607,6 @@ impl Imap {
|
||||
let _fetch_msgs_lock_guard = context.fetch_msgs_mutex.lock().await;
|
||||
|
||||
let mut uids_fetch: Vec<u32> = Vec::new();
|
||||
let mut available_post_msgs: Vec<String> = Vec::new();
|
||||
let mut download_later: Vec<String> = Vec::new();
|
||||
let mut uid_message_ids = BTreeMap::new();
|
||||
let mut largest_uid_skipped = None;
|
||||
@@ -695,8 +694,6 @@ impl Imap {
|
||||
.is_some()
|
||||
{
|
||||
info!(context, "{message_id:?} is a post-message.");
|
||||
available_post_msgs.push(message_id.clone());
|
||||
|
||||
let is_bot = context.get_config_bool(Config::Bot).await?;
|
||||
if is_bot && download_limit.is_none_or(|download_limit| size <= download_limit)
|
||||
{
|
||||
@@ -792,18 +789,8 @@ impl Imap {
|
||||
chat::mark_old_messages_as_noticed(context, received_msgs).await?;
|
||||
|
||||
if fetch_res.is_ok() {
|
||||
info!(
|
||||
context,
|
||||
"available_post_msgs: {}, download_later: {}.",
|
||||
available_post_msgs.len(),
|
||||
download_later.len(),
|
||||
);
|
||||
info!(context, "download_later: {}.", download_later.len(),);
|
||||
let trans_fn = |t: &mut rusqlite::Transaction| {
|
||||
let mut stmt = t.prepare("INSERT OR IGNORE INTO available_post_msgs VALUES (?)")?;
|
||||
for rfc724_mid in available_post_msgs {
|
||||
stmt.execute((rfc724_mid,))
|
||||
.context("INSERT OR IGNORE INTO available_post_msgs")?;
|
||||
}
|
||||
let mut stmt =
|
||||
t.prepare("INSERT OR IGNORE INTO download (rfc724_mid, msg_id) VALUES (?,0)")?;
|
||||
for rfc724_mid in download_later {
|
||||
|
||||
@@ -1723,10 +1723,6 @@ pub async fn delete_msgs_ex(
|
||||
"DELETE FROM download WHERE rfc724_mid=?",
|
||||
(&msg.rfc724_mid,),
|
||||
)?;
|
||||
trans.execute(
|
||||
"DELETE FROM available_post_msgs WHERE rfc724_mid=?",
|
||||
(&msg.rfc724_mid,),
|
||||
)?;
|
||||
Ok(())
|
||||
};
|
||||
if let Err(e) = context.sql.transaction(update_db).await {
|
||||
|
||||
@@ -1702,23 +1702,19 @@ impl MimeFactory {
|
||||
|
||||
let footer = if is_reaction { "" } else { &self.selfstatus };
|
||||
|
||||
let message_text = if self.pre_message_mode == PreMessageMode::Post {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"{}{}{}{}{}{}",
|
||||
fwdhint.unwrap_or_default(),
|
||||
quoted_text.unwrap_or_default(),
|
||||
escape_message_footer_marks(final_text),
|
||||
if !final_text.is_empty() && !footer.is_empty() {
|
||||
"\r\n\r\n"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if !footer.is_empty() { "-- \r\n" } else { "" },
|
||||
footer
|
||||
)
|
||||
};
|
||||
let message_text = format!(
|
||||
"{}{}{}{}{}{}",
|
||||
fwdhint.unwrap_or_default(),
|
||||
quoted_text.unwrap_or_default(),
|
||||
escape_message_footer_marks(final_text),
|
||||
if !final_text.is_empty() && !footer.is_empty() {
|
||||
"\r\n\r\n"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if !footer.is_empty() { "-- \r\n" } else { "" },
|
||||
footer
|
||||
);
|
||||
|
||||
let mut main_part = MimePart::new("text/plain", message_text);
|
||||
if is_reaction {
|
||||
|
||||
@@ -533,19 +533,10 @@ pub(crate) async fn receive_imf_inner(
|
||||
"Receiving message {rfc724_mid_orig:?}, seen={seen}...",
|
||||
);
|
||||
|
||||
// These checks must be done before processing of SecureJoin and other special messages.
|
||||
if mime_parser.pre_message == mimeparser::PreMessageMode::Post {
|
||||
// Post-Message just replaces the attachment and modifies Params, not the whole message.
|
||||
// This is done in the `handle_post_message` method.
|
||||
} else if let Some(msg_id) = message::rfc724_mid_exists(context, rfc724_mid_orig).await? {
|
||||
info!(
|
||||
context,
|
||||
"Message {rfc724_mid} is already in some chat or deleted."
|
||||
);
|
||||
if mime_parser.incoming {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let existing_msg_id = message::rfc724_mid_exists(context, rfc724_mid_orig).await?;
|
||||
if let Some(msg_id) = existing_msg_id
|
||||
&& !mime_parser.incoming
|
||||
{
|
||||
// It sometimes happens that a slow server (usually a classical email server)
|
||||
// receives a message via SMTP,
|
||||
// but then the connection to the server dies before it sends the OK response.
|
||||
@@ -566,6 +557,16 @@ pub(crate) async fn receive_imf_inner(
|
||||
if !msg_has_pending_smtp_job(context, msg_id).await? {
|
||||
msg_id.set_delivered(context).await?;
|
||||
}
|
||||
}
|
||||
// These checks must be done before processing of SecureJoin and other special messages.
|
||||
if mime_parser.pre_message == mimeparser::PreMessageMode::Post {
|
||||
// Post-Message just replaces the attachment and modifies Params, not the whole message.
|
||||
// This is done in the `update_from_post_msg` method.
|
||||
} else if existing_msg_id.is_some() {
|
||||
info!(
|
||||
context,
|
||||
"Message {rfc724_mid} is already in some chat or deleted."
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -2088,7 +2089,7 @@ async fn add_parts(
|
||||
}
|
||||
|
||||
handle_edit_delete(context, mime_parser, from_id, &mime_headers).await?;
|
||||
handle_post_message(context, mime_parser, from_id, state).await?;
|
||||
update_from_post_msg(context, mime_parser, from_id, state).await?;
|
||||
|
||||
if mime_parser.is_system_message == SystemMessage::CallAccepted
|
||||
|| mime_parser.is_system_message == SystemMessage::CallEnded
|
||||
@@ -2282,8 +2283,7 @@ INSERT INTO msgs
|
||||
|
||||
// Maybe set logging xdc and add gossip topics for webxdcs.
|
||||
for (part, msg_id) in mime_parser.parts.iter().zip(&created_db_entries) {
|
||||
if mime_parser.pre_message != PreMessageMode::Post
|
||||
&& part.typ == Viewtype::Webxdc
|
||||
if part.typ == Viewtype::Webxdc
|
||||
&& let Some(topic) = mime_parser.get_header(HeaderDef::IrohGossipTopic)
|
||||
{
|
||||
let topic = iroh_topic_from_str(topic)?;
|
||||
@@ -2433,7 +2433,7 @@ async fn handle_edit_delete(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_post_message(
|
||||
async fn update_from_post_msg(
|
||||
context: &Context,
|
||||
mime_parser: &MimeMessage,
|
||||
from_id: ContactId,
|
||||
@@ -2451,7 +2451,7 @@ async fn handle_post_message(
|
||||
let Some(msg_id) = message::rfc724_mid_exists(context, &rfc724_mid).await? else {
|
||||
warn!(
|
||||
context,
|
||||
"handle_post_message: {rfc724_mid}: Database entry does not exist."
|
||||
"update_from_post_msg: {rfc724_mid}: Database entry does not exist."
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
@@ -2459,7 +2459,7 @@ async fn handle_post_message(
|
||||
// else: message is processed like a normal message
|
||||
warn!(
|
||||
context,
|
||||
"handle_post_message: {rfc724_mid}: Pre-message was not downloaded yet so treat as normal message."
|
||||
"update_from_post_msg: {rfc724_mid}: Pre-message was not downloaded yet so treat as normal message."
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
@@ -2470,7 +2470,7 @@ async fn handle_post_message(
|
||||
// Do nothing if safety checks fail, the worst case is the message modifies the chat if the
|
||||
// sender is a member.
|
||||
if from_id != original_msg.from_id {
|
||||
warn!(context, "handle_post_message: {rfc724_mid}: Bad sender.");
|
||||
warn!(context, "update_from_post_msg: {rfc724_mid}: Bad sender.");
|
||||
return Ok(());
|
||||
}
|
||||
let post_msg_showpadlock = part
|
||||
@@ -2478,14 +2478,17 @@ async fn handle_post_message(
|
||||
.get_bool(Param::GuaranteeE2ee)
|
||||
.unwrap_or_default();
|
||||
if !post_msg_showpadlock && original_msg.get_showpadlock() {
|
||||
warn!(context, "handle_post_message: {rfc724_mid}: Not encrypted.");
|
||||
warn!(
|
||||
context,
|
||||
"update_from_post_msg: {rfc724_mid}: Not encrypted."
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !part.typ.has_file() {
|
||||
warn!(
|
||||
context,
|
||||
"handle_post_message: {rfc724_mid}: First mime part's message-viewtype has no file."
|
||||
"update_from_post_msg: {rfc724_mid}: First mime part's message-viewtype has no file."
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -2516,7 +2519,10 @@ WHERE id=?
|
||||
part.typ,
|
||||
part.bytes as isize,
|
||||
part.error.as_deref().unwrap_or_default(),
|
||||
state,
|
||||
match mime_parser.incoming {
|
||||
true => state,
|
||||
false => MessageState::Undefined,
|
||||
},
|
||||
DownloadState::Done as u32,
|
||||
original_msg.id,
|
||||
),
|
||||
|
||||
@@ -5696,27 +5696,27 @@ async fn test_mark_message_as_delivered_only_after_sent_out_fully() -> Result<()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (pre_msg_id, pre_msg_payload) = first_row_in_smtp_queue(alice).await;
|
||||
assert_eq!(msg_id, pre_msg_id);
|
||||
assert!(pre_msg_payload.len() < file_bytes.len());
|
||||
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
|
||||
// Alice receives her own pre-message because of bcc_self
|
||||
// This should not yet mark the message as delivered,
|
||||
// because not everything was sent,
|
||||
// but it does remove the pre-message from the SMTP queue
|
||||
receive_imf(alice, pre_msg_payload.as_bytes(), false).await?;
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
|
||||
|
||||
let (post_msg_id, post_msg_payload) = first_row_in_smtp_queue(alice).await;
|
||||
assert_eq!(msg_id, post_msg_id);
|
||||
assert!(post_msg_payload.len() > file_bytes.len());
|
||||
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
|
||||
// Alice receives her own post-message because of bcc_self
|
||||
// This should not yet mark the message as delivered,
|
||||
// because not everything was sent,
|
||||
// but it does remove the post-message from the SMTP queue.
|
||||
receive_imf(alice, post_msg_payload.as_bytes(), false).await?;
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
|
||||
|
||||
let (pre_msg_id, pre_msg_payload) = first_row_in_smtp_queue(alice).await;
|
||||
assert_eq!(msg_id, pre_msg_id);
|
||||
assert!(pre_msg_payload.len() < file_bytes.len());
|
||||
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
|
||||
// Alice receives her own pre-message because of bcc_self
|
||||
// This should now mark the message as delivered,
|
||||
// because everything was sent by now.
|
||||
receive_imf(alice, post_msg_payload.as_bytes(), false).await?;
|
||||
receive_imf(alice, pre_msg_payload.as_bytes(), false).await?;
|
||||
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutDelivered);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -14,7 +14,7 @@ pub(crate) use self::connectivity::ConnectivityStore;
|
||||
use crate::config::Config;
|
||||
use crate::contact::{ContactId, RecentlySeenLoop};
|
||||
use crate::context::Context;
|
||||
use crate::download::{download_known_post_messages_without_pre_message, download_msgs};
|
||||
use crate::download::download_msgs;
|
||||
use crate::ephemeral;
|
||||
use crate::events::EventType;
|
||||
use crate::imap::{Imap, session::Session};
|
||||
@@ -486,7 +486,6 @@ async fn fetch_idle(ctx: &Context, connection: &mut Imap, mut session: Session)
|
||||
.await
|
||||
.context("fetch_move_delete")?;
|
||||
|
||||
download_known_post_messages_without_pre_message(ctx, &mut session).await?;
|
||||
download_msgs(ctx, &mut session)
|
||||
.await
|
||||
.context("download_msgs")?;
|
||||
|
||||
@@ -379,7 +379,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
if retries > 6 {
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp WHERE id=?", (rowid,))
|
||||
.execute("DELETE FROM smtp WHERE msg_id=?", (msg_id,))
|
||||
.await
|
||||
.context("Failed to remove message with exceeded retry limit from smtp table")?;
|
||||
if let Some(mut msg) = Message::load_from_db_optional(context, msg_id).await? {
|
||||
@@ -457,7 +457,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
}
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp WHERE id=?", (rowid,))
|
||||
.execute("DELETE FROM smtp WHERE msg_id=?", (msg_id,))
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -737,12 +737,14 @@ ORDER BY id"
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `SentMessage` instances representing `smtp` rows for the given message. Returned
|
||||
/// items go in reverse order for historical reasons.
|
||||
pub async fn get_smtp_rows_for_msg<'a>(&'a self, msg_id: MsgId) -> Vec<SentMessage<'a>> {
|
||||
let sent_msgs = self
|
||||
.ctx
|
||||
.sql
|
||||
.query_map_vec(
|
||||
"SELECT id, msg_id, mime, recipients FROM smtp WHERE msg_id=?",
|
||||
"SELECT id, msg_id, mime, recipients FROM smtp WHERE msg_id=? ORDER BY id DESC",
|
||||
(msg_id,),
|
||||
|row| {
|
||||
let _id: MsgId = row.get(0)?;
|
||||
@@ -1070,9 +1072,14 @@ ORDER BY id"
|
||||
/// This is not hooked up to any SMTP-IMAP pipeline, so the other account must call
|
||||
/// [`TestContext::recv_msg`] with the returned [`SentMessage`] if it wants to receive
|
||||
/// the message.
|
||||
///
|
||||
/// Removes SMTP jobs existed before and marks the corresponding messages as delivered, as
|
||||
/// tracking of these jobs is probably already lost by the test code.
|
||||
pub async fn send_msg(&self, chat_id: ChatId, msg: &mut Message) -> SentMessage<'_> {
|
||||
while self.pop_sent_msg_opt().await.is_some() {}
|
||||
let msg_id = chat::send_msg(self, chat_id, msg).await.unwrap();
|
||||
let res = self.pop_sent_msg().await;
|
||||
let rev_order = false;
|
||||
let res = self.pop_sent_msg_ex(rev_order).await.unwrap();
|
||||
assert_eq!(
|
||||
res.sender_msg_id, msg_id,
|
||||
"Apparently the message was not actually sent out"
|
||||
|
||||
@@ -105,7 +105,7 @@ async fn test_receive_both() -> Result<()> {
|
||||
assert_eq!(msg.text, "test".to_owned());
|
||||
|
||||
forward_msgs(alice, &[alice_msg_id], alice_chat_id).await?;
|
||||
let rev_order = false;
|
||||
let rev_order = true;
|
||||
let msg = bob
|
||||
.recv_msg(&alice.pop_sent_msg_ex(rev_order).await.unwrap())
|
||||
.await;
|
||||
|
||||
@@ -257,21 +257,21 @@ async fn test_lost_pre_msg() -> Result<()> {
|
||||
let _pre_msg = alice.pop_sent_msg().await;
|
||||
let msg = bob.recv_msg(&full_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Done);
|
||||
assert_eq!(msg.text, "");
|
||||
assert_eq!(msg.text, "populate");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pre_msg_mdn_before_sending_full() -> Result<()> {
|
||||
pre_msg_mdn_before_sending_full("").await
|
||||
async fn test_post_msg_mdn_before_sending_pre_msg() -> Result<()> {
|
||||
post_msg_mdn_before_sending_pre_msg("").await
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pre_msg_mdn_before_sending_full_with_text() -> Result<()> {
|
||||
pre_msg_mdn_before_sending_full("text").await
|
||||
async fn test_post_msg_mdn_before_sending_pre_msg_with_text() -> Result<()> {
|
||||
post_msg_mdn_before_sending_pre_msg("text").await
|
||||
}
|
||||
|
||||
async fn pre_msg_mdn_before_sending_full(text: &str) -> Result<()> {
|
||||
async fn post_msg_mdn_before_sending_pre_msg(text: &str) -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
@@ -283,11 +283,11 @@ async fn pre_msg_mdn_before_sending_full(text: &str) -> Result<()> {
|
||||
msg.set_text(text.to_string());
|
||||
chat::send_msg(alice, alice_chat_id, &mut msg).await?;
|
||||
let rev_order = false;
|
||||
let pre_msg = alice.pop_sent_msg_ex(rev_order).await.unwrap();
|
||||
let post_msg = alice.pop_sent_msg_ex(rev_order).await.unwrap();
|
||||
let alice_msg_id = msg.id;
|
||||
|
||||
let msg = bob.recv_msg(&pre_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Available);
|
||||
let msg = bob.recv_msg(&post_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Done);
|
||||
assert_eq!(msg.id.get_state(bob).await?, MessageState::InFresh);
|
||||
assert_eq!(msg.text, text);
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
|
||||
@@ -308,11 +308,30 @@ async fn pre_msg_mdn_before_sending_full(text: &str) -> Result<()> {
|
||||
MessageState::OutPending
|
||||
);
|
||||
|
||||
let _full_msg = alice.pop_sent_msg().await;
|
||||
// Bob's other device receives the pre-message first.
|
||||
let bob = &tcm.bob().await;
|
||||
let pre_msg = alice.pop_sent_msg().await;
|
||||
assert_eq!(
|
||||
alice_msg_id.get_state(alice).await?,
|
||||
MessageState::OutDelivered
|
||||
);
|
||||
let msg = bob.recv_msg(&pre_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Available);
|
||||
assert_eq!(msg.id.get_state(bob).await?, MessageState::InFresh);
|
||||
assert_eq!(msg.text, text);
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
|
||||
msg.chat_id.accept(bob).await?;
|
||||
markseen_msgs(bob, vec![msg.id]).await?;
|
||||
assert_eq!(msg.id.get_state(bob).await?, MessageState::InSeen);
|
||||
assert_eq!(
|
||||
bob.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM smtp_mdns WHERE from_id=?",
|
||||
(msg.from_id,)
|
||||
)
|
||||
.await?,
|
||||
1
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -613,8 +632,8 @@ async fn test_webxdc_updates_in_post_message_after_pre_message() -> Result<()> {
|
||||
.await?;
|
||||
|
||||
send_msg(alice, alice_chat_id, &mut alice_instance).await?;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
let pre_message = alice.pop_sent_msg().await;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
|
||||
let bob_instance = bob.recv_msg(&pre_message).await;
|
||||
assert_eq!(bob_instance.download_state, DownloadState::Available);
|
||||
@@ -659,8 +678,8 @@ async fn test_large_webxdc_without_text() -> Result<()> {
|
||||
.await?;
|
||||
|
||||
send_msg(bob, bob_chat_id, &mut bob_instance).await?;
|
||||
let post_message = bob.pop_sent_msg().await;
|
||||
let pre_message = bob.pop_sent_msg().await;
|
||||
let post_message = bob.pop_sent_msg().await;
|
||||
|
||||
tcm.section("Alice receives a pre-message");
|
||||
let alice_instance = alice.recv_msg(&pre_message).await;
|
||||
@@ -704,8 +723,8 @@ async fn test_webxdc_updates_in_post_message_after_deleted_pre_message() -> Resu
|
||||
.await?;
|
||||
|
||||
send_msg(alice, alice_chat_id, &mut alice_instance).await?;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
let pre_message = alice.pop_sent_msg().await;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
|
||||
let bob_instance = bob.recv_msg(&pre_message).await;
|
||||
assert_eq!(bob_instance.download_state, DownloadState::Available);
|
||||
@@ -746,8 +765,8 @@ async fn test_webxdc_updates_in_post_message_without_pre_message() -> Result<()>
|
||||
.await?;
|
||||
|
||||
send_msg(alice, alice_chat_id, &mut alice_instance).await?;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
let pre_message = alice.pop_sent_msg().await;
|
||||
let post_message = alice.pop_sent_msg().await;
|
||||
|
||||
// Bob receives post-message first.
|
||||
let bob_instance = bob.recv_msg(&post_message).await;
|
||||
|
||||
Reference in New Issue
Block a user