Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -3956,28 +3956,6 @@ int dc_chat_is_protected (const dc_chat_t* chat);
int dc_chat_is_encrypted (const dc_chat_t *chat);


/**
* Checks if the chat was protected, and then an incoming message broke this protection.
*
* This function is only useful if the UI enabled the `verified_one_on_one_chats` feature flag,
* otherwise it will return false for all chats.
*
* 1:1 chats are automatically set as protected when a contact is verified.
* When a message comes in that is not encrypted / signed correctly,
* the chat is automatically set as unprotected again.
* dc_chat_is_protection_broken() will return true until dc_accept_chat() is called.
*
* The UI should let the user confirm that this is OK with a message like
* `Bob sent a message from another device. Tap to learn more` and then call dc_accept_chat().
*
* @deprecated 2025-07 chats protection cannot break any longer
* @memberof dc_chat_t
* @param chat The chat object.
* @return 1=chat protection broken, 0=otherwise.
*/
int dc_chat_is_protection_broken (const dc_chat_t* chat);


/**
* Check if locations are sent to the chat
* at the time the object was created using dc_get_chat().
Expand Down Expand Up @@ -7214,6 +7192,8 @@ void dc_event_unref(dc_event_t* event);
/// "Unknown sender for this chat. See 'info' for more details."
///
/// Use as message text if assigning the message to a chat is not totally correct.
///
/// @deprecated 2025-08-18
#define DC_STR_UNKNOWN_SENDER_FOR_CHAT 72

/// "Message from %1$s"
Expand Down
10 changes: 0 additions & 10 deletions deltachat-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3247,16 +3247,6 @@ pub unsafe extern "C" fn dc_chat_is_encrypted(chat: *mut dc_chat_t) -> libc::c_i
.unwrap_or_log_default(&ffi_chat.context, "Failed dc_chat_is_encrypted") as libc::c_int
}

#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_protection_broken(chat: *mut dc_chat_t) -> libc::c_int {
if chat.is_null() {
eprintln!("ignoring careless call to dc_chat_is_protection_broken()");
return 0;
}
let ffi_chat = &*chat;
ffi_chat.chat.is_protection_broken() as libc::c_int
}

#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
if chat.is_null() {
Expand Down
6 changes: 0 additions & 6 deletions deltachat-jsonrpc/src/api/types/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ pub struct FullChat {
fresh_message_counter: usize,
// is_group - please check over chat.type in frontend instead
is_contact_request: bool,
/// Deprecated 2025-07. Chats protection cannot break any longer.
is_protection_broken: bool,

is_device_chat: bool,
self_in_group: bool,
Expand Down Expand Up @@ -147,7 +145,6 @@ impl FullChat {
color,
fresh_message_counter,
is_contact_request: chat.is_contact_request(),
is_protection_broken: chat.is_protection_broken(),
is_device_chat: chat.is_device_talk(),
self_in_group: contact_ids.contains(&ContactId::SELF),
is_muted: chat.is_muted(),
Expand Down Expand Up @@ -218,8 +215,6 @@ pub struct BasicChat {
is_self_talk: bool,
color: String,
is_contact_request: bool,
/// Deprecated 2025-07. Chats protection cannot break any longer.
is_protection_broken: bool,

is_device_chat: bool,
is_muted: bool,
Expand Down Expand Up @@ -249,7 +244,6 @@ impl BasicChat {
is_self_talk: chat.is_self_talk(),
color,
is_contact_request: chat.is_contact_request(),
is_protection_broken: chat.is_protection_broken(),
is_device_chat: chat.is_device_talk(),
is_muted: chat.is_muted(),
})
Expand Down
8 changes: 0 additions & 8 deletions deltachat-jsonrpc/src/api/types/contact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ pub struct ContactObject {
/// See [`Self::verifier_id`]/`Contact.verifierId` for a guidance how to display these information.
is_verified: bool,

/// True if the contact profile title should have a green checkmark.
///
/// This indicates whether 1:1 chat has a green checkmark
/// or will have a green checkmark if created.
is_profile_verified: bool,

/// The contact ID that verified a contact.
///
/// As verifier may be unknown,
Expand Down Expand Up @@ -87,7 +81,6 @@ impl ContactObject {
None => None,
};
let is_verified = contact.is_verified(context).await?;
let is_profile_verified = contact.is_profile_verified(context).await?;

let verifier_id = contact
.get_verifier_id(context)
Expand All @@ -109,7 +102,6 @@ impl ContactObject {
is_key_contact: contact.is_key_contact(),
e2ee_avail: contact.e2ee_avail(context).await?,
is_verified,
is_profile_verified,
verifier_id,
last_seen: contact.last_seen(),
was_seen_recently: contact.was_seen_recently(),
Expand Down
2 changes: 1 addition & 1 deletion python/tests/test_1_online.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ def test_forward_messages(acfactory, lp):
lp.sec("ac2: check new chat has a forwarded message")
assert chat3.is_promoted()
messages = chat3.get_messages()
assert len(messages) == 2
assert len(messages) == 3
msg = messages[-1]
assert msg.is_forwarded()
ac2.delete_messages(messages)
Expand Down
2 changes: 1 addition & 1 deletion python/tests/test_3_offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,4 +663,4 @@ def test_audit_log_view_without_daymarker(self, acfactory, lp):

lp.sec("check message count of only system messages (without daymarkers)")
sysmessages = [x for x in chat.get_messages() if x.is_system_message()]
assert len(sysmessages) == 3
assert len(sysmessages) == 4
23 changes: 13 additions & 10 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1920,11 +1920,6 @@ impl Chat {
Ok(is_encrypted)
}

/// Deprecated 2025-07. Returns false.
pub fn is_protection_broken(&self) -> bool {
false
}

/// Returns true if location streaming is enabled in the chat.
pub fn is_sending_locations(&self) -> bool {
self.is_sending_locations
Expand Down Expand Up @@ -3731,11 +3726,19 @@ pub async fn create_group_ex(
chatlist_events::emit_chatlist_changed(context);
chatlist_events::emit_chatlist_item_changed(context, chat_id);

if encryption == Some(ProtectionStatus::Protected) {
let protect = ProtectionStatus::Protected;
chat_id
.set_protection_for_timestamp_sort(context, protect, timestamp, None)
.await?;
match encryption {
Some(ProtectionStatus::Protected) => {
let protect = ProtectionStatus::Protected;
chat_id
.set_protection_for_timestamp_sort(context, protect, timestamp, None)
.await?;
}
Some(ProtectionStatus::Unprotected) => {
// Add "Messages are end-to-end encrypted." message
// even to unprotected chats.
chat_id.maybe_add_encrypted_msg(context, timestamp).await?;
}
None => {}
}

if !context.get_config_bool(Config::Bot).await?
Expand Down
6 changes: 4 additions & 2 deletions src/chat/chat_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use crate::test_utils::{
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager,
TimeShiftFalsePositiveNote, sync,
};
use crate::tools::SystemTime;
use pretty_assertions::assert_eq;
use std::time::Duration;
use strum::IntoEnumIterator;
use tokio::fs;

Expand Down Expand Up @@ -1644,7 +1646,7 @@ async fn test_set_mute_duration() {
async fn test_add_info_msg() -> Result<()> {
let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
add_info_msg(&t, chat_id, "foo info", 200000).await?;
add_info_msg(&t, chat_id, "foo info", time()).await?;

let msg = t.get_last_msg_in(chat_id).await;
assert_eq!(msg.get_chat_id(), chat_id);
Expand All @@ -1666,7 +1668,7 @@ async fn test_add_info_msg_with_cmd() -> Result<()> {
chat_id,
"foo bar info",
SystemMessage::EphemeralTimerChanged,
10000,
time(),
None,
None,
None,
Expand Down
4 changes: 4 additions & 0 deletions src/chatlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ mod tests {
use crate::stock_str::StockMessage;
use crate::test_utils::TestContext;
use crate::test_utils::TestContextManager;
use crate::tools::SystemTime;
use std::time::Duration;

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_try_load() {
Expand All @@ -510,6 +512,8 @@ mod tests {
assert_eq!(chats.get_chat_id(1).unwrap(), chat_id2);
assert_eq!(chats.get_chat_id(2).unwrap(), chat_id1);

SystemTime::shift(Duration::from_secs(5));

// New drafts are sorted to the top
// We have to set a draft on the other two messages, too, as
// chat timestamps are only exact to the second and sorting by timestamp
Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ pub enum Config {
/// Regardless of this setting, `chat.is_protected()` returns true while the key is verified,
/// and when the key changes, an info message is posted into the chat.
/// 0=Nothing else happens when the key changes.
/// 1=After the key changed, `can_send()` returns false and `is_protection_broken()` returns true
/// 1=After the key changed, `can_send()` returns false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this happen now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually cannot happen to key-contacts. We should remove this setting, it is only used in get_info() for logging its value.

/// until `chat_id.accept()` is called.
#[strum(props(default = "0"))]
VerifiedOneOnOneChats,
Expand Down
25 changes: 1 addition & 24 deletions src/contact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use tokio::task;
use tokio::time::{Duration, timeout};

use crate::blob::BlobObject;
use crate::chat::{ChatId, ChatIdBlocked, ProtectionStatus};
use crate::chat::ChatId;
use crate::color::str_to_color;
use crate::config::Config;
use crate::constants::{self, Blocked, Chattype};
Expand Down Expand Up @@ -1650,29 +1650,6 @@ impl Contact {
}
}

/// Returns if the contact profile title should display a green checkmark.
///
/// This generally should be consistent with the 1:1 chat with the contact
/// so 1:1 chat with the contact and the contact profile
/// either both display the green checkmark or both don't display a green checkmark.
///
/// UI often knows beforehand if a chat exists and can also call
/// `chat.is_protected()` (if there is a chat)
/// or `contact.is_verified()` (if there is no chat) directly.
/// This is often easier and also skips some database calls.
pub async fn is_profile_verified(&self, context: &Context) -> Result<bool> {
let contact_id = self.id;

if let Some(ChatIdBlocked { id: chat_id, .. }) =
ChatIdBlocked::lookup_by_contact(context, contact_id).await?
{
Ok(chat_id.is_protected(context).await? == ProtectionStatus::Protected)
} else {
// 1:1 chat does not exist.
Ok(self.is_verified(context).await?)
}
}

/// Returns the number of real (i.e. non-special) contacts in the database.
pub async fn get_real_cnt(context: &Context) -> Result<usize> {
if !context.sql.is_open().await {
Expand Down
3 changes: 1 addition & 2 deletions src/contact/contact_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use deltachat_contact_tools::{addr_cmp, may_be_valid_addr};

use super::*;
use crate::chat::{Chat, get_chat_contacts, send_text_msg};
use crate::chat::{Chat, ProtectionStatus, get_chat_contacts, send_text_msg};
use crate::chatlist::Chatlist;
use crate::receive_imf::receive_imf;
use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote};
Expand Down Expand Up @@ -1302,7 +1302,6 @@ async fn test_self_is_verified() -> Result<()> {

let contact = Contact::get_by_id(&alice, ContactId::SELF).await?;
assert_eq!(contact.is_verified(&alice).await?, true);
assert!(contact.is_profile_verified(&alice).await?);
assert!(contact.get_verifier_id(&alice).await?.is_none());
assert!(contact.is_key_contact());

Expand Down
9 changes: 0 additions & 9 deletions src/mimeparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,15 +1582,6 @@ impl MimeMessage {
}
}

pub fn replace_msg_by_error(&mut self, error_msg: &str) {
self.is_system_message = SystemMessage::Unknown;
if let Some(part) = self.parts.first_mut() {
part.typ = Viewtype::Text;
part.msg = format!("[{error_msg}]");
self.parts.truncate(1);
}
}

pub(crate) fn get_rfc724_mid(&self) -> Option<String> {
self.get_header(HeaderDef::MessageId)
.and_then(|msgid| parse_message_id(msgid).ok())
Expand Down
29 changes: 0 additions & 29 deletions src/receive_imf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1693,12 +1693,6 @@ async fn add_parts(
let name: &str = from.display_name.as_ref().unwrap_or(&from.addr);
for part in &mut mime_parser.parts {
part.param.set(Param::OverrideSenderDisplayname, name);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This usage of OverrideSenderDisplayname looks unnecessarily overloaded, i'd prefer we use smth else for this case. But maybe some translated string like "~non-member: NAME" would be good, at least this "~" won't cause confusion


if chat.is_protected() {
// In protected chat, also mark the message with an error.
let s = stock_str::unknown_sender_for_chat(context).await;
part.error = Some(s);
}
}
}
}
Expand Down Expand Up @@ -1870,25 +1864,6 @@ async fn add_parts(
None
};

let mut verification_failed = false;
if !chat_id.is_special() && is_partial_download.is_none() {
// For outgoing emails in the 1:1 chat we have an exception that
// they are allowed to be unencrypted:
// 1. They can't be an attack (they are outgoing, not incoming)
// 2. Probably the unencryptedness is just a temporary state, after all
// the user obviously still uses DC
// -> Showing info messages every time would be a lot of noise
// 3. The info messages that are shown to the user ("Your chat partner
// likely reinstalled DC" or similar) would be wrong.
if chat.is_protected() && (mime_parser.incoming || chat.typ != Chattype::Single) {
if let VerifiedEncryption::NotVerified(err) = verified_encryption {
verification_failed = true;
warn!(context, "Verification problem: {err:#}.");
let s = format!("{err}. Re-download the message or see 'Info' for more details");
mime_parser.replace_msg_by_error(&s);
}
}
}
drop(chat); // Avoid using stale `chat` object.

let sort_timestamp = tweak_sort_timestamp(
Expand Down Expand Up @@ -2162,10 +2137,6 @@ RETURNING id
DownloadState::Available
} else if mime_parser.decrypting_failed {
DownloadState::Undecipherable
} else if verification_failed {
// Verification can fail because of message reordering. Re-downloading the
// message should help if so.
DownloadState::Available
} else {
DownloadState::Done
},
Expand Down
18 changes: 3 additions & 15 deletions src/receive_imf/receive_imf_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5133,21 +5133,9 @@ async fn test_unverified_member_msg() -> Result<()> {
let fiona_chat_id = fiona.get_last_msg().await.chat_id;
let fiona_sent_msg = fiona.send_text(fiona_chat_id, "Hi").await;

// The message can't be verified, but the user can re-download it.
let bob_msg = bob.recv_msg(&fiona_sent_msg).await;
assert_eq!(bob_msg.download_state, DownloadState::Available);
assert!(
bob_msg
.text
.contains("Re-download the message or see 'Info' for more details")
);

let alice_sent_msg = alice
.send_text(alice_chat_id, "Hi all, it's Alice introducing Fiona")
.await;
bob.recv_msg(&alice_sent_msg).await;

// Now Bob has Fiona's key and can verify the message.
// The message is by non-verified member,
// but the checks have been removed
// and the message should be downloaded as usual.
let bob_msg = bob.recv_msg(&fiona_sent_msg).await;
assert_eq!(bob_msg.download_state, DownloadState::Done);
assert_eq!(bob_msg.text, "Hi");
Expand Down
2 changes: 1 addition & 1 deletion src/securejoin/securejoin_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ async fn test_unknown_sender() -> Result<()> {
// The message from Bob is delivered late, Bob is already removed.
let msg = alice.recv_msg(&sent).await;
assert_eq!(msg.text, "Hi hi!");
assert_eq!(msg.error.unwrap(), "Unknown sender for this chat.");
assert_eq!(msg.get_override_sender_name().unwrap(), "[email protected]");

Ok(())
}
Expand Down
Loading
Loading