mirror of
https://github.com/chatmail/core.git
synced 2026-05-14 04:16:30 +03:00
Compare commits
3 Commits
hoc/remove
...
link2xt/pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97064eb2ae | ||
|
|
c67dc519cd | ||
|
|
ceadf60a7a |
@@ -413,6 +413,11 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* Messages in the "saved messages" chat (see dc_chat_is_self_talk()) are skipped.
|
||||
* Messages are deleted whether they were seen or not, the UI should clearly point that out.
|
||||
* See also dc_estimate_deletion_cnt().
|
||||
* - `delete_server_after` = 0=do not delete messages from server automatically (default),
|
||||
* 1=delete messages directly after receiving from server, mvbox is skipped.
|
||||
* >1=seconds, after which messages are deleted automatically from the server, mvbox is used as defined.
|
||||
* "Saved messages" are deleted from the server as well as emails, the UI should clearly point that out.
|
||||
* See also dc_estimate_deletion_cnt().
|
||||
* - `media_quality` = DC_MEDIA_QUALITY_BALANCED (0) =
|
||||
* good outgoing images/videos/voice quality at reasonable sizes (default)
|
||||
* DC_MEDIA_QUALITY_WORSE (1)
|
||||
@@ -482,6 +487,7 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* 0 = Everybody (except explicitly blocked contacts),
|
||||
* 1 = Contacts (default, does not include contact requests),
|
||||
* 2 = Nobody (calls never result in a notification).
|
||||
* - `force_encryption` = 1 (default) to force encryption, 0 to allow receiving unencrypted messages.
|
||||
*
|
||||
* Also, there are configs that are only needed
|
||||
* if you want to use the deprecated dc_configure() API, such as:
|
||||
@@ -1456,16 +1462,16 @@ dc_chatlist_t* dc_get_similar_chatlist (dc_context_t* context, uint32_t ch
|
||||
|
||||
/**
|
||||
* Estimate the number of messages that will be deleted
|
||||
* by the dc_set_config()-option `delete_device_after`.
|
||||
* by the dc_set_config()-options `delete_device_after` or `delete_server_after`.
|
||||
* This is typically used to show the estimated impact to the user
|
||||
* before actually enabling deletion of old messages.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param from_server Deprecated, pass 0 here
|
||||
* @param from_server 1=Estimate deletion count for server, 0=Estimate deletion count for device
|
||||
* @param seconds Count messages older than the given number of seconds.
|
||||
* @return Number of messages that are older than the given number of seconds.
|
||||
* Messages in the "Saved Messages" chat are not counted as they will not be deleted automatically.
|
||||
* Messages in the "saved messages" folder are not counted as they will not be deleted automatically.
|
||||
*/
|
||||
int dc_estimate_deletion_cnt (dc_context_t* context, int from_server, int64_t seconds);
|
||||
|
||||
|
||||
@@ -735,19 +735,10 @@ impl CommandApi {
|
||||
Ok(msg_ids)
|
||||
}
|
||||
|
||||
/// Estimates the number of messages that will be deleted
|
||||
/// by the `set_config()`-option `delete_device_after`.
|
||||
///
|
||||
/// Estimate the number of messages that will be deleted
|
||||
/// by the set_config()-options `delete_device_after` or `delete_server_after`.
|
||||
/// This is typically used to show the estimated impact to the user
|
||||
/// before actually enabling deletion of old messages.
|
||||
///
|
||||
/// Messages in the "Saved Messages" chat are not counted as they will not be deleted automatically.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `from_server`: Deprecated, pass `false` here
|
||||
/// - `seconds`: Count messages older than the given number of seconds.
|
||||
///
|
||||
/// Returns the number of messages that are older than the given number of seconds.
|
||||
async fn estimate_auto_deletion_count(
|
||||
&self,
|
||||
account_id: u32,
|
||||
|
||||
@@ -29,7 +29,7 @@ $ pip install .
|
||||
|
||||
1. Build `deltachat-rpc-server` with `cargo build -p deltachat-rpc-server`.
|
||||
2. Install tox `pip install -U tox`
|
||||
3. Run `CHATMAIL_DOMAIN=ci-chatmail.testrun.org PATH="../target/debug:$PATH" tox`.
|
||||
3. Run `CHATMAIL_DOMAIN=nine.testrun.org PATH="../target/debug:$PATH" tox`.
|
||||
|
||||
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ def test_moved_markseen(acfactory, direct_imap, log):
|
||||
ac2.add_or_update_transport({"addr": addr, "password": password})
|
||||
ac2.bring_online()
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac1.set_config("bcc_self", "1")
|
||||
ac2.set_config("bcc_self", "1")
|
||||
|
||||
log.section("ac2: creating DeltaChat folder")
|
||||
ac2_direct_imap = direct_imap(ac2)
|
||||
ac2_direct_imap.create_folder("DeltaChat")
|
||||
ac2.set_config("delete_server_after", "0")
|
||||
ac2.set_config("sync_msgs", "0") # Do not send a sync message when accepting a contact request.
|
||||
|
||||
ac2.add_or_update_transport({"addr": addr, "password": password, "imapFolder": "DeltaChat"})
|
||||
@@ -60,9 +57,11 @@ def test_moved_markseen(acfactory, direct_imap, log):
|
||||
def test_markseen_message_and_mdn(acfactory, direct_imap):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac1.set_config("bcc_self", "1")
|
||||
ac2.set_config("bcc_self", "1")
|
||||
for ac in ac1, ac2:
|
||||
ac.set_config("delete_server_after", "0")
|
||||
|
||||
# Do not send BCC to self, we only want to test MDN on ac1.
|
||||
ac1.set_config("bcc_self", "0")
|
||||
|
||||
acfactory.get_accepted_chat(ac1, ac2).send_text("hi")
|
||||
msg = ac2.wait_for_incoming_msg()
|
||||
@@ -82,18 +81,17 @@ def test_markseen_message_and_mdn(acfactory, direct_imap):
|
||||
ac1_direct_imap.select_folder("INBOX")
|
||||
ac2_direct_imap.select_folder("INBOX")
|
||||
|
||||
# Check that the mdn and original message is marked as seen
|
||||
assert len(list(ac1_direct_imap.conn.fetch(AND(seen=True), mark_seen=False))) == 2
|
||||
assert len(list(ac2_direct_imap.conn.fetch(AND(seen=True), mark_seen=False))) == 2
|
||||
# Check that the mdn is marked as seen
|
||||
assert len(list(ac1_direct_imap.conn.fetch(AND(seen=True), mark_seen=False))) == 1
|
||||
# Check original message is marked as seen
|
||||
assert len(list(ac2_direct_imap.conn.fetch(AND(seen=True), mark_seen=False))) == 1
|
||||
|
||||
|
||||
def test_trash_multiple_messages(acfactory, direct_imap, log):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac2.stop_io()
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac2.set_config("bcc_self", "1")
|
||||
|
||||
ac2.set_config("delete_server_after", "0")
|
||||
ac2.set_config("sync_msgs", "0")
|
||||
|
||||
ac2.start_io()
|
||||
|
||||
@@ -4,29 +4,39 @@ from deltachat_rpc_client import EventType
|
||||
from deltachat_rpc_client.const import MessageState
|
||||
|
||||
|
||||
def test_bcc_self_is_enabled_when_setting_up_second_device(acfactory):
|
||||
def test_bcc_self_delete_server_after_defaults(acfactory):
|
||||
"""Test default values for bcc_self and delete_server_after."""
|
||||
ac = acfactory.get_online_account()
|
||||
|
||||
# Initially after getting online
|
||||
# the setting bcc_self is set to 0 because there is only one device
|
||||
# and delete_server_after is "1", meaning immediate deletion.
|
||||
assert ac.get_config("bcc_self") == "0"
|
||||
assert ac.get_config("delete_server_after") == "1"
|
||||
|
||||
# Setup a second device.
|
||||
ac_clone = ac.clone()
|
||||
ac_clone.bring_online()
|
||||
|
||||
# Second device setup enables bcc_self.
|
||||
# Second device setup
|
||||
# enables bcc_self and changes default delete_server_after.
|
||||
assert ac.get_config("bcc_self") == "1"
|
||||
assert ac_clone.get_config("bcc_self") == "1"
|
||||
assert ac.get_config("delete_server_after") == "0"
|
||||
|
||||
# Test manually disabling bcc_self
|
||||
assert ac_clone.get_config("bcc_self") == "1"
|
||||
assert ac_clone.get_config("delete_server_after") == "0"
|
||||
|
||||
# Manually disabling bcc_self
|
||||
# also restores the default for delete_server_after.
|
||||
ac.set_config("bcc_self", "0")
|
||||
assert ac.get_config("bcc_self") == "0"
|
||||
assert ac.get_config("delete_server_after") == "1"
|
||||
|
||||
# Cloning the account again enables bcc_self again
|
||||
# Cloning the account again enables bcc_self
|
||||
# even though it was manually disabled.
|
||||
ac_clone = ac.clone()
|
||||
assert ac.get_config("bcc_self") == "1"
|
||||
assert ac.get_config("delete_server_after") == "0"
|
||||
|
||||
|
||||
def test_one_account_send_bcc_setting(acfactory, log, direct_imap):
|
||||
|
||||
@@ -1232,12 +1232,11 @@ def test_leave_and_delete_group(acfactory, log):
|
||||
|
||||
|
||||
def test_immediate_autodelete(acfactory, direct_imap, log):
|
||||
"""
|
||||
`bcc_self` is off by default,
|
||||
so that messages are supposed to be immediately autodeleted
|
||||
"""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
|
||||
# "1" means delete immediately, while "0" means do not delete
|
||||
ac2.set_config("delete_server_after", "1")
|
||||
|
||||
log.section("ac1: create chat with ac2")
|
||||
chat1 = ac1.create_chat(ac2)
|
||||
ac2.create_chat(ac1)
|
||||
|
||||
@@ -521,6 +521,7 @@ class ACFactory:
|
||||
assert "addr" in configdict and "mail_pw" in configdict, configdict
|
||||
configdict.setdefault("bcc_self", False)
|
||||
configdict.setdefault("sync_msgs", False)
|
||||
configdict.setdefault("delete_server_after", 0)
|
||||
ac.update_config(configdict)
|
||||
self._acsetup._account2config[ac] = configdict
|
||||
self._preconfigure_key(ac)
|
||||
|
||||
@@ -298,6 +298,73 @@ def test_use_new_verified_group_after_going_online(acfactory, data, tmp_path, lp
|
||||
assert msg_in.text == msg_out.text
|
||||
|
||||
|
||||
def test_verified_group_vs_delete_server_after(acfactory, tmp_path, lp):
|
||||
"""Test for the issue #4346:
|
||||
- User is added to a verified group.
|
||||
- First device of the user downloads "member added" from the group.
|
||||
- First device removes "member added" from the server.
|
||||
- Some new messages are sent to the group.
|
||||
- Second device comes online, receives these new messages.
|
||||
The result is an unverified group with unverified members.
|
||||
- First device re-gossips Autocrypt keys to the group.
|
||||
- Now the second device has all members and group verified.
|
||||
"""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
acfactory.remove_preconfigured_keys()
|
||||
ac2_offl = acfactory.new_online_configuring_account(cloned_from=ac2)
|
||||
for ac in [ac2, ac2_offl]:
|
||||
ac.set_config("bcc_self", "1")
|
||||
ac2.set_config("delete_server_after", "1")
|
||||
ac2.set_config("gossip_period", "0") # Re-gossip in every message
|
||||
acfactory.bring_accounts_online()
|
||||
dir = tmp_path / "exportdir"
|
||||
dir.mkdir()
|
||||
ac2.export_self_keys(str(dir))
|
||||
ac2_offl.import_self_keys(str(dir))
|
||||
ac2_offl.stop_io()
|
||||
|
||||
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
|
||||
chat1 = ac1.create_group_chat("hello")
|
||||
qr = chat1.get_join_qr()
|
||||
lp.sec("ac2: start QR-code based join-group protocol")
|
||||
chat2 = ac2.qr_join_chat(qr)
|
||||
ac1._evtracker.wait_securejoin_inviter_progress(1000)
|
||||
# Wait for "Member Me (<addr>) added by <addr>." message.
|
||||
msg_in = ac2._evtracker.wait_next_incoming_message()
|
||||
assert msg_in.is_system_message()
|
||||
|
||||
lp.sec("ac2: waiting for 'member added' to be deleted on the server")
|
||||
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
|
||||
|
||||
lp.sec("ac1: sending 'hi' to the group")
|
||||
ac2.set_config("delete_server_after", "0")
|
||||
chat1.send_text("hi")
|
||||
|
||||
lp.sec("ac2_offl: going online, checking the 'hi' message")
|
||||
ac2_offl.start_io()
|
||||
msg_in = ac2_offl._evtracker.wait_next_incoming_message()
|
||||
assert not msg_in.is_system_message()
|
||||
assert msg_in.text == "hi"
|
||||
ac2_offl_ac1_contact = msg_in.get_sender_contact()
|
||||
assert ac2_offl_ac1_contact.addr == ac1.get_config("addr")
|
||||
assert not ac2_offl_ac1_contact.is_verified()
|
||||
chat2_offl = msg_in.chat
|
||||
|
||||
lp.sec("ac2: sending message re-gossiping Autocrypt keys")
|
||||
chat2.send_text("hi2")
|
||||
|
||||
lp.sec("ac2_offl: receiving message")
|
||||
ev = ac2_offl._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
msg_in = ac2_offl.get_message_by_id(ev.data2)
|
||||
assert not msg_in.is_system_message()
|
||||
assert msg_in.text == "hi2"
|
||||
assert msg_in.chat == chat2_offl
|
||||
assert msg_in.get_sender_contact().addr == ac2.get_config("addr")
|
||||
# Until we reset verifications and then send the _verified header,
|
||||
# verification is not gossiped here:
|
||||
assert not ac2_offl_ac1_contact.is_verified()
|
||||
|
||||
|
||||
def test_deleted_msgs_dont_reappear(acfactory):
|
||||
ac1 = acfactory.new_online_configuring_account()
|
||||
acfactory.bring_accounts_online()
|
||||
|
||||
@@ -15,9 +15,6 @@ def test_basic_imap_api(acfactory, tmp_path):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
chat12 = acfactory.get_accepted_chat(ac1, ac2)
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac2.set_config("bcc_self", "1")
|
||||
|
||||
imap2 = ac2.direct_imap
|
||||
|
||||
with imap2.idle() as idle2:
|
||||
@@ -165,9 +162,6 @@ def test_webxdc_message(acfactory, data, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
chat = acfactory.get_accepted_chat(ac1, ac2)
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac2.set_config("bcc_self", "1")
|
||||
|
||||
lp.sec("ac1: prepare and send text message to ac2")
|
||||
msg1 = chat.send_text("message0")
|
||||
assert not msg1.is_webxdc()
|
||||
@@ -368,10 +362,6 @@ def test_send_and_receive_message_markseen(acfactory, lp):
|
||||
# make DC's life harder wrt to encodings
|
||||
ac1.set_config("displayname", "ä name")
|
||||
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac1.set_config("bcc_self", "1")
|
||||
ac2.set_config("bcc_self", "1")
|
||||
|
||||
# clear any fresh device messages
|
||||
ac1.get_device_chat().mark_noticed()
|
||||
ac2.get_device_chat().mark_noticed()
|
||||
@@ -516,15 +506,9 @@ def test_mdn_asymmetric(acfactory, lp):
|
||||
ac1.set_config("mdns_enabled", "1")
|
||||
ac2.set_config("mdns_enabled", "1")
|
||||
|
||||
# Make sure that the mdn is not immediately auto-deleted on the server:
|
||||
ac1.set_config("bcc_self", "1")
|
||||
|
||||
lp.sec("sending text message from ac1 to ac2")
|
||||
msg_out = chat.send_text("message1")
|
||||
|
||||
# Wait for the message to be marked as seen on IMAP.
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
|
||||
assert len(chat.get_messages()) == 1 + E2EE_INFO_MSGS
|
||||
|
||||
lp.sec("disable ac1 MDNs")
|
||||
@@ -541,7 +525,7 @@ def test_mdn_asymmetric(acfactory, lp):
|
||||
lp.sec("ac1: waiting for incoming activity")
|
||||
assert len(chat.get_messages()) == 1 + E2EE_INFO_MSGS
|
||||
|
||||
# Wait for the mdn to be marked as seen on IMAP.
|
||||
# Wait for the message to be marked as seen on IMAP.
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
|
||||
# MDN is received even though MDNs are already disabled
|
||||
@@ -1089,8 +1073,6 @@ def test_send_receive_locations(acfactory, lp):
|
||||
|
||||
def test_delete_multiple_messages(acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
# Make sure that messages are not immediately auto-deleted on the server:
|
||||
ac2.set_config("bcc_self", "1")
|
||||
chat12 = acfactory.get_accepted_chat(ac1, ac2)
|
||||
|
||||
lp.sec("ac1: sending seven messages")
|
||||
|
||||
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
export TZ=UTC
|
||||
|
||||
# Provider database revision.
|
||||
REV=2cba4b72f4c6e6417b83ba549aff7781be5f166c
|
||||
REV=ad097ee40579c884e7757de2d3bb0a51f481a32a
|
||||
|
||||
CORE_ROOT="$PWD"
|
||||
TMP="$(mktemp -d)"
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::config::Config;
|
||||
use crate::constants::DC_CHAT_ID_TRASH;
|
||||
use crate::message::MessageState;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{TestContext, TestContextManager};
|
||||
|
||||
struct CallSetup {
|
||||
@@ -634,20 +635,23 @@ async fn test_forward_call() -> Result<()> {
|
||||
async fn test_end_text_call() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let received1 = receive_imf(
|
||||
alice,
|
||||
b"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <first@example.net>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
\n\
|
||||
Hello\n",
|
||||
false,
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
b"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Message-ID: <first@example.net>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
\r\n\
|
||||
Hello\r\n",
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
.await?;
|
||||
let received1 = receive_imf(alice, encrypted_message.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(received1.msg_ids.len(), 1);
|
||||
let msg = Message::load_from_db(alice, received1.msg_ids[0])
|
||||
.await
|
||||
@@ -656,21 +660,23 @@ async fn test_end_text_call() -> Result<()> {
|
||||
|
||||
// Receiving "Call ended" message that refers
|
||||
// to the text message does not result in an error.
|
||||
let received2 = receive_imf(
|
||||
alice,
|
||||
b"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <second@example.net>\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\
|
||||
In-Reply-To: <first@example.net>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Content: call-ended\n\
|
||||
\n\
|
||||
Call ended\n",
|
||||
false,
|
||||
let encrypted_message2 = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
b"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Message-ID: <second@example.net>\r\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
|
||||
In-Reply-To: <first@example.net>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Chat-Content: call-ended\r\n\
|
||||
\r\n\
|
||||
Call ended\r\n",
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
.await?;
|
||||
let received2 = receive_imf(alice, encrypted_message2.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(received2.msg_ids.len(), 1);
|
||||
assert_eq!(received2.chat_id, DC_CHAT_ID_TRASH);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::message::{Message, MessengerMessage, delete_msgs};
|
||||
use crate::mimeparser::{self, MimeMessage};
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{
|
||||
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager,
|
||||
TimeShiftFalsePositiveNote, sync,
|
||||
@@ -1049,6 +1050,7 @@ async fn chatlist_len(ctx: &Context, listflags: usize) -> usize {
|
||||
async fn test_archive() {
|
||||
// create two chats
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let mut msg = Message::new_text("foo".to_string());
|
||||
let msg_id = add_device_msg(&t, None, Some(&mut msg)).await.unwrap();
|
||||
let chat_id1 = message::Message::load_from_db(&t, msg_id)
|
||||
@@ -1159,46 +1161,51 @@ async fn test_archive() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_unarchive_if_muted() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
async fn msg_from_bob(t: &TestContext, num: u32) -> Result<()> {
|
||||
receive_imf(
|
||||
t,
|
||||
let msg_from_bob = async |num: u32| {
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[t],
|
||||
format!(
|
||||
"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <{num}@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2022 19:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Message-ID: <{num}@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Date: Sun, 22 Mar 2022 19:37:57 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
)
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(t, encrypted_message.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
};
|
||||
|
||||
msg_from_bob(&t, 1).await?;
|
||||
msg_from_bob(1).await;
|
||||
let chat_id = t.get_last_msg().await.get_chat_id();
|
||||
chat_id.accept(&t).await?;
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 1);
|
||||
chat_id.accept(t).await?;
|
||||
chat_id.set_visibility(t, ChatVisibility::Archived).await?;
|
||||
assert_eq!(get_archived_cnt(t).await?, 1);
|
||||
|
||||
// not muted chat is unarchived on receiving a message
|
||||
msg_from_bob(&t, 2).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 0);
|
||||
msg_from_bob(2).await;
|
||||
assert_eq!(get_archived_cnt(t).await?, 0);
|
||||
|
||||
// forever muted chat is not unarchived on receiving a message
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
set_muted(&t, chat_id, MuteDuration::Forever).await?;
|
||||
msg_from_bob(&t, 3).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 1);
|
||||
chat_id.set_visibility(t, ChatVisibility::Archived).await?;
|
||||
set_muted(t, chat_id, MuteDuration::Forever).await?;
|
||||
msg_from_bob(3).await;
|
||||
assert_eq!(get_archived_cnt(t).await?, 1);
|
||||
|
||||
// otherwise muted chat is not unarchived on receiving a message
|
||||
set_muted(
|
||||
&t,
|
||||
t,
|
||||
chat_id,
|
||||
MuteDuration::Until(
|
||||
SystemTime::now()
|
||||
@@ -1207,12 +1214,12 @@ async fn test_unarchive_if_muted() -> Result<()> {
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
msg_from_bob(&t, 4).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 1);
|
||||
msg_from_bob(4).await;
|
||||
assert_eq!(get_archived_cnt(t).await?, 1);
|
||||
|
||||
// expired mute will unarchive the chat
|
||||
set_muted(
|
||||
&t,
|
||||
t,
|
||||
chat_id,
|
||||
MuteDuration::Until(
|
||||
SystemTime::now()
|
||||
@@ -1221,20 +1228,20 @@ async fn test_unarchive_if_muted() -> Result<()> {
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
msg_from_bob(&t, 5).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 0);
|
||||
msg_from_bob(5).await;
|
||||
assert_eq!(get_archived_cnt(t).await?, 0);
|
||||
|
||||
// no unarchiving on sending to muted chat or on adding info messages to muted chat
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
set_muted(&t, chat_id, MuteDuration::Forever).await?;
|
||||
send_text_msg(&t, chat_id, "out".to_string()).await?;
|
||||
add_info_msg(&t, chat_id, "info").await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 1);
|
||||
chat_id.set_visibility(t, ChatVisibility::Archived).await?;
|
||||
set_muted(t, chat_id, MuteDuration::Forever).await?;
|
||||
send_text_msg(t, chat_id, "out".to_string()).await?;
|
||||
add_info_msg(t, chat_id, "info").await?;
|
||||
assert_eq!(get_archived_cnt(t).await?, 1);
|
||||
|
||||
// finally, unarchive on sending to not muted chat
|
||||
set_muted(&t, chat_id, MuteDuration::NotMuted).await?;
|
||||
send_text_msg(&t, chat_id, "out2".to_string()).await?;
|
||||
assert_eq!(get_archived_cnt(&t).await?, 0);
|
||||
set_muted(t, chat_id, MuteDuration::NotMuted).await?;
|
||||
send_text_msg(t, chat_id, "out2".to_string()).await?;
|
||||
assert_eq!(get_archived_cnt(t).await?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1381,6 +1388,9 @@ async fn test_markfresh_chat() -> Result<()> {
|
||||
async fn test_archive_fresh_msgs() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
// FIXME: use encrypted messages
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
async fn msg_from(t: &TestContext, name: &str, num: u32) -> Result<()> {
|
||||
receive_imf(
|
||||
t,
|
||||
@@ -1873,45 +1883,38 @@ async fn test_lookup_self_by_contact_id() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_marknoticed_chat() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat = t.create_chat_with_contact("bob", "bob@example.org").await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let chat = alice.create_chat(bob).await;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: bob@example.org\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <1@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Fri, 23 Apr 2021 10:00:57 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let bob_chat_id = bob.create_chat_id(alice).await;
|
||||
let sent = bob.send_text(bob_chat_id, "hello").await;
|
||||
alice.recv_msg(&sent).await;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(chats.get_chat_id(0)?, chat.id);
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 1);
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 1);
|
||||
assert_eq!(alice.get_fresh_msgs().await?.len(), 1);
|
||||
|
||||
let msgs = get_chat_msgs(&t, chat.id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
let msg_id = match msgs.first().unwrap() {
|
||||
let msgs = get_chat_msgs(alice, chat.id).await?;
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let msg_id = match msgs.last().unwrap() {
|
||||
ChatItem::Message { msg_id } => *msg_id,
|
||||
_ => MsgId::new_unset(),
|
||||
};
|
||||
let msg = message::Message::load_from_db(&t, msg_id).await?;
|
||||
let msg = message::Message::load_from_db(alice, msg_id).await?;
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
|
||||
marknoticed_chat(&t, chat.id).await?;
|
||||
marknoticed_chat(alice, chat.id).await?;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
let msg = message::Message::load_from_db(&t, msg_id).await?;
|
||||
let msg = message::Message::load_from_db(alice, msg_id).await?;
|
||||
assert_eq!(msg.state, MessageState::InNoticed);
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 0);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 0);
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 0);
|
||||
assert_eq!(alice.get_fresh_msgs().await?.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1919,6 +1922,7 @@ async fn test_marknoticed_chat() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_request_fresh_messages() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 0);
|
||||
@@ -1970,40 +1974,43 @@ async fn test_contact_request_fresh_messages() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_request_archive() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: bob@example.org\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <2@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2021 19:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let bob_chat_id = bob.create_chat_id(alice).await;
|
||||
let bob_sent_text = bob.send_text(bob_chat_id, "hello").await;
|
||||
alice.recv_msg(&bob_sent_text).await;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
let chat_id = chats.get_chat_id(0)?;
|
||||
assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request());
|
||||
assert_eq!(get_archived_cnt(&t).await?, 0);
|
||||
assert!(
|
||||
Chat::load_from_db(alice, chat_id)
|
||||
.await?
|
||||
.is_contact_request()
|
||||
);
|
||||
assert_eq!(get_archived_cnt(alice).await?, 0);
|
||||
|
||||
// archive request without accepting or blocking
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
chat_id
|
||||
.set_visibility(alice, ChatVisibility::Archived)
|
||||
.await?;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
let chat_id = chats.get_chat_id(0)?;
|
||||
assert!(chat_id.is_archived_link());
|
||||
assert_eq!(get_archived_cnt(&t).await?, 1);
|
||||
assert_eq!(get_archived_cnt(alice).await?, 1);
|
||||
|
||||
let chats = Chatlist::try_load(&t, DC_GCL_ARCHIVED_ONLY, None, None).await?;
|
||||
let chats = Chatlist::try_load(alice, DC_GCL_ARCHIVED_ONLY, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
let chat_id = chats.get_chat_id(0)?;
|
||||
assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request());
|
||||
assert!(
|
||||
Chat::load_from_db(alice, chat_id)
|
||||
.await?
|
||||
.is_contact_request()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2011,6 +2018,7 @@ async fn test_contact_request_archive() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_classic_email_chat() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
// Alice receives a classic (non-chat) message from Bob.
|
||||
receive_imf(
|
||||
@@ -4695,10 +4703,12 @@ async fn test_sync_delete_chat() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync_adhoc_grp() -> Result<()> {
|
||||
let alice0 = &TestContext::new_alice().await;
|
||||
let alice1 = &TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice0 = &tcm.alice().await;
|
||||
let alice1 = &tcm.alice().await;
|
||||
for a in [alice0, alice1] {
|
||||
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
a.allow_unencrypted().await?;
|
||||
}
|
||||
|
||||
let mut chat_ids = Vec::new();
|
||||
@@ -6070,6 +6080,7 @@ async fn test_no_key_contacts_in_adhoc_chats() -> Result<()> {
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let charlie = &tcm.charlie().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let chat_id = receive_imf(
|
||||
alice,
|
||||
|
||||
@@ -665,6 +665,7 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_single_chat() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// receive a one-to-one-message
|
||||
receive_imf(
|
||||
@@ -725,6 +726,7 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// receive a one-to-one-message without authname set
|
||||
receive_imf(
|
||||
|
||||
@@ -194,6 +194,17 @@ pub enum Config {
|
||||
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
|
||||
MediaQuality,
|
||||
|
||||
/// Timer in seconds after which the message is deleted from the
|
||||
/// server.
|
||||
///
|
||||
/// 0 means messages are never deleted by Delta Chat.
|
||||
///
|
||||
/// Value 1 is treated as "delete at once": messages are deleted
|
||||
/// immediately, without moving to DeltaChat folder.
|
||||
///
|
||||
/// Default is 1 for chatmail accounts without `BccSelf`, 0 otherwise.
|
||||
DeleteServerAfter,
|
||||
|
||||
/// Timer in seconds after which the message is deleted from the
|
||||
/// device.
|
||||
///
|
||||
@@ -475,6 +486,12 @@ pub enum Config {
|
||||
/// Experimental option denoting that the current profile is shared between multiple team members.
|
||||
/// For now, the only effect of this option is that seen flags are not synchronized.
|
||||
TeamProfile,
|
||||
|
||||
/// Process unencrypted messages.
|
||||
///
|
||||
/// Unencrypted messages are fetched and processed only if this setting is explicitly enabled.
|
||||
#[strum(props(default = "1"))]
|
||||
ForceEncryption,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -490,7 +507,11 @@ impl Config {
|
||||
pub(crate) fn is_synced(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Displayname | Self::MdnsEnabled | Self::Selfavatar | Self::Selfstatus,
|
||||
Self::Displayname
|
||||
| Self::MdnsEnabled
|
||||
| Self::Selfavatar
|
||||
| Self::Selfstatus
|
||||
| Self::ForceEncryption,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -543,6 +564,14 @@ impl Context {
|
||||
// Default values
|
||||
let val = match key {
|
||||
Config::ConfiguredInboxFolder => Some("INBOX".to_string()),
|
||||
Config::DeleteServerAfter => {
|
||||
match !Box::pin(self.get_config_bool(Config::BccSelf)).await?
|
||||
&& Box::pin(self.is_chatmail()).await?
|
||||
{
|
||||
true => Some("1".to_string()),
|
||||
false => Some("0".to_string()),
|
||||
}
|
||||
}
|
||||
Config::Addr => self.get_config_opt(Config::ConfiguredAddr).await?,
|
||||
_ => key.get_str("default").map(|s| s.to_string()),
|
||||
};
|
||||
@@ -623,6 +652,23 @@ impl Context {
|
||||
self.get_config_bool(Config::MdnsEnabled).await
|
||||
}
|
||||
|
||||
/// Gets configured "delete_server_after" value.
|
||||
///
|
||||
/// `None` means never delete the message, `Some(0)` means delete
|
||||
/// at once, `Some(x)` means delete after `x` seconds.
|
||||
pub async fn get_config_delete_server_after(&self) -> Result<Option<i64>> {
|
||||
let val = match self
|
||||
.get_config_parsed::<i64>(Config::DeleteServerAfter)
|
||||
.await?
|
||||
.unwrap_or(0)
|
||||
{
|
||||
0 => None,
|
||||
1 => Some(0),
|
||||
x => Some(x),
|
||||
};
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Gets the configured provider.
|
||||
///
|
||||
/// The provider is determined by the current primary transport.
|
||||
|
||||
@@ -142,6 +142,28 @@ async fn test_mdns_default_behaviour() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_server_after_default() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
|
||||
// Check that the settings are displayed correctly.
|
||||
assert_eq!(t.get_config(Config::BccSelf).await?, Some("1".to_string()));
|
||||
assert_eq!(
|
||||
t.get_config(Config::DeleteServerAfter).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
|
||||
// Leaving emails on the server even w/o `BccSelf` is a good default at least because other
|
||||
// MUAs do so even if the server doesn't save sent messages to some sentbox (like Gmail
|
||||
// does).
|
||||
t.set_config_bool(Config::BccSelf, false).await?;
|
||||
assert_eq!(
|
||||
t.get_config(Config::DeleteServerAfter).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const SAVED_MESSAGES_DEDUPLICATED_FILE: &str = "969142cb84015bc135767bc2370934a.png";
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
|
||||
@@ -335,6 +335,7 @@ async fn test_add_or_lookup() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_name_changes() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// first message creates contact and one-to-one-chat without name set
|
||||
receive_imf(
|
||||
@@ -927,9 +928,12 @@ async fn test_synchronize_status() -> Result<()> {
|
||||
// Alice has two devices.
|
||||
let alice1 = &tcm.alice().await;
|
||||
let alice2 = &tcm.alice().await;
|
||||
alice1.allow_unencrypted().await?;
|
||||
alice2.allow_unencrypted().await?;
|
||||
|
||||
// Bob has one device.
|
||||
let bob = &tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
let default_status = alice1.get_config(Config::Selfstatus).await?;
|
||||
|
||||
@@ -998,33 +1002,18 @@ async fn test_selfavatar_changed_event() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_last_seen() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let (contact_id, _) = Contact::add_or_lookup(
|
||||
&alice,
|
||||
"Bob",
|
||||
&ContactAddress::new("bob@example.net")?,
|
||||
Origin::ManuallyCreated,
|
||||
)
|
||||
.await?;
|
||||
let contact = Contact::get_by_id(&alice, contact_id).await?;
|
||||
let contact = alice.add_or_lookup_contact(bob).await;
|
||||
assert_eq!(contact.last_seen(), 0);
|
||||
|
||||
let mime = br#"Subject: Hello
|
||||
Message-ID: message@example.net
|
||||
To: Alice <alice@example.org>
|
||||
From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
Chat-Version: 1.0
|
||||
Date: Sun, 22 Mar 2020 22:37:55 +0000
|
||||
|
||||
Hi."#;
|
||||
receive_imf(&alice, mime, false).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
let msg = tcm.send_recv(bob, alice, "Hi.").await;
|
||||
|
||||
let timestamp = msg.get_timestamp();
|
||||
assert!(timestamp > 0);
|
||||
let contact = Contact::get_by_id(&alice, contact_id).await?;
|
||||
let contact = Contact::get_by_id(alice, contact.id).await?;
|
||||
assert_eq!(contact.last_seen(), timestamp);
|
||||
|
||||
Ok(())
|
||||
@@ -1098,6 +1087,7 @@ async fn test_was_seen_recently_event() -> Result<()> {
|
||||
async fn test_lookup_id_by_addr_recent_ex(accept_unencrypted_chat: bool) -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let bob = &tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
|
||||
assert!(std::str::from_utf8(raw)?.contains("Date: Thu, 24 Nov 2022 20:05:57 +0100"));
|
||||
|
||||
@@ -973,6 +973,12 @@ impl Context {
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"delete_server_after",
|
||||
self.get_config_int(Config::DeleteServerAfter)
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"last_housekeeping",
|
||||
self.get_config_int(Config::LastHousekeeping)
|
||||
@@ -1050,6 +1056,12 @@ impl Context {
|
||||
"team_profile",
|
||||
self.get_config_bool(Config::TeamProfile).await?.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"force_encryption",
|
||||
self.get_config_bool(Config::ForceEncryption)
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let elapsed = time_elapsed(&self.creation_time);
|
||||
res.insert("uptime", duration_to_str(elapsed));
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::chatlist::Chatlist;
|
||||
use crate::constants::Chattype;
|
||||
use crate::message::Message;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::{E2EE_INFO_MSGS, TestContext};
|
||||
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager};
|
||||
use crate::tools::{SystemTime, create_outgoing_rfc724_mid};
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -54,6 +54,7 @@ async fn receive_msg(t: &TestContext, chat: &Chat) {
|
||||
async fn test_get_fresh_msgs_and_muted_chats() {
|
||||
// receive various mails in 3 chats
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let bob = t.create_chat_with_contact("", "bob@g.it").await;
|
||||
let claire = t.create_chat_with_contact("", "claire@g.it").await;
|
||||
let dave = t.create_chat_with_contact("", "dave@g.it").await;
|
||||
@@ -103,6 +104,7 @@ async fn test_get_fresh_msgs_and_muted_chats() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_fresh_msgs_and_muted_until() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let bob = t.create_chat_with_contact("", "bob@g.it").await;
|
||||
receive_msg(&t, &bob).await;
|
||||
assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1);
|
||||
@@ -158,6 +160,7 @@ async fn test_get_fresh_msgs_and_muted_until() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_muted_context() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 0);
|
||||
t.set_config(Config::IsMuted, Some("1")).await?;
|
||||
let chat = t.create_chat_with_contact("", "bob@g.it").await;
|
||||
@@ -385,49 +388,40 @@ async fn test_search_msgs() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_unaccepted_requests() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: BobBar <bob@example.org>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: <msg1234@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Tue, 25 Oct 2022 13:37:00 +0000\n\
|
||||
\n\
|
||||
hello bob, foobar test!\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let chat_id = t.get_last_msg().await.get_chat_id();
|
||||
let chat = Chat::load_from_db(&t, chat_id).await?;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
bob.set_config(Config::Displayname, Some("BobBar")).await?;
|
||||
let msg = tcm.send_recv(bob, t, "hello bob, foobar test!").await;
|
||||
let chat_id = msg.get_chat_id();
|
||||
let chat = Chat::load_from_db(t, chat_id).await?;
|
||||
assert_eq!(chat.get_type(), Chattype::Single);
|
||||
assert!(chat.is_contact_request());
|
||||
assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 1);
|
||||
|
||||
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 1);
|
||||
assert_eq!(
|
||||
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(),
|
||||
Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1);
|
||||
assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 1);
|
||||
|
||||
chat_id.block(&t).await?;
|
||||
chat_id.block(t).await?;
|
||||
|
||||
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 0);
|
||||
assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 0);
|
||||
assert_eq!(
|
||||
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(),
|
||||
Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
|
||||
0
|
||||
);
|
||||
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 0);
|
||||
assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 0);
|
||||
|
||||
let contact_ids = get_chat_contacts(&t, chat_id).await?;
|
||||
Contact::unblock(&t, *contact_ids.first().unwrap()).await?;
|
||||
let contact_ids = get_chat_contacts(t, chat_id).await?;
|
||||
Contact::unblock(t, *contact_ids.first().unwrap()).await?;
|
||||
|
||||
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 1);
|
||||
assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 1);
|
||||
assert_eq!(
|
||||
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(),
|
||||
Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1);
|
||||
|
||||
@@ -402,6 +402,7 @@ mod tests {
|
||||
assert!(get_attachment_mime(&mail).is_some());
|
||||
|
||||
let bob = TestContext::new_bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
receive_imf(&bob, attachment_mime, false).await?;
|
||||
let msg = bob.get_last_msg().await;
|
||||
// Subject should be prepended because the attachment doesn't have "Chat-Version".
|
||||
@@ -416,6 +417,7 @@ mod tests {
|
||||
// Desktop via MS Exchange (actually made with TB though).
|
||||
let mixed_up_mime = include_bytes!("../test-data/message/mixed-up-long.eml");
|
||||
let bob = TestContext::new_bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
receive_imf(&bob, mixed_up_mime, false).await?;
|
||||
let msg = bob.get_last_msg().await;
|
||||
assert!(!msg.get_text().is_empty());
|
||||
|
||||
@@ -15,6 +15,12 @@ use crate::{EventType, chatlist_events};
|
||||
pub(crate) mod post_msg_metadata;
|
||||
pub(crate) use post_msg_metadata::PostMsgMetadata;
|
||||
|
||||
/// If a message is downloaded only partially
|
||||
/// and `delete_server_after` is set to small timeouts (eg. "at once"),
|
||||
/// the user might have no chance to actually download that message.
|
||||
/// `MIN_DELETE_SERVER_AFTER` increases the timeout in this case.
|
||||
pub(crate) const MIN_DELETE_SERVER_AFTER: i64 = 48 * 60 * 60;
|
||||
|
||||
/// From this point onward outgoing messages are considered large
|
||||
/// and get a Pre-Message, which announces the Post-Message.
|
||||
/// This is only about sending so we can modify it any time.
|
||||
|
||||
18
src/e2ee.rs
18
src/e2ee.rs
@@ -42,12 +42,25 @@ impl EncryptHelper {
|
||||
compress: bool,
|
||||
seipd_version: SeipdVersion,
|
||||
) -> Result<String> {
|
||||
let sign_key = load_self_secret_key(context).await?;
|
||||
|
||||
let mut raw_message = Vec::new();
|
||||
let cursor = Cursor::new(&mut raw_message);
|
||||
mail_to_encrypt.clone().write_part(cursor).ok();
|
||||
|
||||
let ctext = self
|
||||
.encrypt_raw(context, keyring, raw_message, compress, seipd_version)
|
||||
.await?;
|
||||
Ok(ctext)
|
||||
}
|
||||
|
||||
pub async fn encrypt_raw(
|
||||
self,
|
||||
context: &Context,
|
||||
keyring: Vec<SignedPublicKey>,
|
||||
raw_message: Vec<u8>,
|
||||
compress: bool,
|
||||
seipd_version: SeipdVersion,
|
||||
) -> Result<String> {
|
||||
let sign_key = load_self_secret_key(context).await?;
|
||||
let ctext =
|
||||
pgp::pk_encrypt(raw_message, keyring, sign_key, compress, seipd_version).await?;
|
||||
|
||||
@@ -147,6 +160,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
let mut tcm = TestContextManager::new();
|
||||
let bob = &tcm.bob().await;
|
||||
bob.set_config_bool(Config::IsChatmail, true).await?;
|
||||
bob.allow_unencrypted().await?;
|
||||
let bob_chat_id = receive_imf(
|
||||
bob,
|
||||
b"From: alice@example.org\n\
|
||||
|
||||
153
src/ephemeral.rs
153
src/ephemeral.rs
@@ -23,15 +23,16 @@
|
||||
//! ## Device settings
|
||||
//!
|
||||
//! In addition to per-chat ephemeral message setting, each device has
|
||||
//! a global user-configured setting that complements per-chat
|
||||
//! settings, `delete_device_after`.
|
||||
//! This setting is not synchronized among devices and applies to all
|
||||
//! two global user-configured settings that complement per-chat
|
||||
//! settings: `delete_device_after` and `delete_server_after`. These
|
||||
//! settings are not synchronized among devices and apply to all
|
||||
//! messages known to the device, including messages sent or received
|
||||
//! before configuring the setting.
|
||||
//!
|
||||
//! `delete_device_after` configures the maximum time device is
|
||||
//! storing the messages locally,
|
||||
//! but does not delete messages from the server.
|
||||
//! storing the messages locally. `delete_server_after` configures the
|
||||
//! time after which device will delete the messages it knows about
|
||||
//! from the server.
|
||||
//!
|
||||
//! ## How messages are deleted
|
||||
//!
|
||||
@@ -59,8 +60,9 @@
|
||||
//!
|
||||
//! Server deletion happens by updating the `imap` table based on
|
||||
//! the database entries which are expired either according to their
|
||||
//! ephemeral message timers.
|
||||
//! ephemeral message timers or global `delete_server_after` setting.
|
||||
|
||||
use std::cmp::max;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
@@ -73,11 +75,10 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::time::timeout;
|
||||
|
||||
use crate::chat::{ChatId, ChatIdBlocked, send_msg};
|
||||
use crate::config::Config;
|
||||
use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH};
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::download::DownloadState;
|
||||
use crate::download::MIN_DELETE_SERVER_AFTER;
|
||||
use crate::events::EventType;
|
||||
use crate::log::{LogExt, warn};
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
@@ -650,115 +651,37 @@ pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiv
|
||||
}
|
||||
|
||||
/// Schedules expired IMAP messages for deletion.
|
||||
pub(crate) async fn delete_expired_imap_messages(
|
||||
context: &Context,
|
||||
transport_id: u32,
|
||||
is_chatmail: bool,
|
||||
) -> Result<()> {
|
||||
#[expect(clippy::arithmetic_side_effects)]
|
||||
pub(crate) async fn delete_expired_imap_messages(context: &Context) -> Result<()> {
|
||||
let now = time();
|
||||
|
||||
if !context.get_config_bool(Config::BccSelf).await? && is_chatmail {
|
||||
info!(
|
||||
context,
|
||||
"dbg marking all as deleted 1 - rfc724_mids: {:?}",
|
||||
context
|
||||
.sql
|
||||
.query_map_vec(
|
||||
"SELECT rfc724_mid FROM msgs
|
||||
WHERE (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?)
|
||||
OR download_state=?",
|
||||
(now, DownloadState::Done),
|
||||
|row| Ok(row.get::<_, String>(0)?)
|
||||
)
|
||||
.await
|
||||
);
|
||||
info!(
|
||||
context,
|
||||
"dbg marking all as deleted 1 - pre_rfc724_mids: {:?}",
|
||||
context
|
||||
.sql
|
||||
.query_map_vec(
|
||||
"SELECT pre_rfc724_mid FROM msgs
|
||||
WHERE pre_rfc724_mid!=''",
|
||||
(),
|
||||
|row| Ok(row.get::<_, String>(0)?)
|
||||
)
|
||||
.await
|
||||
);
|
||||
// This the only device using this relay.
|
||||
// Mark all downloaded messages for deletion, because they are not needed anymore.
|
||||
//
|
||||
// For pre- and post-messages, `rfc724_mid` contains the post-message's Message-Id.
|
||||
// The pre-message's Message-Id is in pre_rfc724_mid, if it exists.
|
||||
//
|
||||
// Pre-messages can be deleted even if the message wasn't fully downloaded yet,
|
||||
// because it's only the post-message that hasn't been downloaded.
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE imap
|
||||
SET target=''
|
||||
WHERE transport_id=?1
|
||||
AND rfc724_mid IN (
|
||||
SELECT rfc724_mid FROM msgs
|
||||
WHERE (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?2)
|
||||
OR download_state=?3
|
||||
UNION
|
||||
SELECT pre_rfc724_mid FROM msgs
|
||||
WHERE pre_rfc724_mid!=''
|
||||
)",
|
||||
(transport_id, now, DownloadState::Done),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"dbg marking ephemeral as deleted 1 - rfc724_mids: {:?}",
|
||||
context
|
||||
.sql
|
||||
.query_map_vec(
|
||||
"SELECT rfc724_mid FROM msgs
|
||||
WHERE (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?2)",
|
||||
(transport_id, now),
|
||||
|row| Ok(row.get::<_, String>(0)?)
|
||||
)
|
||||
.await
|
||||
);
|
||||
info!(
|
||||
context,
|
||||
"dbg marking ephemeral as deleted 1 - pre_rfc724_mids: {:?}",
|
||||
context
|
||||
.sql
|
||||
.query_map_vec(
|
||||
"SELECT pre_rfc724_mid FROM msgs
|
||||
WHERE pre_rfc724_mid!=''
|
||||
AND (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?)",
|
||||
(now,),
|
||||
|row| Ok(row.get::<_, String>(0)?)
|
||||
)
|
||||
.await
|
||||
);
|
||||
// There may be other devices using this relay,
|
||||
// either because there is multi-relay or because this is a classical email server.
|
||||
// Only delete expired ephemeral messages.
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE imap
|
||||
SET target=''
|
||||
WHERE transport_id=?1
|
||||
AND rfc724_mid IN (
|
||||
SELECT rfc724_mid FROM msgs
|
||||
WHERE (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?2)
|
||||
UNION
|
||||
SELECT pre_rfc724_mid FROM msgs
|
||||
WHERE pre_rfc724_mid!=''
|
||||
AND (ephemeral_timestamp!=0 AND ephemeral_timestamp<=?2)
|
||||
)",
|
||||
(transport_id, now),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let (threshold_timestamp, threshold_timestamp_extended) =
|
||||
match context.get_config_delete_server_after().await? {
|
||||
None => (0, 0),
|
||||
Some(delete_server_after) => (
|
||||
match delete_server_after {
|
||||
// Guarantee immediate deletion.
|
||||
0 => i64::MAX,
|
||||
_ => now - delete_server_after,
|
||||
},
|
||||
now - max(delete_server_after, MIN_DELETE_SERVER_AFTER),
|
||||
),
|
||||
};
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE imap
|
||||
SET target=''
|
||||
WHERE rfc724_mid IN (
|
||||
SELECT rfc724_mid FROM msgs
|
||||
WHERE ((download_state = 0 AND timestamp < ?) OR
|
||||
(download_state != 0 AND timestamp < ?) OR
|
||||
(ephemeral_timestamp != 0 AND ephemeral_timestamp <= ?))
|
||||
)",
|
||||
(threshold_timestamp, threshold_timestamp_extended, now),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::download::DownloadState;
|
||||
use crate::location;
|
||||
use crate::message::markseen_msgs;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{TestContext, TestContextManager};
|
||||
use crate::timesmearing::MAX_SECONDS_TO_LEND_FROM_FUTURE;
|
||||
use crate::{
|
||||
@@ -451,178 +452,105 @@ async fn test_delete_expired_imap_messages() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
const HOUR: i64 = 60 * 60;
|
||||
let now = time();
|
||||
let transport_id: u32 = 1;
|
||||
let other_transport_id: u32 = 2;
|
||||
let uidvalidity = 12345u32;
|
||||
|
||||
async fn is_deleted(context: &Context, mid: &str) -> Result<bool> {
|
||||
Ok(context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM imap WHERE target='' AND rfc724_mid=?",
|
||||
(mid,),
|
||||
let transport_id = 1;
|
||||
let uidvalidity = 12345;
|
||||
for (id, timestamp, ephemeral_timestamp) in &[
|
||||
(900, now - 2 * HOUR, 0),
|
||||
(1000, now - 23 * HOUR - MIN_DELETE_SERVER_AFTER, 0),
|
||||
(1010, now - 23 * HOUR, 0),
|
||||
(1020, now - 21 * HOUR, 0),
|
||||
(1030, now - 19 * HOUR, 0),
|
||||
(2000, now - 18 * HOUR, now - HOUR),
|
||||
(2020, now - 17 * HOUR, now + HOUR),
|
||||
(3000, now + HOUR, 0),
|
||||
] {
|
||||
let message_id = id.to_string();
|
||||
t.sql
|
||||
.execute(
|
||||
"INSERT INTO msgs (id, rfc724_mid, timestamp, ephemeral_timestamp) VALUES (?,?,?,?);",
|
||||
(id, &message_id, timestamp, ephemeral_timestamp),
|
||||
)
|
||||
.await?;
|
||||
t.sql
|
||||
.execute(
|
||||
"INSERT INTO imap (transport_id, rfc724_mid, folder, uid, target, uidvalidity) VALUES (?, ?,'INBOX',?, 'INBOX', ?);",
|
||||
(transport_id, &message_id, id, uidvalidity),
|
||||
)
|
||||
.await?
|
||||
== 1)
|
||||
.await?;
|
||||
}
|
||||
|
||||
async fn reset_targets(context: &Context) {
|
||||
async fn test_marked_for_deletion(context: &Context, id: u32) -> Result<()> {
|
||||
assert_eq!(
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM imap WHERE target='' AND rfc724_mid=?",
|
||||
(id.to_string(),),
|
||||
)
|
||||
.await?,
|
||||
1
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_uid(context: &Context, id: u32) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute("UPDATE imap SET target='INBOX'", ())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// ── Test messages ────────────────────────────────────────────────────────
|
||||
//
|
||||
// (id, rfc724_mid, ephemeral_timestamp, download_state, pre_rfc724_mid)
|
||||
//
|
||||
// "expired" – expired ephemeral, no pre-msg
|
||||
// "no_expire" – ephemeral_timestamp=0, not Done → never deleted
|
||||
// "future" – future ephemeral, not Done → never deleted
|
||||
// "done" – Done, no ephemeral → branch 1 only
|
||||
// "pre_no_expire_*" – has pre-msg, but no expiry/Done
|
||||
// "pre_expired_*" – has pre-msg, expired ephemeral
|
||||
// "pre_future_*" – has pre-msg, future ephemeral
|
||||
// "wrong_tid" – expired+Done, but wrong transport_id in imap
|
||||
let msgs: &[(&str, i64, DownloadState, &str)] = &[
|
||||
("expired", now - HOUR, DownloadState::Available, ""),
|
||||
("no_expire", 0, DownloadState::Available, ""),
|
||||
("future", now + HOUR, DownloadState::Available, ""),
|
||||
("done", 0, DownloadState::Done, ""),
|
||||
(
|
||||
"pre_no_expire_post",
|
||||
0,
|
||||
DownloadState::Available,
|
||||
"pre_no_expire_pre",
|
||||
),
|
||||
(
|
||||
"pre_expired_post",
|
||||
now - HOUR,
|
||||
DownloadState::Available,
|
||||
"pre_expired_pre",
|
||||
),
|
||||
(
|
||||
"pre_future_post",
|
||||
now + HOUR,
|
||||
DownloadState::Available,
|
||||
"pre_future_pre",
|
||||
),
|
||||
("wrong_tid", now - HOUR, DownloadState::Done, ""),
|
||||
];
|
||||
for (mid, eph_ts, dl_state, pre_mid) in msgs {
|
||||
t.sql
|
||||
.execute(
|
||||
"INSERT INTO msgs \
|
||||
(rfc724_mid, timestamp, ephemeral_timestamp, download_state, pre_rfc724_mid) \
|
||||
VALUES (?,?,0,?,?,?)",
|
||||
(*mid, *eph_ts, *dl_state, *pre_mid),
|
||||
)
|
||||
.execute("DELETE FROM imap WHERE rfc724_mid=?", (id.to_string(),))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// One imap row per mid (including separate rows for pre-messages),
|
||||
// plus "wrong_tid" on a different transport_id.
|
||||
let imap_rows: &[(&str, u32)] = &[
|
||||
("expired", transport_id),
|
||||
("no_expire", transport_id),
|
||||
("future", transport_id),
|
||||
("done", transport_id),
|
||||
("pre_no_expire_post", transport_id),
|
||||
("pre_no_expire_pre", transport_id), // the pre-message's own imap row
|
||||
("pre_expired_post", transport_id),
|
||||
("pre_expired_pre", transport_id),
|
||||
("pre_future_post", transport_id),
|
||||
("pre_future_pre", transport_id),
|
||||
("wrong_tid", other_transport_id), // transport_id filter test
|
||||
];
|
||||
for (i, (mid, tid)) in imap_rows.iter().enumerate() {
|
||||
// This should mark message 2000 for deletion.
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
test_marked_for_deletion(&t, 2000).await?;
|
||||
remove_uid(&t, 2000).await?;
|
||||
// No other messages are marked for deletion.
|
||||
assert_eq!(
|
||||
t.sql
|
||||
.execute(
|
||||
"INSERT INTO imap \
|
||||
(transport_id, rfc724_mid, folder, uid, target, uidvalidity) \
|
||||
VALUES (?,?,'INBOX',?,'INBOX',?)",
|
||||
(*tid, *mid, (i + 1) as u32, uidvalidity),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
.count("SELECT COUNT(*) FROM imap WHERE target=''", ())
|
||||
.await?,
|
||||
0
|
||||
);
|
||||
|
||||
// ── Branch 1: is_chatmail=true, BccSelf=false (default) ─────────────────
|
||||
//
|
||||
// SQL deletes: (ephemeral_timestamp!=0 AND <=now) OR download_state=Done
|
||||
// Pre-messages: ALL with pre_rfc724_mid!='' unconditionally.
|
||||
delete_expired_imap_messages(&t, transport_id, true).await?;
|
||||
t.set_config(Config::DeleteServerAfter, Some(&*(25 * HOUR).to_string()))
|
||||
.await?;
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
test_marked_for_deletion(&t, 1000).await?;
|
||||
|
||||
// Tests (ephemeral_timestamp!=0 AND ephemeral_timestamp<=now) path.
|
||||
assert!(is_deleted(&t, "expired").await?);
|
||||
// Tests the ephemeral_timestamp!=0 guard: timestamp=0 satisfies <=now but must not match.
|
||||
assert!(!is_deleted(&t, "no_expire").await?);
|
||||
// Tests the ephemeral_timestamp<=now guard.
|
||||
assert!(!is_deleted(&t, "future").await?);
|
||||
// Tests the OR download_state=Done clause.
|
||||
assert!(is_deleted(&t, "done").await?);
|
||||
// Post-message: no expiry, not Done → not deleted.
|
||||
assert!(!is_deleted(&t, "pre_no_expire_post").await?);
|
||||
// Pre-message: deleted unconditionally (tests UNION SELECT pre_rfc724_mid ... WHERE pre_rfc724_mid!='').
|
||||
assert!(is_deleted(&t, "pre_no_expire_pre").await?);
|
||||
// Post-message with expired ephemeral → deleted.
|
||||
assert!(is_deleted(&t, "pre_expired_post").await?);
|
||||
// Pre-message of expired post → deleted (unconditional pre path).
|
||||
assert!(is_deleted(&t, "pre_expired_pre").await?);
|
||||
// Post-message with future ephemeral → not deleted.
|
||||
assert!(!is_deleted(&t, "pre_future_post").await?);
|
||||
// Pre-message of future post → still deleted (branch 1 pre path has NO ephemeral condition).
|
||||
// If the pre UNION clause gains an ephemeral condition, this would wrongly not be deleted.
|
||||
assert!(is_deleted(&t, "pre_future_pre").await?);
|
||||
// Tests transport_id=?1: expired+Done but on wrong transport_id → not deleted.
|
||||
assert!(!is_deleted(&t, "wrong_tid").await?);
|
||||
MsgId::new(1000)
|
||||
.update_download_state(&t, DownloadState::Available)
|
||||
.await?;
|
||||
t.sql
|
||||
.execute("UPDATE imap SET target=folder WHERE rfc724_mid='1000'", ())
|
||||
.await?;
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
test_marked_for_deletion(&t, 1000).await?; // Delete downloadable anyway.
|
||||
remove_uid(&t, 1000).await?;
|
||||
|
||||
reset_targets(&t).await;
|
||||
t.set_config(Config::DeleteServerAfter, Some(&*(22 * HOUR).to_string()))
|
||||
.await?;
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
test_marked_for_deletion(&t, 1010).await?;
|
||||
t.sql
|
||||
.execute("UPDATE imap SET target=folder WHERE rfc724_mid='1010'", ())
|
||||
.await?;
|
||||
|
||||
// ── Branch 2: is_chatmail=false ──────────────────────────────────────────
|
||||
//
|
||||
// SQL deletes: ephemeral_timestamp!=0 AND <=now only (no Done).
|
||||
// Pre-messages: only when the post also satisfies the ephemeral condition.
|
||||
delete_expired_imap_messages(&t, transport_id, false).await?;
|
||||
MsgId::new(1010)
|
||||
.update_download_state(&t, DownloadState::Available)
|
||||
.await?;
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
// Keep downloadable for now.
|
||||
assert_eq!(
|
||||
t.sql
|
||||
.count("SELECT COUNT(*) FROM imap WHERE target=''", ())
|
||||
.await?,
|
||||
0
|
||||
);
|
||||
|
||||
// Expired ephemeral → deleted.
|
||||
assert!(is_deleted(&t, "expired").await?);
|
||||
// ephemeral_timestamp=0 → not deleted (tests !=0 guard in branch 2).
|
||||
assert!(!is_deleted(&t, "no_expire").await?);
|
||||
// Future ephemeral → not deleted (tests <=now guard in branch 2).
|
||||
assert!(!is_deleted(&t, "future").await?);
|
||||
// Done without expired ephemeral → NOT deleted (key branch 1 vs 2 difference).
|
||||
// If download_state=Done were added to branch 2, this would wrongly be deleted.
|
||||
assert!(!is_deleted(&t, "done").await?);
|
||||
// Post-message: no expiry → not deleted.
|
||||
assert!(!is_deleted(&t, "pre_no_expire_post").await?);
|
||||
// Pre-message of non-expiring post → NOT deleted
|
||||
// (tests ephemeral_timestamp!=0 in branch 2's pre subquery).
|
||||
assert!(!is_deleted(&t, "pre_no_expire_pre").await?);
|
||||
// Post-message with expired ephemeral → deleted.
|
||||
assert!(is_deleted(&t, "pre_expired_post").await?);
|
||||
// Pre-message of expired post → deleted (tests full ephemeral condition in pre subquery).
|
||||
assert!(is_deleted(&t, "pre_expired_pre").await?);
|
||||
// Post-message with future ephemeral → not deleted.
|
||||
assert!(!is_deleted(&t, "pre_future_post").await?);
|
||||
// Pre-message of future post → NOT deleted
|
||||
// (tests ephemeral_timestamp<=now in branch 2's pre subquery).
|
||||
// If the <=now guard were removed there, this would wrongly be deleted.
|
||||
assert!(!is_deleted(&t, "pre_future_pre").await?);
|
||||
// Wrong transport_id → not deleted.
|
||||
assert!(!is_deleted(&t, "wrong_tid").await?);
|
||||
|
||||
reset_targets(&t).await;
|
||||
|
||||
// ── BccSelf=true forces branch 2 even when is_chatmail=true ─────────────
|
||||
//
|
||||
// Tests the `!BccSelf` part of the Rust condition.
|
||||
// If `!BccSelf` were dropped, Done would be deleted here (branch 1 behaviour).
|
||||
t.set_config(Config::BccSelf, Some("1")).await?;
|
||||
delete_expired_imap_messages(&t, transport_id, true).await?;
|
||||
assert!(!is_deleted(&t, "done").await?); // must stay on branch 2
|
||||
assert!(is_deleted(&t, "expired").await?); // branch 2 still runs normally
|
||||
t.set_config(Config::DeleteServerAfter, Some("1")).await?;
|
||||
delete_expired_imap_messages(&t).await?;
|
||||
test_marked_for_deletion(&t, 3000).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -630,50 +558,54 @@ async fn test_delete_expired_imap_messages() -> Result<()> {
|
||||
// Regression test for a bug in the timer rollback protection.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_timer_references() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
// Message with Message-ID <first@example.com> and no timer is received.
|
||||
receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Subject: Subject\n\
|
||||
Message-ID: <first@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 00:10:00 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
false,
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: Alice <alice@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Subject: Subject\r\n\
|
||||
Message-ID: <first@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 00:10:00 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n",
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
|
||||
|
||||
let msg = alice.get_last_msg().await;
|
||||
let chat_id = msg.chat_id;
|
||||
assert_eq!(chat_id.get_ephemeral_timer(&alice).await?, Timer::Disabled);
|
||||
assert_eq!(chat_id.get_ephemeral_timer(alice).await?, Timer::Disabled);
|
||||
|
||||
// Message with Message-ID <second@example.com> is received.
|
||||
receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Subject: Subject\n\
|
||||
Message-ID: <second@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 00:11:00 +0000\n\
|
||||
Ephemeral-Timer: 60\n\
|
||||
\n\
|
||||
second message\n",
|
||||
false,
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: Alice <alice@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Subject: Subject\r\n\
|
||||
Message-ID: <second@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 00:11:00 +0000\r\n\
|
||||
Ephemeral-Timer: 60\r\n\
|
||||
\r\n\
|
||||
second message\r\n",
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
|
||||
assert_eq!(
|
||||
chat_id.get_ephemeral_timer(&alice).await?,
|
||||
chat_id.get_ephemeral_timer(alice).await?,
|
||||
Timer::Enabled { duration: 60 }
|
||||
);
|
||||
let msg = alice.get_last_msg().await;
|
||||
|
||||
// Message is deleted when its timer expires.
|
||||
msg.id.trash(&alice, false).await?;
|
||||
msg.id.trash(alice, false).await?;
|
||||
|
||||
// Message with Message-ID <third@example.com>, referencing <first@example.com> and
|
||||
// <second@example.com>, is received. The message <second@example.come> is not in the
|
||||
@@ -687,25 +619,26 @@ async fn test_ephemeral_timer_references() -> Result<()> {
|
||||
//
|
||||
// The message also contains a quote of the first message to test that only References:
|
||||
// header and not In-Reply-To: is consulted by the rollback protection.
|
||||
receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Subject: Subject\n\
|
||||
Message-ID: <third@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 00:12:00 +0000\n\
|
||||
References: <first@example.com> <second@example.com>\n\
|
||||
In-Reply-To: <first@example.com>\n\
|
||||
\n\
|
||||
> hello\n",
|
||||
false,
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: Alice <alice@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Subject: Subject\r\n\
|
||||
Message-ID: <third@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 00:12:00 +0000\r\n\
|
||||
References: <first@example.com> <second@example.com>\r\n\
|
||||
In-Reply-To: <first@example.com>\r\n\
|
||||
\r\n\
|
||||
> hello\r\n",
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
|
||||
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(
|
||||
msg.chat_id.get_ephemeral_timer(&alice).await?,
|
||||
msg.chat_id.get_ephemeral_timer(alice).await?,
|
||||
Timer::Disabled
|
||||
);
|
||||
|
||||
|
||||
@@ -521,6 +521,7 @@ mod test_chatlist_events {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
let mime = br#"Subject: First thread
|
||||
Message-ID: first@example.org
|
||||
To: Alice <alice@example.org>, Bob <bob@example.net>
|
||||
|
||||
29
src/html.rs
29
src/html.rs
@@ -287,6 +287,7 @@ impl MsgId {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chat::{self, Chat, forward_msgs, save_msgs};
|
||||
|
||||
use crate::constants;
|
||||
use crate::contact::ContactId;
|
||||
use crate::message::{MessengerMessage, Viewtype};
|
||||
@@ -450,6 +451,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
// alice receives a non-delta html-message
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
let chat = alice
|
||||
.create_chat_with_contact("", "sender@testrun.org")
|
||||
.await;
|
||||
@@ -483,6 +485,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
|
||||
// bob: check that bob also got the html-part of the forwarded message
|
||||
let bob = &tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
let chat_bob = bob.create_chat_with_contact("", "alice@example.org").await;
|
||||
async fn check_receiver(ctx: &TestContext, chat: &Chat, sender: &TestContext) {
|
||||
let msg = ctx.recv_msg(&sender.pop_sent_msg().await).await;
|
||||
@@ -520,31 +523,30 @@ test some special html-characters as < > and & but also " and &#x
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_save_msg() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
// Alice receives a non-delta html-message
|
||||
let alice = TestContext::new_alice().await;
|
||||
let chat = alice
|
||||
.create_chat_with_contact("", "sender@testrun.org")
|
||||
.await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
|
||||
receive_imf(&alice, raw, false).await?;
|
||||
receive_imf(alice, raw, false).await?;
|
||||
let msg = alice.get_last_msg_in(chat.get_id()).await;
|
||||
|
||||
// Alice saves the message
|
||||
let self_chat = alice.get_self_chat().await;
|
||||
save_msgs(&alice, &[msg.id]).await?;
|
||||
save_msgs(alice, &[msg.id]).await?;
|
||||
let saved_msg = alice.get_last_msg_in(self_chat.get_id()).await;
|
||||
assert_ne!(saved_msg.id, msg.id);
|
||||
assert_eq!(
|
||||
saved_msg.get_original_msg_id(&alice).await?.unwrap(),
|
||||
msg.id
|
||||
);
|
||||
assert_eq!(saved_msg.get_original_msg_id(alice).await?.unwrap(), msg.id);
|
||||
assert!(!saved_msg.is_forwarded()); // UI should not flag "saved messages" as "forwarded"
|
||||
assert_ne!(saved_msg.get_from_id(), ContactId::SELF);
|
||||
assert_eq!(saved_msg.get_from_id(), msg.get_from_id());
|
||||
assert_eq!(saved_msg.is_dc_message, MessengerMessage::No);
|
||||
assert!(saved_msg.get_text().contains("this is plain"));
|
||||
assert!(saved_msg.has_html());
|
||||
let html = saved_msg.get_id().get_html(&alice).await?.unwrap();
|
||||
let html = saved_msg.get_id().get_html(alice).await?.unwrap();
|
||||
assert!(html.contains("this is <b>html</b>"));
|
||||
|
||||
Ok(())
|
||||
@@ -555,6 +557,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
let mut tcm = TestContextManager::new();
|
||||
// Alice receives a non-delta html-message
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
let chat = alice
|
||||
.create_chat_with_contact("", "sender@testrun.org")
|
||||
.await;
|
||||
@@ -618,18 +621,20 @@ test some special html-characters as < > and & but also " and &#x
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cp1252_html() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
receive_imf(
|
||||
&t,
|
||||
alice,
|
||||
include_bytes!("../test-data/message/cp1252-html.eml"),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let msg = t.get_last_msg().await;
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.viewtype, Viewtype::Text);
|
||||
assert!(msg.text.contains("foo bar ä ö ü ß"));
|
||||
assert!(msg.has_html());
|
||||
let html = msg.get_id().get_html(&t).await?.unwrap();
|
||||
let html = msg.get_id().get_html(alice).await?.unwrap();
|
||||
println!("{html}");
|
||||
assert!(html.contains("foo bar ä ö ü ß"));
|
||||
Ok(())
|
||||
|
||||
39
src/imap.rs
39
src/imap.rs
@@ -21,6 +21,9 @@ use futures_lite::FutureExt;
|
||||
use ratelimit::Ratelimit;
|
||||
use url::Url;
|
||||
|
||||
use crate::calls::{
|
||||
UnresolvedIceServer, create_fallback_ice_servers, create_ice_servers_from_metadata,
|
||||
};
|
||||
use crate::chat::{self, ChatId, ChatIdBlocked, add_device_msg};
|
||||
use crate::chatlist_events;
|
||||
use crate::config::Config;
|
||||
@@ -46,10 +49,6 @@ use crate::tools::{self, create_id, duration_to_str, time};
|
||||
use crate::transport::{
|
||||
ConfiguredLoginParam, ConfiguredServerLoginParam, prioritize_server_login_params,
|
||||
};
|
||||
use crate::{
|
||||
calls::{UnresolvedIceServer, create_fallback_ice_servers, create_ice_servers_from_metadata},
|
||||
ephemeral::delete_expired_imap_messages,
|
||||
};
|
||||
|
||||
pub(crate) mod capabilities;
|
||||
mod client;
|
||||
@@ -526,12 +525,6 @@ impl Imap {
|
||||
context.scheduler.interrupt_ephemeral_task().await;
|
||||
}
|
||||
|
||||
// Mark expired messages for deletion. Note that `delete_expired_imap_messages` is not
|
||||
// not well optimized and should not be called before fetching.
|
||||
delete_expired_imap_messages(context, session.transport_id(), session.is_chatmail())
|
||||
.await
|
||||
.context("delete_expired_imap_messages")?;
|
||||
|
||||
session
|
||||
.move_delete_messages(context, watch_folder)
|
||||
.await
|
||||
@@ -1386,21 +1379,6 @@ impl Session {
|
||||
"Passing message UID {} to receive_imf().", request_uid
|
||||
);
|
||||
let res = receive_imf_inner(context, rfc724_mid, body, is_seen).await;
|
||||
|
||||
// TODO I don't think this code is needed anymore:
|
||||
// // If the message is not needed anymore on the server, mark it for deletion:
|
||||
// if !context.get_config_bool(Config::BccSelf).await? && is_chatmail {
|
||||
// context
|
||||
// .sql
|
||||
// .execute(
|
||||
// "UPDATE imap SET target='' WHERE rfc724_mid=?",
|
||||
// (rfc724_mid,),
|
||||
// )
|
||||
// .await?;
|
||||
// context.scheduler.interrupt_inbox().await;
|
||||
// }
|
||||
|
||||
// If there was an error receiving the message, show a device message:
|
||||
let received_msg = match res {
|
||||
Err(err) => {
|
||||
warn!(context, "receive_imf error: {err:#}.");
|
||||
@@ -2016,12 +1994,21 @@ pub(crate) async fn prefetch_should_download(
|
||||
// prevent_rename=true as this might be a mailing list message and in this case it would be bad if we rename the contact.
|
||||
// (prevent_rename is the last argument of from_field_to_contact_id())
|
||||
|
||||
let is_encrypted = if let Some(content_type) = headers.get_header_value(HeaderDef::ContentType)
|
||||
{
|
||||
mailparse::parse_content_type(&content_type).mimetype == "multipart/encrypted"
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if flags.any(|f| f == Flag::Draft) {
|
||||
info!(context, "Ignoring draft message");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let should_download = !blocked_contact || maybe_ndn;
|
||||
let should_download = maybe_ndn
|
||||
|| (!blocked_contact
|
||||
&& (is_encrypted || !context.get_config_bool(Config::ForceEncryption).await?));
|
||||
Ok(should_download)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ const PREFETCH_FLAGS: &str = "(UID RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\
|
||||
DATE \
|
||||
X-MICROSOFT-ORIGINAL-MESSAGE-ID \
|
||||
FROM \
|
||||
CONTENT-TYPE \
|
||||
CHAT-VERSION \
|
||||
CHAT-IS-POST-MESSAGE \
|
||||
AUTOCRYPT-SETUP-MESSAGE\
|
||||
|
||||
27
src/imex.rs
27
src/imex.rs
@@ -980,15 +980,20 @@ mod tests {
|
||||
|
||||
// Check that the settings are displayed correctly.
|
||||
assert_eq!(
|
||||
context1.get_config(Config::BccSelf).await?,
|
||||
context1.get_config(Config::DeleteServerAfter).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
context1.set_config_bool(Config::IsChatmail, true).await?;
|
||||
assert_eq!(
|
||||
context1.get_config(Config::BccSelf).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
context1.get_config(Config::DeleteServerAfter).await?,
|
||||
Some("1".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(context1.get_config_bool(Config::IsMuted).await?, false);
|
||||
context1.set_config_bool(Config::IsMuted, true).await?;
|
||||
assert_eq!(context1.get_config_bool(Config::IsMuted).await?, true);
|
||||
|
||||
assert_eq!(context1.get_config_delete_server_after().await?, Some(0));
|
||||
imex(context1, ImexMode::ExportBackup, backup_dir.path(), None).await?;
|
||||
let _event = context1
|
||||
.evtracker
|
||||
@@ -1005,9 +1010,15 @@ mod tests {
|
||||
assert!(context2.is_configured().await?);
|
||||
assert!(context2.is_chatmail().await?);
|
||||
for ctx in [context1, context2] {
|
||||
// BccSelf should be enabled automatically when exporting a backup
|
||||
assert_eq!(ctx.get_config_bool(Config::BccSelf).await?, true);
|
||||
assert_eq!(ctx.get_config_bool(Config::IsMuted).await?, true);
|
||||
assert_eq!(
|
||||
ctx.get_config(Config::BccSelf).await?,
|
||||
Some("1".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.get_config(Config::DeleteServerAfter).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
assert_eq!(ctx.get_config_delete_server_after().await?, None);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -871,6 +871,7 @@ mod tests {
|
||||
use crate::config::Config;
|
||||
use crate::message::MessageState;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{ExpectedEvents, TestContext, TestContextManager};
|
||||
use crate::tools::SystemTime;
|
||||
|
||||
@@ -939,12 +940,15 @@ mod tests {
|
||||
/// Tests that location.kml is hidden.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn receive_location_kml() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
receive_imf(
|
||||
&alice,
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
br#"Subject: Hello
|
||||
Message-ID: hello@example.net
|
||||
Message-ID: <hello@example.net>
|
||||
To: Alice <alice@example.org>
|
||||
From: Bob <bob@example.net>
|
||||
Date: Mon, 20 Dec 2021 00:00:00 +0000
|
||||
@@ -952,14 +956,15 @@ Chat-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
Text message."#,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
let received_msg = alice.get_last_msg().await;
|
||||
assert_eq!(received_msg.text, "Text message.");
|
||||
|
||||
receive_imf(
|
||||
&alice,
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
br#"Subject: locations
|
||||
MIME-Version: 1.0
|
||||
To: <alice@example.org>
|
||||
@@ -986,16 +991,14 @@ Content-Disposition: attachment; filename="location.kml"
|
||||
</Document>
|
||||
</kml>
|
||||
|
||||
--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#).await?;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
|
||||
// Received location message is not visible, last message stays the same.
|
||||
let received_msg2 = alice.get_last_msg().await;
|
||||
assert_eq!(received_msg2.id, received_msg.id);
|
||||
|
||||
let locations = get_range(&alice, None, None, 0, 0).await?;
|
||||
let locations = get_range(alice, None, None, 0, 0).await?;
|
||||
assert_eq!(locations.len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1003,10 +1006,13 @@ Content-Disposition: attachment; filename="location.kml"
|
||||
/// Tests that `location.kml` is not hidden and not seen if it contains a message.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn receive_visible_location_kml() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
receive_imf(
|
||||
&alice,
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
br#"Subject: locations
|
||||
MIME-Version: 1.0
|
||||
To: <alice@example.org>
|
||||
@@ -1034,16 +1040,15 @@ Content-Disposition: attachment; filename="location.kml"
|
||||
</Document>
|
||||
</kml>
|
||||
|
||||
--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#).await?;
|
||||
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
|
||||
let received_msg = alice.get_last_msg().await;
|
||||
assert_eq!(received_msg.text, "Text message.");
|
||||
assert_eq!(received_msg.state, MessageState::InFresh);
|
||||
|
||||
let locations = get_range(&alice, None, None, 0, 0).await?;
|
||||
let locations = get_range(alice, None, None, 0, 0).await?;
|
||||
assert_eq!(locations.len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2099,52 +2099,63 @@ pub async fn get_request_msg_cnt(context: &Context) -> usize {
|
||||
}
|
||||
|
||||
/// Estimates the number of messages that will be deleted
|
||||
/// by the `set_config()`-option `delete_device_after`.
|
||||
/// by the options `delete_device_after` or `delete_server_after`.
|
||||
///
|
||||
/// This is typically used to show the estimated impact to the user
|
||||
/// before actually enabling deletion of old messages.
|
||||
///
|
||||
/// Messages in the "Saved Messages" chat are not counted as they will not be deleted automatically.
|
||||
/// If `from_server` is true,
|
||||
/// estimate deletion count for server,
|
||||
/// otherwise estimate deletion count for device.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `from_server`: Deprecated, pass `false` here
|
||||
/// - `seconds`: Count messages older than the given number of seconds.
|
||||
/// Count messages older than the given number of `seconds`.
|
||||
///
|
||||
/// Returns the number of messages that are older than the given number of seconds.
|
||||
/// Messages in the "saved messages" folder are not counted as they will not be deleted automatically.
|
||||
#[expect(clippy::arithmetic_side_effects)]
|
||||
pub async fn estimate_deletion_cnt(
|
||||
context: &Context,
|
||||
from_server: bool,
|
||||
seconds: i64,
|
||||
) -> Result<usize> {
|
||||
ensure!(
|
||||
!from_server,
|
||||
"The `delete_server_after` config option was removed. You need to pass `false` for `from_server`."
|
||||
);
|
||||
|
||||
let self_chat_id = ChatIdBlocked::lookup_by_contact(context, ContactId::SELF)
|
||||
.await?
|
||||
.map(|c| c.id)
|
||||
.unwrap_or_default();
|
||||
let threshold_timestamp = time() - seconds;
|
||||
|
||||
let cnt = context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
let cnt = if from_server {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs m
|
||||
WHERE m.id > ?
|
||||
AND timestamp < ?
|
||||
AND chat_id != ?
|
||||
AND EXISTS (SELECT * FROM imap WHERE rfc724_mid=m.rfc724_mid);",
|
||||
(DC_MSG_ID_LAST_SPECIAL, threshold_timestamp, self_chat_id),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs m
|
||||
WHERE m.id > ?
|
||||
AND timestamp < ?
|
||||
AND chat_id != ?
|
||||
AND chat_id != ? AND hidden = 0;",
|
||||
(
|
||||
DC_MSG_ID_LAST_SPECIAL,
|
||||
threshold_timestamp,
|
||||
self_chat_id,
|
||||
DC_CHAT_ID_TRASH,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
(
|
||||
DC_MSG_ID_LAST_SPECIAL,
|
||||
threshold_timestamp,
|
||||
self_chat_id,
|
||||
DC_CHAT_ID_TRASH,
|
||||
),
|
||||
)
|
||||
.await?
|
||||
};
|
||||
Ok(cnt)
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
|
||||
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
tcm.section("Bob sends encrypted message to Alice");
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
@@ -139,23 +140,14 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_chat_id() {
|
||||
// Alice receives a message that pops up as a contact request
|
||||
let alice = TestContext::new_alice().await;
|
||||
receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <123@example.com>\n\
|
||||
Date: Fri, 29 Jan 2021 21:37:55 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let chat_id = bob.create_chat_id(alice).await;
|
||||
let sent = bob.send_text(chat_id, "hello").await;
|
||||
let msg = alice.recv_msg(&sent).await;
|
||||
|
||||
// check chat-id of this message
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert!(!msg.get_chat_id().is_special());
|
||||
assert_eq!(msg.get_text(), "hello".to_string());
|
||||
}
|
||||
@@ -465,7 +457,9 @@ async fn test_get_state() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_is_bot() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
// Alice receives an auto-generated non-chat message.
|
||||
//
|
||||
@@ -473,7 +467,7 @@ async fn test_is_bot() -> Result<()> {
|
||||
// in which case the message should be marked as bot-generated,
|
||||
// but the contact should not.
|
||||
receive_imf(
|
||||
&alice,
|
||||
alice,
|
||||
b"From: Claire <claire@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <789@example.com>\n\
|
||||
@@ -487,12 +481,12 @@ async fn test_is_bot() -> Result<()> {
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.get_text(), "hello".to_string());
|
||||
assert!(msg.is_bot());
|
||||
let contact = Contact::get_by_id(&alice, msg.from_id).await?;
|
||||
let contact = Contact::get_by_id(alice, msg.from_id).await?;
|
||||
assert!(!contact.is_bot());
|
||||
|
||||
// Alice receives a message from Bob the bot.
|
||||
receive_imf(
|
||||
&alice,
|
||||
alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Chat-Version: 1.0\n\
|
||||
@@ -507,12 +501,12 @@ async fn test_is_bot() -> Result<()> {
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.get_text(), "hello".to_string());
|
||||
assert!(msg.is_bot());
|
||||
let contact = Contact::get_by_id(&alice, msg.from_id).await?;
|
||||
let contact = Contact::get_by_id(alice, msg.from_id).await?;
|
||||
assert!(contact.is_bot());
|
||||
|
||||
// Alice receives a message from Bob who is not the bot anymore.
|
||||
receive_imf(
|
||||
&alice,
|
||||
alice,
|
||||
b"From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Chat-Version: 1.0\n\
|
||||
@@ -526,7 +520,7 @@ async fn test_is_bot() -> Result<()> {
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.get_text(), "hello again".to_string());
|
||||
assert!(!msg.is_bot());
|
||||
let contact = Contact::get_by_id(&alice, msg.from_id).await?;
|
||||
let contact = Contact::get_by_id(alice, msg.from_id).await?;
|
||||
assert!(!contact.is_bot());
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::headerdef::HeaderDef;
|
||||
use crate::message;
|
||||
use crate::mimeparser::MimeMessage;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{TestContext, TestContextManager, get_chat_msg};
|
||||
use crate::tools::SystemTime;
|
||||
|
||||
@@ -132,14 +133,13 @@ async fn test_subject_from_mua() {
|
||||
// 1.: Receive a mail from an MUA
|
||||
assert_eq!(
|
||||
msg_to_subject_str(
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Antw: Chat: hello\n\
|
||||
Message-ID: <2222@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: Antw: Chat: hello\r\n\
|
||||
Message-ID: <2222@example.net>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
)
|
||||
.await,
|
||||
"Re: Chat: hello"
|
||||
@@ -147,14 +147,13 @@ async fn test_subject_from_mua() {
|
||||
|
||||
assert_eq!(
|
||||
msg_to_subject_str(
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Infos: 42\n\
|
||||
Message-ID: <2222@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: Infos: 42\r\n\
|
||||
Message-ID: <2222@example.net>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
)
|
||||
.await,
|
||||
"Re: Infos: 42"
|
||||
@@ -166,15 +165,14 @@ async fn test_subject_from_dc() {
|
||||
// 2. Receive a message from Delta Chat
|
||||
assert_eq!(
|
||||
msg_to_subject_str(
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: bob@example.com\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Chat: hello\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <2223@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
b"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: Chat: hello\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <2223@example.net>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
)
|
||||
.await,
|
||||
"Re: Chat: hello"
|
||||
@@ -199,29 +197,27 @@ async fn test_subject_outgoing() {
|
||||
async fn test_subject_unicode() {
|
||||
// 4. Receive messages with unicode characters and make sure that we do not panic (we do not care about the result)
|
||||
msg_to_subject_str(
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: bob@example.com\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: äääää\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <2893@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: äääää\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <2893@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.await;
|
||||
|
||||
msg_to_subject_str(
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: bob@example.com\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: aäääää\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <2893@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: aäääää\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <2893@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.await;
|
||||
@@ -230,73 +226,76 @@ async fn test_subject_unicode() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_mdn() {
|
||||
// 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
receive_imf(
|
||||
&t,
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: alice@example.org\n\
|
||||
To: bob@example.com\n\
|
||||
Subject: Hello, Bob\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <2893@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
t,
|
||||
b"From: alice@example.org\r\n\
|
||||
To: bob@example.net\r\n\
|
||||
Subject: Hello, Bob\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <2893@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n",
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut new_msg = incoming_msg_to_reply_msg(
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: bob@example.com\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: message opened\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <Mr.12345678902@example.com>\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\n\
|
||||
\n\
|
||||
\n\
|
||||
--SNIPP\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
Read receipts do not guarantee sth. was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--SNIPP\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Reporting-UA: Delta Chat 1.28.0\n\
|
||||
Original-Recipient: rfc822;bob@example.com\n\
|
||||
Final-Recipient: rfc822;bob@example.com\n\
|
||||
Original-Message-ID: <2893@example.com>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
\n", &t).await;
|
||||
chat::send_msg(&t, new_msg.chat_id, &mut new_msg)
|
||||
b"From: bob@example.net\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: message opened\r\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <Mr.12345678902@example.com>\r\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\r\n\
|
||||
\r\n\
|
||||
\r\n\
|
||||
--SNIPP\r\n\
|
||||
Content-Type: text/plain; charset=utf-8\r\n\
|
||||
\r\n\
|
||||
Read receipts do not guarantee sth. was read.\r\n\
|
||||
\r\n\
|
||||
\r\n\
|
||||
--SNIPP\r\n\
|
||||
Content-Type: message/disposition-notification\r\n\
|
||||
\r\n\
|
||||
Reporting-UA: Delta Chat 1.28.0\r\n\
|
||||
Original-Recipient: rfc822;bob@example.com\r\n\
|
||||
Final-Recipient: rfc822;bob@example.com\r\n\
|
||||
Original-Message-ID: <2893@example.com>\r\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\r\n\
|
||||
\r\n", t, bob).await;
|
||||
chat::send_msg(t, new_msg.chat_id, &mut new_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
let mf = MimeFactory::from_msg(&t, new_msg).await.unwrap();
|
||||
let mf = MimeFactory::from_msg(t, new_msg).await.unwrap();
|
||||
// The subject string should not be "Re: message opened"
|
||||
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap());
|
||||
assert_eq!("Re: Hello, Bob", mf.subject_str(t).await.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mdn_create_encrypted() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
alice
|
||||
.set_config(Config::Displayname, Some("Alice Exampleorg"))
|
||||
.await?;
|
||||
let bob = tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
bob.set_config(Config::Displayname, Some("Bob Examplenet"))
|
||||
.await?;
|
||||
bob.set_config(Config::Selfstatus, Some("Bob Examplenet"))
|
||||
.await?;
|
||||
bob.set_config_bool(Config::MdnsEnabled, true).await?;
|
||||
|
||||
// MDN for unencrypted message is not encrypted.
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.param.set_int(Param::SkipAutocrypt, 1);
|
||||
let chat_alice = alice.create_chat(&bob).await.id;
|
||||
let chat_alice = alice.create_email_chat(&bob).await.id;
|
||||
let sent = alice.send_msg(chat_alice, &mut msg).await;
|
||||
|
||||
let rcvd = bob.recv_msg(&sent).await;
|
||||
@@ -311,13 +310,13 @@ async fn test_mdn_create_encrypted() -> Result<()> {
|
||||
let bob_alice_contact = bob.add_or_lookup_contact(&alice).await;
|
||||
assert_eq!(bob_alice_contact.get_authname(), "Alice Exampleorg");
|
||||
|
||||
// MDN for encrypted message is encrypted.
|
||||
let rcvd = tcm.send_recv(&alice, &bob, "Heyho").await;
|
||||
message::markseen_msgs(&bob, vec![rcvd.id]).await?;
|
||||
|
||||
let mimefactory = MimeFactory::from_mdn(&bob, rcvd.from_id, rcvd.rfc724_mid, vec![]).await?;
|
||||
let rendered_msg = mimefactory.render(&bob).await?;
|
||||
|
||||
// When encrypted, the MDN should be encrypted as well
|
||||
assert!(rendered_msg.is_encrypted);
|
||||
assert!(!rendered_msg.message.contains("Bob Examplenet"));
|
||||
assert!(!rendered_msg.message.contains("Alice Exampleorg"));
|
||||
@@ -417,7 +416,7 @@ async fn first_subject_str(t: TestContext) -> String {
|
||||
mf.subject_str(&t).await.unwrap()
|
||||
}
|
||||
|
||||
// In `imf_raw`, From has to be bob@example.com, To has to be alice@example.org
|
||||
// In `imf_raw`, From has to be bob@example.net, To has to be alice@example.org
|
||||
async fn msg_to_subject_str(imf_raw: &[u8]) -> String {
|
||||
let subject_str = msg_to_subject_str_inner(imf_raw, false, false, false).await;
|
||||
|
||||
@@ -465,48 +464,62 @@ async fn msg_to_subject_str_inner(
|
||||
reply: bool,
|
||||
message_arrives_inbetween: bool,
|
||||
) -> String {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut new_msg = incoming_msg_to_reply_msg(imf_raw, &t).await;
|
||||
let incoming_msg = get_chat_msg(&t, new_msg.chat_id, 0, 1).await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let mut new_msg = incoming_msg_to_reply_msg(imf_raw, t, bob).await;
|
||||
let incoming_msg = get_chat_msg(t, new_msg.chat_id, 1, 2).await;
|
||||
|
||||
if delete_original_msg {
|
||||
incoming_msg.id.trash(&t, false).await.unwrap();
|
||||
incoming_msg.id.trash(t, false).await.unwrap();
|
||||
}
|
||||
|
||||
if message_arrives_inbetween {
|
||||
receive_imf(
|
||||
&t,
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: Bob <bob@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Some other, completely unrelated subject\n\
|
||||
Message-ID: <3cl4@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
Some other, completely unrelated content\n",
|
||||
false,
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[t],
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: Some other, completely unrelated subject\r\n\
|
||||
Message-ID: <3cl4@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
Some other, completely unrelated content\r\n",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(t, encrypted_msg.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let arrived_msg = t.get_last_msg().await;
|
||||
assert_eq!(arrived_msg.chat_id, incoming_msg.chat_id);
|
||||
}
|
||||
|
||||
if reply {
|
||||
new_msg.set_quote(&t, Some(&incoming_msg)).await.unwrap();
|
||||
new_msg.set_quote(t, Some(&incoming_msg)).await.unwrap();
|
||||
}
|
||||
|
||||
chat::send_msg(&t, new_msg.chat_id, &mut new_msg)
|
||||
chat::send_msg(t, new_msg.chat_id, &mut new_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
let mf = MimeFactory::from_msg(&t, new_msg).await.unwrap();
|
||||
mf.subject_str(&t).await.unwrap()
|
||||
let mf = MimeFactory::from_msg(t, new_msg).await.unwrap();
|
||||
mf.subject_str(t).await.unwrap()
|
||||
}
|
||||
|
||||
// Creates a `Message` that replies "Hi" to the incoming email in `imf_raw`.
|
||||
async fn incoming_msg_to_reply_msg(imf_raw: &[u8], context: &Context) -> Message {
|
||||
receive_imf(context, imf_raw, false).await.unwrap();
|
||||
async fn incoming_msg_to_reply_msg(
|
||||
imf_raw: &[u8],
|
||||
context: &TestContext,
|
||||
from: &TestContext,
|
||||
) -> Message {
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(from, &[context], imf_raw)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
receive_imf(context, encrypted_msg.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(context, 0, None, None).await.unwrap();
|
||||
|
||||
@@ -522,30 +535,31 @@ async fn incoming_msg_to_reply_msg(imf_raw: &[u8], context: &Context) -> Message
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
// This test could still be extended
|
||||
async fn test_render_reply() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let context = &t;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
let charlie = &tcm.charlie().await;
|
||||
|
||||
let mut msg = incoming_msg_to_reply_msg(
|
||||
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: Charlie <charlie@example.com>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Chat: hello\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <2223@example.com>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
context,
|
||||
b"From: Charlie <charlie@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: Chat: hello\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: <2223@example.com>\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n",
|
||||
t,
|
||||
charlie,
|
||||
)
|
||||
.await;
|
||||
chat::send_msg(&t, msg.chat_id, &mut msg).await.unwrap();
|
||||
chat::send_msg(t, msg.chat_id, &mut msg).await.unwrap();
|
||||
|
||||
let mimefactory = MimeFactory::from_msg(&t, msg).await.unwrap();
|
||||
let mimefactory = MimeFactory::from_msg(t, msg).await.unwrap();
|
||||
|
||||
let recipients = mimefactory.recipients();
|
||||
assert_eq!(recipients, vec!["charlie@example.com"]);
|
||||
assert_eq!(recipients, vec!["charlie@example.net"]);
|
||||
|
||||
let rendered_msg = mimefactory.render(context).await.unwrap();
|
||||
let rendered_msg = mimefactory.render(t).await.unwrap();
|
||||
|
||||
let mail = mailparse::parse_mail(rendered_msg.message.as_bytes()).unwrap();
|
||||
assert_eq!(
|
||||
@@ -557,7 +571,7 @@ async fn test_render_reply() {
|
||||
"1.0"
|
||||
);
|
||||
|
||||
let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes())
|
||||
let _mime_msg = MimeMessage::from_bytes(t, rendered_msg.message.as_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
message::{MessageState, MessengerMessage},
|
||||
receive_imf::receive_imf,
|
||||
securejoin::QrInvite,
|
||||
test_utils::{TestContext, TestContextManager},
|
||||
test_utils::{self, TestContext, TestContextManager},
|
||||
tools::time,
|
||||
};
|
||||
|
||||
@@ -1185,6 +1185,7 @@ async fn test_allinkl_blockquote() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_subj_to_multimedia_msg() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
receive_imf(
|
||||
&t.ctx,
|
||||
include_bytes!("../../test-data/message/subj_with_multimedia_msg.eml"),
|
||||
@@ -1461,6 +1462,7 @@ async fn test_intended_recipient_fingerprint() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_long_in_reply_to() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// A message with a long Message-ID.
|
||||
// Long message-IDs are generated by Mailjet.
|
||||
@@ -1503,96 +1505,89 @@ Some reply
|
||||
// Test that WantsMdn parameter is not set on outgoing messages.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_wants_mdn() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let alice2 = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let raw = br"Date: Thu, 28 Jan 2021 00:26:57 +0000
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <foobarbaz@example.org>
|
||||
To: Bob <bob@example.org>
|
||||
From: Alice <alice@example.org>
|
||||
Subject: subject
|
||||
Chat-Disposition-Notification-To: alice@example.org
|
||||
|
||||
Message.
|
||||
";
|
||||
let chat_id = alice.create_chat(bob).await.id;
|
||||
let sent = alice.send_text(chat_id, "Message.").await;
|
||||
|
||||
// Bob receives message.
|
||||
receive_imf(&bob, raw, false).await?;
|
||||
let msg = bob.get_last_msg().await;
|
||||
let bob_msg = bob.recv_msg(&sent).await;
|
||||
// Message is incoming.
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).unwrap());
|
||||
assert!(bob_msg.param.get_bool(Param::WantsMdn).unwrap());
|
||||
|
||||
// Alice receives copy-to-self.
|
||||
receive_imf(&alice, raw, false).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
let alice2_msg = alice2.recv_msg(&sent).await;
|
||||
// Message is outgoing, don't send read receipt to self.
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).is_none());
|
||||
assert!(alice2_msg.param.get_bool(Param::WantsMdn).is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ignore_read_receipt_to_self() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
// Alice receives BCC-self copy of a message sent to Bob.
|
||||
receive_imf(
|
||||
&alice,
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: alice@example.org\n\
|
||||
To: bob@example.net\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: first@example.com\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Disposition-Notification-To: alice@example.org\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n"
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
alice,
|
||||
&[bob],
|
||||
"From: alice@example.org\r\n\
|
||||
To: bob@example.net\r\n\
|
||||
Subject: foo\r\n\
|
||||
Message-ID: first@example.com\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Chat-Disposition-Notification-To: alice@example.org\r\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\r\n\
|
||||
\r\n\
|
||||
hello\r\n"
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.state, MessageState::OutDelivered);
|
||||
|
||||
// Due to a bug in the old version running on the other device, Alice receives a read
|
||||
// receipt from self.
|
||||
receive_imf(
|
||||
&alice,
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: alice@example.org\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: message opened\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: second@example.com\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\n\
|
||||
\n\
|
||||
\n\
|
||||
--SNIPP\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
Read receipts do not guarantee sth. was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--SNIPP\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Original-Recipient: rfc822;bob@example.com\n\
|
||||
Final-Recipient: rfc822;bob@example.com\n\
|
||||
Original-Message-ID: <first@example.com>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
\n\
|
||||
\n\
|
||||
--SNIPP--"
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
// Alice receives a read receipt from self.
|
||||
//
|
||||
// This should mark the message as seen, i.e. not counted as fresh,
|
||||
// but not "read" (having double checkmark in the UI).
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
alice,
|
||||
&[alice],
|
||||
"From: alice@example.org\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: message opened\r\n\
|
||||
Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Message-ID: second@example.com\r\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\r\n\
|
||||
\r\n\
|
||||
\r\n\
|
||||
--SNIPP\r\n\
|
||||
Content-Type: text/plain; charset=utf-8\r\n\
|
||||
\r\n\
|
||||
Read receipts do not guarantee sth. was read.\r\n\
|
||||
\r\n\
|
||||
\r\n\
|
||||
--SNIPP\r\n\
|
||||
Content-Type: message/disposition-notification\r\n\
|
||||
\r\n\
|
||||
Original-Recipient: rfc822;bob@example.com\r\n\
|
||||
Final-Recipient: rfc822;bob@example.com\r\n\
|
||||
Original-Message-ID: <first@example.com>\r\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\r\n\
|
||||
\r\n\
|
||||
\r\n\
|
||||
--SNIPP--".as_bytes()).await?;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
|
||||
// Check that the state has not changed to `MessageState::OutMdnRcvd`.
|
||||
let msg = Message::load_from_db(&alice, msg.id).await?;
|
||||
let msg = Message::load_from_db(alice, msg.id).await?;
|
||||
assert_eq!(msg.state, MessageState::OutDelivered);
|
||||
|
||||
Ok(())
|
||||
@@ -1604,7 +1599,9 @@ async fn test_ignore_read_receipt_to_self() -> Result<()> {
|
||||
/// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ms_exchange_mdn() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = tcm.alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let original =
|
||||
include_bytes!("../../test-data/message/ms_exchange_report_original_message.eml");
|
||||
@@ -1700,17 +1697,24 @@ Content-Disposition: reaction\n\
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_jpeg_as_application_octet_stream() -> Result<()> {
|
||||
let context = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../../test-data/message/jpeg-as-application-octet-stream.eml");
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
alice,
|
||||
&[bob],
|
||||
include_bytes!("../../test-data/message/jpeg-as-application-octet-stream.eml"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let msg = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let msg = MimeMessage::from_bytes(bob, encrypted_msg.as_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msg.parts.len(), 1);
|
||||
assert_eq!(msg.parts[0].typ, Viewtype::Image);
|
||||
|
||||
receive_imf(&context, &raw[..], false).await?;
|
||||
let msg = context.get_last_msg().await;
|
||||
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::Image);
|
||||
|
||||
Ok(())
|
||||
@@ -1984,6 +1988,8 @@ async fn test_chat_edit_imf_header() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
bob.allow_unencrypted().await?;
|
||||
let alice_chat = alice.create_email_chat(bob).await;
|
||||
|
||||
// Alice sends a message, then sends an invalid edit request.
|
||||
@@ -2048,6 +2054,7 @@ async fn test_multiple_autocrypt_hdrs() -> Result<()> {
|
||||
async fn test_receive_signed_only() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let bob = &tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
let imf_raw = include_bytes!("../../test-data/message/unencrypted_signed_simple.eml");
|
||||
let msg = receive_imf(bob, imf_raw, false).await?.unwrap();
|
||||
@@ -2065,19 +2072,24 @@ async fn test_receive_signed_only() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_huge_image_becomes_file() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let msg_id = receive_imf(
|
||||
&t,
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
include_bytes!("../../test-data/message/image_huge_64M.eml"),
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
.unwrap()
|
||||
.msg_ids[0];
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
.await?;
|
||||
|
||||
let msg_id = receive_imf(alice, encrypted_msg.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap()
|
||||
.msg_ids[0];
|
||||
let msg = Message::load_from_db(alice, msg_id).await.unwrap();
|
||||
// Huge image should be treated as file:
|
||||
assert_eq!(msg.viewtype, Viewtype::File);
|
||||
assert!(msg.get_file(&t).is_some());
|
||||
assert!(msg.get_file(alice).is_some());
|
||||
assert_eq!(msg.get_filename().unwrap(), "huge_image.png");
|
||||
assert_eq!(msg.get_filemime().unwrap(), "image/png");
|
||||
// File has no width or height
|
||||
@@ -2088,19 +2100,24 @@ async fn test_huge_image_becomes_file() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_4k_image_stays_image() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let msg_id = receive_imf(
|
||||
&t,
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let encrypted_msg = test_utils::encrypt_raw_message(
|
||||
bob,
|
||||
&[alice],
|
||||
include_bytes!("../../test-data/message/image_4k.eml"),
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
.unwrap()
|
||||
.msg_ids[0];
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
.await?;
|
||||
|
||||
let msg_id = receive_imf(alice, encrypted_msg.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap()
|
||||
.msg_ids[0];
|
||||
let msg = Message::load_from_db(alice, msg_id).await.unwrap();
|
||||
// 4K image should be treated as image:
|
||||
assert_eq!(msg.viewtype, Viewtype::Image);
|
||||
assert!(msg.get_file(&t).is_some());
|
||||
assert!(msg.get_file(alice).is_some());
|
||||
assert_eq!(msg.get_filename().unwrap(), "4k_image.png");
|
||||
assert_eq!(msg.get_filemime().unwrap(), "image/png");
|
||||
assert_eq!(msg.param.get_int(Param::Width).unwrap_or_default(), 3840);
|
||||
|
||||
16
src/net.rs
16
src/net.rs
@@ -109,8 +109,8 @@ pub(crate) async fn connect_tcp_inner(
|
||||
) -> Result<Pin<Box<TimeoutStream<TcpStream>>>> {
|
||||
let tcp_stream = timeout(TIMEOUT, TcpStream::connect(addr))
|
||||
.await
|
||||
.with_context(|| format!("Connection to {addr} timed out"))?
|
||||
.with_context(|| format!("Connection to {addr} failed"))?;
|
||||
.context("Connection timeout")?
|
||||
.context("Connection failure")?;
|
||||
|
||||
// Disable Nagle's algorithm.
|
||||
tcp_stream.set_nodelay(true)?;
|
||||
@@ -180,7 +180,7 @@ where
|
||||
delay_set.spawn(tokio::time::sleep(delay));
|
||||
}
|
||||
|
||||
let mut all_errors = Vec::new();
|
||||
let mut first_error = None;
|
||||
|
||||
let res = loop {
|
||||
if let Some(fut) = futures.next() {
|
||||
@@ -200,7 +200,7 @@ where
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
// Some connection attempt failed.
|
||||
all_errors.push(err);
|
||||
first_error.get_or_insert(err);
|
||||
}
|
||||
Err(err) => {
|
||||
break Err(err);
|
||||
@@ -211,11 +211,9 @@ where
|
||||
// Out of connection attempts.
|
||||
//
|
||||
// Break out of the loop and return error.
|
||||
break if all_errors.is_empty() {
|
||||
Err(format_err!("No connection attempts were made"))
|
||||
} else {
|
||||
Err(format_err!("All connection attempts failed: {}", all_errors.into_iter().map(|err| format!("{err:#}")).collect::<Vec<String>>().join("; ")))
|
||||
};
|
||||
break Err(
|
||||
first_error.unwrap_or_else(|| format_err!("No connection attempts were made"))
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -890,10 +890,16 @@ static P_NAUTA_CU: Provider = Provider {
|
||||
strict_tls: false,
|
||||
..ProviderOptions::new()
|
||||
},
|
||||
config_defaults: Some(&[ConfigDefault {
|
||||
key: Config::MediaQuality,
|
||||
value: "1",
|
||||
}]),
|
||||
config_defaults: Some(&[
|
||||
ConfigDefault {
|
||||
key: Config::DeleteServerAfter,
|
||||
value: "1",
|
||||
},
|
||||
ConfigDefault {
|
||||
key: Config::MediaQuality,
|
||||
value: "1",
|
||||
},
|
||||
]),
|
||||
oauth2_authorizer: None,
|
||||
};
|
||||
|
||||
@@ -2376,4 +2382,4 @@ pub(crate) static PROVIDER_IDS: LazyLock<HashMap<&'static str, &'static Provider
|
||||
});
|
||||
|
||||
pub static _PROVIDER_UPDATED: LazyLock<chrono::NaiveDate> =
|
||||
LazyLock::new(|| chrono::NaiveDate::from_ymd_opt(2026, 5, 6).unwrap());
|
||||
LazyLock::new(|| chrono::NaiveDate::from_ymd_opt(2026, 4, 21).unwrap());
|
||||
|
||||
@@ -335,18 +335,17 @@ impl Chat {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deltachat_contact_tools::ContactAddress;
|
||||
|
||||
use super::*;
|
||||
use crate::chat::{forward_msgs, get_chat_msgs, marknoticed_chat, send_text_msg};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::config::Config;
|
||||
use crate::contact::{Contact, Origin};
|
||||
use crate::contact::Contact;
|
||||
use crate::key::{load_self_public_key, load_self_secret_key};
|
||||
use crate::message::{MessageState, Viewtype, delete_msgs, markseen_msgs};
|
||||
use crate::pgp::{SeipdVersion, pk_encrypt};
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::sql::housekeeping;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::E2EE_INFO_MSGS;
|
||||
use crate::test_utils::TestContext;
|
||||
use crate::test_utils::TestContextManager;
|
||||
@@ -386,59 +385,54 @@ mod tests {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_reaction() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
// Alice receives BCC-self copy of a message sent to Bob.
|
||||
receive_imf(
|
||||
&alice,
|
||||
"To: bob@example.net\n\
|
||||
From: alice@example.org\n\
|
||||
Date: Today, 29 February 2021 00:00:00 -800\n\
|
||||
Message-ID: 12345@example.org\n\
|
||||
Subject: Meeting\n\
|
||||
\n\
|
||||
Can we chat at 1pm pacific, today?"
|
||||
.as_bytes(),
|
||||
false,
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
alice,
|
||||
&[alice, bob],
|
||||
b"To: bob@example.net\r\n\
|
||||
From: alice@example.org\r\n\
|
||||
Date: Today, 29 February 2021 00:00:00 -800\r\n\
|
||||
Message-ID: 12345@example.org\r\n\
|
||||
Subject: Meeting\r\n\
|
||||
\r\n\
|
||||
Can we chat at 1pm pacific, today?",
|
||||
)
|
||||
.await?;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.state, MessageState::OutDelivered);
|
||||
let reactions = get_msg_reactions(&alice, msg.id).await?;
|
||||
let reactions = get_msg_reactions(alice, msg.id).await?;
|
||||
let contacts = reactions.contacts();
|
||||
assert_eq!(contacts.len(), 0);
|
||||
|
||||
let bob_id = Contact::add_or_lookup(
|
||||
&alice,
|
||||
"",
|
||||
&ContactAddress::new("bob@example.net")?,
|
||||
Origin::ManuallyCreated,
|
||||
)
|
||||
.await?
|
||||
.0;
|
||||
let bob_id = alice.add_or_lookup_contact_id(bob).await;
|
||||
let bob_reaction = reactions.get(bob_id);
|
||||
assert!(bob_reaction.is_empty()); // Bob has not reacted to message yet.
|
||||
|
||||
// Alice receives reaction to her message from Bob.
|
||||
receive_imf(
|
||||
&alice,
|
||||
"To: alice@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Date: Today, 29 February 2021 00:00:10 -800\n\
|
||||
Message-ID: 56789@example.net\n\
|
||||
In-Reply-To: 12345@example.org\n\
|
||||
Subject: Meeting\n\
|
||||
Mime-Version: 1.0 (1.0)\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
Content-Disposition: reaction\n\
|
||||
\n\
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
"To: alice@example.org\r\n\
|
||||
From: bob@example.net\r\n\
|
||||
Date: Today, 29 February 2021 00:00:10 -800\r\n\
|
||||
Message-ID: 56789@example.net\r\n\
|
||||
In-Reply-To: 12345@example.org\r\n\
|
||||
Subject: Meeting\r\n\
|
||||
Mime-Version: 1.0 (1.0)\r\n\
|
||||
Content-Type: text/plain; charset=utf-8\r\n\
|
||||
Content-Disposition: reaction\r\n\
|
||||
\r\n\
|
||||
\u{1F44D}"
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let reactions = get_msg_reactions(&alice, msg.id).await?;
|
||||
let reactions = get_msg_reactions(alice, msg.id).await?;
|
||||
assert_eq!(reactions.to_string(), "👍1");
|
||||
|
||||
let contacts = reactions.contacts();
|
||||
@@ -451,8 +445,9 @@ Content-Disposition: reaction\n\
|
||||
assert_eq!(bob_reaction.as_str(), "👍");
|
||||
|
||||
// Alice receives reaction to her message from Bob with a footer.
|
||||
receive_imf(
|
||||
&alice,
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
"To: alice@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Date: Today, 29 February 2021 00:00:10 -800\n\
|
||||
@@ -469,16 +464,16 @@ Content-Disposition: reaction\n\
|
||||
_______________________________________________\n\
|
||||
Here's my footer -- bob@example.net"
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let reactions = get_msg_reactions(&alice, msg.id).await?;
|
||||
let reactions = get_msg_reactions(alice, msg.id).await?;
|
||||
assert_eq!(reactions.to_string(), "😀1");
|
||||
|
||||
// Alice receives a message with reaction to her message from Bob.
|
||||
let msg_bob = receive_imf(
|
||||
&alice,
|
||||
let msg_bob = test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
"To: alice@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Date: Today, 29 February 2021 00:00:10 -800\n\
|
||||
@@ -502,18 +497,16 @@ Content-Disposition: reaction\n\
|
||||
\n\
|
||||
--YiEDa0DAkWCtVeE4--"
|
||||
.as_bytes(),
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
let msg_bob = Message::load_from_db(&alice, msg_bob.msg_ids[0]).await?;
|
||||
.await?;
|
||||
let msg_bob = Message::load_from_db(alice, msg_bob.msg_ids[0]).await?;
|
||||
assert_eq!(msg_bob.from_id, bob_id);
|
||||
assert_eq!(msg_bob.chat_id, msg.chat_id);
|
||||
assert_eq!(msg_bob.viewtype, Viewtype::Text);
|
||||
assert_eq!(msg_bob.state, MessageState::InFresh);
|
||||
assert_eq!(msg_bob.hidden, false);
|
||||
assert_eq!(msg_bob.text, "Reply + reaction");
|
||||
let reactions = get_msg_reactions(&alice, msg.id).await?;
|
||||
let reactions = get_msg_reactions(alice, msg.id).await?;
|
||||
assert_eq!(reactions.to_string(), "👍1");
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -505,6 +505,14 @@ pub(crate) async fn receive_imf_inner(
|
||||
Ok(mime_parser) => mime_parser,
|
||||
};
|
||||
|
||||
if !mime_parser.was_encrypted()
|
||||
&& mime_parser.get_header(HeaderDef::SecureJoin).is_none()
|
||||
&& context.get_config_bool(Config::ForceEncryption).await?
|
||||
{
|
||||
warn!(context, "Fetched unencrypted message, ignoring");
|
||||
return trash().await;
|
||||
}
|
||||
|
||||
let rfc724_mid_orig = &mime_parser
|
||||
.get_rfc724_mid()
|
||||
.unwrap_or(rfc724_mid.to_string());
|
||||
@@ -902,8 +910,10 @@ UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
|
||||
}
|
||||
|
||||
// Get user-configured server deletion
|
||||
let delete_server_after = context.get_config_delete_server_after().await?;
|
||||
|
||||
if !received_msg.msg_ids.is_empty() {
|
||||
let target = if received_msg.needs_delete_job {
|
||||
let target = if received_msg.needs_delete_job || delete_server_after == Some(0) {
|
||||
Some("".to_string())
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::imap::prefetch_should_download;
|
||||
use crate::imex::{ImexMode, imex};
|
||||
use crate::key;
|
||||
use crate::securejoin::get_securejoin_qr;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{
|
||||
TestContext, TestContextManager, alice_keypair, get_chat_msg, mark_as_verified,
|
||||
};
|
||||
@@ -80,6 +81,7 @@ static GRP_MAIL: &[u8] =
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_is_shown() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
@@ -100,6 +102,7 @@ async fn test_adhoc_group_is_shown() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_accepted_contact_accepted() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
// accept Bob by accepting a delta-message from Bob
|
||||
receive_imf(&t, MSGRMSG, false).await.unwrap();
|
||||
@@ -135,6 +138,7 @@ async fn test_adhoc_group_show_accepted_contact_accepted() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_all() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
receive_imf(&t, GRP_MAIL, false).await.unwrap();
|
||||
|
||||
// adhoc-group with unknown contacts with show_emails=all will show up in a single chat
|
||||
@@ -154,6 +158,7 @@ async fn test_adhoc_group_show_all() {
|
||||
async fn test_adhoc_groups_merge() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
receive_imf(
|
||||
alice,
|
||||
b"From: bob@example.net\n\
|
||||
@@ -354,6 +359,7 @@ async fn test_no_message_id_header() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_escaped_from() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -387,6 +393,7 @@ async fn test_escaped_from() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_escaped_recipients() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
Contact::create(&t, "foobar", "foobar@example.com")
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -434,6 +441,7 @@ async fn test_escaped_recipients() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cc_to_contact() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
Contact::create(&t, "foobar", "foobar@example.com")
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -590,6 +598,7 @@ async fn test_parse_ndn(
|
||||
) -> (TestContext, MsgId) {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr(self_addr).await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -674,6 +683,7 @@ async fn test_resend_after_ndn() -> Result<()> {
|
||||
async fn test_parse_ndn_group_msg() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr("alice@gmail.com").await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -715,6 +725,7 @@ async fn test_parse_ndn_group_msg() -> Result<()> {
|
||||
async fn test_concat_multiple_ndns() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr("alice@posteo.org").await;
|
||||
t.allow_unencrypted().await?;
|
||||
let mid = "1234@mail.gmail.com";
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -772,6 +783,7 @@ async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_only_mail() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let msg = load_imf_email(&t, include_bytes!("../../test-data/message/wrong-html.eml")).await;
|
||||
assert_eq!(
|
||||
msg.text,
|
||||
@@ -807,6 +819,7 @@ static GH_MAILINGLIST2: &str = "Received: (Postfix, from userid 1000); Mon, 4 De
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_github_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(&t.ctx, GH_MAILINGLIST, false).await?;
|
||||
|
||||
@@ -880,6 +893,8 @@ static DC_MAILINGLIST2: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_classic_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
@@ -921,6 +936,8 @@ Hello mailinglist!\r\n"
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_other_device_writes_to_mailinglist() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
|
||||
let first_msg = t.get_last_msg().await;
|
||||
let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?;
|
||||
@@ -971,6 +988,7 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_block_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
t.evtracker.wait_next_incoming_message().await;
|
||||
@@ -1005,6 +1023,7 @@ async fn test_block_mailing_list() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_block_then_unblock() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
|
||||
let blocked = Contact::get_all_blocked(&t).await.unwrap();
|
||||
@@ -1035,6 +1054,7 @@ async fn test_mailing_list_decide_block_then_unblock() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_not_now() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1062,6 +1082,7 @@ async fn test_mailing_list_decide_not_now() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_accept() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1084,6 +1105,8 @@ async fn test_mailing_list_decide_accept() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Foo Bar <foo@bar.org>\n\
|
||||
@@ -1108,6 +1131,7 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// test mailing lists not having a `ListId:`-header
|
||||
receive_imf(
|
||||
@@ -1160,6 +1184,7 @@ async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailchimp_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1193,6 +1218,7 @@ async fn test_mailchimp_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dhl_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1218,6 +1244,7 @@ async fn test_dhl_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dpd_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1243,6 +1270,7 @@ async fn test_dpd_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xt_local_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1276,6 +1304,7 @@ async fn test_xt_local_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xing_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1298,6 +1327,7 @@ async fn test_xing_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ttline_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1318,6 +1348,7 @@ async fn test_ttline_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
// the mailing list message contains two top-level texts.
|
||||
// the second text is a footer that is added by some mailing list software
|
||||
@@ -1345,6 +1376,7 @@ async fn test_mailing_list_with_mimepart_footer() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1369,6 +1401,7 @@ async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_apply_mailinglist_changes_assigned_by_reply() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&t, GH_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1407,6 +1440,7 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_chat_message() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1429,6 +1463,7 @@ async fn test_mailing_list_chat_message() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_bot() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
t.set_config(Config::Bot, Some("1")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
@@ -1461,6 +1496,8 @@ async fn test_dont_show_noreply_in_contacts_list() {
|
||||
|
||||
async fn check_dont_show_in_contacts_list(addr: &str) {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
format!(
|
||||
@@ -1490,6 +1527,7 @@ YEAAAAAA!.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pdf_filename_simple() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let msg = load_imf_email(
|
||||
&t,
|
||||
include_bytes!("../../test-data/message/pdf_filename_simple.eml"),
|
||||
@@ -1510,6 +1548,7 @@ async fn test_pdf_filename_simple() {
|
||||
async fn test_pdf_filename_continuation() {
|
||||
// test filenames split across multiple header lines, see rfc 2231
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let msg = load_imf_email(
|
||||
&t,
|
||||
include_bytes!("../../test-data/message/pdf_filename_continuation.eml"),
|
||||
@@ -1535,6 +1574,7 @@ async fn test_pdf_filename_continuation() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_many_images() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1555,6 +1595,7 @@ async fn test_many_images() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_in_reply_to() {
|
||||
let t = TestContext::new().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
t.configure_addr("bob@example.com").await;
|
||||
|
||||
// Receive message from Alice about group "foo".
|
||||
@@ -1630,6 +1671,7 @@ async fn test_save_mime_headers_off() -> anyhow::Result<()> {
|
||||
async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
|
||||
// Claire, a customer, sends a support request
|
||||
// to the alias address <support@example.org>.
|
||||
@@ -1696,6 +1738,8 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
|
||||
let claire = tcm.unconfigured().await;
|
||||
claire.configure_addr("claire@example.org").await;
|
||||
claire.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(&claire, claire_request.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1832,47 +1876,57 @@ async fn test_alias_support_answer_from_dc_chat_group() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_assign_to_trash_by_parent() {
|
||||
let t = TestContext::new_alice().await;
|
||||
println!("\n========= Receive a message ==========");
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Nu Bar <nu@bar.org>\n\
|
||||
To: alice@example.org, bob@example.org\n\
|
||||
Subject: Hi\n\
|
||||
Message-ID: <4444@example.org>\n\
|
||||
\n\
|
||||
hello\n",
|
||||
false,
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let charlie = &tcm.charlie().await;
|
||||
|
||||
tcm.section("Receive a message");
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
charlie,
|
||||
&[alice, bob],
|
||||
b"From: Charlie <charlie@example.net>\r\n\
|
||||
To: alice@example.org, bob@example.org\r\n\
|
||||
Subject: Hi\r\n\
|
||||
Message-ID: <4444@example.org>\r\n\
|
||||
\r\n\
|
||||
hello\r\n",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = t.get_last_msg().await.chat_id;
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let msg = get_chat_msg(&t, chat_id, 0, 1).await; // Make sure that the message is actually in the chat
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = alice.get_last_msg().await.chat_id;
|
||||
chat_id.accept(alice).await.unwrap();
|
||||
let msg = get_chat_msg(alice, chat_id, 0, 1).await; // Make sure that the message is actually in the chat
|
||||
assert!(!msg.chat_id.is_special());
|
||||
assert_eq!(msg.text, "Hi – hello");
|
||||
|
||||
println!("\n========= Delete the message ==========");
|
||||
msg.id.trash(&t, false).await.unwrap();
|
||||
tcm.section("Delete the message");
|
||||
msg.id.trash(alice, false).await.unwrap();
|
||||
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(alice, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
|
||||
println!("\n========= Receive a message that is a reply to the deleted message ==========");
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Nu Bar <nu@bar.org>\n\
|
||||
To: alice@example.org, bob@example.org\n\
|
||||
Subject: Re: Hi\n\
|
||||
Message-ID: <5555@example.org>\n\
|
||||
In-Reply-To: <4444@example.org\n\
|
||||
\n\
|
||||
Reply\n",
|
||||
false,
|
||||
tcm.section("Receive a message that is a reply to the deleted message");
|
||||
let encrypted_message = test_utils::encrypt_raw_message(
|
||||
charlie,
|
||||
&[alice, bob],
|
||||
b"From: Charlie <charlie@example.net>\r\n\
|
||||
To: alice@example.org, bob@example.org\r\n\
|
||||
Subject: Re: Hi\r\n\
|
||||
Message-ID: <5555@example.org>\r\n\
|
||||
In-Reply-To: <4444@example.org\r\n\
|
||||
\r\n\
|
||||
Reply\r\n",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
receive_imf(alice, encrypted_message.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert!(!msg.chat_id.is_special()); // Esp. check that the chat_id is not TRASH
|
||||
assert_eq!(msg.text, "Reply");
|
||||
}
|
||||
@@ -1882,10 +1936,12 @@ async fn test_dont_show_all_outgoing_msgs_in_self_chat() {
|
||||
// Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>:
|
||||
// Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to
|
||||
// be shown in the self-chat.
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
b"Bcc: alice@example.org
|
||||
Received: from [127.0.0.1]
|
||||
Subject: s
|
||||
@@ -1910,6 +1966,7 @@ Message content",
|
||||
async fn test_unencrypted_doesnt_goto_self_chat() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let mut chat_id = None;
|
||||
|
||||
for (i, to) in [
|
||||
@@ -1976,16 +2033,22 @@ async fn test_no_smtp_job_for_self_chat() -> Result<()> {
|
||||
assert!(bob.pop_sent_msg_opt(Duration::ZERO).await.is_none());
|
||||
|
||||
bob.set_config_bool(Config::BccSelf, true).await?;
|
||||
bob.set_config(Config::DeleteServerAfter, Some("1")).await?;
|
||||
let mut msg = Message::new_text("Happy birthday to me".to_string());
|
||||
chat::send_msg(bob, chat_id, &mut msg).await?;
|
||||
assert!(bob.pop_sent_msg_opt(Duration::ZERO).await.is_none());
|
||||
|
||||
bob.set_config(Config::DeleteServerAfter, None).await?;
|
||||
let mut msg = Message::new_text("Happy birthday to me".to_string());
|
||||
chat::send_msg(bob, chat_id, &mut msg).await?;
|
||||
assert!(bob.pop_sent_msg_opt(Duration::ZERO).await.is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_classic_mail_creates_chat() {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
|
||||
// Alice downloads outgoing classic email.
|
||||
receive_imf(
|
||||
@@ -2011,6 +2074,7 @@ Message content",
|
||||
async fn test_duplicate_message() -> Result<()> {
|
||||
// Test that duplicate messages are ignored based on the Message-ID
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let bob_contact_id = Contact::add_or_lookup(
|
||||
&alice,
|
||||
@@ -2069,6 +2133,8 @@ Second signature";
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ignore_footer_status_from_mailinglist() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let bob_id = Contact::add_or_lookup(
|
||||
&t,
|
||||
"",
|
||||
@@ -2148,6 +2214,8 @@ Original signature updated",
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ignore_old_status_updates() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let bob_id = Contact::add_or_lookup(
|
||||
&t,
|
||||
"",
|
||||
@@ -2217,11 +2285,13 @@ sig thursday",
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_private_classical_reply() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
|
||||
Subject: =?utf-8?q?single_reply-to?=
|
||||
@@ -2258,12 +2328,12 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
|
||||
"Hello, I've just created the group \"single reply-to\" for us."
|
||||
}
|
||||
);
|
||||
let group_chat = Chat::load_from_db(&t, group_msg.chat_id).await.unwrap();
|
||||
let group_chat = Chat::load_from_db(t, group_msg.chat_id).await.unwrap();
|
||||
assert_eq!(group_chat.typ, Chattype::Group);
|
||||
assert_eq!(group_chat.name, "single reply-to");
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Subject: Re: single reply-to
|
||||
To: "Alice" <alice@example.org>
|
||||
@@ -2292,7 +2362,7 @@ Private reply"#,
|
||||
|
||||
let private_msg = t.get_last_msg().await;
|
||||
assert_eq!(private_msg.text, "Private reply");
|
||||
let private_chat = Chat::load_from_db(&t, private_msg.chat_id).await.unwrap();
|
||||
let private_chat = Chat::load_from_db(t, private_msg.chat_id).await.unwrap();
|
||||
assert_eq!(private_chat.typ, Chattype::Single);
|
||||
assert_ne!(private_msg.chat_id, group_msg.chat_id);
|
||||
}
|
||||
@@ -2300,16 +2370,17 @@ Private reply"#,
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_private_chat_reply() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
for (outgoing_is_classical, outgoing_has_multiple_recipients) in
|
||||
&[(true, true), (false, true), (false, false)]
|
||||
{
|
||||
let t = TestContext::new_alice().await;
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
|
||||
Subject: =?utf-8?q?single_reply-to?=
|
||||
r#"Subject: =?utf-8?q?single_reply-to?=
|
||||
{}
|
||||
Date: Fri, 28 May 2021 10:15:05 +0000
|
||||
To: Bob <bob@example.com>, Charlie <charlie@example.net>{}
|
||||
@@ -2347,12 +2418,12 @@ Message-ID: <Gr.iy1KCE2y65_.mH2TM52miv9@testrun.org>"
|
||||
"Hello, I've just created the group \"single reply-to\" for us."
|
||||
}
|
||||
);
|
||||
let group_chat = Chat::load_from_db(&t, group_msg.chat_id).await.unwrap();
|
||||
let group_chat = Chat::load_from_db(t, group_msg.chat_id).await.unwrap();
|
||||
assert_eq!(group_chat.typ, Chattype::Group);
|
||||
assert_eq!(group_chat.name, "single reply-to");
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Subject: =?utf-8?q?Re=3A_single_reply-to?=
|
||||
MIME-Version: 1.0
|
||||
@@ -2387,7 +2458,7 @@ Sent with my Delta Chat Messenger: https://delta.chat
|
||||
|
||||
let private_msg = t.get_last_msg().await;
|
||||
assert_eq!(private_msg.text, "Private reply");
|
||||
let private_chat = Chat::load_from_db(&t, private_msg.chat_id).await.unwrap();
|
||||
let private_chat = Chat::load_from_db(t, private_msg.chat_id).await.unwrap();
|
||||
assert_eq!(private_chat.typ, Chattype::Single);
|
||||
assert_ne!(private_msg.chat_id, group_msg.chat_id);
|
||||
}
|
||||
@@ -2395,11 +2466,13 @@ Sent with my Delta Chat Messenger: https://delta.chat
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_nonprivate_classical_reply() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
|
||||
Subject: =?utf-8?q?single_reply-to?=
|
||||
@@ -2435,13 +2508,13 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
|
||||
"Hello, I've just created the group \"single reply-to\" for us."
|
||||
}
|
||||
);
|
||||
let group_chat = Chat::load_from_db(&t, group_msg.chat_id).await.unwrap();
|
||||
let group_chat = Chat::load_from_db(t, group_msg.chat_id).await.unwrap();
|
||||
assert_eq!(group_chat.typ, Chattype::Group);
|
||||
assert_eq!(group_chat.name, "single reply-to");
|
||||
|
||||
// =============== Receive another outgoing message and check that it is put into the same chat ===============
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
format!(
|
||||
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
|
||||
Subject: Out subj
|
||||
@@ -2466,13 +2539,13 @@ Outgoing reply to all"#,
|
||||
|
||||
let reply = t.get_last_msg().await;
|
||||
assert_eq!(reply.text, "Out subj – Outgoing reply to all");
|
||||
let reply_chat = Chat::load_from_db(&t, reply.chat_id).await.unwrap();
|
||||
let reply_chat = Chat::load_from_db(t, reply.chat_id).await.unwrap();
|
||||
assert_eq!(reply_chat.typ, Chattype::Group);
|
||||
assert_eq!(reply.chat_id, group_msg.chat_id);
|
||||
|
||||
// =============== Receive an incoming message and check that it is put into the same chat ===============
|
||||
receive_imf(
|
||||
&t,
|
||||
t,
|
||||
br#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
|
||||
Subject: In subj
|
||||
To: "Bob" <bob@example.com>, "Claire" <claire@example.com>
|
||||
@@ -2489,7 +2562,7 @@ Reply to all"#,
|
||||
|
||||
let reply = t.get_last_msg().await;
|
||||
assert_eq!(reply.text, "In subj – Reply to all");
|
||||
let reply_chat = Chat::load_from_db(&t, reply.chat_id).await.unwrap();
|
||||
let reply_chat = Chat::load_from_db(t, reply.chat_id).await.unwrap();
|
||||
assert_eq!(reply_chat.typ, Chattype::Group);
|
||||
assert_eq!(reply.chat_id, group_msg.chat_id);
|
||||
}
|
||||
@@ -2507,6 +2580,9 @@ async fn test_chat_assignment_adhoc() -> Result<()> {
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
bob.allow_unencrypted().await?;
|
||||
fiona.allow_unencrypted().await?;
|
||||
|
||||
let first_thread_mime = br#"Subject: First thread
|
||||
Message-ID: first@example.org
|
||||
@@ -2651,6 +2727,7 @@ async fn test_read_receipts_dont_unmark_bots() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_gmx_forwarded_msg() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2738,7 +2815,9 @@ async fn get_parent_message(
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_parent_message() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let mime = br#"Subject: First
|
||||
Message-ID: first@example.net
|
||||
@@ -2747,8 +2826,8 @@ From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
receive_imf(&t, mime, false).await?;
|
||||
let first = t.get_last_msg().await;
|
||||
test_utils::receive_encrypted_imf(alice, bob, mime).await?;
|
||||
let first = alice.get_last_msg().await;
|
||||
let mime = br#"Subject: Second
|
||||
Message-ID: second@example.net
|
||||
To: Alice <alice@example.org>
|
||||
@@ -2756,8 +2835,8 @@ From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
receive_imf(&t, mime, false).await?;
|
||||
let second = t.get_last_msg().await;
|
||||
test_utils::receive_encrypted_imf(alice, bob, mime).await?;
|
||||
let second = alice.get_last_msg().await;
|
||||
let mime = br#"Subject: Third
|
||||
Message-ID: third@example.net
|
||||
To: Alice <alice@example.org>
|
||||
@@ -2765,8 +2844,8 @@ From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
receive_imf(&t, mime, false).await?;
|
||||
let third = t.get_last_msg().await;
|
||||
test_utils::receive_encrypted_imf(alice, bob, mime).await?;
|
||||
let third = alice.get_last_msg().await;
|
||||
|
||||
let mime = br#"Subject: Message with references.
|
||||
Message-ID: second@example.net
|
||||
@@ -2777,21 +2856,22 @@ References: <second@example.net> <nonexistent@example.net> <first@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
Message with references."#;
|
||||
let mime_parser = MimeMessage::from_bytes(&t, &mime[..]).await?;
|
||||
let encrypted_mime = test_utils::encrypt_raw_message(bob, &[alice], mime).await?;
|
||||
let mime_parser = MimeMessage::from_bytes(alice, encrypted_mime.as_bytes()).await?;
|
||||
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, first.id);
|
||||
|
||||
message::delete_msgs(&t, &[first.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
message::delete_msgs(alice, &[first.id]).await?;
|
||||
let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, second.id);
|
||||
|
||||
message::delete_msgs(&t, &[second.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
message::delete_msgs(alice, &[second.id]).await?;
|
||||
let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, third.id);
|
||||
|
||||
message::delete_msgs(&t, &[third.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?;
|
||||
message::delete_msgs(alice, &[third.id]).await?;
|
||||
let parent = get_parent_message(alice, &mime_parser).await?;
|
||||
assert!(parent.is_none());
|
||||
|
||||
Ok(())
|
||||
@@ -3017,7 +3097,8 @@ async fn test_auto_accept_for_bots() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_auto_accept_group_for_bots() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::Bot, Some("1")).await.unwrap();
|
||||
t.set_config_bool(Config::Bot, true).await.unwrap();
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let msg = load_imf_email(&t, GRP_MAIL).await;
|
||||
|
||||
let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?;
|
||||
@@ -3246,6 +3327,7 @@ async fn test_blocked_contact_creates_group() -> Result<()> {
|
||||
async fn test_outgoing_undecryptable() -> Result<()> {
|
||||
let alice = &TestContext::new().await;
|
||||
alice.configure_addr("alice@example.org").await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
|
||||
receive_imf(alice, raw, false).await?;
|
||||
@@ -3282,6 +3364,7 @@ async fn test_outgoing_undecryptable() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_autocrypt() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
|
||||
let received_msg = receive_imf(&t, raw, false).await?.unwrap();
|
||||
@@ -3329,6 +3412,7 @@ async fn test_issuer_fingerprint() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
// The message has public key attached *and* Autocrypt header.
|
||||
//
|
||||
@@ -3379,7 +3463,9 @@ async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
|
||||
let t = &TestContext::new_bob().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.bob().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml");
|
||||
let received_msg = receive_imf(t, raw, false).await?.unwrap();
|
||||
|
||||
@@ -3389,7 +3475,8 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
|
||||
assert!(!msg.chat_id.is_trash());
|
||||
assert!(!msg.get_showpadlock());
|
||||
|
||||
let t = &TestContext::new_bob().await;
|
||||
let t = &tcm.bob().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let raw = String::from_utf8(raw.to_vec())?.replace("alice@example.org", "clarice@example.org");
|
||||
let received_msg = receive_imf(t, raw.as_bytes(), false).await?.unwrap();
|
||||
assert!(received_msg.chat_id.is_trash());
|
||||
@@ -3400,6 +3487,8 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
|
||||
let t = &TestContext::new_bob().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml");
|
||||
let raw = String::from_utf8(raw.to_vec())?.replace("From: Alice", "From: A");
|
||||
receive_imf(t, raw.as_bytes(), false).await?.unwrap();
|
||||
@@ -3414,6 +3503,7 @@ async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
let bob = &TestContext::new_bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
// Thunderbird message with Autocrypt header and a signature,
|
||||
// but not encrypted.
|
||||
@@ -3452,6 +3542,8 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_unsigned() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
// TODO: same test without process unencrypted should trash the message
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
|
||||
// Alice receives an unsigned message from Bob.
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml");
|
||||
@@ -3471,6 +3563,7 @@ async fn test_thunderbird_unsigned() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_unsigned_with_unencrypted_subject() -> Result<()> {
|
||||
let bob = TestContext::new_bob().await;
|
||||
bob.allow_unencrypted().await.unwrap();
|
||||
|
||||
let raw = include_bytes!(
|
||||
"../../test-data/message/thunderbird_encrypted_unsigned_with_unencrypted_subject.eml"
|
||||
@@ -3503,18 +3596,20 @@ async fn test_messed_up_message_id() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_big_forwarded_with_big_attachment() -> Result<()> {
|
||||
let t = &TestContext::new_bob().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/big_forwarded_with_big_attachment.eml");
|
||||
let rcvd = receive_imf(t, raw, false).await?.unwrap();
|
||||
let rcvd = test_utils::receive_encrypted_imf(bob, alice, raw).await?;
|
||||
assert_eq!(rcvd.msg_ids.len(), 3);
|
||||
|
||||
let msg = Message::load_from_db(t, rcvd.msg_ids[0]).await?;
|
||||
let msg = Message::load_from_db(bob, rcvd.msg_ids[0]).await?;
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::Text);
|
||||
assert_eq!(msg.get_text(), "Hello!");
|
||||
assert!(!msg.has_html());
|
||||
|
||||
let msg = Message::load_from_db(t, rcvd.msg_ids[1]).await?;
|
||||
let msg = Message::load_from_db(bob, rcvd.msg_ids[1]).await?;
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::Text);
|
||||
assert!(
|
||||
msg.get_text()
|
||||
@@ -3523,10 +3618,10 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> {
|
||||
assert!(msg.get_text().ends_with("[...]"));
|
||||
assert!(!msg.has_html());
|
||||
|
||||
let msg = Message::load_from_db(t, rcvd.msg_ids[2]).await?;
|
||||
let msg = Message::load_from_db(bob, rcvd.msg_ids[2]).await?;
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::File);
|
||||
assert!(msg.has_html());
|
||||
let html = msg.id.get_html(t).await?.unwrap();
|
||||
let html = msg.id.get_html(bob).await?.unwrap();
|
||||
let tail = html
|
||||
.split_once("Hello!")
|
||||
.unwrap()
|
||||
@@ -3561,6 +3656,7 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_user_adds_member() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -3612,6 +3708,7 @@ async fn test_mua_user_adds_member() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
// Alice sends a 1:1 message to Bob, creating a 1:1 chat.
|
||||
let msg = receive_imf(
|
||||
@@ -4044,6 +4141,7 @@ async fn test_dont_readd_with_normal_msg() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_cant_remove() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let now = time();
|
||||
|
||||
@@ -4136,6 +4234,7 @@ async fn test_mua_cant_remove() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_can_add() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let now = time();
|
||||
|
||||
@@ -4195,6 +4294,7 @@ async fn test_mua_can_add() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_can_readd() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
// Alice creates chat with 3 contacts.
|
||||
let msg = receive_imf(
|
||||
@@ -4358,6 +4458,8 @@ async fn test_keep_member_list_if_possibly_nomember() -> Result<()> {
|
||||
async fn test_adhoc_grp_name_no_prefix() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let chat_id = receive_imf(
|
||||
alice,
|
||||
b"Subject: Re: Once upon a time this was with the only Re: here\n\
|
||||
@@ -4393,12 +4495,12 @@ async fn test_outgoing_msg_forgery() -> Result<()> {
|
||||
imex(alice, ImexMode::ExportSelfKeys, export_dir.path(), None).await?;
|
||||
// We need Bob only to encrypt the forged message to Alice's key, actually Bob doesn't
|
||||
// participate in the scenario.
|
||||
let bob = &TestContext::new().await;
|
||||
let bob = &tcm.unconfigured().await;
|
||||
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 0);
|
||||
bob.configure_addr("bob@example.net").await;
|
||||
imex(bob, ImexMode::ImportSelfKeys, export_dir.path(), None).await?;
|
||||
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1);
|
||||
let malice = &TestContext::new().await;
|
||||
let malice = &tcm.unconfigured().await;
|
||||
malice.configure_addr(alice_addr).await;
|
||||
|
||||
let malice_chat_id = tcm
|
||||
@@ -4408,9 +4510,8 @@ async fn test_outgoing_msg_forgery() -> Result<()> {
|
||||
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1);
|
||||
|
||||
let sent_msg = malice.send_text(malice_chat_id, "hi from malice").await;
|
||||
let msg = alice.recv_msg(&sent_msg).await;
|
||||
assert_eq!(msg.state, MessageState::OutDelivered);
|
||||
assert!(!msg.get_showpadlock());
|
||||
let msg = alice.recv_msg_opt(&sent_msg).await;
|
||||
assert!(msg.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4505,6 +4606,7 @@ async fn test_protected_group_add_remove_member_missing_key() -> Result<()> {
|
||||
async fn test_older_message_from_2nd_device() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
let chat_id = alice
|
||||
.create_chat_with_contact("", "bob@example.net")
|
||||
.await
|
||||
@@ -4640,6 +4742,7 @@ async fn test_forged_from() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_multiline_iso_8859_1_subject() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let mail = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
From: bob@example.com\n\
|
||||
To: alice@example.org, claire@example.com\n\
|
||||
@@ -4704,6 +4807,7 @@ async fn test_references() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_list_from() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/list-from.eml");
|
||||
let received = receive_imf(t, raw, false).await?.unwrap();
|
||||
@@ -4833,6 +4937,7 @@ async fn test_make_n_send_vcard() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_unencrypted_group_id_no_recipients() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let raw = "From: alice@example.org
|
||||
Subject: Group
|
||||
Chat-Version: 1.0
|
||||
@@ -5124,10 +5229,12 @@ async fn test_recv_outgoing_msg_no_intended_recipient_fingerprint() -> Result<()
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sanitize_filename_in_received() -> Result<()> {
|
||||
let alice = &TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let raw = b"Message-ID: Mr.XA6y3og8-az.WGbH9_dNcQx@testr
|
||||
To: <tmp_5890965001269692@testrun.org>
|
||||
From: \"=?utf-8?q??=\" <tmp_6272287793210918@testrun.org>
|
||||
To: <alice@example.org>
|
||||
From: \"=?utf-8?q??=\" <bob@example.net>
|
||||
Content-Type: multipart/mixed; boundary=\"mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z\"
|
||||
|
||||
|
||||
@@ -5146,7 +5253,7 @@ PGh0bWw+PGJvZHk+dGV4dDwvYm9keT5kYXRh
|
||||
|
||||
--mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z--";
|
||||
|
||||
let msg = receive_imf(alice, raw, false).await?.unwrap();
|
||||
let msg = test_utils::receive_encrypted_imf(alice, bob, raw).await?;
|
||||
let msg = Message::load_from_db(alice, msg.msg_ids[0]).await?;
|
||||
|
||||
assert_eq!(msg.get_filename().unwrap(), "test.HTML");
|
||||
@@ -5262,6 +5369,7 @@ async fn test_outgoing_unencrypted_chat_assignment() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
|
||||
tcm.section("Alice receives unencrypted message from Bob");
|
||||
receive_imf(
|
||||
@@ -5306,6 +5414,7 @@ async fn test_outgoing_unencrypted_chat_assignment() {
|
||||
async fn test_incoming_reply_with_date_in_past() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let msg0 = receive_imf(
|
||||
alice,
|
||||
@@ -5447,6 +5556,8 @@ async fn test_small_unencrypted_group() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
let alice_chat_id = chat::create_group_unencrypted(alice, "Unencrypted group").await?;
|
||||
let alice_bob_id = alice.add_or_lookup_address_contact_id(bob).await;
|
||||
@@ -5479,6 +5590,7 @@ async fn test_small_unencrypted_group() -> Result<()> {
|
||||
async fn test_bcc_not_a_group() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.allow_unencrypted().await.unwrap();
|
||||
|
||||
let received = receive_imf(
|
||||
alice,
|
||||
@@ -5526,6 +5638,7 @@ async fn test_lookup_key_contact_by_address_self() -> Result<()> {
|
||||
async fn test_calendar_alternative() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let t = &tcm.alice().await;
|
||||
t.allow_unencrypted().await?;
|
||||
let raw = include_bytes!("../../test-data/message/calendar-alternative.eml");
|
||||
let msg = receive_imf(t, raw, false).await?.unwrap();
|
||||
assert_eq!(msg.msg_ids.len(), 1);
|
||||
|
||||
@@ -15,7 +15,7 @@ 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::ephemeral;
|
||||
use crate::ephemeral::{self, delete_expired_imap_messages};
|
||||
use crate::events::EventType;
|
||||
use crate::imap::{Imap, session::Session};
|
||||
use crate::location;
|
||||
@@ -484,6 +484,14 @@ async fn fetch_idle(ctx: &Context, connection: &mut Imap, mut session: Session)
|
||||
.await
|
||||
.context("fetch_move_delete")?;
|
||||
|
||||
// Mark expired messages for deletion. Marked messages will be deleted from the server
|
||||
// on the next iteration of `fetch_move_delete`. `delete_expired_imap_messages` is not
|
||||
// called right before `fetch_move_delete` because it is not well optimized and would
|
||||
// otherwise slow down message fetching.
|
||||
delete_expired_imap_messages(ctx)
|
||||
.await
|
||||
.context("delete_expired_imap_messages")?;
|
||||
|
||||
download_known_post_messages_without_pre_message(ctx, &mut session).await?;
|
||||
download_msgs(ctx, &mut session)
|
||||
.await
|
||||
|
||||
@@ -772,6 +772,7 @@ fn manipulate_qr(v3: bool, remove_invite: bool, qr: &mut String) {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_no_qr() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let mime = br#"Subject: First thread
|
||||
Message-ID: first@example.org
|
||||
@@ -1415,6 +1416,7 @@ async fn test_vc_request_encrypted_at_rest() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
let qr = get_securejoin_qr(alice, None).await?;
|
||||
|
||||
|
||||
34
src/smtp.rs
34
src/smtp.rs
@@ -699,22 +699,26 @@ pub(crate) async fn add_self_recipients(
|
||||
recipients: &mut Vec<String>,
|
||||
encrypted: bool,
|
||||
) -> Result<()> {
|
||||
// Avoid sending unencrypted messages to all transports, chatmail relays won't accept
|
||||
// them. Normally the user should have a non-chatmail primary transport to send unencrypted
|
||||
// messages.
|
||||
if encrypted {
|
||||
for addr in context.get_published_secondary_self_addrs().await? {
|
||||
recipients.push(addr);
|
||||
// Previous versions of Delta Chat did not send BCC self
|
||||
// if DeleteServerAfter was set to immediately delete messages
|
||||
// from the server. This is not the case anymore
|
||||
// because BCC-self messages are also used to detect
|
||||
// that message was sent if SMTP server is slow to respond
|
||||
// and connection is frequently lost
|
||||
// before receiving status line. NB: This is not a problem for chatmail servers, so `BccSelf`
|
||||
// disabled by default is fine.
|
||||
if context.get_config_delete_server_after().await? != Some(0) || !recipients.is_empty() {
|
||||
// Avoid sending unencrypted messages to all transports, chatmail relays won't accept
|
||||
// them. Normally the user should have a non-chatmail primary transport to send unencrypted
|
||||
// messages.
|
||||
if encrypted {
|
||||
for addr in context.get_published_secondary_self_addrs().await? {
|
||||
recipients.push(addr);
|
||||
}
|
||||
}
|
||||
// `from` must be the last addr, see `receive_imf_inner()` why.
|
||||
let from = context.get_primary_self_addr().await?;
|
||||
recipients.push(from);
|
||||
}
|
||||
// `from` must be the last addr
|
||||
// because `receive_imf_inner()` marks the message as 'delivered'
|
||||
// if it arrives to the self-server via `bcc_self`.
|
||||
// This helps with marking messages as delivered
|
||||
// if the server is slow and we never get an `OK` response
|
||||
// before the connection times out.
|
||||
let from = context.get_primary_self_addr().await?;
|
||||
recipients.push(from);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
19
src/sync.rs
19
src/sync.rs
@@ -810,4 +810,23 @@ mod tests {
|
||||
assert_eq!(msg.text, "Member Me added by alice@example.org.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that "force encryption" setting is synced.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync_force_encryption() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let alice2 = &tcm.alice().await;
|
||||
alice.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
alice2.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
|
||||
assert_eq!(alice.get_config_bool(Config::ForceEncryption).await?, true);
|
||||
alice2
|
||||
.set_config_bool(Config::ForceEncryption, false)
|
||||
.await?;
|
||||
test_utils::sync(alice2, alice).await;
|
||||
assert_eq!(alice.get_config_bool(Config::ForceEncryption).await?, false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use pretty_assertions::assert_eq;
|
||||
use tempfile::{TempDir, tempdir};
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::{fs, task};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::chat::{
|
||||
self, Chat, ChatId, ChatIdBlocked, MessageListOptions, add_to_chat_contacts_table, create_group,
|
||||
@@ -32,13 +33,15 @@ use crate::contact::{
|
||||
Contact, ContactId, Modifier, Origin, import_vcard, make_vcard, mark_contact_id_as_verified,
|
||||
};
|
||||
use crate::context::Context;
|
||||
use crate::e2ee::EncryptHelper;
|
||||
use crate::events::{Event, EventEmitter, EventType, Events};
|
||||
use crate::key::{self, DcKey, self_fingerprint};
|
||||
use crate::log::warn;
|
||||
use crate::login_param::EnteredLoginParam;
|
||||
use crate::message::{Message, MessageState, MsgId, update_msg_state};
|
||||
use crate::mimeparser::{MimeMessage, SystemMessage};
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::pgp::SeipdVersion;
|
||||
use crate::receive_imf::{ReceivedMsg, receive_imf};
|
||||
use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
||||
use crate::smtp::msg_has_pending_smtp_job;
|
||||
use crate::stock_str::StockStrings;
|
||||
@@ -831,10 +834,7 @@ ORDER BY id"
|
||||
|
||||
/// Receive a message using the `receive_imf()` pipeline. This is similar
|
||||
/// to `recv_msg()`, but doesn't assume that the message is shown in the chat.
|
||||
pub async fn recv_msg_opt(
|
||||
&self,
|
||||
msg: &SentMessage<'_>,
|
||||
) -> Option<crate::receive_imf::ReceivedMsg> {
|
||||
pub async fn recv_msg_opt(&self, msg: &SentMessage<'_>) -> Option<ReceivedMsg> {
|
||||
receive_imf(self, msg.payload().as_bytes(), false)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -1229,6 +1229,74 @@ ORDER BY id"
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Allow reception of unencrypted messages.
|
||||
pub async fn allow_unencrypted(&self) -> Result<()> {
|
||||
self.set_config_bool(Config::ForceEncryption, false).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn encrypt_raw_message(
|
||||
context: &Context,
|
||||
receivers: &[&TestContext],
|
||||
payload: &[u8],
|
||||
) -> Result<String> {
|
||||
let encryption_helper = EncryptHelper::new(context).await?;
|
||||
let mut encryption_keyring = vec![encryption_helper.public_key.clone()];
|
||||
|
||||
for receiver in receivers {
|
||||
encryption_keyring.push(key::load_self_public_key(receiver).await?);
|
||||
}
|
||||
|
||||
let from = context.get_primary_self_addr().await?;
|
||||
let compress = false;
|
||||
|
||||
let mut cleartext = format!("Autocrypt: {}", encryption_helper.get_aheader()).into_bytes();
|
||||
cleartext.extend_from_slice(b"\r\n");
|
||||
cleartext.extend_from_slice(payload);
|
||||
let encrypted_payload = encryption_helper
|
||||
.encrypt_raw(
|
||||
context,
|
||||
encryption_keyring,
|
||||
cleartext,
|
||||
compress,
|
||||
SeipdVersion::V2,
|
||||
)
|
||||
.await?;
|
||||
let boundary = Uuid::new_v4();
|
||||
|
||||
let res = format!(
|
||||
"Content-Type: multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"{boundary}\"\r
|
||||
MIME-Version: 1.0\r
|
||||
From: {from}\r
|
||||
Subject: [...]\r
|
||||
\r
|
||||
\r
|
||||
--{boundary}
|
||||
Content-Type: application/pgp-encrypted\r
|
||||
\r
|
||||
Version: 1\r
|
||||
\r
|
||||
--{boundary}\r
|
||||
Content-Type: application/octet-stream\r
|
||||
\r
|
||||
{encrypted_payload}
|
||||
--{boundary}--\r
|
||||
");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn receive_encrypted_imf(
|
||||
context: &TestContext,
|
||||
from: &TestContext,
|
||||
imf_raw: &[u8],
|
||||
) -> Result<ReceivedMsg> {
|
||||
let encrypted_message = encrypt_raw_message(from, &[context], imf_raw).await?;
|
||||
let received_msg = receive_imf(context, encrypted_message.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap();
|
||||
Ok(received_msg)
|
||||
}
|
||||
|
||||
impl Deref for TestContext {
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::mimeparser::SystemMessage;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
||||
use crate::stock_str;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{
|
||||
E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified,
|
||||
};
|
||||
@@ -172,32 +173,33 @@ async fn test_missing_key_reexecute_securejoin() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_degrade_verified_oneonone_chat() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
mark_as_verified(&alice, &bob).await;
|
||||
mark_as_verified(alice, bob).await;
|
||||
|
||||
let alice_chat = alice.create_chat(&bob).await;
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
|
||||
receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.net>\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <1234-2@example.org>\n\
|
||||
\n\
|
||||
hello\n",
|
||||
alice,
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Message-ID: <1234-2@example.net>\r\n\
|
||||
\r\n\
|
||||
hello\r\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let msg0 = get_chat_msg(&alice, alice_chat.id, 0, 1).await;
|
||||
let enabled = stock_str::messages_e2ee_info_msg(&alice);
|
||||
let msg0 = get_chat_msg(alice, alice_chat.id, 0, 1).await;
|
||||
let enabled = stock_str::messages_e2ee_info_msg(alice);
|
||||
assert_eq!(msg0.text, enabled);
|
||||
assert_eq!(msg0.param.get_cmd(), SystemMessage::ChatE2ee);
|
||||
|
||||
let email_chat = alice.get_email_chat(&bob).await;
|
||||
assert!(!email_chat.is_encrypted(&alice).await?);
|
||||
let email_msg = get_chat_msg(&alice, email_chat.id, 0, 1).await;
|
||||
let email_chat = alice.get_email_chat(bob).await;
|
||||
assert!(!email_chat.is_encrypted(alice).await?);
|
||||
let email_msg = get_chat_msg(alice, email_chat.id, 0, 1).await;
|
||||
assert_eq!(email_msg.text, "hello".to_string());
|
||||
assert!(!email_msg.is_system_message());
|
||||
|
||||
@@ -209,32 +211,32 @@ async fn test_degrade_verified_oneonone_chat() -> Result<()> {
|
||||
/// This test tests that the messages are still in the right order.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_old_message_4() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let msg_incoming = receive_imf(
|
||||
&alice,
|
||||
b"From: Bob <bob@example.net>\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <1234-2-3@example.org>\n\
|
||||
Date: Sun, 08 Dec 2019 19:00:27 +0000\n\
|
||||
\n\
|
||||
Thanks, Alice!\n",
|
||||
true,
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let msg_incoming = test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
b"From: Bob <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Message-ID: <1234-2-3@example.org>\r\n\
|
||||
Date: Sun, 08 Dec 2019 19:00:27 +0000\r\n\
|
||||
\r\n\
|
||||
Thanks, Alice!\r\n",
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
.await?;
|
||||
|
||||
let msg_sent = receive_imf(
|
||||
&alice,
|
||||
b"From: alice@example.org\n\
|
||||
To: Bob <bob@example.net>\n\
|
||||
Message-ID: <1234-2-4@example.org>\n\
|
||||
Date: Sat, 07 Dec 2019 19:00:27 +0000\n\
|
||||
\n\
|
||||
Happy birthday, Bob!\n",
|
||||
true,
|
||||
let msg_sent = test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
alice,
|
||||
b"From: alice@example.org\r\n\
|
||||
To: Bob <bob@example.net>\r\n\
|
||||
Message-ID: <1234-2-4@example.org>\r\n\
|
||||
Date: Sat, 07 Dec 2019 19:00:27 +0000\r\n\
|
||||
\r\n\
|
||||
Happy birthday, Bob!\r\n",
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
.await?;
|
||||
|
||||
// The "Happy birthday" message should be shown first, and then the "Thanks" message
|
||||
assert!(msg_sent.sort_timestamp < msg_incoming.sort_timestamp);
|
||||
@@ -269,17 +271,18 @@ async fn test_mdn_doesnt_disable_verification() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_mua_msg() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
mark_as_verified(&alice, &bob).await;
|
||||
mark_as_verified(&bob, &alice).await;
|
||||
mark_as_verified(alice, bob).await;
|
||||
mark_as_verified(bob, alice).await;
|
||||
|
||||
tcm.send_recv_accept(&bob, &alice, "Heyho from DC").await;
|
||||
assert_verified(&alice, &bob).await;
|
||||
tcm.send_recv_accept(bob, alice, "Heyho from DC").await;
|
||||
assert_verified(alice, bob).await;
|
||||
|
||||
let sent = receive_imf(
|
||||
&alice,
|
||||
alice,
|
||||
b"From: alice@example.org\n\
|
||||
To: bob@example.net\n\
|
||||
\n\
|
||||
@@ -288,7 +291,7 @@ async fn test_outgoing_mua_msg() -> Result<()> {
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
tcm.send_recv(&alice, &bob, "Sending with DC again").await;
|
||||
tcm.send_recv(alice, bob, "Sending with DC again").await;
|
||||
|
||||
// Unencrypted message from MUA gets into a separate chat.
|
||||
// PGP chat gets all encrypted messages.
|
||||
@@ -296,7 +299,7 @@ async fn test_outgoing_mua_msg() -> Result<()> {
|
||||
.golden_test_chat(sent.chat_id, "test_outgoing_mua_msg")
|
||||
.await;
|
||||
alice
|
||||
.golden_test_chat(alice.get_chat(&bob).await.id, "test_outgoing_mua_msg_pgp")
|
||||
.golden_test_chat(alice.get_chat(bob).await.id, "test_outgoing_mua_msg_pgp")
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@@ -332,6 +335,7 @@ async fn test_reply() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
|
||||
if verified {
|
||||
mark_as_verified(&alice, &bob).await;
|
||||
|
||||
@@ -60,6 +60,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
|
||||
async fn check_parse_receive_headers_integration(raw: &[u8], expected: &str) {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.allow_unencrypted().await.unwrap();
|
||||
let received = receive_imf(&t, raw, false).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(received.msg_ids.len(), 1);
|
||||
|
||||
@@ -87,8 +87,8 @@ impl Params {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chat::Chat;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::TestContextManager;
|
||||
use crate::tools::time;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -115,37 +115,39 @@ mod tests {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_out_of_order_subject() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Bob Authname <bob@example.org>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: updated subject\n\
|
||||
Message-ID: <msg2@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2021 23:37:57 +0000\n\
|
||||
\n\
|
||||
second message\n",
|
||||
false,
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
b"From: Bob Authname <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: updated subject\r\n\
|
||||
Message-ID: <msg2@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Date: Sun, 22 Mar 2021 23:37:57 +0000\r\n\
|
||||
\r\n\
|
||||
second message\r\n",
|
||||
)
|
||||
.await?;
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Bob Authname <bob@example.org>\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: original subject\n\
|
||||
Message-ID: <msg1@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2021 22:37:57 +0000\n\
|
||||
\n\
|
||||
first message\n",
|
||||
false,
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
b"From: Bob Authname <bob@example.net>\r\n\
|
||||
To: alice@example.org\r\n\
|
||||
Subject: original subject\r\n\
|
||||
Message-ID: <msg1@example.org>\r\n\
|
||||
Chat-Version: 1.0\r\n\
|
||||
Date: Sun, 22 Mar 2021 22:37:57 +0000\r\n\
|
||||
\r\n\
|
||||
first message\r\n",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await?;
|
||||
let msg = alice.get_last_msg().await;
|
||||
let chat = Chat::load_from_db(alice, msg.chat_id).await?;
|
||||
assert_eq!(
|
||||
chat.param.get(Param::LastSubject).unwrap(),
|
||||
"updated subject"
|
||||
|
||||
@@ -11,8 +11,8 @@ use crate::chat::{
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::config::Config;
|
||||
use crate::ephemeral;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::securejoin::get_securejoin_qr;
|
||||
use crate::test_utils;
|
||||
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager};
|
||||
use crate::tools::{self, SystemTime};
|
||||
use crate::{message, sql};
|
||||
@@ -257,24 +257,26 @@ async fn test_resend_webxdc_instance_and_info() -> Result<()> {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_webxdc_instance() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
receive_imf(
|
||||
&t,
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
include_bytes!("../../test-data/message/webxdc_good_extension.eml"),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let instance = t.get_last_msg().await;
|
||||
let instance = alice.get_last_msg().await;
|
||||
assert_eq!(instance.viewtype, Viewtype::Webxdc);
|
||||
assert_eq!(instance.get_filename().unwrap(), "minimal.xdc");
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
test_utils::receive_encrypted_imf(
|
||||
alice,
|
||||
bob,
|
||||
include_bytes!("../../test-data/message/webxdc_bad_extension.eml"),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let instance = t.get_last_msg().await;
|
||||
let instance = alice.get_last_msg().await;
|
||||
assert_eq!(instance.viewtype, Viewtype::File); // we require the correct extension, only a mime type is not sufficient
|
||||
assert_eq!(instance.get_filename().unwrap(), "index.html");
|
||||
|
||||
@@ -682,13 +684,16 @@ async fn expect_status_update_event(t: &TestContext, instance_id: MsgId) -> Resu
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.set_config_bool(Config::BccSelf, true).await?;
|
||||
let bob = TestContext::new_bob().await;
|
||||
alice.allow_unencrypted().await?;
|
||||
let bob = &tcm.bob().await;
|
||||
bob.allow_unencrypted().await?;
|
||||
|
||||
// Alice sends an webxdc instance and a status update
|
||||
let alice_chat = alice.create_email_chat(&bob).await;
|
||||
let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?;
|
||||
let alice_chat = alice.create_email_chat(bob).await;
|
||||
let alice_instance = send_webxdc_instance(alice, alice_chat.id).await?;
|
||||
let sent1 = &alice.pop_sent_msg().await;
|
||||
assert_eq!(alice_instance.viewtype, Viewtype::Webxdc);
|
||||
assert!(!sent1.payload().contains("report-type=status-update"));
|
||||
@@ -697,7 +702,7 @@ async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar"}}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
expect_status_update_event(&alice, alice_instance.id).await?;
|
||||
expect_status_update_event(alice, alice_instance.id).await?;
|
||||
let sent2 = &alice.pop_sent_msg().await;
|
||||
let alice_update = sent2.load_from_db().await;
|
||||
assert!(alice_update.hidden);
|
||||
@@ -706,10 +711,10 @@ async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
||||
assert_eq!(alice_update.chat_id, alice_instance.chat_id);
|
||||
assert_eq!(
|
||||
alice_update.parent(&alice).await?.unwrap().id,
|
||||
alice_update.parent(alice).await?.unwrap().id,
|
||||
alice_instance.id
|
||||
);
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 1);
|
||||
assert!(sent2.payload().contains("report-type=status-update"));
|
||||
assert!(sent2.payload().contains(BODY_DESCR));
|
||||
assert_eq!(
|
||||
@@ -735,12 +740,12 @@ async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
let bob_chat_id = bob_instance.chat_id;
|
||||
assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid);
|
||||
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
|
||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
|
||||
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 1);
|
||||
|
||||
let bob_received_update = bob.recv_msg_opt(sent2).await;
|
||||
assert!(bob_received_update.is_none());
|
||||
expect_status_update_event(&bob, bob_instance.id).await?;
|
||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
|
||||
expect_status_update_event(bob, bob_instance.id).await?;
|
||||
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 1);
|
||||
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
@@ -749,19 +754,20 @@ async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
);
|
||||
|
||||
// Alice has a second device and also receives messages there
|
||||
let alice2 = TestContext::new_alice().await;
|
||||
let alice2 = &tcm.alice().await;
|
||||
alice2.allow_unencrypted().await?;
|
||||
alice2.recv_msg(sent1).await;
|
||||
alice2.recv_msg_trash(sent2).await;
|
||||
let alice2_instance = alice2.get_last_msg().await;
|
||||
let alice2_chat_id = alice2_instance.chat_id;
|
||||
assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc);
|
||||
assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 1);
|
||||
assert_eq!(alice2_chat_id.get_msg_cnt(alice2).await?, 1);
|
||||
|
||||
// To support the second device, Alice has enabled bcc_self and will receive their own messages;
|
||||
// these messages, however, should be ignored
|
||||
alice.recv_msg_opt(sent1).await;
|
||||
alice.recv_msg_opt(sent2).await;
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 1);
|
||||
assert_eq!(
|
||||
alice
|
||||
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
|
||||
@@ -983,7 +989,7 @@ async fn test_pop_status_update() -> Result<()> {
|
||||
async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
let alice_chat_id = alice.create_email_chat(&bob).await.id;
|
||||
let alice_chat_id = alice.create_chat(&bob).await.id;
|
||||
|
||||
// prepare webxdc instance,
|
||||
// status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None)
|
||||
@@ -1030,8 +1036,6 @@ async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
|
||||
let bob_instance = bob.recv_msg(&sent1).await;
|
||||
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
|
||||
assert_eq!(bob_instance.get_filename().unwrap(), "minimal.xdc");
|
||||
assert!(sent1.payload().contains("Content-Type: application/json"));
|
||||
assert!(sent1.payload().contains("status-update.json"));
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)
|
||||
From: Test Sender <sender@testrun.org>
|
||||
Subject: Large image test
|
||||
To: alice@example.org
|
||||
Message-ID: <big-image-test@testrun.org>
|
||||
Date: Thu, 17 Dec 2020 15:38:45 +0100
|
||||
User-Agent: Test-Agent/1.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="------------BIG_IMAGE_BOUNDARY"
|
||||
Content-Language: en-US
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------BIG_IMAGE_BOUNDARY
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Here is a 4k image
|
||||
|
||||
--------------BIG_IMAGE_BOUNDARY
|
||||
Content-Type: image/png;
|
||||
name="4k_image.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment;
|
||||
filename="4k_image.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
|
||||
--------------BIG_IMAGE_BOUNDARY--
|
||||
From: Test Sender <bob@example.net>
|
||||
Subject: Large image test
|
||||
To: alice@example.org
|
||||
Message-ID: <big-image-test@testrun.org>
|
||||
Date: Thu, 17 Dec 2020 15:38:45 +0100
|
||||
User-Agent: Test-Agent/1.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="------------BIG_IMAGE_BOUNDARY"
|
||||
Content-Language: en-US
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------BIG_IMAGE_BOUNDARY
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Here is a 4k image
|
||||
|
||||
--------------BIG_IMAGE_BOUNDARY
|
||||
Content-Type: image/png;
|
||||
name="4k_image.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment;
|
||||
filename="4k_image.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
|
||||
--------------BIG_IMAGE_BOUNDARY--
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)
|
||||
From: Test Sender <sender@testrun.org>
|
||||
Subject: Large image test
|
||||
To: alice@example.org
|
||||
Message-ID: <huge-image-test@testrun.org>
|
||||
Date: Thu, 17 Dec 2020 15:38:45 +0100
|
||||
User-Agent: Test-Agent/1.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="------------HUGE_IMAGE_BOUNDARY"
|
||||
Content-Language: en-US
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------HUGE_IMAGE_BOUNDARY
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Here is a huge image
|
||||
|
||||
--------------HUGE_IMAGE_BOUNDARY
|
||||
Content-Type: image/png;
|
||||
name="huge_image.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment;
|
||||
filename="huge_image.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAH0AAAB9ACAIAAACJkzqjAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
|
||||
--------------HUGE_IMAGE_BOUNDARY--
|
||||
From: Test Sender <bob@example.net>
|
||||
Subject: Large image test
|
||||
To: alice@example.org
|
||||
Message-ID: <huge-image-test@localhost>
|
||||
Date: Thu, 17 Dec 2020 15:38:45 +0100
|
||||
User-Agent: Test-Agent/1.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="------------HUGE_IMAGE_BOUNDARY"
|
||||
Content-Language: en-US
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------HUGE_IMAGE_BOUNDARY
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Here is a huge image
|
||||
|
||||
--------------HUGE_IMAGE_BOUNDARY
|
||||
Content-Type: image/png;
|
||||
name="huge_image.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment;
|
||||
filename="huge_image.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAH0AAAB9ACAIAAACJkzqjAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
|
||||
--------------HUGE_IMAGE_BOUNDARY--
|
||||
|
||||
@@ -1,76 +1,74 @@
|
||||
X-Mozilla-Status: 0801
|
||||
X-Mozilla-Status2: 10000000
|
||||
Content-Type: multipart/mixed; boundary="------------L1v4sF5IlAZ0HirXymXElgpK"
|
||||
Message-ID: <1e3b3bb0-f34f-71e2-6b86-bce80bef2c6f@example.org>
|
||||
Date: Thu, 3 Aug 2023 13:31:01 -0300
|
||||
MIME-Version: 1.0
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||
Thunderbird/102.13.0
|
||||
Content-Language: en-US
|
||||
To: bob@example.net
|
||||
From: Alice <alice@example.org>
|
||||
X-Identity-Key: id3
|
||||
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK
|
||||
Content-Type: application/octet-stream; name="rusty_deltachat_logo.jpeg"
|
||||
Content-Disposition: attachment; filename="rusty_deltachat_logo.jpeg"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
/9j/4AAQSkZJRgABAQIAzADMAAD/2wBDAP//////////////////////////////////////
|
||||
////////////////////////////////////////////////2wBDAf//////////////////
|
||||
////////////////////////////////////////////////////////////////////wAAR
|
||||
CAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAA
|
||||
AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
|
||||
FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
|
||||
h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl
|
||||
5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA
|
||||
AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk
|
||||
NOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
|
||||
hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk
|
||||
5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCSiiigAooooAKKKKACiiigAooprHAzQA6i
|
||||
mhgfY0McA0AAYGnVX6U/efagA3Hdnt6VL15qvS5OMZ4oAUt82R26f596lByM1BSgkcA0APZ+
|
||||
eO3X3qSq9PV8DBoAl6UVCzZ+lPQ8fSgB9FFICD0oAWiiigAooooAKKKKACiiigAooooAKKKK
|
||||
ACiiigApCcDNLTH6UAKGB9j6UjkYx3qKigAooooAKKKKACiiigAooooAKKKKAClBI6UlFACk
|
||||
k9akTofrUVKCR0oAnoqEuT7fSnp0P1oAfRRRQAUUUUAFFFFABRRRQAUUUUAFIwyDTd496QuM
|
||||
cUAMBI6GkJJ60UUAFFFFABRSqMmpgAOlAEYQnrxTtg96fSEgdTQAmxfT+dLtX0FJvX1o3r6/
|
||||
zoAXavoKTYvp/OlBB6GloAZsHvTSh7HNS0UAQEEdRSVYqNk7j8qAI6KKKACnq2ODTKKAJS47
|
||||
CnDkA1AOtWKACiiigAooooAKKKKACimscDOM03zPb9f/AK1ADXGD9eabSk5OTSUAFFFFABRR
|
||||
RQBMowB7806kXoPpQeh+hoAjZ+w/OmUUUAFFFFABTw/r+dMooAsdaKhDEfT0qUEHkUALRRRQ
|
||||
BG69x+NR1O33T9KgoAKKKKACnBiBim0UASKSTyeKkpiYwfWn0AFFFFABRRTWbb2zQA4jPFV6
|
||||
eX46Y/GmUAFFFFABRRRQAUUUUASIe35VJVepVbPB6/zoAay45HT+VMqxTCgPTg0ARUUpBHWk
|
||||
oAKKKKAClBI6UlFAEocd+KXevr+hqGigBzNn6U2iigAooooAKKKKAAEjpTgWJxk02pExk+tA
|
||||
ElFFFABSEZGKCQOtNLjtzQBF0ooooAKKKKACiiigAooooAKKKKAJFfsfz/xqSq9OViPcUATY
|
||||
z1qMp6flTwQelLQBX6UVOQD1qMoR05/nQAyiiigAooooAKKKKACiiigAooooAKAcdKKKAHBm
|
||||
+tTUxAMZ70+gBCARioSCDzU+cdahY5PHagBtFFFABRRRQAUUUUAFFFFABRRRQAUUdelPCHvx
|
||||
QA0EjpUqsD9fSkCD3NOCgdqAFooooAaVB+vrURUj/Gp6KAK9FSMncfl/hUdABRRRQAUUUUAF
|
||||
FFFABRRRQA4MR0p6tnjHNRgZOKmCgUARsp69R/KmVYqA9Tj1oASiiigAooooAKKKKACiilAJ
|
||||
4FACVIE9fypwUD6+tOoAQADpS0UhIHJoAWkLAdTUZcnpwKZQBIXHYUB+eelR0UAWKKiRux/C
|
||||
paACmMueR1/nT6KAK9FSsueR1/nUVABRRRQAUUUUAFFFFACg4OalDA+31qGpFTufyoAazE8d
|
||||
B6U2pyAetQsMEigBKKKKACiiigAooooABzxU4GBTEHf8qkoAKKKKADpUDHJ/lT3Pb8TUdABR
|
||||
RRQAUUUUAFTKcj3FQ05Tg/pQBNRRRQAVE4wc9j/OpaQjIIoAgooooAKKKKACiiigBRwQanqv
|
||||
T03Z9vegB7Nj61DUxUGmMuKAGUUUUAFFFFABRRSr1H1oAmAwAKWiigAooooAgY5JpKKKACii
|
||||
igAooooAKKKKAJwcgGlpifdp9ABRRRQBCwwx/Om09+o+lMoAKKKKACiiigCRB1qSoAcHNTA5
|
||||
GaAFqJ2zwPxqWo9h9aAI6KUjBwaSgAooooAKcv3hTaUdR9aAJ6KKKACkPQ/Q0tFAFeiiigAo
|
||||
oooAKKKKACiiigCVOh+v9BT6an3adQAUUUUARP1H0plOc5Y/lTaACiiigApVGSBSUdOaAJ8D
|
||||
0FLSA5GaWgAoooPTjrQBE5yfpTKfsb2ppBBwaAEooooAKKKKAJ1OQDS1Ehwcev8AOpaACiii
|
||||
gCAjBP1pKe45z60ygAooooAKKKKACiinIMn2FAEoGABS0UUAFITgZpaidsnHYUAMooooAKKK
|
||||
KACpti+n6moalVs8Hr/OgBwGOBS0UUAFFFFABUb9hUlRlCSTkUAR0U8oQM9aZQAUUUUAFSq2
|
||||
eD1/nUVFAFiimK2eD1/nT6AEIyMVCQQcGp6QgHrQBBRTyh7c03afQ0AJRS7T6GnBD34oAaAS
|
||||
cCpgMDFAAHSloAKKKjZ+w/P/AAoAGbsPxqOiigAooooAKKKKAFUZOKlCgf41EDg5FSBwevFA
|
||||
D6KKKACiiigAooooAQnAJqCpyMjFIEUds/WgCGinuoHI/KmUAFFFFABTw5HXn+dMooAnBB6G
|
||||
lqvTgxHegCaiot59BS+Z7frQBJRUfme1JvPsKAJaaXA96iJJ6mkoAcWJ+npTaKKACiiigAoo
|
||||
ooAKKKKACpVXHJ61FUocYGetAD6KQEHoaWgAooooAKKKKACiiigBj9B9aaEJ68VLRQBAQR1p
|
||||
KlfoPrTApNADaKUgjrSUAFFFFABRRRQAUUUUAFFFFABRRSkEdRQAlFKBkgVKUGOOKAIaKXBz
|
||||
ipto9BQBBRQRg4p+w4z39KAGUUUUASouOe5/lT6QHIzS0AFFFFABRRRQAUUUUAFFFFACEA9a
|
||||
WiigBr/d+lRqufpU1FAEbJgZFR1YqNU557dKAG7W9KbVioip3YHegBoBPQUlTgYGKay55HX+
|
||||
dADFXdSshHPWnqMD3p1AESdfwqWkAA6d6WgBgQA5H5U+iigBMDOe9LRRQAm0ZzS0UUARFTu9
|
||||
jzmpNoxjFLRQAgGOlLRRQB//2Q==
|
||||
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK--
|
||||
Content-Type: multipart/mixed; boundary="------------L1v4sF5IlAZ0HirXymXElgpK"
|
||||
Message-ID: <1e3b3bb0-f34f-71e2-6b86-bce80bef2c6f@example.net>
|
||||
Date: Thu, 3 Aug 2023 13:31:01 -0300
|
||||
MIME-Version: 1.0
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||
Thunderbird/102.13.0
|
||||
Content-Language: en-US
|
||||
To: bob@example.net
|
||||
From: Alice <alice@example.org>
|
||||
X-Identity-Key: id3
|
||||
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK
|
||||
Content-Type: application/octet-stream; name="rusty_deltachat_logo.jpeg"
|
||||
Content-Disposition: attachment; filename="rusty_deltachat_logo.jpeg"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
/9j/4AAQSkZJRgABAQIAzADMAAD/2wBDAP//////////////////////////////////////
|
||||
////////////////////////////////////////////////2wBDAf//////////////////
|
||||
////////////////////////////////////////////////////////////////////wAAR
|
||||
CAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAA
|
||||
AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
|
||||
FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
|
||||
h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl
|
||||
5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA
|
||||
AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk
|
||||
NOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
|
||||
hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk
|
||||
5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCSiiigAooooAKKKKACiiigAooprHAzQA6i
|
||||
mhgfY0McA0AAYGnVX6U/efagA3Hdnt6VL15qvS5OMZ4oAUt82R26f596lByM1BSgkcA0APZ+
|
||||
eO3X3qSq9PV8DBoAl6UVCzZ+lPQ8fSgB9FFICD0oAWiiigAooooAKKKKACiiigAooooAKKKK
|
||||
ACiiigApCcDNLTH6UAKGB9j6UjkYx3qKigAooooAKKKKACiiigAooooAKKKKAClBI6UlFACk
|
||||
k9akTofrUVKCR0oAnoqEuT7fSnp0P1oAfRRRQAUUUUAFFFFABRRRQAUUUUAFIwyDTd496QuM
|
||||
cUAMBI6GkJJ60UUAFFFFABRSqMmpgAOlAEYQnrxTtg96fSEgdTQAmxfT+dLtX0FJvX1o3r6/
|
||||
zoAXavoKTYvp/OlBB6GloAZsHvTSh7HNS0UAQEEdRSVYqNk7j8qAI6KKKACnq2ODTKKAJS47
|
||||
CnDkA1AOtWKACiiigAooooAKKKKACimscDOM03zPb9f/AK1ADXGD9eabSk5OTSUAFFFFABRR
|
||||
RQBMowB7806kXoPpQeh+hoAjZ+w/OmUUUAFFFFABTw/r+dMooAsdaKhDEfT0qUEHkUALRRRQ
|
||||
BG69x+NR1O33T9KgoAKKKKACnBiBim0UASKSTyeKkpiYwfWn0AFFFFABRRTWbb2zQA4jPFV6
|
||||
eX46Y/GmUAFFFFABRRRQAUUUUASIe35VJVepVbPB6/zoAay45HT+VMqxTCgPTg0ARUUpBHWk
|
||||
oAKKKKAClBI6UlFAEocd+KXevr+hqGigBzNn6U2iigAooooAKKKKAAEjpTgWJxk02pExk+tA
|
||||
ElFFFABSEZGKCQOtNLjtzQBF0ooooAKKKKACiiigAooooAKKKKAJFfsfz/xqSq9OViPcUATY
|
||||
z1qMp6flTwQelLQBX6UVOQD1qMoR05/nQAyiiigAooooAKKKKACiiigAooooAKAcdKKKAHBm
|
||||
+tTUxAMZ70+gBCARioSCDzU+cdahY5PHagBtFFFABRRRQAUUUUAFFFFABRRRQAUUdelPCHvx
|
||||
QA0EjpUqsD9fSkCD3NOCgdqAFooooAaVB+vrURUj/Gp6KAK9FSMncfl/hUdABRRRQAUUUUAF
|
||||
FFFABRRRQA4MR0p6tnjHNRgZOKmCgUARsp69R/KmVYqA9Tj1oASiiigAooooAKKKKACiilAJ
|
||||
4FACVIE9fypwUD6+tOoAQADpS0UhIHJoAWkLAdTUZcnpwKZQBIXHYUB+eelR0UAWKKiRux/C
|
||||
paACmMueR1/nT6KAK9FSsueR1/nUVABRRRQAUUUUAFFFFACg4OalDA+31qGpFTufyoAazE8d
|
||||
B6U2pyAetQsMEigBKKKKACiiigAooooABzxU4GBTEHf8qkoAKKKKADpUDHJ/lT3Pb8TUdABR
|
||||
RRQAUUUUAFTKcj3FQ05Tg/pQBNRRRQAVE4wc9j/OpaQjIIoAgooooAKKKKACiiigBRwQanqv
|
||||
T03Z9vegB7Nj61DUxUGmMuKAGUUUUAFFFFABRRSr1H1oAmAwAKWiigAooooAgY5JpKKKACii
|
||||
igAooooAKKKKAJwcgGlpifdp9ABRRRQBCwwx/Om09+o+lMoAKKKKACiiigCRB1qSoAcHNTA5
|
||||
GaAFqJ2zwPxqWo9h9aAI6KUjBwaSgAooooAKcv3hTaUdR9aAJ6KKKACkPQ/Q0tFAFeiiigAo
|
||||
oooAKKKKACiiigCVOh+v9BT6an3adQAUUUUARP1H0plOc5Y/lTaACiiigApVGSBSUdOaAJ8D
|
||||
0FLSA5GaWgAoooPTjrQBE5yfpTKfsb2ppBBwaAEooooAKKKKAJ1OQDS1Ehwcev8AOpaACiii
|
||||
gCAjBP1pKe45z60ygAooooAKKKKACiinIMn2FAEoGABS0UUAFITgZpaidsnHYUAMooooAKKK
|
||||
KACpti+n6moalVs8Hr/OgBwGOBS0UUAFFFFABUb9hUlRlCSTkUAR0U8oQM9aZQAUUUUAFSq2
|
||||
eD1/nUVFAFiimK2eD1/nT6AEIyMVCQQcGp6QgHrQBBRTyh7c03afQ0AJRS7T6GnBD34oAaAS
|
||||
cCpgMDFAAHSloAKKKjZ+w/P/AAoAGbsPxqOiigAooooAKKKKAFUZOKlCgf41EDg5FSBwevFA
|
||||
D6KKKACiiigAooooAQnAJqCpyMjFIEUds/WgCGinuoHI/KmUAFFFFABTw5HXn+dMooAnBB6G
|
||||
lqvTgxHegCaiot59BS+Z7frQBJRUfme1JvPsKAJaaXA96iJJ6mkoAcWJ+npTaKKACiiigAoo
|
||||
ooAKKKKACpVXHJ61FUocYGetAD6KQEHoaWgAooooAKKKKACiiigBj9B9aaEJ68VLRQBAQR1p
|
||||
KlfoPrTApNADaKUgjrSUAFFFFABRRRQAUUUUAFFFFABRRSkEdRQAlFKBkgVKUGOOKAIaKXBz
|
||||
ipto9BQBBRQRg4p+w4z39KAGUUUUASouOe5/lT6QHIzS0AFFFFABRRRQAUUUUAFFFFACEA9a
|
||||
WiigBr/d+lRqufpU1FAEbJgZFR1YqNU557dKAG7W9KbVioip3YHegBoBPQUlTgYGKay55HX+
|
||||
dADFXdSshHPWnqMD3p1AESdfwqWkAA6d6WgBgQA5H5U+iigBMDOe9LRRQAm0ZzS0UUARFTu9
|
||||
jzmpNoxjFLRQAgGOlLRRQB//2Q==
|
||||
|
||||
--------------L1v4sF5IlAZ0HirXymXElgpK--
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
Subject: webxdc object attached
|
||||
Message-ID: 67890@example.org
|
||||
Date: Fri, 03 Dec 2021 10:00:27 +0000
|
||||
To: alice@example.org
|
||||
From: bob@example.org
|
||||
Chat-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="==BREAK=="
|
||||
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
webxdc with bad extension and bad content.
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: application/webxdc+zip
|
||||
Content-Disposition: attachment; filename=index.html
|
||||
|
||||
<html>hey!<html>
|
||||
|
||||
--==BREAK==--
|
||||
Subject: webxdc object attached
|
||||
Message-ID: 67890@example.net
|
||||
Date: Fri, 03 Dec 2021 10:00:27 +0000
|
||||
To: alice@example.org
|
||||
From: bob@example.net
|
||||
Chat-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="==BREAK=="
|
||||
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
webxdc with bad extension and bad content.
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: application/webxdc+zip
|
||||
Content-Disposition: attachment; filename=index.html
|
||||
|
||||
<html>hey!<html>
|
||||
|
||||
--==BREAK==--
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
Subject: webxdc object attached
|
||||
Message-ID: 12345@example.org
|
||||
Date: Fri, 03 Dec 2021 10:00:27 +0000
|
||||
To: alice@example.org
|
||||
From: bob@example.org
|
||||
Chat-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="==BREAK=="
|
||||
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
webxdc with good extension;
|
||||
the mimetype is ignored then,
|
||||
content is checked.
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/html
|
||||
Content-Disposition: attachment; filename=minimal.xdc
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ
|
||||
T1AQAABBQAAACzUXTxdw6JDHBVyCjJzbHjsoFQCgo2SfkplSCGgkJJanGJIphlU5xclFlQAhFWUEjJ
|
||||
Ty7NTc0r0SsvyixJ1VAqyU8EqlTStIYo1kdWbZOXj6o5uiQjsxhodkWJQnFGfmlOikJefolCUiqIV5
|
||||
4XCzUCWZeNPsRNNvoQRwIAUEsBAh4DFAACAAgAUmqdU4i2NKh8AAAAvAAAAAoAGAAAAAAAAQAAAKSB
|
||||
AAAAAGluZGV4Lmh0bWxVVAUAAxtSzGF1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBQAAAAwAAAAA
|
||||
AA
|
||||
|
||||
--==BREAK==--
|
||||
Subject: webxdc object attached
|
||||
Message-ID: 12345@example.net
|
||||
Date: Fri, 03 Dec 2021 10:00:27 +0000
|
||||
To: alice@example.org
|
||||
From: bob@example.net
|
||||
Chat-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="==BREAK=="
|
||||
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
webxdc with good extension;
|
||||
the mimetype is ignored then,
|
||||
content is checked.
|
||||
|
||||
--==BREAK==
|
||||
Content-Type: text/html
|
||||
Content-Disposition: attachment; filename=minimal.xdc
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ
|
||||
T1AQAABBQAAACzUXTxdw6JDHBVyCjJzbHjsoFQCgo2SfkplSCGgkJJanGJIphlU5xclFlQAhFWUEjJ
|
||||
Ty7NTc0r0SsvyixJ1VAqyU8EqlTStIYo1kdWbZOXj6o5uiQjsxhodkWJQnFGfmlOikJefolCUiqIV5
|
||||
4XCzUCWZeNPsRNNvoQRwIAUEsBAh4DFAACAAgAUmqdU4i2NKh8AAAAvAAAAAoAGAAAAAAAAQAAAKSB
|
||||
AAAAAGluZGV4Lmh0bWxVVAUAAxtSzGF1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBQAAAAwAAAAA
|
||||
AA
|
||||
|
||||
--==BREAK==--
|
||||
|
||||
Reference in New Issue
Block a user