mirror of
https://github.com/chatmail/core.git
synced 2026-04-18 22:16:30 +03:00
Add dc_get_chat_encrinfo()
This commit is contained in:
@@ -1220,6 +1220,18 @@ void dc_delete_chat (dc_context_t* context, uint32_t ch
|
||||
*/
|
||||
dc_array_t* dc_get_chat_contacts (dc_context_t* context, uint32_t chat_id);
|
||||
|
||||
/**
|
||||
* Get encryption info for a chat.
|
||||
* Get a multi-line encryption info, containing encryption preferences of all members.
|
||||
* Can be used to find out why messages sent to group are not encrypted.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param chat_id ID of the chat to get the encryption info for.
|
||||
* @return Multi-line text, must be released using dc_str_unref() after usage.
|
||||
*/
|
||||
char* dc_get_chat_encrinfo (dc_context_t* context, uint32_t chat_id);
|
||||
|
||||
/**
|
||||
* Get the chat's ephemeral message timer.
|
||||
* The ephemeral message timer is set by dc_set_chat_ephemeral_timer()
|
||||
|
||||
@@ -1322,6 +1322,29 @@ pub unsafe extern "C" fn dc_set_chat_mute_duration(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_get_chat_encrinfo(
|
||||
context: *mut dc_context_t,
|
||||
chat_id: u32,
|
||||
) -> *mut libc::c_char {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_get_chat_encrinfo()");
|
||||
return "".strdup();
|
||||
}
|
||||
let ctx = &*context;
|
||||
|
||||
block_on(async move {
|
||||
ChatId::new(chat_id)
|
||||
.get_encryption_info(&ctx)
|
||||
.await
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|e| {
|
||||
error!(&ctx, "{}", e);
|
||||
ptr::null_mut()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_get_chat_ephemeral_timer(
|
||||
context: *mut dc_context_t,
|
||||
|
||||
@@ -167,6 +167,13 @@ class Chat(object):
|
||||
"""
|
||||
return lib.dc_chat_get_type(self._dc_chat)
|
||||
|
||||
def get_encryption_info(self):
|
||||
"""Return encryption info for this chat.
|
||||
|
||||
:returns: a string with encryption preferences of all chat members"""
|
||||
res = lib.dc_get_chat_encrinfo(self.account._dc_context, self.id)
|
||||
return from_dc_charpointer(res)
|
||||
|
||||
def get_join_qr(self):
|
||||
""" get/create Join-Group QR Code as ascii-string.
|
||||
|
||||
|
||||
@@ -1130,6 +1130,7 @@ class TestOnlineAccount:
|
||||
msg = ac1._evtracker.wait_next_incoming_message()
|
||||
assert msg.text == "first message"
|
||||
assert not msg.is_encrypted()
|
||||
assert msg.chat.get_encryption_info() == f"{ac2.get_config('addr')} End-to-end encryption preferred."
|
||||
|
||||
lp.sec("ac2 learns that ac3 prefers encryption")
|
||||
ac2.create_chat(ac3)
|
||||
@@ -1141,6 +1142,7 @@ class TestOnlineAccount:
|
||||
lp.sec("ac3 does not know that ac1 prefers encryption")
|
||||
ac1.create_chat(ac3)
|
||||
chat = ac3.create_chat(ac1)
|
||||
assert chat.get_encryption_info() == f"{ac1.get_config('addr')} No encryption."
|
||||
msg = chat.send_text("not encrypted")
|
||||
msg = ac1._evtracker.wait_next_incoming_message()
|
||||
assert msg.text == "not encrypted"
|
||||
@@ -1149,6 +1151,8 @@ class TestOnlineAccount:
|
||||
lp.sec("ac1 creates a group chat with ac2")
|
||||
group_chat = ac1.create_group_chat("hello")
|
||||
group_chat.add_contact(ac2)
|
||||
encryption_info = group_chat.get_encryption_info()
|
||||
assert encryption_info == f"{ac2.get_config('addr')} End-to-end encryption preferred."
|
||||
msg = group_chat.send_text("hi")
|
||||
|
||||
msg = ac2._evtracker.wait_next_incoming_message()
|
||||
@@ -1161,6 +1165,9 @@ class TestOnlineAccount:
|
||||
|
||||
lp.sec("ac3 learns that ac1 prefers encryption")
|
||||
msg = ac3._evtracker.wait_next_incoming_message()
|
||||
encryption_info = msg.chat.get_encryption_info().splitlines()
|
||||
assert f"{ac1.get_config('addr')} End-to-end encryption preferred." in encryption_info
|
||||
assert f"{ac2.get_config('addr')} End-to-end encryption preferred." in encryption_info
|
||||
msg = chat.send_text("encrypted")
|
||||
assert msg.is_encrypted()
|
||||
|
||||
|
||||
43
src/chat.rs
43
src/chat.rs
@@ -12,6 +12,7 @@ use itertools::Itertools;
|
||||
use num_traits::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::blob::{BlobError, BlobObject};
|
||||
use crate::chatlist::dc_get_archived_cnt;
|
||||
use crate::config::Config;
|
||||
@@ -35,6 +36,7 @@ use crate::job::{self, Action};
|
||||
use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::{Param, Params};
|
||||
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
@@ -637,6 +639,47 @@ impl ChatId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns multi-line text summary of encryption preferences of all chat contacts.
|
||||
///
|
||||
/// This can be used to find out if encryption is not available because
|
||||
/// keys for some users are missing or simply because the majority of the users in a group
|
||||
/// prefer plaintext emails.
|
||||
///
|
||||
/// To get more verbose summary for a contact, including its key fingerprint, use [`Contact::get_encrinfo`].
|
||||
pub async fn get_encryption_info(self, context: &Context) -> Result<String, Error> {
|
||||
let mut ret = String::new();
|
||||
|
||||
for contact_id in get_chat_contacts(context, self)
|
||||
.await
|
||||
.iter()
|
||||
.filter(|&contact_id| *contact_id > DC_CONTACT_ID_LAST_SPECIAL)
|
||||
{
|
||||
let contact = Contact::load_from_db(context, *contact_id).await?;
|
||||
let addr = contact.get_addr();
|
||||
let peerstate = Peerstate::from_addr(context, addr).await?;
|
||||
|
||||
let stock_message = peerstate
|
||||
.filter(|peerstate| {
|
||||
peerstate
|
||||
.peek_key(PeerstateVerifiedStatus::Unverified)
|
||||
.is_some()
|
||||
})
|
||||
.map(|peerstate| match peerstate.prefer_encrypt {
|
||||
EncryptPreference::Mutual => StockMessage::E2ePreferred,
|
||||
EncryptPreference::NoPreference => StockMessage::E2eAvailable,
|
||||
EncryptPreference::Reset => StockMessage::EncrNone,
|
||||
})
|
||||
.unwrap_or(StockMessage::EncrNone);
|
||||
|
||||
if !ret.is_empty() {
|
||||
ret.push('\n')
|
||||
}
|
||||
ret += &format!("{} {}", addr, context.stock_str(stock_message).await);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Bad evil escape hatch.
|
||||
///
|
||||
/// Avoid using this, eventually types should be cleaned up enough
|
||||
|
||||
Reference in New Issue
Block a user