Files
chatmail-core/src/dc_securejoin.rs
2019-09-03 19:05:21 +02:00

1011 lines
40 KiB
Rust

use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use std::ptr;
use crate::aheader::EncryptPreference;
use crate::chat::{self, Chat};
use crate::configure::*;
use crate::constants::*;
use crate::contact::*;
use crate::context::Context;
use crate::dc_mimeparser::*;
use crate::dc_token::*;
use crate::dc_tools::*;
use crate::e2ee::*;
use crate::key::*;
use crate::lot::LotState;
use crate::message::*;
use crate::param::*;
use crate::peerstate::*;
use crate::qr::check_qr;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
pub unsafe fn dc_get_securejoin_qr(
context: &Context,
group_chat_id: uint32_t,
) -> *mut libc::c_char {
/* =========================================================
==== Alice - the inviter side ====
==== Step 1 in "Setup verified contact" protocol ====
========================================================= */
let mut fingerprint = ptr::null_mut();
let mut qr: Option<String> = None;
ensure_secret_key_exists(context).ok();
let invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id)
.unwrap_or_else(|| {
let invitenumber_s = dc_create_id();
dc_token_save(
context,
DC_TOKEN_INVITENUMBER,
group_chat_id,
&invitenumber_s,
);
invitenumber_s
});
let auth = dc_token_lookup(context, DC_TOKEN_AUTH, group_chat_id).unwrap_or_else(|| {
let auth_s = dc_create_id();
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, &auth_s);
auth_s
});
let self_addr = context.sql.get_config(context, "configured_addr");
let cleanup = |fingerprint| {
free(fingerprint as *mut libc::c_void);
if let Some(qr) = qr {
qr.strdup()
} else {
std::ptr::null_mut()
}
};
if self_addr.is_none() {
error!(context, 0, "Not configured, cannot generate QR code.",);
return cleanup(fingerprint);
}
let self_addr = self_addr.unwrap();
let self_name = context
.sql
.get_config(context, "displayname")
.unwrap_or_default();
fingerprint = get_self_fingerprint(context);
if fingerprint.is_null() {
return cleanup(fingerprint);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
qr = if 0 != group_chat_id {
if let Ok(chat) = Chat::load_from_db(context, group_chat_id) {
let group_name = chat.get_name();
let group_name_urlencoded =
utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string();
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
&group_name_urlencoded,
&chat.grpid,
&invitenumber,
&auth,
))
} else {
error!(
context,
0, "Cannot get QR-code for chat-id {}", group_chat_id,
);
return cleanup(fingerprint);
}
} else {
Some(format!(
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
self_name_urlencoded,
&invitenumber,
&auth,
))
};
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
cleanup(fingerprint)
}
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
if let Some(self_addr) = context.sql.get_config(context, "configured_addr") {
if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) {
return key.fingerprint_c();
}
}
std::ptr::null_mut()
}
pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> uint32_t {
/* ==========================================================
==== Bob - the joiner's side =====
==== Step 2 in "Setup verified contact" protocol =====
========================================================== */
let mut ret_chat_id: libc::c_int = 0i32;
let ongoing_allocated: libc::c_int;
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut join_vg: libc::c_int = 0i32;
info!(context, 0, "Requesting secure-join ...",);
ensure_secret_key_exists(context).ok();
ongoing_allocated = dc_alloc_ongoing(context);
if !(ongoing_allocated == 0i32) {
let qr_scan = check_qr(context, as_str(qr));
if qr_scan.state != LotState::QrAskVerifyContact
&& qr_scan.state != LotState::QrAskVerifyGroup
{
error!(context, 0, "Unknown QR code.",);
} else {
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
if contact_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Unknown contact.",);
} else if !(context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing)
{
join_vg = (qr_scan.get_state() == LotState::QrAskVerifyGroup) as libc::c_int;
{
let mut bob = context.bob.write().unwrap();
bob.status = 0;
bob.qr_scan = Some(qr_scan);
}
if 0 != fingerprint_equals_sender(
context,
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap(),
contact_chat_id,
) {
info!(context, 0, "Taking protocol shortcut.");
context.bob.write().unwrap().expects = 6;
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
chat_id_2_contact_id(context, contact_chat_id) as uintptr_t,
400i32 as uintptr_t,
);
let own_fingerprint: *mut libc::c_char = get_self_fingerprint(context);
send_handshake_msg(
context,
contact_chat_id,
if 0 != join_vg {
b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char
} else {
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char
},
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string(),
own_fingerprint,
if 0 != join_vg {
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string()
} else {
"".to_string()
},
);
free(own_fingerprint as *mut libc::c_void);
} else {
context.bob.write().unwrap().expects = 2;
send_handshake_msg(
context,
contact_chat_id,
if 0 != join_vg {
b"vg-request\x00" as *const u8 as *const libc::c_char
} else {
b"vc-request\x00" as *const u8 as *const libc::c_char
},
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.invitenumber
.as_ref()
.unwrap(),
ptr::null(),
"",
);
}
// Bob -> Alice
while !(context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing)
{
std::thread::sleep(std::time::Duration::new(0, 3_000_000));
}
}
}
}
let mut bob = context.bob.write().unwrap();
bob.expects = 0;
if bob.status == 1 {
if 0 != join_vg {
ret_chat_id = chat::get_chat_id_by_grpid(
context,
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
None,
ptr::null_mut(),
) as libc::c_int
} else {
ret_chat_id = contact_chat_id as libc::c_int
}
}
bob.qr_scan = None;
if 0 != ongoing_allocated {
dc_free_ongoing(context);
}
ret_chat_id as uint32_t
}
unsafe fn send_handshake_msg(
context: &Context,
contact_chat_id: uint32_t,
step: *const libc::c_char,
param2: impl AsRef<str>,
fingerprint: *const libc::c_char,
grpid: impl AsRef<str>,
) {
let mut msg = dc_msg_new_untyped(context);
msg.type_0 = Viewtype::Text;
msg.text = Some(format!("Secure-Join: {}", to_string(step)));
msg.hidden = true;
msg.param.set_int(Param::Cmd, 7);
if step.is_null() {
msg.param.remove(Param::Arg);
} else {
msg.param.set(Param::Arg, as_str(step));
}
if !param2.as_ref().is_empty() {
msg.param.set(Param::Arg2, param2);
}
if !fingerprint.is_null() {
msg.param.set(Param::Arg3, as_str(fingerprint));
}
if !grpid.as_ref().is_empty() {
msg.param.set(Param::Arg4, grpid.as_ref());
}
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
{
msg.param.set_int(
Param::ForcePlaintext,
ForcePlaintext::AddAutocryptHeader as i32,
);
} else {
msg.param.set_int(Param::GuranteeE2ee, 1);
}
// TODO. handle cleanup on error
chat::send_msg(context, contact_chat_id, &mut msg).unwrap();
}
unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t {
let contacts = chat::get_chat_contacts(context, contact_chat_id);
if contacts.len() == 1 {
contacts[0]
} else {
0
}
}
unsafe fn fingerprint_equals_sender(
context: &Context,
fingerprint: impl AsRef<str>,
contact_chat_id: u32,
) -> libc::c_int {
let mut fingerprint_equal = 0;
let contacts = chat::get_chat_contacts(context, contact_chat_id);
if contacts.len() == 1 {
if let Ok(contact) = Contact::load_from_db(context, contacts[0]) {
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
{
let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref());
if peerstate.public_key_fingerprint.is_some()
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
{
fingerprint_equal = 1;
}
}
} else {
return 0;
}
}
fingerprint_equal
}
/* library private: secure-join */
pub unsafe fn dc_handle_securejoin_handshake(
context: &Context,
mimeparser: &dc_mimeparser_t,
contact_id: uint32_t,
) -> libc::c_int {
let mut ok_to_continue: bool;
let step: *const libc::c_char;
let join_vg: libc::c_int;
let mut own_fingerprint: *mut libc::c_char = ptr::null_mut();
let contact_chat_id: u32;
let contact_chat_id_blocked: Blocked;
let mut grpid = "".to_string();
let mut ret: libc::c_int = 0i32;
if !(contact_id <= 9i32 as libc::c_uint) {
step = lookup_field(mimeparser, "Secure-Join");
if !step.is_null() {
info!(
context,
0,
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received",
as_str(step),
);
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
as libc::c_int;
let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not)
.unwrap_or_default();
contact_chat_id = id;
contact_chat_id_blocked = bl;
if Blocked::Not != contact_chat_id_blocked {
chat::unblock(context, contact_chat_id);
}
ret = 0x2i32;
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
{
/* =========================================================
==== Alice - the inviter side ====
==== Step 3 in "Setup verified contact" protocol ====
========================================================= */
// this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet)
// it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
let invitenumber: *const libc::c_char;
invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber");
if invitenumber.is_null() {
warn!(context, 0, "Secure-join denied (invitenumber missing).",);
ok_to_continue = false;
} else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, as_str(invitenumber)) {
warn!(context, 0, "Secure-join denied (bad invitenumber).",);
ok_to_continue = false;
} else {
info!(context, 0, "Secure-join requested.",);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
300i32 as uintptr_t,
);
send_handshake_msg(
context,
contact_chat_id,
if 0 != join_vg {
b"vg-auth-required\x00" as *const u8 as *const libc::c_char
} else {
b"vc-auth-required\x00" as *const u8 as *const libc::c_char
},
"",
ptr::null(),
"",
);
ok_to_continue = true;
}
} else if strcmp(
step,
b"vg-auth-required\x00" as *const u8 as *const libc::c_char,
) == 0i32
|| strcmp(
step,
b"vc-auth-required\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
let cond = {
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| bob.expects != 2
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
};
if cond {
warn!(context, 0, "auth-required message out of sync.",);
// no error, just aborted somehow or a mail from another handshake
ok_to_continue = false;
} else {
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
let auth = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
}
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
if mimeparser.e2ee_helper.encrypted {
b"No valid signature.\x00" as *const u8 as *const libc::c_char
} else {
b"Not encrypted.\x00" as *const u8 as *const libc::c_char
},
);
end_bobs_joining(context, 0i32);
ok_to_continue = false;
} else if 0
== fingerprint_equals_sender(
context,
&scanned_fingerprint_of_alice,
contact_chat_id,
)
{
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Fingerprint mismatch on joiner-side.\x00" as *const u8
as *const libc::c_char,
);
end_bobs_joining(context, 0i32);
ok_to_continue = false;
} else {
info!(context, 0, "Fingerprint verified.",);
own_fingerprint = get_self_fingerprint(context);
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
contact_id as uintptr_t,
400i32 as uintptr_t,
);
context.bob.write().unwrap().expects = 6;
send_handshake_msg(
context,
contact_chat_id,
if 0 != join_vg {
b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char
} else {
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char
},
auth,
own_fingerprint,
grpid,
);
ok_to_continue = true;
}
}
} else if strcmp(
step,
b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char,
) == 0i32
|| strcmp(
step,
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
/* ============================================================
==== Alice - the inviter side ====
==== Steps 5+6 in "Setup verified contact" protocol ====
==== Step 6 in "Out-of-band verified groups" protocol ====
============================================================ */
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
if fingerprint.is_null() {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Fingerprint not provided.\x00" as *const u8 as *const libc::c_char,
);
ok_to_continue = false;
} else if !encrypted_and_signed(mimeparser, as_str(fingerprint)) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Auth not encrypted.\x00" as *const u8 as *const libc::c_char,
);
ok_to_continue = false;
} else if 0
== fingerprint_equals_sender(context, as_str(fingerprint), contact_chat_id)
{
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Fingerprint mismatch on inviter-side.\x00" as *const u8
as *const libc::c_char,
);
ok_to_continue = false;
} else {
info!(context, 0, "Fingerprint verified.",);
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
let auth_0: *const libc::c_char;
auth_0 = lookup_field(mimeparser, "Secure-Join-Auth");
if auth_0.is_null() {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Auth not provided.\x00" as *const u8 as *const libc::c_char,
);
ok_to_continue = false;
} else if !dc_token_exists(context, DC_TOKEN_AUTH, as_str(auth_0)) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Auth invalid.\x00" as *const u8 as *const libc::c_char,
);
ok_to_continue = false;
} else if 0 == mark_peer_as_verified(context, as_str(fingerprint)) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Fingerprint mismatch on inviter-side.\x00" as *const u8
as *const libc::c_char,
);
ok_to_continue = false;
} else {
Contact::scaleup_origin_by_id(
context,
contact_id,
Origin::SecurejoinInvited,
);
info!(context, 0, "Auth verified.",);
secure_connection_established(context, contact_chat_id);
context.call_cb(
Event::CONTACTS_CHANGED,
contact_id as uintptr_t,
0i32 as uintptr_t,
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
600i32 as uintptr_t,
);
if 0 != join_vg {
grpid = to_string(lookup_field(mimeparser, "Secure-Join-Group"));
let group_chat_id: uint32_t =
chat::get_chat_id_by_grpid(context, &grpid, None, ptr::null_mut());
if group_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Chat {} not found.", &grpid);
ok_to_continue = false;
} else {
chat::add_contact_to_chat_ex(
context,
group_chat_id,
contact_id,
0x1i32,
);
ok_to_continue = true;
}
} else {
send_handshake_msg(
context,
contact_chat_id,
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
"",
ptr::null(),
"",
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
1000i32 as uintptr_t,
);
ok_to_continue = true;
}
}
}
} else if strcmp(
step,
b"vg-member-added\x00" as *const u8 as *const libc::c_char,
) == 0i32
|| strcmp(
step,
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
if 0 != join_vg {
ret = 0x1i32
}
if context.bob.read().unwrap().expects != 6 {
info!(context, 0, "Message belongs to a different handshake.",);
ok_to_continue = false;
} else {
let cond = {
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
};
if cond {
warn!(
context,
0, "Message out of sync or belongs to a different handshake.",
);
ok_to_continue = false;
} else {
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
}
let mut vg_expect_encrypted: libc::c_int = 1i32;
if 0 != join_vg {
let mut is_verified_group: libc::c_int = 0i32;
chat::get_chat_id_by_grpid(
context,
grpid,
None,
&mut is_verified_group,
);
if 0 == is_verified_group {
vg_expect_encrypted = 0i32
}
}
if 0 != vg_expect_encrypted {
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Contact confirm message not encrypted.\x00" as *const u8
as *const libc::c_char,
);
end_bobs_joining(context, 0i32);
ok_to_continue = false;
} else {
ok_to_continue = true;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
if 0 == mark_peer_as_verified(context, &scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Fingerprint mismatch on joiner-side.\x00" as *const u8
as *const libc::c_char,
);
ok_to_continue = false;
} else {
Contact::scaleup_origin_by_id(
context,
contact_id,
Origin::SecurejoinJoined,
);
context.call_cb(
Event::CONTACTS_CHANGED,
0i32 as uintptr_t,
0i32 as uintptr_t,
);
if 0 != join_vg {
if !addr_equals_self(
context,
as_str(lookup_field(mimeparser, "Chat-Group-Member-Added")),
) {
info!(
context,
0,
"Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."
);
ok_to_continue = false;
} else {
ok_to_continue = true;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
secure_connection_established(context, contact_chat_id);
context.bob.write().unwrap().expects = 0;
if 0 != join_vg {
send_handshake_msg(
context,
contact_chat_id,
b"vg-member-added-received\x00" as *const u8
as *const libc::c_char,
"",
ptr::null(),
"",
);
}
end_bobs_joining(context, 1i32);
ok_to_continue = true;
}
}
}
}
}
} else if strcmp(
step,
b"vg-member-added-received\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
/* ============================================================
==== Alice - the inviter side ====
==== Step 8 in "Out-of-band verified groups" protocol ====
============================================================ */
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
if contact.is_verified() == VerifiedStatus::Unverified {
warn!(context, 0, "vg-member-added-received invalid.",);
ok_to_continue = false;
} else {
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
800i32 as uintptr_t,
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
1000i32 as uintptr_t,
);
ok_to_continue = true;
}
} else {
warn!(context, 0, "vg-member-added-received invalid.",);
ok_to_continue = false;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
if 0 != ret & 0x2i32 {
ret |= 0x4i32
}
}
}
}
free(own_fingerprint as *mut libc::c_void);
ret
}
unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
context.bob.write().unwrap().status = status;
dc_stop_ongoing_process(context);
}
unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) {
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
let contact = Contact::get_by_id(context, contact_id);
let addr = if let Ok(ref contact) = contact {
contact.get_addr()
} else {
"?"
};
let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr);
chat::add_device_msg(context, contact_chat_id, msg);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
0i32 as uintptr_t,
);
}
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
let mut value: *const libc::c_char = ptr::null();
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key);
if field.is_null()
|| (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
|| (*field).fld_data.fld_optional_field.is_null()
|| {
value = (*(*field).fld_data.fld_optional_field).fld_value;
value.is_null()
}
{
return ptr::null();
}
value
}
unsafe fn could_not_establish_secure_connection(
context: &Context,
contact_chat_id: uint32_t,
details: *const libc::c_char,
) {
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
let contact = Contact::get_by_id(context, contact_id);
let msg = context.stock_string_repl_str(
StockMessage::ContactNotVerified,
if let Ok(ref contact) = contact {
contact.get_addr()
} else {
"?"
},
);
chat::add_device_msg(context, contact_chat_id, &msg);
error!(context, 0, "{} ({})", &msg, as_str(details));
}
unsafe fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> libc::c_int {
let mut success = 0;
if let Some(ref mut peerstate) =
Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref())
{
if peerstate.set_verified(1, fingerprint.as_ref(), 2) {
peerstate.prefer_encrypt = EncryptPreference::Mutual;
peerstate.to_save = Some(ToSave::All);
peerstate.save_to_db(&context.sql, false);
success = 1;
}
}
success
}
/* ******************************************************************************
* Tools: Misc.
******************************************************************************/
unsafe fn encrypted_and_signed(
mimeparser: &dc_mimeparser_t,
expected_fingerprint: impl AsRef<str>,
) -> bool {
if !mimeparser.e2ee_helper.encrypted {
warn!(mimeparser.context, 0, "Message not encrypted.",);
return false;
}
if mimeparser.e2ee_helper.signatures.len() <= 0 {
warn!(mimeparser.context, 0, "Message not signed.",);
return false;
}
if expected_fingerprint.as_ref().is_empty() {
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
return false;
}
if !mimeparser
.e2ee_helper
.signatures
.contains(expected_fingerprint.as_ref())
{
warn!(
mimeparser.context,
0,
"Message does not match expected fingerprint {}.",
expected_fingerprint.as_ref(),
);
return false;
}
true
}
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
// together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother
// with things they cannot fix, so the user is just kicked from the verified group
// (and he will know this and can fix this)
if Some(DegradeEvent::FingerprintChanged) == peerstate.degrade_event {
let contact_id: i32 = context
.sql
.query_row_col(
context,
"SELECT id FROM contacts WHERE addr=?;",
params![&peerstate.addr],
0,
)
.unwrap_or_default();
if contact_id > 0 {
let (contact_chat_id, _) =
chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop)
.unwrap_or_default();
let peeraddr: &str = match peerstate.addr {
Some(ref addr) => &addr,
None => "",
};
let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr);
chat::add_device_msg(context, contact_chat_id, msg);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
0 as uintptr_t,
);
}
}
}