add set_chat_protection() api

This commit is contained in:
B. Petersen
2020-10-03 20:01:47 +02:00
parent a7998c190c
commit 49b5962568
4 changed files with 143 additions and 2 deletions

View File

@@ -1170,6 +1170,24 @@ dc_array_t* dc_get_chat_media (dc_context_t* context, uint32_t ch
uint32_t dc_get_next_media (dc_context_t* context, uint32_t msg_id, int dir, int msg_type, int msg_type2, int msg_type3);
/**
* Enable or disable protection against active attacks.
* To enable protection, it is needed that all members are verified;
* if this condition is met, end-to-end-encryption is always enabled
* and only the verified keys are used.
*
* Sends out #DC_EVENT_CHAT_MODIFIED on changes
* and #DC_EVENT_MSGS_CHANGED if a status message was sent.
*
* @memberof dc_context_t
* @param context The context object as returned from dc_context_new().
* @param chat_id The ID of the chat to change the protection for.
* @param protect 1=protect chat, 0=unprotect chat
* @return 1=success, 0=error, eg. some members may be unverified
*/
int dc_set_chat_protection (dc_context_t* context, uint32_t chat_id, int protect);
/**
* Set chat visibility to pinned, archived or normal.
*

View File

@@ -1057,6 +1057,35 @@ pub unsafe extern "C" fn dc_get_next_media(
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_set_chat_protection(
context: *mut dc_context_t,
chat_id: u32,
protect: libc::c_int,
) -> libc::c_int {
if context.is_null() {
eprintln!("ignoring careless call to dc_set_chat_protection()");
return 0;
}
let ctx = &*context;
let protect = if let Some(s) = contact::ProtectionStatus::from_i32(protect) {
s
} else {
eprintln!("bad protect-value for dc_set_chat_protection()");
return 0;
};
block_on(async move {
match ChatId::new(chat_id).set_protection(&ctx, protect).await {
Ok(()) => 1,
Err(err) => {
error!(ctx, "Cannot protect chat. Are all members verified?");
0
}
}
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_set_chat_visibility(
context: *mut dc_context_t,

View File

@@ -379,6 +379,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
unarchive <chat-id>\n\
pin <chat-id>\n\
unpin <chat-id>\n\
protect <chat-id>\n\
unprotect <chat-id>\n\
delchat <chat-id>\n\
===========================Message commands==\n\
listmsgs <query>\n\
@@ -918,6 +920,20 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
)
.await?;
}
"protect" | "unprotect" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = ChatId::new(arg1.parse()?);
chat_id
.set_protection(
&context,
match arg0 {
"protect" => ProtectionStatus::Protected,
"unprotect" => ProtectionStatus::Unprotected,
_ => panic!("Unexpected command (This should never happen)"),
},
)
.await?;
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = ChatId::new(arg1.parse()?);

View File

@@ -174,6 +174,79 @@ impl ChatId {
self.set_blocked(context, Blocked::Not).await;
}
/// Set protection without sending a message.
/// Used when a message arrives indicating that someone else has
/// changed the protection value for a chat.
pub(crate) async fn inner_set_protection(
self,
context: &Context,
protect: ProtectionStatus,
send_to_others: bool,
) -> Result<(), Error> {
ensure!(!self.is_special(), "set protection: invalid chat-id.");
let chat = Chat::load_from_db(context, self).await?;
if protect == chat.protected {
info!(context, "Protection status unchanged for {}.", self);
return Ok(());
}
match protect {
ProtectionStatus::Protected => match chat.typ {
Chattype::Single | Chattype::Group => {
let contact_ids = get_chat_contacts(context, self).await;
for contact_id in contact_ids.into_iter() {
let contact = Contact::get_by_id(context, contact_id).await?;
if contact.is_verified(context).await != VerifiedStatus::BidirectVerified {
bail!(
"{} is not verified; cannot enable protection.",
contact.get_display_name()
);
}
}
}
Chattype::Undefined => bail!("set protection: undefined group type"),
},
ProtectionStatus::Unprotected => {}
};
context
.sql
.execute(
"UPDATE chats SET protected=? WHERE id=?;",
paramsv![protect, self],
)
.await?;
context.emit_event(EventType::ChatModified(self));
if send_to_others {}
Ok(())
}
pub async fn set_protection(
self,
context: &Context,
protect: ProtectionStatus,
) -> Result<(), Error> {
ensure!(!self.is_special(), "set protection: invalid chat-id.");
let chat = Chat::load_from_db(context, self).await?;
match self
.inner_set_protection(context, protect, chat.is_promoted())
.await
{
Ok(()) => Ok(()),
Err(err) => {
error!(context, "{}", err); // make error user-visible
Err(err)
}
}
}
/// Archives or unarchives a chat.
pub async fn set_visibility(
self,
@@ -1904,13 +1977,12 @@ pub async fn create_group_chat(
let grpid = dc_create_id();
context.sql.execute(
"INSERT INTO chats (type, name, grpid, param, created_timestamp, protected) VALUES(?, ?, ?, \'U=1\', ?, ?);",
"INSERT INTO chats (type, name, grpid, param, created_timestamp) VALUES(?, ?, ?, \'U=1\', ?);",
paramsv![
Chattype::Group,
chat_name,
grpid,
time(),
protect,
],
).await?;
@@ -1931,6 +2003,12 @@ pub async fn create_group_chat(
chat_id: ChatId::new(0),
});
if protect == ProtectionStatus::Protected {
chat_id
.inner_set_protection(context, protect, false)
.await?;
}
Ok(chat_id)
}