mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
feat: add "e2ee encrypted" info message to all e2ee chats (#7008)
this PR adds a info message "messages are end-to-end-encrypted" also for chats created by eg. vcards. by the removal of lock icons, this is a good place to hint for that in addition; this is also what eg. whatsapp and others are doing the wording itself is tweaked at https://github.com/deltachat/deltachat-android/pull/3817 (and there is also the rough idea to make the message a little more outstanding, by some more dedicated colors) ~~did not test in practise, if this leads to double "e2ee info messages" on secure join, tests look good, however.~~ EDIT: did lots of practise tests meanwhile :) most of the changes in this PR are about test ... ftr, in another PR, after 2.0 reeases, there could probably quite some code cleanup wrt set-protection, protection-disabled etc. --------- Co-authored-by: Hocuri <hocuri@gmx.de>
This commit is contained in:
@@ -4535,12 +4535,12 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
|||||||
* - DC_INFO_MEMBER_ADDED_TO_GROUP (4) - "Member CONTACT added by OTHER_CONTACT"
|
* - DC_INFO_MEMBER_ADDED_TO_GROUP (4) - "Member CONTACT added by OTHER_CONTACT"
|
||||||
* - DC_INFO_MEMBER_REMOVED_FROM_GROUP (5) - "Member CONTACT removed by OTHER_CONTACT"
|
* - DC_INFO_MEMBER_REMOVED_FROM_GROUP (5) - "Member CONTACT removed by OTHER_CONTACT"
|
||||||
* - DC_INFO_EPHEMERAL_TIMER_CHANGED (10) - "Disappearing messages CHANGED_TO by CONTACT"
|
* - DC_INFO_EPHEMERAL_TIMER_CHANGED (10) - "Disappearing messages CHANGED_TO by CONTACT"
|
||||||
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
|
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is protected"
|
||||||
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
|
|
||||||
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
||||||
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
|
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
|
||||||
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
||||||
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
|
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
|
||||||
|
* - DC_INFO_CHAT_E2EE (50) - Info-message for "Chat is end-to-end-encrypted"
|
||||||
*
|
*
|
||||||
* For the messages that refer to a CONTACT,
|
* For the messages that refer to a CONTACT,
|
||||||
* dc_msg_get_info_contact_id() returns the contact ID.
|
* dc_msg_get_info_contact_id() returns the contact ID.
|
||||||
@@ -4593,9 +4593,10 @@ uint32_t dc_msg_get_info_contact_id (const dc_msg_t* msg);
|
|||||||
#define DC_INFO_LOCATION_ONLY 9
|
#define DC_INFO_LOCATION_ONLY 9
|
||||||
#define DC_INFO_EPHEMERAL_TIMER_CHANGED 10
|
#define DC_INFO_EPHEMERAL_TIMER_CHANGED 10
|
||||||
#define DC_INFO_PROTECTION_ENABLED 11
|
#define DC_INFO_PROTECTION_ENABLED 11
|
||||||
#define DC_INFO_PROTECTION_DISABLED 12
|
#define DC_INFO_PROTECTION_DISABLED 12 // deprecated 2025-07
|
||||||
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
|
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
|
||||||
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
|
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
|
||||||
|
#define DC_INFO_CHAT_E2EE 50
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6898,9 +6899,7 @@ void dc_event_unref(dc_event_t* event);
|
|||||||
/// Used in summaries.
|
/// Used in summaries.
|
||||||
#define DC_STR_GIF 23
|
#define DC_STR_GIF 23
|
||||||
|
|
||||||
/// "Encrypted message"
|
/// @deprecated 2025-07, this string is no longer needed.
|
||||||
///
|
|
||||||
/// Used in subjects of outgoing messages.
|
|
||||||
#define DC_STR_ENCRYPTEDMSG 24
|
#define DC_STR_ENCRYPTEDMSG 24
|
||||||
|
|
||||||
/// "End-to-end encryption available."
|
/// "End-to-end encryption available."
|
||||||
@@ -7605,7 +7604,7 @@ void dc_event_unref(dc_event_t* event);
|
|||||||
/// Used as a device message after a successful backup transfer.
|
/// Used as a device message after a successful backup transfer.
|
||||||
#define DC_STR_BACKUP_TRANSFER_MSG_BODY 163
|
#define DC_STR_BACKUP_TRANSFER_MSG_BODY 163
|
||||||
|
|
||||||
/// "Messages are guaranteed to be end-to-end encrypted from now on."
|
/// "Messages are end-to-end encrypted."
|
||||||
///
|
///
|
||||||
/// Used in info messages.
|
/// Used in info messages.
|
||||||
#define DC_STR_CHAT_PROTECTION_ENABLED 170
|
#define DC_STR_CHAT_PROTECTION_ENABLED 170
|
||||||
@@ -7613,6 +7612,7 @@ void dc_event_unref(dc_event_t* event);
|
|||||||
/// "%1$s sent a message from another device."
|
/// "%1$s sent a message from another device."
|
||||||
///
|
///
|
||||||
/// Used in info messages.
|
/// Used in info messages.
|
||||||
|
/// @deprecated 2025-07
|
||||||
#define DC_STR_CHAT_PROTECTION_DISABLED 171
|
#define DC_STR_CHAT_PROTECTION_DISABLED 171
|
||||||
|
|
||||||
/// "Others will only see this group after you sent a first message."
|
/// "Others will only see this group after you sent a first message."
|
||||||
|
|||||||
@@ -416,6 +416,9 @@ pub enum SystemMessageType {
|
|||||||
/// Chat ephemeral message timer is changed.
|
/// Chat ephemeral message timer is changed.
|
||||||
EphemeralTimerChanged,
|
EphemeralTimerChanged,
|
||||||
|
|
||||||
|
// Chat is e2ee
|
||||||
|
ChatE2ee,
|
||||||
|
|
||||||
// Chat protection state changed
|
// Chat protection state changed
|
||||||
ChatProtectionEnabled,
|
ChatProtectionEnabled,
|
||||||
ChatProtectionDisabled,
|
ChatProtectionDisabled,
|
||||||
@@ -450,6 +453,7 @@ impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
|
|||||||
SystemMessage::LocationStreamingEnabled => SystemMessageType::LocationStreamingEnabled,
|
SystemMessage::LocationStreamingEnabled => SystemMessageType::LocationStreamingEnabled,
|
||||||
SystemMessage::LocationOnly => SystemMessageType::LocationOnly,
|
SystemMessage::LocationOnly => SystemMessageType::LocationOnly,
|
||||||
SystemMessage::EphemeralTimerChanged => SystemMessageType::EphemeralTimerChanged,
|
SystemMessage::EphemeralTimerChanged => SystemMessageType::EphemeralTimerChanged,
|
||||||
|
SystemMessage::ChatE2ee => SystemMessageType::ChatE2ee,
|
||||||
SystemMessage::ChatProtectionEnabled => SystemMessageType::ChatProtectionEnabled,
|
SystemMessage::ChatProtectionEnabled => SystemMessageType::ChatProtectionEnabled,
|
||||||
SystemMessage::ChatProtectionDisabled => SystemMessageType::ChatProtectionDisabled,
|
SystemMessage::ChatProtectionDisabled => SystemMessageType::ChatProtectionDisabled,
|
||||||
SystemMessage::MultiDeviceSync => SystemMessageType::MultiDeviceSync,
|
SystemMessage::MultiDeviceSync => SystemMessageType::MultiDeviceSync,
|
||||||
|
|||||||
@@ -95,8 +95,10 @@ describe("online tests", function () {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(messageList).have.length(1);
|
// There are 2 messages in the chat:
|
||||||
const message = await dc.rpc.getMessage(accountId2, messageList[0]);
|
// 'Messages are end-to-end encrypted' (info message) and 'Hello'
|
||||||
|
expect(messageList).have.length(2);
|
||||||
|
const message = await dc.rpc.getMessage(accountId2, messageList[1]);
|
||||||
expect(message.text).equal("Hello");
|
expect(message.text).equal("Hello");
|
||||||
expect(message.showPadlock).equal(true);
|
expect(message.showPadlock).equal(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ from . import Account, AttrDict, Bot, Chat, Client, DeltaChat, EventType, Messag
|
|||||||
from ._utils import futuremethod
|
from ._utils import futuremethod
|
||||||
from .rpc import Rpc
|
from .rpc import Rpc
|
||||||
|
|
||||||
|
E2EE_INFO_MSGS = 1
|
||||||
|
"""
|
||||||
|
The number of info messages added to new e2ee chats.
|
||||||
|
Currently this is "End-to-end encryption available".
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ACFactory:
|
class ACFactory:
|
||||||
"""Test account factory."""
|
"""Test account factory."""
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ def test_one_account_send_bcc_setting(acfactory, log, direct_imap):
|
|||||||
assert ac1.get_config("bcc_self") == "1"
|
assert ac1.get_config("bcc_self") == "1"
|
||||||
|
|
||||||
# Second client receives only second message, but not the first.
|
# Second client receives only second message, but not the first.
|
||||||
|
ev_msg = ac1_clone.wait_for_event(EventType.MSGS_CHANGED)
|
||||||
|
assert ac1_clone.get_message_by_id(ev_msg.msg_id).get_snapshot().text == "Messages are end-to-end encrypted."
|
||||||
|
|
||||||
ev_msg = ac1_clone.wait_for_event(EventType.MSGS_CHANGED)
|
ev_msg = ac1_clone.wait_for_event(EventType.MSGS_CHANGED)
|
||||||
assert ac1_clone.get_message_by_id(ev_msg.msg_id).get_snapshot().text == msg_out.get_snapshot().text
|
assert ac1_clone.get_message_by_id(ev_msg.msg_id).get_snapshot().text == msg_out.get_snapshot().text
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import pytest
|
|||||||
|
|
||||||
from deltachat_rpc_client import Contact, EventType, Message, events
|
from deltachat_rpc_client import Contact, EventType, Message, events
|
||||||
from deltachat_rpc_client.const import ChatType, DownloadState, MessageState
|
from deltachat_rpc_client.const import ChatType, DownloadState, MessageState
|
||||||
|
from deltachat_rpc_client.pytestplugin import E2EE_INFO_MSGS
|
||||||
from deltachat_rpc_client.rpc import JsonRpcError
|
from deltachat_rpc_client.rpc import JsonRpcError
|
||||||
|
|
||||||
|
|
||||||
@@ -457,8 +458,12 @@ def test_wait_next_messages(acfactory) -> None:
|
|||||||
alice_chat_bot.send_text("Hello!")
|
alice_chat_bot.send_text("Hello!")
|
||||||
|
|
||||||
next_messages = next_messages_task.result()
|
next_messages = next_messages_task.result()
|
||||||
assert len(next_messages) == 1
|
|
||||||
snapshot = next_messages[0].get_snapshot()
|
if len(next_messages) == E2EE_INFO_MSGS:
|
||||||
|
next_messages += bot.wait_next_messages()
|
||||||
|
|
||||||
|
assert len(next_messages) == 1 + E2EE_INFO_MSGS
|
||||||
|
snapshot = next_messages[0 + E2EE_INFO_MSGS].get_snapshot()
|
||||||
assert snapshot.text == "Hello!"
|
assert snapshot.text == "Hello!"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ import deltachat
|
|||||||
from . import Account, account_hookimpl, const, get_core_info
|
from . import Account, account_hookimpl, const, get_core_info
|
||||||
from .events import FFIEventLogger, FFIEventTracker
|
from .events import FFIEventLogger, FFIEventTracker
|
||||||
|
|
||||||
|
E2EE_INFO_MSGS = 1
|
||||||
|
"""
|
||||||
|
The number of info messages added to new e2ee chats.
|
||||||
|
Currently this is "End-to-end encryption available".
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup("deltachat testplugin options")
|
group = parser.getgroup("deltachat testplugin options")
|
||||||
@@ -606,7 +612,7 @@ class ACFactory:
|
|||||||
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
msg = ac2.get_message_by_id(ev.data2)
|
msg = ac2.get_message_by_id(ev.data2)
|
||||||
assert msg is not None
|
assert msg is not None
|
||||||
assert msg.text == "Messages are guaranteed to be end-to-end encrypted from now on."
|
assert msg.text == "Messages are end-to-end encrypted."
|
||||||
msg = ac2._evtracker.wait_next_incoming_message()
|
msg = ac2._evtracker.wait_next_incoming_message()
|
||||||
assert msg is not None
|
assert msg is not None
|
||||||
assert "Member Me " in msg.text and " added by " in msg.text
|
assert "Member Me " in msg.text and " added by " in msg.text
|
||||||
|
|||||||
@@ -133,8 +133,7 @@ def test_qr_verified_group_and_chatting(acfactory, lp):
|
|||||||
assert "added" in msg.text.lower()
|
assert "added" in msg.text.lower()
|
||||||
|
|
||||||
assert any(
|
assert any(
|
||||||
m.is_system_message() and m.text == "Messages are guaranteed to be end-to-end encrypted from now on."
|
m.is_system_message() and m.text == "Messages are end-to-end encrypted." for m in msg.chat.get_messages()
|
||||||
for m in msg.chat.get_messages()
|
|
||||||
)
|
)
|
||||||
lp.sec("ac1: send message")
|
lp.sec("ac1: send message")
|
||||||
msg_out = chat1.send_text("hello")
|
msg_out = chat1.send_text("hello")
|
||||||
@@ -338,7 +337,7 @@ def test_use_new_verified_group_after_going_online(acfactory, data, tmp_path, lp
|
|||||||
assert contact.addr == ac1.get_config("addr")
|
assert contact.addr == ac1.get_config("addr")
|
||||||
chat2 = msg_in.chat
|
chat2 = msg_in.chat
|
||||||
assert chat2.is_protected()
|
assert chat2.is_protected()
|
||||||
assert chat2.get_messages()[0].text == "Messages are guaranteed to be end-to-end encrypted from now on."
|
assert chat2.get_messages()[0].text == "Messages are end-to-end encrypted."
|
||||||
assert open(contact.get_profile_image(), "rb").read() == open(avatar_path, "rb").read()
|
assert open(contact.get_profile_image(), "rb").read() == open(avatar_path, "rb").read()
|
||||||
|
|
||||||
lp.sec("ac2_offl: sending message")
|
lp.sec("ac2_offl: sending message")
|
||||||
@@ -412,7 +411,7 @@ def test_verified_group_vs_delete_server_after(acfactory, tmp_path, lp):
|
|||||||
ev = ac2_offl._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2_offl._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
msg_in = ac2_offl.get_message_by_id(ev.data2)
|
msg_in = ac2_offl.get_message_by_id(ev.data2)
|
||||||
assert msg_in.is_system_message()
|
assert msg_in.is_system_message()
|
||||||
assert msg_in.text == "Messages are guaranteed to be end-to-end encrypted from now on."
|
assert msg_in.text == "Messages are end-to-end encrypted."
|
||||||
|
|
||||||
# We need to consume one event that has data2=0
|
# We need to consume one event that has data2=0
|
||||||
ev = ac2_offl._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2_offl._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from imap_tools import AND, U
|
|||||||
import deltachat as dc
|
import deltachat as dc
|
||||||
from deltachat import account_hookimpl, Message
|
from deltachat import account_hookimpl, Message
|
||||||
from deltachat.tracker import ImexTracker
|
from deltachat.tracker import ImexTracker
|
||||||
|
from deltachat.testplugin import E2EE_INFO_MSGS
|
||||||
|
|
||||||
|
|
||||||
def test_basic_imap_api(acfactory, tmp_path):
|
def test_basic_imap_api(acfactory, tmp_path):
|
||||||
@@ -408,6 +409,10 @@ def test_forward_messages(acfactory, lp):
|
|||||||
msg_out = chat.send_text("message2")
|
msg_out = chat.send_text("message2")
|
||||||
|
|
||||||
lp.sec("ac2: wait for receive")
|
lp.sec("ac2: wait for receive")
|
||||||
|
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
|
msg_in = ac2.get_message_by_id(ev.data2)
|
||||||
|
assert msg_in.text == "Messages are end-to-end encrypted."
|
||||||
|
|
||||||
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
assert ev.data2 == msg_out.id
|
assert ev.data2 == msg_out.id
|
||||||
msg_in = ac2.get_message_by_id(msg_out.id)
|
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||||
@@ -622,6 +627,11 @@ def test_moved_markseen(acfactory):
|
|||||||
|
|
||||||
with ac2.direct_imap.idle() as idle2:
|
with ac2.direct_imap.idle() as idle2:
|
||||||
ac2.start_io()
|
ac2.start_io()
|
||||||
|
|
||||||
|
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
|
msg = ac2.get_message_by_id(ev.data2)
|
||||||
|
assert msg.text == "Messages are end-to-end encrypted."
|
||||||
|
|
||||||
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
msg = ac2.get_message_by_id(ev.data2)
|
msg = ac2.get_message_by_id(ev.data2)
|
||||||
|
|
||||||
@@ -738,7 +748,7 @@ def test_mdn_asymmetric(acfactory, lp):
|
|||||||
lp.sec("sending text message from ac1 to ac2")
|
lp.sec("sending text message from ac1 to ac2")
|
||||||
msg_out = chat.send_text("message1")
|
msg_out = chat.send_text("message1")
|
||||||
|
|
||||||
assert len(chat.get_messages()) == 1
|
assert len(chat.get_messages()) == 1 + E2EE_INFO_MSGS
|
||||||
|
|
||||||
lp.sec("disable ac1 MDNs")
|
lp.sec("disable ac1 MDNs")
|
||||||
ac1.set_config("mdns_enabled", "0")
|
ac1.set_config("mdns_enabled", "0")
|
||||||
@@ -746,7 +756,7 @@ def test_mdn_asymmetric(acfactory, lp):
|
|||||||
lp.sec("wait for ac2 to receive message")
|
lp.sec("wait for ac2 to receive message")
|
||||||
msg = ac2._evtracker.wait_next_incoming_message()
|
msg = ac2._evtracker.wait_next_incoming_message()
|
||||||
|
|
||||||
assert len(msg.chat.get_messages()) == 1
|
assert len(msg.chat.get_messages()) == 1 + E2EE_INFO_MSGS
|
||||||
|
|
||||||
lp.sec("ac2: mark incoming message as seen")
|
lp.sec("ac2: mark incoming message as seen")
|
||||||
ac2.mark_seen_messages([msg])
|
ac2.mark_seen_messages([msg])
|
||||||
@@ -755,7 +765,7 @@ def test_mdn_asymmetric(acfactory, lp):
|
|||||||
# MDN should be moved even though MDNs are already disabled
|
# MDN should be moved even though MDNs are already disabled
|
||||||
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||||
|
|
||||||
assert len(chat.get_messages()) == 1
|
assert len(chat.get_messages()) == 1 + E2EE_INFO_MSGS
|
||||||
|
|
||||||
# Wait for the message 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 DeltaChat as seen.")
|
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder DeltaChat as seen.")
|
||||||
@@ -1123,6 +1133,11 @@ def test_send_and_receive_image(acfactory, lp, data):
|
|||||||
assert m == msg_out
|
assert m == msg_out
|
||||||
|
|
||||||
lp.sec("wait for ac2 to receive message")
|
lp.sec("wait for ac2 to receive message")
|
||||||
|
|
||||||
|
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED|DC_EVENT_INCOMING_MSG")
|
||||||
|
msg_in = ac2.get_message_by_id(ev.data2)
|
||||||
|
assert msg_in.text == "Messages are end-to-end encrypted."
|
||||||
|
|
||||||
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED|DC_EVENT_INCOMING_MSG")
|
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED|DC_EVENT_INCOMING_MSG")
|
||||||
assert ev.data2 == msg_out.id
|
assert ev.data2 == msg_out.id
|
||||||
msg_in = ac2.get_message_by_id(msg_out.id)
|
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||||
@@ -1158,10 +1173,10 @@ def test_import_export_online_all(acfactory, tmp_path, data, lp):
|
|||||||
assert contact2.addr == some1_addr
|
assert contact2.addr == some1_addr
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 3
|
assert len(messages) == 3 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert messages[1].filemime == "image/png"
|
assert messages[1 + E2EE_INFO_MSGS].filemime == "image/png"
|
||||||
assert os.stat(messages[1].filename).st_size == os.stat(original_image_path).st_size
|
assert os.stat(messages[1 + E2EE_INFO_MSGS].filename).st_size == os.stat(original_image_path).st_size
|
||||||
ac.set_config("displayname", "new displayname")
|
ac.set_config("displayname", "new displayname")
|
||||||
assert ac.get_config("displayname") == "new displayname"
|
assert ac.get_config("displayname") == "new displayname"
|
||||||
|
|
||||||
@@ -1414,8 +1429,8 @@ def test_connectivity(acfactory, lp):
|
|||||||
ac1.maybe_network()
|
ac1.maybe_network()
|
||||||
ac1._evtracker.wait_for_connectivity(dc.const.DC_CONNECTIVITY_CONNECTED)
|
ac1._evtracker.wait_for_connectivity(dc.const.DC_CONNECTIVITY_CONNECTED)
|
||||||
msgs = ac1.create_chat(ac2).get_messages()
|
msgs = ac1.create_chat(ac2).get_messages()
|
||||||
assert len(msgs) == 1
|
assert len(msgs) == 1 + E2EE_INFO_MSGS
|
||||||
assert msgs[0].text == "Hi"
|
assert msgs[0 + E2EE_INFO_MSGS].text == "Hi"
|
||||||
|
|
||||||
lp.sec("Test that the connectivity changes to WORKING while new messages are fetched")
|
lp.sec("Test that the connectivity changes to WORKING while new messages are fetched")
|
||||||
|
|
||||||
@@ -1425,8 +1440,8 @@ def test_connectivity(acfactory, lp):
|
|||||||
ac1._evtracker.wait_for_connectivity_change(dc.const.DC_CONNECTIVITY_WORKING, dc.const.DC_CONNECTIVITY_CONNECTED)
|
ac1._evtracker.wait_for_connectivity_change(dc.const.DC_CONNECTIVITY_WORKING, dc.const.DC_CONNECTIVITY_CONNECTED)
|
||||||
|
|
||||||
msgs = ac1.create_chat(ac2).get_messages()
|
msgs = ac1.create_chat(ac2).get_messages()
|
||||||
assert len(msgs) == 2
|
assert len(msgs) == 2 + E2EE_INFO_MSGS
|
||||||
assert msgs[1].text == "Hi 2"
|
assert msgs[1 + E2EE_INFO_MSGS].text == "Hi 2"
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_deleted_msg(acfactory, lp):
|
def test_fetch_deleted_msg(acfactory, lp):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import pytest
|
|||||||
import deltachat as dc
|
import deltachat as dc
|
||||||
from deltachat.tracker import ImexFailed
|
from deltachat.tracker import ImexFailed
|
||||||
from deltachat import Account, Message
|
from deltachat import Account, Message
|
||||||
|
from deltachat.testplugin import E2EE_INFO_MSGS
|
||||||
|
|
||||||
|
|
||||||
class TestOfflineAccountBasic:
|
class TestOfflineAccountBasic:
|
||||||
@@ -461,9 +462,9 @@ class TestOfflineChat:
|
|||||||
assert contact2.addr == ac_contact.get_config("addr")
|
assert contact2.addr == ac_contact.get_config("addr")
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
def test_import_export_on_encrypted_acct(self, acfactory, tmp_path):
|
def test_import_export_on_encrypted_acct(self, acfactory, tmp_path):
|
||||||
passphrase1 = "passphrase1"
|
passphrase1 = "passphrase1"
|
||||||
@@ -500,9 +501,9 @@ class TestOfflineChat:
|
|||||||
contact2_addr = contact2.addr
|
contact2_addr = contact2.addr
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
ac2.shutdown()
|
ac2.shutdown()
|
||||||
|
|
||||||
@@ -517,9 +518,9 @@ class TestOfflineChat:
|
|||||||
assert contact2.addr == contact2_addr
|
assert contact2.addr == contact2_addr
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
def test_import_export_with_passphrase(self, acfactory, tmp_path):
|
def test_import_export_with_passphrase(self, acfactory, tmp_path):
|
||||||
passphrase = "test_passphrase"
|
passphrase = "test_passphrase"
|
||||||
@@ -557,9 +558,9 @@ class TestOfflineChat:
|
|||||||
assert contact2.addr == ac_contact.get_config("addr")
|
assert contact2.addr == ac_contact.get_config("addr")
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
def test_import_encrypted_bak_into_encrypted_acct(self, acfactory, tmp_path):
|
def test_import_encrypted_bak_into_encrypted_acct(self, acfactory, tmp_path):
|
||||||
"""
|
"""
|
||||||
@@ -603,9 +604,9 @@ class TestOfflineChat:
|
|||||||
assert contact2.addr == ac_contact.get_config("addr")
|
assert contact2.addr == ac_contact.get_config("addr")
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
ac2.shutdown()
|
ac2.shutdown()
|
||||||
|
|
||||||
@@ -620,9 +621,9 @@ class TestOfflineChat:
|
|||||||
assert contact2.addr == ac_contact.get_config("addr")
|
assert contact2.addr == ac_contact.get_config("addr")
|
||||||
chat2 = contact2.create_chat()
|
chat2 = contact2.create_chat()
|
||||||
messages = chat2.get_messages()
|
messages = chat2.get_messages()
|
||||||
assert len(messages) == 2
|
assert len(messages) == 2 + E2EE_INFO_MSGS
|
||||||
assert messages[0].text == "msg1"
|
assert messages[0 + E2EE_INFO_MSGS].text == "msg1"
|
||||||
assert os.path.exists(messages[1].filename)
|
assert os.path.exists(messages[1 + E2EE_INFO_MSGS].filename)
|
||||||
|
|
||||||
def test_set_get_draft(self, chat1):
|
def test_set_get_draft(self, chat1):
|
||||||
msg1 = Message.new_empty(chat1.account, "text")
|
msg1 = Message.new_empty(chat1.account, "text")
|
||||||
|
|||||||
42
src/chat.rs
42
src/chat.rs
@@ -349,6 +349,8 @@ impl ChatId {
|
|||||||
chat_id
|
chat_id
|
||||||
.add_protection_msg(context, ProtectionStatus::Protected, None, timestamp)
|
.add_protection_msg(context, ProtectionStatus::Protected, None, timestamp)
|
||||||
.await?;
|
.await?;
|
||||||
|
} else {
|
||||||
|
chat_id.maybe_add_encrypted_msg(context, timestamp).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@@ -604,6 +606,42 @@ impl ChatId {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds message "Messages are end-to-end encrypted" if appropriate.
|
||||||
|
///
|
||||||
|
/// This function is rather slow because it does a lot of database queries,
|
||||||
|
/// but this is fine because it is only called on chat creation.
|
||||||
|
async fn maybe_add_encrypted_msg(self, context: &Context, timestamp_sort: i64) -> Result<()> {
|
||||||
|
let chat = Chat::load_from_db(context, self).await?;
|
||||||
|
|
||||||
|
// as secure-join adds its own message on success (after some other messasges),
|
||||||
|
// we do not want to add "Messages are end-to-end encrypted" on chat creation.
|
||||||
|
// we detect secure join by `can_send` (for Bob, scanner side) and by `blocked` (for Alice, inviter side) below.
|
||||||
|
if !chat.is_encrypted(context).await?
|
||||||
|
|| self <= DC_CHAT_ID_LAST_SPECIAL
|
||||||
|
|| chat.is_device_talk()
|
||||||
|
|| chat.is_self_talk()
|
||||||
|
|| (!chat.can_send(context).await? && !chat.is_contact_request())
|
||||||
|
|| chat.blocked == Blocked::Yes
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = stock_str::messages_e2e_encrypted(context).await;
|
||||||
|
add_info_msg_with_cmd(
|
||||||
|
context,
|
||||||
|
self,
|
||||||
|
&text,
|
||||||
|
SystemMessage::ChatE2ee,
|
||||||
|
timestamp_sort,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets protection and adds a message.
|
/// Sets protection and adds a message.
|
||||||
///
|
///
|
||||||
/// `timestamp_sort` is used as the timestamp of the added message
|
/// `timestamp_sort` is used as the timestamp of the added message
|
||||||
@@ -2673,6 +2711,10 @@ impl ChatIdBlocked {
|
|||||||
smeared_time,
|
smeared_time,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
} else {
|
||||||
|
chat_id
|
||||||
|
.maybe_add_encrypted_msg(context, smeared_time)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::message::{MessengerMessage, delete_msgs};
|
|||||||
use crate::mimeparser::{self, MimeMessage};
|
use crate::mimeparser::{self, MimeMessage};
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::test_utils::{
|
use crate::test_utils::{
|
||||||
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, TestContext, TestContextManager,
|
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager,
|
||||||
TimeShiftFalsePositiveNote, sync,
|
TimeShiftFalsePositiveNote, sync,
|
||||||
};
|
};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@@ -2104,7 +2104,7 @@ async fn test_forward_basic() -> Result<()> {
|
|||||||
forward_msgs(&bob, &[msg.id], bob_chat.get_id()).await?;
|
forward_msgs(&bob, &[msg.id], bob_chat.get_id()).await?;
|
||||||
|
|
||||||
let forwarded_msg = bob.pop_sent_msg().await;
|
let forwarded_msg = bob.pop_sent_msg().await;
|
||||||
assert_eq!(bob_chat.id.get_msg_cnt(&bob).await?, 2);
|
assert_eq!(bob_chat.id.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 2);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
forwarded_msg.load_from_db().await.rfc724_mid,
|
forwarded_msg.load_from_db().await.rfc724_mid,
|
||||||
msg.rfc724_mid,
|
msg.rfc724_mid,
|
||||||
@@ -2132,7 +2132,7 @@ async fn test_forward_info_msg() -> Result<()> {
|
|||||||
assert!(msg1.get_text().contains("bob@example.net"));
|
assert!(msg1.get_text().contains("bob@example.net"));
|
||||||
|
|
||||||
let chat_id2 = ChatId::create_for_contact(alice, bob_id).await?;
|
let chat_id2 = ChatId::create_for_contact(alice, bob_id).await?;
|
||||||
assert_eq!(get_chat_msgs(alice, chat_id2).await?.len(), 0);
|
assert_eq!(get_chat_msgs(alice, chat_id2).await?.len(), E2EE_INFO_MSGS);
|
||||||
forward_msgs(alice, &[msg1.id], chat_id2).await?;
|
forward_msgs(alice, &[msg1.id], chat_id2).await?;
|
||||||
let msg2 = alice.get_last_msg_in(chat_id2).await;
|
let msg2 = alice.get_last_msg_in(chat_id2).await;
|
||||||
assert!(!msg2.is_info()); // forwarded info-messages lose their info-state
|
assert!(!msg2.is_info()); // forwarded info-messages lose their info-state
|
||||||
@@ -2518,22 +2518,34 @@ async fn test_resend_own_message() -> Result<()> {
|
|||||||
let sent1_ts_sent = msg.timestamp_sent;
|
let sent1_ts_sent = msg.timestamp_sent;
|
||||||
assert_eq!(msg.get_text(), "alice->bob");
|
assert_eq!(msg.get_text(), "alice->bob");
|
||||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 2);
|
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 2);
|
||||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 1);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
bob.recv_msg(&sent2).await;
|
bob.recv_msg(&sent2).await;
|
||||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
||||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 2);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
let received = bob.recv_msg_opt(&sent3).await;
|
let received = bob.recv_msg_opt(&sent3).await;
|
||||||
// No message should actually be added since we already know this message:
|
// No message should actually be added since we already know this message:
|
||||||
assert!(received.is_none());
|
assert!(received.is_none());
|
||||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
||||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 2);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
|
||||||
// Fiona does not receive the first message, however, due to resending, she has a similar view as Alice and Bob
|
// Fiona does not receive the first message, however, due to resending, she has a similar view as Alice and Bob
|
||||||
fiona.recv_msg(&sent2).await;
|
fiona.recv_msg(&sent2).await;
|
||||||
let msg = fiona.recv_msg(&sent3).await;
|
let msg = fiona.recv_msg(&sent3).await;
|
||||||
assert_eq!(msg.get_text(), "alice->bob");
|
assert_eq!(msg.get_text(), "alice->bob");
|
||||||
assert_eq!(get_chat_contacts(&fiona, msg.chat_id).await?.len(), 3);
|
assert_eq!(get_chat_contacts(&fiona, msg.chat_id).await?.len(), 3);
|
||||||
assert_eq!(get_chat_msgs(&fiona, msg.chat_id).await?.len(), 2);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&fiona, msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
let msg_from = Contact::get_by_id(&fiona, msg.get_from_id()).await?;
|
let msg_from = Contact::get_by_id(&fiona, msg.get_from_id()).await?;
|
||||||
assert_eq!(msg_from.get_addr(), "alice@example.org");
|
assert_eq!(msg_from.get_addr(), "alice@example.org");
|
||||||
assert!(sent1_ts_sent < msg.timestamp_sent);
|
assert!(sent1_ts_sent < msg.timestamp_sent);
|
||||||
@@ -4454,13 +4466,13 @@ async fn test_receive_edit_request_after_removal() -> Result<()> {
|
|||||||
let bob_msg = bob.recv_msg(&sent1).await;
|
let bob_msg = bob.recv_msg(&sent1).await;
|
||||||
let bob_chat_id = bob_msg.chat_id;
|
let bob_chat_id = bob_msg.chat_id;
|
||||||
assert_eq!(bob_msg.text, "zext me in delra.cat");
|
assert_eq!(bob_msg.text, "zext me in delra.cat");
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 1);
|
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
delete_msgs(bob, &[bob_msg.id]).await?;
|
delete_msgs(bob, &[bob_msg.id]).await?;
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 0);
|
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS);
|
||||||
|
|
||||||
bob.recv_msg_trash(&sent2).await;
|
bob.recv_msg_trash(&sent2).await;
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 0);
|
assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -4549,28 +4561,34 @@ async fn test_send_delete_request() -> Result<()> {
|
|||||||
// Alice sends a message, then sends a deletion request
|
// Alice sends a message, then sends a deletion request
|
||||||
let sent1 = alice.send_text(alice_chat.id, "wtf").await;
|
let sent1 = alice.send_text(alice_chat.id, "wtf").await;
|
||||||
let alice_msg = sent1.load_from_db().await;
|
let alice_msg = sent1.load_from_db().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 2);
|
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, E2EE_INFO_MSGS + 2);
|
||||||
|
|
||||||
message::delete_msgs_ex(alice, &[alice_msg.id], true).await?;
|
message::delete_msgs_ex(alice, &[alice_msg.id], true).await?;
|
||||||
let sent2 = alice.pop_sent_msg().await;
|
let sent2 = alice.pop_sent_msg().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 1);
|
assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
// Bob receives both messages and has nothing the end
|
// Bob receives both messages and has nothing the end
|
||||||
let bob_msg = bob.recv_msg(&sent1).await;
|
let bob_msg = bob.recv_msg(&sent1).await;
|
||||||
assert_eq!(bob_msg.text, "wtf");
|
assert_eq!(bob_msg.text, "wtf");
|
||||||
assert_eq!(bob_msg.chat_id.get_msg_cnt(bob).await?, 2);
|
assert_eq!(bob_msg.chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS + 2);
|
||||||
|
|
||||||
bob.recv_msg_opt(&sent2).await;
|
bob.recv_msg_opt(&sent2).await;
|
||||||
assert_eq!(bob_msg.chat_id.get_msg_cnt(bob).await?, 1);
|
assert_eq!(bob_msg.chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
// Alice has another device, and there is also nothing at the end
|
// Alice has another device, and there is also nothing at the end
|
||||||
let alice2 = &tcm.alice().await;
|
let alice2 = &tcm.alice().await;
|
||||||
alice2.recv_msg(&sent0).await;
|
alice2.recv_msg(&sent0).await;
|
||||||
let alice2_msg = alice2.recv_msg(&sent1).await;
|
let alice2_msg = alice2.recv_msg(&sent1).await;
|
||||||
assert_eq!(alice2_msg.chat_id.get_msg_cnt(alice2).await?, 2);
|
assert_eq!(
|
||||||
|
alice2_msg.chat_id.get_msg_cnt(alice2).await?,
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
|
||||||
alice2.recv_msg_opt(&sent2).await;
|
alice2.recv_msg_opt(&sent2).await;
|
||||||
assert_eq!(alice2_msg.chat_id.get_msg_cnt(alice2).await?, 1);
|
assert_eq!(
|
||||||
|
alice2_msg.chat_id.get_msg_cnt(alice2).await?,
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::chatlist::Chatlist;
|
|||||||
use crate::constants::Chattype;
|
use crate::constants::Chattype;
|
||||||
use crate::mimeparser::SystemMessage;
|
use crate::mimeparser::SystemMessage;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::test_utils::{TestContext, get_chat_msg};
|
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, get_chat_msg};
|
||||||
use crate::tools::{SystemTime, create_outgoing_rfc724_mid};
|
use crate::tools::{SystemTime, create_outgoing_rfc724_mid};
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
@@ -571,7 +571,7 @@ async fn test_get_next_msgs() -> Result<()> {
|
|||||||
|
|
||||||
let alice_chat = alice.create_chat(&bob).await;
|
let alice_chat = alice.create_chat(&bob).await;
|
||||||
|
|
||||||
assert!(alice.get_next_msgs().await?.is_empty());
|
assert_eq!(alice.get_next_msgs().await?.len(), E2EE_INFO_MSGS);
|
||||||
assert!(bob.get_next_msgs().await?.is_empty());
|
assert!(bob.get_next_msgs().await?.is_empty());
|
||||||
|
|
||||||
let sent_msg = alice.send_text(alice_chat.id, "Hi Bob").await;
|
let sent_msg = alice.send_text(alice_chat.id, "Hi Bob").await;
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ mod tests {
|
|||||||
use crate::chat::{get_chat_msgs, send_msg};
|
use crate::chat::{get_chat_msgs, send_msg};
|
||||||
use crate::ephemeral::Timer;
|
use crate::ephemeral::Timer;
|
||||||
use crate::receive_imf::receive_imf_from_inbox;
|
use crate::receive_imf::receive_imf_from_inbox;
|
||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::{E2EE_INFO_MSGS, TestContext};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_downloadstate_values() {
|
fn test_downloadstate_values() {
|
||||||
@@ -459,7 +459,10 @@ mod tests {
|
|||||||
.await?;
|
.await?;
|
||||||
let msg = bob.get_last_msg().await;
|
let msg = bob.get_last_msg().await;
|
||||||
let chat_id = msg.chat_id;
|
let chat_id = msg.chat_id;
|
||||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 1);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
assert_eq!(msg.download_state(), DownloadState::Available);
|
assert_eq!(msg.download_state(), DownloadState::Available);
|
||||||
|
|
||||||
// downloading the status update afterwards expands to nothing and moves the placeholder to trash-chat
|
// downloading the status update afterwards expands to nothing and moves the placeholder to trash-chat
|
||||||
@@ -472,7 +475,7 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0);
|
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), E2EE_INFO_MSGS);
|
||||||
assert!(
|
assert!(
|
||||||
Message::load_from_db_optional(&bob, msg.id)
|
Message::load_from_db_optional(&bob, msg.id)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@@ -963,6 +963,7 @@ impl Message {
|
|||||||
| SystemMessage::SecurejoinMessage
|
| SystemMessage::SecurejoinMessage
|
||||||
| SystemMessage::LocationStreamingEnabled
|
| SystemMessage::LocationStreamingEnabled
|
||||||
| SystemMessage::LocationOnly
|
| SystemMessage::LocationOnly
|
||||||
|
| SystemMessage::ChatE2ee
|
||||||
| SystemMessage::ChatProtectionEnabled
|
| SystemMessage::ChatProtectionEnabled
|
||||||
| SystemMessage::ChatProtectionDisabled
|
| SystemMessage::ChatProtectionDisabled
|
||||||
| SystemMessage::InvalidUnencryptedMail
|
| SystemMessage::InvalidUnencryptedMail
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::config::Config;
|
|||||||
use crate::reaction::send_reaction;
|
use crate::reaction::send_reaction;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::test_utils;
|
use crate::test_utils;
|
||||||
use crate::test_utils::{TestContext, TestContextManager};
|
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_guess_msgtype_from_suffix() {
|
fn test_guess_msgtype_from_suffix() {
|
||||||
@@ -347,7 +347,7 @@ async fn test_markseen_msgs() -> Result<()> {
|
|||||||
let chats = Chatlist::try_load(&bob, 0, None, None).await?;
|
let chats = Chatlist::try_load(&bob, 0, None, None).await?;
|
||||||
assert_eq!(chats.len(), 1);
|
assert_eq!(chats.len(), 1);
|
||||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
||||||
assert_eq!(msgs.len(), 2);
|
assert_eq!(msgs.len(), E2EE_INFO_MSGS + 2);
|
||||||
assert_eq!(bob.get_fresh_msgs().await?.len(), 0);
|
assert_eq!(bob.get_fresh_msgs().await?.len(), 0);
|
||||||
|
|
||||||
// that has no effect in contact request
|
// that has no effect in contact request
|
||||||
@@ -358,7 +358,7 @@ async fn test_markseen_msgs() -> Result<()> {
|
|||||||
assert_eq!(bob_chat.blocked, Blocked::Request);
|
assert_eq!(bob_chat.blocked, Blocked::Request);
|
||||||
|
|
||||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
||||||
assert_eq!(msgs.len(), 2);
|
assert_eq!(msgs.len(), E2EE_INFO_MSGS + 2);
|
||||||
bob_chat_id.accept(&bob).await.unwrap();
|
bob_chat_id.accept(&bob).await.unwrap();
|
||||||
|
|
||||||
// bob sends to alice,
|
// bob sends to alice,
|
||||||
@@ -761,19 +761,22 @@ async fn test_delete_msgs_sync() -> Result<()> {
|
|||||||
|
|
||||||
// Alice sends a messsage and receives it on the other device
|
// Alice sends a messsage and receives it on the other device
|
||||||
let sent1 = alice.send_text(alice_chat_id, "foo").await;
|
let sent1 = alice.send_text(alice_chat_id, "foo").await;
|
||||||
assert_eq!(alice_chat_id.get_msg_cnt(alice).await?, 1);
|
assert_eq!(alice_chat_id.get_msg_cnt(alice).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
let msg = alice2.recv_msg(&sent1).await;
|
let msg = alice2.recv_msg(&sent1).await;
|
||||||
let alice2_chat_id = msg.chat_id;
|
let alice2_chat_id = msg.chat_id;
|
||||||
assert_eq!(alice2.get_last_msg_in(alice2_chat_id).await.id, msg.id);
|
assert_eq!(alice2.get_last_msg_in(alice2_chat_id).await.id, msg.id);
|
||||||
assert_eq!(alice2_chat_id.get_msg_cnt(alice2).await?, 1);
|
assert_eq!(
|
||||||
|
alice2_chat_id.get_msg_cnt(alice2).await?,
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
|
|
||||||
// Alice deletes the message; this should happen on both devices as well
|
// Alice deletes the message; this should happen on both devices as well
|
||||||
delete_msgs(alice, &[sent1.sender_msg_id]).await?;
|
delete_msgs(alice, &[sent1.sender_msg_id]).await?;
|
||||||
assert_eq!(alice_chat_id.get_msg_cnt(alice).await?, 0);
|
assert_eq!(alice_chat_id.get_msg_cnt(alice).await?, E2EE_INFO_MSGS);
|
||||||
|
|
||||||
test_utils::sync(alice, alice2).await;
|
test_utils::sync(alice, alice2).await;
|
||||||
assert_eq!(alice2_chat_id.get_msg_cnt(alice2).await?, 0);
|
assert_eq!(alice2_chat_id.get_msg_cnt(alice2).await?, E2EE_INFO_MSGS);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,10 +181,10 @@ pub enum SystemMessage {
|
|||||||
/// Chat ephemeral message timer is changed.
|
/// Chat ephemeral message timer is changed.
|
||||||
EphemeralTimerChanged = 10,
|
EphemeralTimerChanged = 10,
|
||||||
|
|
||||||
/// "Messages are guaranteed to be end-to-end encrypted from now on."
|
/// "Messages are end-to-end encrypted."
|
||||||
ChatProtectionEnabled = 11,
|
ChatProtectionEnabled = 11,
|
||||||
|
|
||||||
/// "%1$s sent a message from another device."
|
/// "%1$s sent a message from another device.", deprecated 2025-07
|
||||||
ChatProtectionDisabled = 12,
|
ChatProtectionDisabled = 12,
|
||||||
|
|
||||||
/// Message can't be sent because of `Invalid unencrypted mail to <>`
|
/// Message can't be sent because of `Invalid unencrypted mail to <>`
|
||||||
@@ -213,6 +213,9 @@ pub enum SystemMessage {
|
|||||||
|
|
||||||
/// This message contains a users iroh node address.
|
/// This message contains a users iroh node address.
|
||||||
IrohNodeAddr = 40,
|
IrohNodeAddr = 40,
|
||||||
|
|
||||||
|
/// "Messages are end-to-end encrypted."
|
||||||
|
ChatE2ee = 50,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup";
|
const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup";
|
||||||
|
|||||||
@@ -407,6 +407,7 @@ mod tests {
|
|||||||
use crate::message::{MessageState, delete_msgs};
|
use crate::message::{MessageState, delete_msgs};
|
||||||
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
||||||
use crate::sql::housekeeping;
|
use crate::sql::housekeeping;
|
||||||
|
use crate::test_utils::E2EE_INFO_MSGS;
|
||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::TestContext;
|
||||||
use crate::test_utils::TestContextManager;
|
use crate::test_utils::TestContextManager;
|
||||||
use crate::tools::SystemTime;
|
use crate::tools::SystemTime;
|
||||||
@@ -653,13 +654,25 @@ Here's my footer -- bob@example.net"
|
|||||||
let chat_alice = alice.create_chat(&bob).await;
|
let chat_alice = alice.create_chat(&bob).await;
|
||||||
let alice_msg = alice.send_text(chat_alice.id, "Hi!").await;
|
let alice_msg = alice.send_text(chat_alice.id, "Hi!").await;
|
||||||
let bob_msg = bob.recv_msg(&alice_msg).await;
|
let bob_msg = bob.recv_msg(&alice_msg).await;
|
||||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 1);
|
assert_eq!(
|
||||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 1);
|
get_chat_msgs(&alice, chat_alice.id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, bob_msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 1
|
||||||
|
);
|
||||||
|
|
||||||
let alice_msg2 = alice.send_text(chat_alice.id, "Hi again!").await;
|
let alice_msg2 = alice.send_text(chat_alice.id, "Hi again!").await;
|
||||||
bob.recv_msg(&alice_msg2).await;
|
bob.recv_msg(&alice_msg2).await;
|
||||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
|
assert_eq!(
|
||||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
|
get_chat_msgs(&alice, chat_alice.id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, bob_msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
|
||||||
bob_msg.chat_id.accept(&bob).await?;
|
bob_msg.chat_id.accept(&bob).await?;
|
||||||
|
|
||||||
@@ -667,12 +680,18 @@ Here's my footer -- bob@example.net"
|
|||||||
send_reaction(&bob, bob_msg.id, "👍").await.unwrap();
|
send_reaction(&bob, bob_msg.id, "👍").await.unwrap();
|
||||||
expect_reactions_changed_event(&bob, bob_msg.chat_id, bob_msg.id, ContactId::SELF).await?;
|
expect_reactions_changed_event(&bob, bob_msg.chat_id, bob_msg.id, ContactId::SELF).await?;
|
||||||
expect_no_unwanted_events(&bob).await;
|
expect_no_unwanted_events(&bob).await;
|
||||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&bob, bob_msg.chat_id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
|
||||||
let bob_reaction_msg = bob.pop_sent_msg().await;
|
let bob_reaction_msg = bob.pop_sent_msg().await;
|
||||||
let alice_reaction_msg = alice.recv_msg_hidden(&bob_reaction_msg).await;
|
let alice_reaction_msg = alice.recv_msg_hidden(&bob_reaction_msg).await;
|
||||||
assert_eq!(alice_reaction_msg.state, MessageState::InFresh);
|
assert_eq!(alice_reaction_msg.state, MessageState::InFresh);
|
||||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
|
assert_eq!(
|
||||||
|
get_chat_msgs(&alice, chat_alice.id).await?.len(),
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
|
|
||||||
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
|
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
|
||||||
assert_eq!(reactions.to_string(), "👍1");
|
assert_eq!(reactions.to_string(), "👍1");
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ use crate::download::MIN_DOWNLOAD_LIMIT;
|
|||||||
use crate::imap::prefetch_should_download;
|
use crate::imap::prefetch_should_download;
|
||||||
use crate::imex::{ImexMode, imex};
|
use crate::imex::{ImexMode, imex};
|
||||||
use crate::securejoin::get_securejoin_qr;
|
use crate::securejoin::get_securejoin_qr;
|
||||||
use crate::test_utils::mark_as_verified;
|
use crate::test_utils::{
|
||||||
use crate::test_utils::{TestContext, TestContextManager, get_chat_msg};
|
E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified,
|
||||||
|
};
|
||||||
use crate::tools::{SystemTime, time};
|
use crate::tools::{SystemTime, time};
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
@@ -133,7 +134,7 @@ async fn test_adhoc_group_outgoing_show_accepted_contact_unaccepted() -> Result<
|
|||||||
let chats = Chatlist::try_load(bob, 0, None, None).await?;
|
let chats = Chatlist::try_load(bob, 0, None, None).await?;
|
||||||
assert_eq!(chats.len(), 1);
|
assert_eq!(chats.len(), 1);
|
||||||
let chat_id = chats.get_chat_id(0)?;
|
let chat_id = chats.get_chat_id(0)?;
|
||||||
assert_eq!(chat_id.get_msg_cnt(bob).await?, 1);
|
assert_eq!(chat_id.get_msg_cnt(bob).await?, E2EE_INFO_MSGS + 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4410,7 +4411,7 @@ async fn test_create_group_with_big_msg() -> Result<()> {
|
|||||||
|
|
||||||
// The big message must go away from the 1:1 chat.
|
// The big message must go away from the 1:1 chat.
|
||||||
let msgs = chat::get_chat_msgs(&alice, ab_chat_id).await?;
|
let msgs = chat::get_chat_msgs(&alice, ab_chat_id).await?;
|
||||||
assert!(msgs.is_empty());
|
assert_eq!(msgs.len(), E2EE_INFO_MSGS);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::chatlist::Chatlist;
|
|||||||
use crate::constants::Chattype;
|
use crate::constants::Chattype;
|
||||||
use crate::key::self_fingerprint;
|
use crate::key::self_fingerprint;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::stock_str::{self, chat_protection_enabled};
|
use crate::stock_str::{self, messages_e2e_encrypted};
|
||||||
use crate::test_utils::{
|
use crate::test_utils::{
|
||||||
TestContext, TestContextManager, TimeShiftFalsePositiveNote, get_chat_msg,
|
TestContext, TestContextManager, TimeShiftFalsePositiveNote, get_chat_msg,
|
||||||
};
|
};
|
||||||
@@ -246,7 +246,7 @@ async fn test_setup_contact_ex(case: SetupContactCase) {
|
|||||||
let chat = alice.get_chat(&bob).await;
|
let chat = alice.get_chat(&bob).await;
|
||||||
let msg = get_chat_msg(&alice, chat.get_id(), 0, 1).await;
|
let msg = get_chat_msg(&alice, chat.get_id(), 0, 1).await;
|
||||||
assert!(msg.is_info());
|
assert!(msg.is_info());
|
||||||
let expected_text = chat_protection_enabled(&alice).await;
|
let expected_text = messages_e2e_encrypted(&alice).await;
|
||||||
assert_eq!(msg.get_text(), expected_text);
|
assert_eq!(msg.get_text(), expected_text);
|
||||||
if case == SetupContactCase::CheckProtectionTimestamp {
|
if case == SetupContactCase::CheckProtectionTimestamp {
|
||||||
assert_eq!(msg.timestamp_sort, vc_request_with_auth_ts_sent + 1);
|
assert_eq!(msg.timestamp_sort, vc_request_with_auth_ts_sent + 1);
|
||||||
@@ -296,7 +296,7 @@ async fn test_setup_contact_ex(case: SetupContactCase) {
|
|||||||
assert_eq!(msg.get_text(), stock_str::securejoin_wait(&bob).await);
|
assert_eq!(msg.get_text(), stock_str::securejoin_wait(&bob).await);
|
||||||
let msg = get_chat_msg(&bob, bob_chat.get_id(), i.next().unwrap(), msg_cnt).await;
|
let msg = get_chat_msg(&bob, bob_chat.get_id(), i.next().unwrap(), msg_cnt).await;
|
||||||
assert!(msg.is_info());
|
assert!(msg.is_info());
|
||||||
assert_eq!(msg.get_text(), chat_protection_enabled(&bob).await);
|
assert_eq!(msg.get_text(), messages_e2e_encrypted(&bob).await);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
@@ -540,7 +540,7 @@ async fn test_secure_join() -> Result<()> {
|
|||||||
// - You added member bob@example.net
|
// - You added member bob@example.net
|
||||||
let msg = get_chat_msg(&alice, alice_chatid, 0, 2).await;
|
let msg = get_chat_msg(&alice, alice_chatid, 0, 2).await;
|
||||||
assert!(msg.is_info());
|
assert!(msg.is_info());
|
||||||
let expected_text = chat_protection_enabled(&alice).await;
|
let expected_text = messages_e2e_encrypted(&alice).await;
|
||||||
assert_eq!(msg.get_text(), expected_text);
|
assert_eq!(msg.get_text(), expected_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,9 +65,6 @@ pub enum StockMessage {
|
|||||||
#[strum(props(fallback = "GIF"))]
|
#[strum(props(fallback = "GIF"))]
|
||||||
Gif = 23,
|
Gif = 23,
|
||||||
|
|
||||||
#[strum(props(fallback = "Encrypted message"))]
|
|
||||||
EncryptedMsg = 24,
|
|
||||||
|
|
||||||
#[strum(props(fallback = "End-to-end encryption available"))]
|
#[strum(props(fallback = "End-to-end encryption available"))]
|
||||||
E2eAvailable = 25,
|
E2eAvailable = 25,
|
||||||
|
|
||||||
@@ -380,9 +377,10 @@ pub enum StockMessage {
|
|||||||
#[strum(props(fallback = "I left the group."))]
|
#[strum(props(fallback = "I left the group."))]
|
||||||
MsgILeftGroup = 166,
|
MsgILeftGroup = 166,
|
||||||
|
|
||||||
#[strum(props(fallback = "Messages are guaranteed to be end-to-end encrypted from now on."))]
|
#[strum(props(fallback = "Messages are end-to-end encrypted."))]
|
||||||
ChatProtectionEnabled = 170,
|
ChatProtectionEnabled = 170,
|
||||||
|
|
||||||
|
// deprecated 2025-07
|
||||||
#[strum(props(fallback = "%1$s sent a message from another device."))]
|
#[strum(props(fallback = "%1$s sent a message from another device."))]
|
||||||
ChatProtectionDisabled = 171,
|
ChatProtectionDisabled = 171,
|
||||||
|
|
||||||
@@ -1031,8 +1029,8 @@ pub(crate) async fn error_no_network(context: &Context) -> String {
|
|||||||
translated(context, StockMessage::ErrorNoNetwork).await
|
translated(context, StockMessage::ErrorNoNetwork).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stock string: `Messages are guaranteed to be end-to-end encrypted from now on.`
|
/// Stock string: `Messages are end-to-end encrypted.`
|
||||||
pub(crate) async fn chat_protection_enabled(context: &Context) -> String {
|
pub(crate) async fn messages_e2e_encrypted(context: &Context) -> String {
|
||||||
translated(context, StockMessage::ChatProtectionEnabled).await
|
translated(context, StockMessage::ChatProtectionEnabled).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1303,7 +1301,7 @@ impl Context {
|
|||||||
"[Error] No contact_id given".to_string()
|
"[Error] No contact_id given".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProtectionStatus::Protected => chat_protection_enabled(self).await,
|
ProtectionStatus::Protected => messages_e2e_encrypted(self).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
|||||||
use crate::stock_str::StockStrings;
|
use crate::stock_str::StockStrings;
|
||||||
use crate::tools::time;
|
use crate::tools::time;
|
||||||
|
|
||||||
|
/// The number of info messages added to new e2ee chats.
|
||||||
|
/// Currently this is "End-to-end encryption available", string `E2eAvailable`.
|
||||||
|
pub const E2EE_INFO_MSGS: usize = 1;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const AVATAR_900x900_BYTES: &[u8] = include_bytes!("../test-data/image/avatar900x900.png");
|
pub const AVATAR_900x900_BYTES: &[u8] = include_bytes!("../test-data/image/avatar900x900.png");
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ use crate::mimeparser::SystemMessage;
|
|||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
use crate::securejoin::{get_securejoin_qr, join_securejoin};
|
||||||
use crate::stock_str;
|
use crate::stock_str;
|
||||||
use crate::test_utils::{TestContext, TestContextManager, get_chat_msg, mark_as_verified};
|
use crate::test_utils::{
|
||||||
|
E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified,
|
||||||
|
};
|
||||||
use crate::tools::SystemTime;
|
use crate::tools::SystemTime;
|
||||||
use crate::{e2ee, message};
|
use crate::{e2ee, message};
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ async fn test_create_verified_oneonone_chat() -> Result<()> {
|
|||||||
assert!(chat.is_protected());
|
assert!(chat.is_protected());
|
||||||
|
|
||||||
let msg = get_chat_msg(&alice, chat.id, 0, 1).await;
|
let msg = get_chat_msg(&alice, chat.id, 0, 1).await;
|
||||||
let expected_text = stock_str::chat_protection_enabled(&alice).await;
|
let expected_text = stock_str::messages_e2e_encrypted(&alice).await;
|
||||||
assert_eq!(msg.text, expected_text);
|
assert_eq!(msg.text, expected_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +144,7 @@ async fn test_create_verified_oneonone_chat() -> Result<()> {
|
|||||||
assert!(chat.is_protected());
|
assert!(chat.is_protected());
|
||||||
|
|
||||||
let msg0 = get_chat_msg(&fiona, chat.id, 0, 1).await;
|
let msg0 = get_chat_msg(&fiona, chat.id, 0, 1).await;
|
||||||
let expected_text = stock_str::chat_protection_enabled(&fiona).await;
|
let expected_text = stock_str::messages_e2e_encrypted(&fiona).await;
|
||||||
assert_eq!(msg0.text, expected_text);
|
assert_eq!(msg0.text, expected_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +164,7 @@ async fn test_create_verified_oneonone_chat() -> Result<()> {
|
|||||||
let chat = alice.get_chat(&fiona_new).await;
|
let chat = alice.get_chat(&fiona_new).await;
|
||||||
assert!(!chat.is_protected());
|
assert!(!chat.is_protected());
|
||||||
|
|
||||||
let msg = get_chat_msg(&alice, chat.id, 0, 1).await;
|
let msg = get_chat_msg(&alice, chat.id, 1, E2EE_INFO_MSGS + 1).await;
|
||||||
assert_eq!(msg.text, "I have a new device");
|
assert_eq!(msg.text, "I have a new device");
|
||||||
|
|
||||||
// After recreating the chat, it should still be unprotected
|
// After recreating the chat, it should still be unprotected
|
||||||
@@ -268,7 +270,7 @@ async fn test_degrade_verified_oneonone_chat() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let msg0 = get_chat_msg(&alice, alice_chat.id, 0, 1).await;
|
let msg0 = get_chat_msg(&alice, alice_chat.id, 0, 1).await;
|
||||||
let enabled = stock_str::chat_protection_enabled(&alice).await;
|
let enabled = stock_str::messages_e2e_encrypted(&alice).await;
|
||||||
assert_eq!(msg0.text, enabled);
|
assert_eq!(msg0.text, enabled);
|
||||||
assert_eq!(msg0.param.get_cmd(), SystemMessage::ChatProtectionEnabled);
|
assert_eq!(msg0.param.get_cmd(), SystemMessage::ChatProtectionEnabled);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::config::Config;
|
|||||||
use crate::download::DownloadState;
|
use crate::download::DownloadState;
|
||||||
use crate::ephemeral;
|
use crate::ephemeral;
|
||||||
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
||||||
use crate::test_utils::{TestContext, TestContextManager};
|
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager};
|
||||||
use crate::tools::{self, SystemTime};
|
use crate::tools::{self, SystemTime};
|
||||||
use crate::{message, sql};
|
use crate::{message, sql};
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ async fn test_resend_webxdc_instance_and_info() -> Result<()> {
|
|||||||
);
|
);
|
||||||
let bob_grp = bob_instance.chat_id;
|
let bob_grp = bob_instance.chat_id;
|
||||||
assert_eq!(bob.get_last_msg_in(bob_grp).await.id, bob_instance.id);
|
assert_eq!(bob.get_last_msg_in(bob_grp).await.id, bob_instance.id);
|
||||||
assert_eq!(bob_grp.get_msg_cnt(&bob).await?, 1);
|
assert_eq!(bob_grp.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -869,14 +869,14 @@ async fn test_send_big_webxdc_status_update() -> Result<()> {
|
|||||||
let sent2 = &alice.pop_sent_msg().await;
|
let sent2 = &alice.pop_sent_msg().await;
|
||||||
let alice_update = sent2.load_from_db().await;
|
let alice_update = sent2.load_from_db().await;
|
||||||
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
// Bob receives the instance.
|
// Bob receives the instance.
|
||||||
let bob_instance = bob.recv_msg(sent1).await;
|
let bob_instance = bob.recv_msg(sent1).await;
|
||||||
let bob_chat_id = bob_instance.chat_id;
|
let bob_chat_id = bob_instance.chat_id;
|
||||||
assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid);
|
assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid);
|
||||||
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
|
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?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
// Bob receives the status updates.
|
// Bob receives the status updates.
|
||||||
bob.recv_msg_trash(sent2).await;
|
bob.recv_msg_trash(sent2).await;
|
||||||
@@ -896,7 +896,7 @@ async fn test_send_big_webxdc_status_update() -> Result<()> {
|
|||||||
r#"[{"payload":{"foo":"bar2"},"serial":2,"max_serial":3},
|
r#"[{"payload":{"foo":"bar2"},"serial":2,"max_serial":3},
|
||||||
{"payload":{"foo":"bar3"},"serial":3,"max_serial":3}]"#
|
{"payload":{"foo":"bar3"},"serial":3,"max_serial":3}]"#
|
||||||
);
|
);
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
|
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1485,7 +1485,7 @@ async fn test_webxdc_info_msg() -> Result<()> {
|
|||||||
let alice_chat = alice.create_chat(&bob).await;
|
let alice_chat = alice.create_chat(&bob).await;
|
||||||
let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?;
|
let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?;
|
||||||
let sent1 = &alice.pop_sent_msg().await;
|
let sent1 = &alice.pop_sent_msg().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, E2EE_INFO_MSGS + 1);
|
||||||
|
|
||||||
alice
|
alice
|
||||||
.send_webxdc_status_update(
|
.send_webxdc_status_update(
|
||||||
@@ -1495,7 +1495,7 @@ async fn test_webxdc_info_msg() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
alice.flush_status_updates().await?;
|
alice.flush_status_updates().await?;
|
||||||
let sent2 = &alice.pop_sent_msg().await;
|
let sent2 = &alice.pop_sent_msg().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 2);
|
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, E2EE_INFO_MSGS + 2);
|
||||||
let info_msg = alice.get_last_msg().await;
|
let info_msg = alice.get_last_msg().await;
|
||||||
assert!(info_msg.is_info());
|
assert!(info_msg.is_info());
|
||||||
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
||||||
@@ -1517,7 +1517,7 @@ async fn test_webxdc_info_msg() -> Result<()> {
|
|||||||
let bob_instance = bob.recv_msg(sent1).await;
|
let bob_instance = bob.recv_msg(sent1).await;
|
||||||
let bob_chat_id = bob_instance.chat_id;
|
let bob_chat_id = bob_instance.chat_id;
|
||||||
bob.recv_msg_trash(sent2).await;
|
bob.recv_msg_trash(sent2).await;
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
|
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 2);
|
||||||
let info_msg = bob.get_last_msg().await;
|
let info_msg = bob.get_last_msg().await;
|
||||||
assert!(info_msg.is_info());
|
assert!(info_msg.is_info());
|
||||||
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
||||||
@@ -1536,7 +1536,10 @@ async fn test_webxdc_info_msg() -> Result<()> {
|
|||||||
let alice2_instance = alice2.recv_msg(sent1).await;
|
let alice2_instance = alice2.recv_msg(sent1).await;
|
||||||
let alice2_chat_id = alice2_instance.chat_id;
|
let alice2_chat_id = alice2_instance.chat_id;
|
||||||
alice2.recv_msg_trash(sent2).await;
|
alice2.recv_msg_trash(sent2).await;
|
||||||
assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 2);
|
assert_eq!(
|
||||||
|
alice2_chat_id.get_msg_cnt(&alice2).await?,
|
||||||
|
E2EE_INFO_MSGS + 2
|
||||||
|
);
|
||||||
let info_msg = alice2.get_last_msg().await;
|
let info_msg = alice2.get_last_msg().await;
|
||||||
assert!(info_msg.is_info());
|
assert!(info_msg.is_info());
|
||||||
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
assert_eq!(info_msg.get_info_type(), SystemMessage::WebxdcInfoMessage);
|
||||||
@@ -1572,13 +1575,13 @@ async fn test_webxdc_info_msg_cleanup_series() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
alice.flush_status_updates().await?;
|
alice.flush_status_updates().await?;
|
||||||
let sent2 = &alice.pop_sent_msg().await;
|
let sent2 = &alice.pop_sent_msg().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 2);
|
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, E2EE_INFO_MSGS + 2);
|
||||||
alice
|
alice
|
||||||
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i2", "payload":2}"#)
|
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i2", "payload":2}"#)
|
||||||
.await?;
|
.await?;
|
||||||
alice.flush_status_updates().await?;
|
alice.flush_status_updates().await?;
|
||||||
let sent3 = &alice.pop_sent_msg().await;
|
let sent3 = &alice.pop_sent_msg().await;
|
||||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 2);
|
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, E2EE_INFO_MSGS + 2);
|
||||||
let info_msg = alice.get_last_msg().await;
|
let info_msg = alice.get_last_msg().await;
|
||||||
assert_eq!(info_msg.get_text(), "i2");
|
assert_eq!(info_msg.get_text(), "i2");
|
||||||
|
|
||||||
@@ -1586,9 +1589,9 @@ async fn test_webxdc_info_msg_cleanup_series() -> Result<()> {
|
|||||||
let bob_instance = bob.recv_msg(sent1).await;
|
let bob_instance = bob.recv_msg(sent1).await;
|
||||||
let bob_chat_id = bob_instance.chat_id;
|
let bob_chat_id = bob_instance.chat_id;
|
||||||
bob.recv_msg_trash(sent2).await;
|
bob.recv_msg_trash(sent2).await;
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
|
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 2);
|
||||||
bob.recv_msg_trash(sent3).await;
|
bob.recv_msg_trash(sent3).await;
|
||||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
|
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, E2EE_INFO_MSGS + 2);
|
||||||
let info_msg = bob.get_last_msg().await;
|
let info_msg = bob.get_last_msg().await;
|
||||||
assert_eq!(info_msg.get_text(), "i2");
|
assert_eq!(info_msg.get_text(), "i2");
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
Group#Chat#10: Group chat [3 member(s)]
|
Group#Chat#10: Group chat [3 member(s)]
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
Msg#10🔒: (Contact#Contact#10): Hi! I created a group. [FRESH]
|
Msg#10: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO]
|
||||||
Msg#11🔒: Me (Contact#Contact#Self): You left. [INFO] √
|
Msg#11🔒: (Contact#Contact#10): Hi! I created a group. [FRESH]
|
||||||
Msg#12🔒: (Contact#Contact#10): Member charlie@example.net added by alice@example.org. [FRESH][INFO]
|
Msg#12🔒: Me (Contact#Contact#Self): You left. [INFO] √
|
||||||
Msg#13🔒: (Contact#Contact#10): What a silence! [FRESH]
|
Msg#13🔒: (Contact#Contact#10): Member charlie@example.net added by alice@example.org. [FRESH][INFO]
|
||||||
|
Msg#14🔒: (Contact#Contact#10): What a silence! [FRESH]
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Single#Chat#10: bob@example.net [KEY bob@example.net] 🛡️
|
Single#Chat#10: bob@example.net [KEY bob@example.net] 🛡️
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
|
Msg#10: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO 🛡️]
|
||||||
Msg#11🔒: Me (Contact#Contact#Self): Test – This is encrypted, signed, and has an Autocrypt Header without prefer-encrypt=mutual. √
|
Msg#11🔒: Me (Contact#Contact#Self): Test – This is encrypted, signed, and has an Autocrypt Header without prefer-encrypt=mutual. √
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Single#Chat#10: bob@example.net [KEY bob@example.net] 🛡️
|
Single#Chat#10: bob@example.net [KEY bob@example.net] 🛡️
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
|
Msg#10: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO 🛡️]
|
||||||
Msg#11🔒: (Contact#Contact#10): Heyho from DC [FRESH]
|
Msg#11🔒: (Contact#Contact#10): Heyho from DC [FRESH]
|
||||||
Msg#13🔒: Me (Contact#Contact#Self): Sending with DC again √
|
Msg#13🔒: Me (Contact#Contact#Self): Sending with DC again √
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Msg#11: info (Contact#Contact#Info): alice@example.org invited you to join this
|
|||||||
|
|
||||||
Waiting for the device of alice@example.org to reply… [NOTICED][INFO]
|
Waiting for the device of alice@example.org to reply… [NOTICED][INFO]
|
||||||
Msg#13: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO]
|
Msg#13: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO]
|
||||||
Msg#17: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
|
Msg#17: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO 🛡️]
|
||||||
Msg#18🔒: (Contact#Contact#10): Member Me added by alice@example.org. [FRESH][INFO]
|
Msg#18🔒: (Contact#Contact#10): Member Me added by alice@example.org. [FRESH][INFO]
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user