Add dc_get_chat_encrinfo()

This commit is contained in:
link2xt
2021-02-06 08:43:43 +03:00
committed by link2xt
parent fbec12393d
commit 0cd8710289
5 changed files with 92 additions and 0 deletions

View File

@@ -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()

View File

@@ -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,

View File

@@ -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.

View File

@@ -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()

View File

@@ -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