refactor: move aheader to safe rust

This commit is contained in:
dignifiedquire
2019-05-12 19:05:33 +01:00
parent 00314ffbe0
commit fdd870286e
17 changed files with 468 additions and 630 deletions

View File

@@ -509,3 +509,10 @@ pub const DC_STR_MSGLOCATIONENABLED: usize = 64;
pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
pub const DC_STR_LOCATION: usize = 66;
pub const DC_STR_COUNT: usize = 66;
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[repr(u8)]
pub enum Key {
Public = 0,
Private = 1,
}

View File

@@ -1,283 +1,318 @@
use mmime::mailimf_types::*;
use std::collections::BTreeMap;
use std::ffi::{CStr, CString};
use std::str::FromStr;
use std::{fmt, str};
use mmime::mailimf_types::*;
use num_traits::ToPrimitive;
use crate::constants::*;
use crate::dc_contact::*;
use crate::dc_key::*;
use crate::dc_strbuilder::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
/// Possible values for encryption preference
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
#[repr(u8)]
pub enum EncryptPreference {
NoPreference = 0,
Mutual = 1,
Reset = 20,
}
impl Default for EncryptPreference {
fn default() -> Self {
EncryptPreference::NoPreference
}
}
impl fmt::Display for EncryptPreference {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
EncryptPreference::Mutual => write!(fmt, "mutual"),
EncryptPreference::NoPreference => write!(fmt, "nopreference"),
EncryptPreference::Reset => write!(fmt, "reset"),
}
}
}
impl str::FromStr for EncryptPreference {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"mutual" => Ok(EncryptPreference::Mutual),
"reset" => Ok(EncryptPreference::Reset),
_ => Ok(EncryptPreference::NoPreference),
}
}
}
/// Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_aheader_t {
pub addr: *mut libc::c_char,
pub struct Aheader {
pub addr: String,
pub public_key: *mut dc_key_t,
pub prefer_encrypt: libc::c_int,
pub prefer_encrypt: EncryptPreference,
}
/// the returned pointer is ref'd and must be unref'd after usage
pub unsafe fn dc_aheader_new() -> *mut dc_aheader_t {
let mut aheader = calloc(1, ::std::mem::size_of::<dc_aheader_t>()) as *mut dc_aheader_t;
if aheader.is_null() {
// TODO replace with enum (hardcoded in deltachat-core) /rtn
exit(37);
impl Aheader {
pub fn new(addr: String, public_key: *mut dc_key_t, prefer_encrypt: EncryptPreference) -> Self {
Aheader {
addr,
public_key,
prefer_encrypt,
}
}
(*aheader).public_key = dc_key_new();
pub fn from_imffields(
wanted_from: *const libc::c_char,
header: *const mailimf_fields,
) -> Option<Self> {
if wanted_from.is_null() || header.is_null() {
return None;
}
aheader
}
let mut fine_header = None;
let mut cur = unsafe { (*(*header).fld_list).first };
pub unsafe fn dc_aheader_new_from_imffields(
wanted_from: *const libc::c_char,
header: *const mailimf_fields,
) -> *mut dc_aheader_t {
let mut cur;
let mut fine_header = 0 as *mut dc_aheader_t;
if wanted_from.is_null() || header.is_null() {
return 0 as *mut dc_aheader_t;
}
cur = (*(*header).fld_list).first;
while !cur.is_null() {
let field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field;
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
let optional_field = (*field).fld_data.fld_optional_field;
if !optional_field.is_null()
&& !(*optional_field).fld_name.is_null()
&& strcasecmp(
(*optional_field).fld_name,
b"Autocrypt\x00" as *const u8 as *const libc::c_char,
) == 0
while !cur.is_null() {
let field = unsafe { (*cur).data as *mut mailimf_field };
if !field.is_null()
&& unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
{
let mut test = dc_aheader_new();
if 0 == dc_aheader_set_from_string(test, (*optional_field).fld_value)
|| dc_addr_cmp((*test).addr, wanted_from) != 0
let optional_field = unsafe { (*field).fld_data.fld_optional_field };
if !optional_field.is_null()
&& unsafe { !(*optional_field).fld_name.is_null() }
&& unsafe { CStr::from_ptr((*optional_field).fld_name).to_str().unwrap() }
== "Autocrypt"
{
dc_aheader_unref(test);
test = 0 as *mut dc_aheader_t
}
if fine_header.is_null() {
fine_header = test
} else if !test.is_null() {
dc_aheader_unref(fine_header);
dc_aheader_unref(test);
return 0 as *mut dc_aheader_t;
let value = unsafe {
CStr::from_ptr((*optional_field).fld_value)
.to_str()
.unwrap()
};
match Self::from_str(value) {
Ok(test) => {
// TODO: implement rust-safe version of dc_addr_cmp
let addr = CString::new(test.addr.clone()).unwrap();
if unsafe { dc_addr_cmp(addr.as_ptr(), wanted_from) } == 0 {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
}
}
}
_ => {}
}
}
}
cur = unsafe { (*cur).next };
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
fine_header
}
pub unsafe fn dc_aheader_unref(aheader: *mut dc_aheader_t) {
if aheader.is_null() {
return;
}
free((*aheader).addr as *mut libc::c_void);
dc_key_unref((*aheader).public_key);
free(aheader as *mut libc::c_void);
}
pub unsafe fn dc_aheader_set_from_string(
mut aheader: *mut dc_aheader_t,
header_str__: *const libc::c_char,
) -> libc::c_int {
let current_block: u64;
/* according to RFC 5322 (Internet Message Format), the given string may contain `\r\n` before any whitespace.
we can ignore this issue as
(a) no key or value is expected to contain spaces,
(b) for the key, non-base64-characters are ignored and
(c) for parsing, we ignore `\r\n` as well as tabs for spaces */
let mut header_str = 0 as *mut libc::c_char;
let mut p;
let mut beg_attr_name;
let mut after_attr_name;
let mut beg_attr_value;
let mut success: libc::c_int = 0;
dc_aheader_empty(aheader);
if !(aheader.is_null() || header_str__.is_null()) {
(*aheader).prefer_encrypt = 0;
header_str = dc_strdup(header_str__);
p = header_str;
loop {
if !(0 != *p) {
current_block = 5689316957504528238;
break;
}
p = p.offset(strspn(p, b"\t\r\n =;\x00" as *const u8 as *const libc::c_char) as isize);
beg_attr_name = p;
beg_attr_value = 0 as *mut libc::c_char;
p = p.offset(strcspn(p, b"\t\r\n =;\x00" as *const u8 as *const libc::c_char) as isize);
if !(p != beg_attr_name) {
continue;
}
after_attr_name = p;
p = p.offset(strspn(p, b"\t\r\n \x00" as *const u8 as *const libc::c_char) as isize);
if *p as libc::c_int == '=' as i32 {
p = p.offset(
strspn(p, b"\t\r\n =\x00" as *const u8 as *const libc::c_char) as isize,
);
beg_attr_value = p;
p = p.offset(strcspn(p, b";\x00" as *const u8 as *const libc::c_char) as isize);
if *p as libc::c_int != '\u{0}' as i32 {
*p = '\u{0}' as i32 as libc::c_char;
p = p.offset(1isize)
}
dc_trim(beg_attr_value);
} else {
p = p
.offset(strspn(p, b"\t\r\n ;\x00" as *const u8 as *const libc::c_char) as isize)
}
*after_attr_name = '\u{0}' as i32 as libc::c_char;
if !(0 == add_attribute(aheader, beg_attr_name, beg_attr_value)) {
continue;
}
/* a bad attribute makes the whole header invalid */
current_block = 9271062167157603455;
break;
}
match current_block {
9271062167157603455 => {}
_ => {
if !(*aheader).addr.is_null() && !(*(*aheader).public_key).binary.is_null() {
success = 1
}
}
}
}
free(header_str as *mut libc::c_void);
if 0 == success {
dc_aheader_empty(aheader);
}
success
}
pub unsafe fn dc_aheader_empty(mut aheader: *mut dc_aheader_t) {
if aheader.is_null() {
return;
}
(*aheader).prefer_encrypt = 0;
free((*aheader).addr as *mut libc::c_void);
(*aheader).addr = 0 as *mut libc::c_char;
if !(*(*aheader).public_key).binary.is_null() {
dc_key_unref((*aheader).public_key);
(*aheader).public_key = dc_key_new()
fine_header
}
}
/* ******************************************************************************
* Parse Autocrypt Header
******************************************************************************/
unsafe fn add_attribute(
mut aheader: *mut dc_aheader_t,
name: *const libc::c_char,
value: *const libc::c_char,
) -> libc::c_int {
if strcasecmp(name, b"addr\x00" as *const u8 as *const libc::c_char) == 0 {
if value.is_null() || !dc_may_be_valid_addr(value) || !(*aheader).addr.is_null() {
return 0;
}
(*aheader).addr = dc_addr_normalize(value);
return 1;
} else {
if strcasecmp(
name,
b"prefer-encrypt\x00" as *const u8 as *const libc::c_char,
) == 0
{
if !value.is_null()
&& strcasecmp(value, b"mutual\x00" as *const u8 as *const libc::c_char) == 0
{
(*aheader).prefer_encrypt = 1;
return 1;
}
return 1;
} else {
if strcasecmp(name, b"keydata\x00" as *const u8 as *const libc::c_char) == 0 {
if value.is_null()
|| !(*(*aheader).public_key).binary.is_null()
|| 0 != (*(*aheader).public_key).bytes
{
return 0;
}
return dc_key_set_from_base64((*aheader).public_key, value, 0);
} else {
if *name.offset(0isize) as libc::c_int == '_' as i32 {
return 1;
}
}
}
}
0
}
pub unsafe fn dc_aheader_render(aheader: *const dc_aheader_t) -> *mut libc::c_char {
let mut success: bool = false;
let mut keybase64_wrapped: *mut libc::c_char = 0 as *mut libc::c_char;
let mut ret: dc_strbuilder_t = dc_strbuilder_t {
buf: 0 as *mut libc::c_char,
allocated: 0,
free: 0,
eos: 0 as *mut libc::c_char,
};
dc_strbuilder_init(&mut ret, 0);
if !(aheader.is_null()
|| (*aheader).addr.is_null()
|| (*(*aheader).public_key).binary.is_null()
|| (*(*aheader).public_key).type_0 != 0)
{
dc_strbuilder_cat(&mut ret, b"addr=\x00" as *const u8 as *const libc::c_char);
dc_strbuilder_cat(&mut ret, (*aheader).addr);
dc_strbuilder_cat(&mut ret, b"; \x00" as *const u8 as *const libc::c_char);
if (*aheader).prefer_encrypt == 1 {
dc_strbuilder_cat(
&mut ret,
b"prefer-encrypt=mutual; \x00" as *const u8 as *const libc::c_char,
);
}
dc_strbuilder_cat(
&mut ret,
b"keydata= \x00" as *const u8 as *const libc::c_char,
);
impl fmt::Display for Aheader {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// TODO replace 78 with enum /rtn
/* adds a whitespace every 78 characters, this allows libEtPan to wrap the lines according to RFC 5322
(which may insert a linebreak before every whitespace) */
keybase64_wrapped = dc_key_render_base64((*aheader).public_key, 78);
if !keybase64_wrapped.is_null() {
/*no checksum*/
dc_strbuilder_cat(&mut ret, keybase64_wrapped);
success = true;
}
// adds a whitespace every 78 characters, this allows libEtPan to
// wrap the lines according to RFC 5322
// (which may insert a linebreak before every whitespace)
let keydata = dc_key_render_base64_string(self.public_key, 78);
write!(
fmt,
"addr={}; prefer-encrypt={}; keydata={}",
self.addr, self.prefer_encrypt, keydata
)
}
}
impl str::FromStr for Aheader {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut attributes: BTreeMap<String, String> = s
.split(";")
.filter_map(|a| {
let attribute: Vec<&str> = a.trim().splitn(2, "=").collect();
if attribute.len() < 2 {
return None;
}
Some((
attribute[0].trim().to_string(),
attribute[1].trim().to_string(),
))
})
.collect();
let addr = match attributes.remove("addr") {
Some(addr) => addr,
None => {
return Err(());
}
};
let public_key = match attributes.remove("keydata") {
Some(raw) => {
let key = unsafe { dc_key_new() };
unsafe {
dc_key_set_from_base64(
key,
CString::new(raw).unwrap().as_ptr(),
Key::Public.to_i32().unwrap(),
)
};
key
}
None => {
return Err(());
}
};
let prefer_encrypt = match attributes
.remove("prefer-encrypt")
.and_then(|raw| raw.parse().ok())
{
Some(pref) => pref,
None => EncryptPreference::NoPreference,
};
// Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored
// Autocrypt-Level0: unknown attribute, treat the header as invalid
if attributes.keys().find(|k| !k.starts_with("_")).is_some() {
return Err(());
}
Ok(Aheader {
addr,
public_key,
prefer_encrypt,
})
}
}
impl Drop for Aheader {
fn drop(&mut self) {
unsafe {
dc_key_unref(self.public_key);
}
self.public_key = std::ptr::null_mut();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pgp as rpgp;
fn rawkey() -> String {
"mDMEWFUX7RYJKwYBBAHaRw8BAQdACHq6FkRGsHqBMsNpD7d+aQ2jtxVwTO+Y4NhBaQyHaMj+0HWFsaWNlQHRlc3RzdWl0ZS5hdXRvY3J5cHQub3JniJAEExYIADgWIQQmqmdR/XZoxC+kkkr8dE2p/nPD1AUCWFUX7QIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRD8dE2p/nPD1EqOAP0WUDKwko001X7XTSYbWGWmXfR9P1Aw6917EnkVQMsp3gEA86Ii8ArL3jd+E2qS5JSysx/qiVhuTSwWzmC5K6zKdg+4OARYVRfuEgorBgEEAZdVAQUBAQdAv1A88FoCfwz0zSh6NNnUuKuz1p3ctJ3kXMGotsVYjA0DAQgHiHgEGBYIACAWIQQmqmdR/XZoxC+kkkr8dE2p/nPD1AUCWFUX7gIbDAAKCRD8dE2p/nPD1FTOAP4nS14sX7a/nBXBKWAh/oX8iVtkhmZqjy9tG21BcNqb+wEAq73H4+1ncnkscR3Nu4GYzNRSD3NXq68tEESK28kYvw4=".into()
}
#[test]
fn test_from_str() {
let h: Aheader = format!(
"addr=me@mail.com; prefer-encrypt=mutual; keydata={}",
rawkey()
)
.parse()
.expect("failed to parse");
assert_eq!(h.addr, "me@mail.com");
assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual);
assert!(!h.public_key.is_null());
}
#[test]
fn test_from_str_non_critical() {
let raw = format!("addr=me@mail.com; _foo=one; _bar=two; keydata={}", rawkey());
let h: Aheader = raw.parse().expect("failed to parse");
assert_eq!(h.addr, "me@mail.com");
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
assert!(!h.public_key.is_null());
}
#[test]
fn test_from_str_superflous_critical() {
let raw = format!(
"addr=me@mail.com; _foo=one; _bar=two; other=me; keydata={}",
rawkey()
);
assert!(raw.parse::<Aheader>().is_err());
}
#[test]
fn test_good_headers() {
let fixed_header = "addr=a@b.example.org; prefer-encrypt=mutual; keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g 4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8 ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu88 80iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTll HOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+ws CJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiF Nyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAg dLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72 rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v 81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgC u3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOt kb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKC LhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4s WVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWv BuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGr wdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktE k6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0 j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7x egRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc=";
let ah = Aheader::from_str(fixed_header).expect("failed to parse");
assert_eq!(ah.addr, "a@b.example.org");
// assert_eq!(unsafe { (*ah.public_key).bytes }, 1212);
assert!(valid_key(ah.public_key as *const _));
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
let rendered = ah.to_string();
assert_eq!(rendered, fixed_header);
let ah = Aheader::from_str(" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = RG VsdGEgQ\r\n2hhdA==").expect("failed to parse");
assert_eq!(ah.addr, "a@b.example.org");
assert_eq!(unsafe { (*ah.public_key).bytes }, 10);
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
assert_eq!(
unsafe {
CStr::from_ptr((*ah.public_key).binary as *const _)
.to_str()
.unwrap()
},
"Delta Chat"
);
Aheader::from_str(
"addr=a@b.example.org; prefer-encrypt=ignoreUnknownValues; keydata=RGVsdGEgQ2hhdA==",
)
.expect("failed to parse");
Aheader::from_str("addr=a@b.example.org; keydata=RGVsdGEgQ2hhdA==")
.expect("failed to parse");
}
#[test]
fn test_bad_headers() {
assert!(Aheader::from_str("").is_err());
assert!(Aheader::from_str("foo").is_err());
assert!(Aheader::from_str("\n\n\n").is_err());
assert!(Aheader::from_str(" ;;").is_err());
assert!(Aheader::from_str("addr=a@t.de; unknwon=1; keydata=jau").is_err());
}
fn valid_key(raw_key: *const dc_key_t) -> bool {
let mut key_is_valid = false;
unsafe {
if !(raw_key.is_null() || (*raw_key).binary.is_null() || (*raw_key).bytes <= 0i32) {
let key = rpgp::rpgp_key_from_bytes(
(*raw_key).binary as *const _,
(*raw_key).bytes as usize,
);
if (*raw_key).type_0 == 0i32 && 0 != rpgp::rpgp_key_is_public(key) as libc::c_int {
key_is_valid = true;
} else if (*raw_key).type_0 == 1i32
&& 0 != rpgp::rpgp_key_is_secret(key) as libc::c_int
{
key_is_valid = true;
}
if !key.is_null() {
rpgp::rpgp_key_drop(key);
}
}
}
key_is_valid
}
if !success {
free(ret.buf as *mut libc::c_void);
ret.buf = 0 as *mut libc::c_char
}
free(keybase64_wrapped as *mut libc::c_void);
ret.buf
}

View File

@@ -1,3 +1,7 @@
use std::ffi::{CStr, CString};
use num_traits::ToPrimitive;
use crate::dc_aheader::*;
use crate::dc_chat::*;
use crate::dc_context::dc_context_t;
@@ -81,20 +85,20 @@ unsafe fn dc_apeerstate_empty(mut peerstate: *mut dc_apeerstate_t) {
// TODO should return bool /rtn
pub unsafe fn dc_apeerstate_init_from_header(
mut peerstate: *mut dc_apeerstate_t,
header: *const dc_aheader_t,
header: &Aheader,
message_time: time_t,
) -> libc::c_int {
if peerstate.is_null() || header.is_null() {
if peerstate.is_null() {
return 0i32;
}
dc_apeerstate_empty(peerstate);
(*peerstate).addr = dc_strdup((*header).addr);
(*peerstate).addr = dc_strdup(CString::new(header.addr.clone()).unwrap().as_ptr());
(*peerstate).last_seen = message_time;
(*peerstate).last_seen_autocrypt = message_time;
(*peerstate).to_save |= 0x2i32;
(*peerstate).prefer_encrypt = (*header).prefer_encrypt;
(*peerstate).prefer_encrypt = header.prefer_encrypt.to_i32().unwrap();
(*peerstate).public_key = dc_key_new();
dc_key_set_from_key((*peerstate).public_key, (*header).public_key);
dc_key_set_from_key((*peerstate).public_key, header.public_key);
dc_apeerstate_recalc_fingerprint(peerstate);
1
@@ -152,20 +156,20 @@ pub unsafe fn dc_apeerstate_recalc_fingerprint(mut peerstate: *mut dc_apeerstate
}
// TODO should return bool /rtn
pub unsafe extern "C" fn dc_apeerstate_init_from_gossip(
pub unsafe fn dc_apeerstate_init_from_gossip(
mut peerstate: *mut dc_apeerstate_t,
gossip_header: *const dc_aheader_t,
gossip_header: &Aheader,
message_time: time_t,
) -> libc::c_int {
if peerstate.is_null() || gossip_header.is_null() {
if peerstate.is_null() {
return 0i32;
}
dc_apeerstate_empty(peerstate);
(*peerstate).addr = dc_strdup((*gossip_header).addr);
(*peerstate).addr = dc_strdup(CString::new(gossip_header.addr.clone()).unwrap().as_ptr());
(*peerstate).gossip_timestamp = message_time;
(*peerstate).to_save |= 0x2i32;
(*peerstate).gossip_key = dc_key_new();
dc_key_set_from_key((*peerstate).gossip_key, (*gossip_header).public_key);
dc_key_set_from_key((*peerstate).gossip_key, gossip_header.public_key);
dc_apeerstate_recalc_fingerprint(peerstate);
1
@@ -191,15 +195,17 @@ pub unsafe fn dc_apeerstate_degrade_encryption(
pub unsafe fn dc_apeerstate_apply_header(
mut peerstate: *mut dc_apeerstate_t,
header: *const dc_aheader_t,
header: &Aheader,
message_time: time_t,
) {
if peerstate.is_null()
|| header.is_null()
|| (*peerstate).addr.is_null()
|| (*header).addr.is_null()
|| (*(*header).public_key).binary.is_null()
|| strcasecmp((*peerstate).addr, (*header).addr) != 0i32
|| (*header.public_key).binary.is_null()
|| CStr::from_ptr((*peerstate).addr)
.to_str()
.unwrap()
.to_lowercase()
!= header.addr.to_lowercase()
{
return;
}
@@ -207,13 +213,16 @@ pub unsafe fn dc_apeerstate_apply_header(
(*peerstate).last_seen = message_time;
(*peerstate).last_seen_autocrypt = message_time;
(*peerstate).to_save |= 0x1i32;
if ((*header).prefer_encrypt == 1i32 || (*header).prefer_encrypt == 0i32)
&& (*header).prefer_encrypt != (*peerstate).prefer_encrypt
if (header.prefer_encrypt == EncryptPreference::Mutual
|| header.prefer_encrypt == EncryptPreference::NoPreference)
&& header.prefer_encrypt.to_i32().unwrap() != (*peerstate).prefer_encrypt
{
if (*peerstate).prefer_encrypt == 1i32 && (*header).prefer_encrypt != 1i32 {
if (*peerstate).prefer_encrypt == 1i32
&& header.prefer_encrypt != EncryptPreference::Mutual
{
(*peerstate).degrade_event |= 0x1i32
}
(*peerstate).prefer_encrypt = (*header).prefer_encrypt;
(*peerstate).prefer_encrypt = header.prefer_encrypt.to_i32().unwrap();
(*peerstate).to_save |= 0x2i32
}
if (*peerstate).public_key.is_null() {
@@ -229,15 +238,17 @@ pub unsafe fn dc_apeerstate_apply_header(
pub unsafe fn dc_apeerstate_apply_gossip(
mut peerstate: *mut dc_apeerstate_t,
gossip_header: *const dc_aheader_t,
gossip_header: &Aheader,
message_time: time_t,
) {
if peerstate.is_null()
|| gossip_header.is_null()
|| (*peerstate).addr.is_null()
|| (*gossip_header).addr.is_null()
|| (*(*gossip_header).public_key).binary.is_null()
|| strcasecmp((*peerstate).addr, (*gossip_header).addr) != 0i32
|| CStr::from_ptr((*peerstate).addr)
.to_str()
.unwrap()
.to_lowercase()
!= gossip_header.addr.to_lowercase()
{
return;
}
@@ -259,16 +270,17 @@ pub unsafe fn dc_apeerstate_render_gossip_header(
peerstate: *const dc_apeerstate_t,
min_verified: libc::c_int,
) -> *mut libc::c_char {
let mut ret: *mut libc::c_char = 0 as *mut libc::c_char;
let mut autocryptheader: *mut dc_aheader_t = dc_aheader_new();
if !(peerstate.is_null() || (*peerstate).addr.is_null()) {
(*autocryptheader).prefer_encrypt = 0i32;
(*autocryptheader).addr = dc_strdup((*peerstate).addr);
(*autocryptheader).public_key = dc_key_ref(dc_apeerstate_peek_key(peerstate, min_verified));
ret = dc_aheader_render(autocryptheader)
let addr = CStr::from_ptr((*peerstate).addr).to_str().unwrap().into();
let key = dc_key_ref(dc_apeerstate_peek_key(peerstate, min_verified));
let header = Aheader::new(addr, key, EncryptPreference::NoPreference);
let rendered = header.to_string();
let rendered_c = CString::new(rendered).unwrap();
libc::strdup(rendered_c.as_ptr())
} else {
std::ptr::null_mut()
}
dc_aheader_unref(autocryptheader);
ret
}
pub unsafe fn dc_apeerstate_peek_key(

View File

@@ -263,10 +263,7 @@ pub unsafe fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_new_typed(0, initsize)
}
pub unsafe extern "C" fn dc_array_new_typed(
type_0: libc::c_int,
initsize: size_t,
) -> *mut dc_array_t {
pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut dc_array_t {
let mut array: *mut dc_array_t;
array = calloc(1, ::std::mem::size_of::<dc_array_t>()) as *mut dc_array_t;
if array.is_null() {

View File

@@ -2279,7 +2279,7 @@ pub unsafe fn dc_chat_get_name(chat: *const dc_chat_t) -> *mut libc::c_char {
dc_strdup((*chat).name)
}
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *const dc_chat_t) -> *mut libc::c_char {
pub unsafe fn dc_chat_get_subtitle(chat: *const dc_chat_t) -> *mut libc::c_char {
/* returns either the address or the number of chat members */
let mut ret: *mut libc::c_char = 0 as *mut libc::c_char;
if chat.is_null() || (*chat).magic != 0xc4a7c4a7u32 {

View File

@@ -46,7 +46,7 @@ pub unsafe fn dc_marknoticed_contact(context: &dc_context_t, contact_id: uint32_
}
// handle contacts
pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *const libc::c_char) -> bool {
pub unsafe fn dc_may_be_valid_addr(addr: *const libc::c_char) -> bool {
if addr.is_null() {
return false;
}

View File

@@ -1,3 +1,6 @@
use std::ffi::{CStr, CString};
use std::str::FromStr;
use mmime::clist::*;
use mmime::mailimf::*;
use mmime::mailimf_types::*;
@@ -53,11 +56,9 @@ pub unsafe fn dc_e2ee_encrypt(
mut in_out_message: *mut mailmime,
mut helper: *mut dc_e2ee_helper_t,
) {
let p_0: *mut libc::c_char;
let current_block: u64;
let mut col: libc::c_int = 0i32;
let mut do_encrypt: libc::c_int = 0i32;
let mut autocryptheader: *mut dc_aheader_t = dc_aheader_new();
/*just a pointer into mailmime structure, must not be freed*/
let imffields_unprotected: *mut mailimf_fields;
let keyring: *mut dc_keyring_t = dc_keyring_new();
@@ -73,42 +74,40 @@ pub unsafe fn dc_e2ee_encrypt(
::std::mem::size_of::<dc_e2ee_helper_t>(),
);
}
if !(recipients_addr.is_null()
|| in_out_message.is_null()
|| !(*in_out_message).mm_parent.is_null()
|| autocryptheader.is_null()
|| keyring.is_null()
|| sign_key.is_null()
|| plain.is_null()
|| helper.is_null())
{
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */
(*autocryptheader).prefer_encrypt = 0i32;
if 0 != dc_sqlite3_get_config_int(
context,
&context.sql.clone().read().unwrap(),
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1i32,
) {
(*autocryptheader).prefer_encrypt = 1i32
}
(*autocryptheader).addr = dc_sqlite3_get_config(
let prefer_encrypt = if 0
!= dc_sqlite3_get_config_int(
context,
&context.sql.clone().read().unwrap(),
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1,
) {
EncryptPreference::Mutual
} else {
EncryptPreference::NoPreference
};
let addr = dc_sqlite3_get_config(
context,
&context.sql.clone().read().unwrap(),
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if !(*autocryptheader).addr.is_null() {
if !(0
== load_or_generate_self_public_key(
context,
(*autocryptheader).public_key,
(*autocryptheader).addr,
in_out_message,
))
{
let public_key = dc_key_new();
if !addr.is_null() {
if 0 != load_or_generate_self_public_key(context, public_key, addr, in_out_message) {
/*only for random-seed*/
if (*autocryptheader).prefer_encrypt == 1i32 || 0 != e2ee_guaranteed {
if prefer_encrypt == EncryptPreference::Mutual || 0 != e2ee_guaranteed {
do_encrypt = 1i32;
let mut iter1: *mut clistiter;
iter1 = (*recipients_addr).first;
@@ -121,7 +120,7 @@ pub unsafe fn dc_e2ee_encrypt(
as *const libc::c_char;
let peerstate: *mut dc_apeerstate_t = dc_apeerstate_new(context);
let mut key_to_use: *mut dc_key_t = 0 as *mut dc_key_t;
if !(strcasecmp(recipient_addr, (*autocryptheader).addr) == 0i32) {
if !(strcasecmp(recipient_addr, addr) == 0i32) {
if 0 != dc_apeerstate_load_by_addr(
peerstate,
&context.sql.clone().read().unwrap(),
@@ -148,11 +147,11 @@ pub unsafe fn dc_e2ee_encrypt(
}
}
if 0 != do_encrypt {
dc_keyring_add(keyring, (*autocryptheader).public_key);
dc_keyring_add(keyring, public_key);
if 0 == dc_key_load_self_private(
context,
sign_key,
(*autocryptheader).addr,
addr,
&context.sql.clone().read().unwrap(),
) {
do_encrypt = 0i32
@@ -371,25 +370,24 @@ pub unsafe fn dc_e2ee_encrypt(
match current_block {
14181132614457621749 => {}
_ => {
p_0 = dc_aheader_render(autocryptheader);
if !p_0.is_null() {
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
strdup(
b"Autocrypt\x00" as *const u8 as *const libc::c_char,
),
p_0,
),
);
}
let addr = CStr::from_ptr(addr).to_str().unwrap();
let aheader = Aheader::new(addr.into(), public_key, prefer_encrypt);
let rendered = CString::new(aheader.to_string()).unwrap();
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
libc::strdup(rendered.as_ptr()),
),
);
}
}
}
}
}
}
dc_aheader_unref(autocryptheader);
dc_keyring_unref(keyring);
dc_key_unref(sign_key);
if !plain.is_null() {
@@ -645,7 +643,6 @@ pub unsafe fn dc_e2ee_decrypt(
(to detect parts that could not be decrypted, simply look for left "multipart/encrypted" MIME types */
/*just a pointer into mailmime structure, must not be freed*/
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
let mut autocryptheader: *mut dc_aheader_t = 0 as *mut dc_aheader_t;
let mut message_time: time_t = 0i32 as time_t;
let peerstate: *mut dc_apeerstate_t = dc_apeerstate_new(context);
let mut from: *mut libc::c_char = 0 as *mut libc::c_char;
@@ -680,11 +677,10 @@ pub unsafe fn dc_e2ee_decrypt(
}
}
}
autocryptheader = dc_aheader_new_from_imffields(from, imffields);
if !autocryptheader.is_null() {
if 0 == dc_pgp_is_valid_key(context, (*autocryptheader).public_key) {
dc_aheader_unref(autocryptheader);
autocryptheader = 0 as *mut dc_aheader_t
let mut autocryptheader = Aheader::from_imffields(from, imffields);
if let Some(ref header) = autocryptheader {
if 0 == dc_pgp_is_valid_key(context, header.public_key) {
autocryptheader = None;
}
}
if message_time > 0i32 as libc::c_long && !from.is_null() {
@@ -693,8 +689,8 @@ pub unsafe fn dc_e2ee_decrypt(
&context.sql.clone().read().unwrap(),
from,
) {
if !autocryptheader.is_null() {
dc_apeerstate_apply_header(peerstate, autocryptheader, message_time);
if let Some(ref header) = autocryptheader {
dc_apeerstate_apply_header(peerstate, header, message_time);
dc_apeerstate_save_to_db(peerstate, &context.sql.clone().read().unwrap(), 0i32);
} else if message_time > (*peerstate).last_seen_autocrypt
&& 0 == contains_report(in_out_message)
@@ -702,8 +698,8 @@ pub unsafe fn dc_e2ee_decrypt(
dc_apeerstate_degrade_encryption(peerstate, message_time);
dc_apeerstate_save_to_db(peerstate, &context.sql.clone().read().unwrap(), 0i32);
}
} else if !autocryptheader.is_null() {
dc_apeerstate_init_from_header(peerstate, autocryptheader, message_time);
} else if let Some(ref header) = autocryptheader {
dc_apeerstate_init_from_header(peerstate, header, message_time);
dc_apeerstate_save_to_db(peerstate, &context.sql.clone().read().unwrap(), 1i32);
}
}
@@ -767,7 +763,7 @@ pub unsafe fn dc_e2ee_decrypt(
if !gossip_headers.is_null() {
mailimf_fields_free(gossip_headers);
}
dc_aheader_unref(autocryptheader);
dc_apeerstate_unref(peerstate);
dc_keyring_unref(private_keyring);
dc_keyring_unref(public_keyring_for_validate);
@@ -801,66 +797,70 @@ unsafe fn update_gossip_peerstates(
b"Autocrypt-Gossip\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
let gossip_header: *mut dc_aheader_t = dc_aheader_new();
if 0 != dc_aheader_set_from_string(gossip_header, (*optional_field).fld_value)
&& 0 != dc_pgp_is_valid_key(context, (*gossip_header).public_key)
{
if recipients.is_null() {
recipients = mailimf_get_recipients(imffields)
}
if !dc_hash_find(
recipients,
(*gossip_header).addr as *const libc::c_void,
strlen((*gossip_header).addr) as libc::c_int,
)
.is_null()
{
let peerstate: *mut dc_apeerstate_t = dc_apeerstate_new(context);
if 0 == dc_apeerstate_load_by_addr(
peerstate,
&context.sql.clone().read().unwrap(),
(*gossip_header).addr,
) {
dc_apeerstate_init_from_gossip(peerstate, gossip_header, message_time);
dc_apeerstate_save_to_db(
let value = CStr::from_ptr((*optional_field).fld_value)
.to_str()
.unwrap();
let gossip_header = Aheader::from_str(value);
if let Ok(ref header) = gossip_header {
if 0 != dc_pgp_is_valid_key(context, header.public_key) {
if recipients.is_null() {
recipients = mailimf_get_recipients(imffields)
}
if !dc_hash_find(
recipients,
CString::new(header.addr.clone()).unwrap().as_ptr()
as *const libc::c_void,
header.addr.len() as i32,
)
.is_null()
{
let peerstate: *mut dc_apeerstate_t = dc_apeerstate_new(context);
if 0 == dc_apeerstate_load_by_addr(
peerstate,
&context.sql.clone().read().unwrap(),
1i32,
CString::new(header.addr.clone()).unwrap().as_ptr(),
) {
dc_apeerstate_init_from_gossip(peerstate, header, message_time);
dc_apeerstate_save_to_db(
peerstate,
&context.sql.clone().read().unwrap(),
1i32,
);
} else {
dc_apeerstate_apply_gossip(peerstate, header, message_time);
dc_apeerstate_save_to_db(
peerstate,
&context.sql.clone().read().unwrap(),
0i32,
);
}
if 0 != (*peerstate).degrade_event {
dc_handle_degrade_event(context, peerstate);
}
dc_apeerstate_unref(peerstate);
if gossipped_addr.is_null() {
gossipped_addr =
malloc(::std::mem::size_of::<dc_hash_t>()) as *mut dc_hash_t;
dc_hash_init(gossipped_addr, 3i32, 1i32);
}
dc_hash_insert(
gossipped_addr,
CString::new(header.addr.clone()).unwrap().as_ptr()
as *const libc::c_void,
header.addr.len() as libc::c_int,
1i32 as *mut libc::c_void,
);
} else {
dc_apeerstate_apply_gossip(peerstate, gossip_header, message_time);
dc_apeerstate_save_to_db(
peerstate,
&context.sql.clone().read().unwrap(),
dc_log_info(
context,
0i32,
b"Ignoring gossipped \"%s\" as the address is not in To/Cc list.\x00"
as *const u8 as *const libc::c_char,
CString::new(header.addr.clone()).unwrap().as_ptr(),
);
}
if 0 != (*peerstate).degrade_event {
dc_handle_degrade_event(context, peerstate);
}
dc_apeerstate_unref(peerstate);
if gossipped_addr.is_null() {
gossipped_addr =
malloc(::std::mem::size_of::<dc_hash_t>()) as *mut dc_hash_t;
dc_hash_init(gossipped_addr, 3i32, 1i32);
}
dc_hash_insert(
gossipped_addr,
(*gossip_header).addr as *const libc::c_void,
strlen((*gossip_header).addr) as libc::c_int,
1i32 as *mut libc::c_void,
);
} else {
dc_log_info(
context,
0i32,
b"Ignoring gossipped \"%s\" as the address is not in To/Cc list.\x00"
as *const u8 as *const libc::c_char,
(*gossip_header).addr,
);
}
}
dc_aheader_unref(gossip_header);
}
}
cur1 = if !cur1.is_null() {

View File

@@ -224,7 +224,7 @@ pub unsafe fn dc_hash_insert(
/* Link an element into the hash table
*/
unsafe extern "C" fn insertElement(
unsafe fn insertElement(
mut pH: *mut dc_hash_t,
mut pEntry: *mut _ht,
mut pNew: *mut dc_hashelem_t,

View File

@@ -303,7 +303,7 @@ unsafe fn dc_suspend_smtp_thread(context: &dc_context_t, suspend: libc::c_int) {
}
}
}
unsafe extern "C" fn dc_job_do_DC_JOB_SEND(context: &dc_context_t, job: &mut dc_job_t) {
unsafe fn dc_job_do_DC_JOB_SEND(context: &dc_context_t, job: &mut dc_job_t) {
let mut current_block: u64;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;

View File

@@ -86,10 +86,7 @@ pub unsafe fn dc_jobthread_suspend(
}
}
pub unsafe extern "C" fn dc_jobthread_interrupt_idle(
context: &dc_context_t,
jobthread: &dc_jobthread_t,
) {
pub unsafe fn dc_jobthread_interrupt_idle(context: &dc_context_t, jobthread: &dc_jobthread_t) {
{
jobthread.state.clone().0.lock().unwrap().jobs_needed = 1;
}

View File

@@ -118,7 +118,7 @@ pub unsafe fn dc_key_set_from_key(key: *mut dc_key_t, o: *const dc_key_t) -> lib
}
// TODO should return bool /rtn
pub unsafe extern "C" fn dc_key_set_from_stmt(
pub unsafe fn dc_key_set_from_stmt(
key: *mut dc_key_t,
stmt: *mut sqlite3_stmt,
index: libc::c_int,
@@ -297,11 +297,8 @@ pub unsafe fn dc_key_load_self_private(
success
}
/* the result must be freed */
pub fn dc_key_render_base64(key: *const dc_key_t, break_every: usize) -> *mut libc::c_char {
if key.is_null() {
return std::ptr::null_mut();
}
pub fn dc_key_render_base64_string(key: *const dc_key_t, break_every: usize) -> String {
assert!(!key.is_null(), "missing key");
let key = unsafe { *key };
let bytes = unsafe { slice::from_raw_parts(key.binary as *const u8, key.bytes as usize) };
@@ -318,7 +315,7 @@ pub fn dc_key_render_base64(key: *const dc_key_t, break_every: usize) -> *mut li
};
let encoded = base64::encode(&buf);
let res = encoded
encoded
.as_bytes()
.chunks(break_every)
.fold(String::new(), |mut res, buf| {
@@ -326,8 +323,14 @@ pub fn dc_key_render_base64(key: *const dc_key_t, break_every: usize) -> *mut li
res += unsafe { std::str::from_utf8_unchecked(buf) };
res += " ";
res
});
})
.trim()
.to_string()
}
/* the result must be freed */
pub fn dc_key_render_base64(key: *const dc_key_t, break_every: usize) -> *mut libc::c_char {
let res = dc_key_render_base64_string(key, break_every);
let res_c = CString::new(res.trim()).unwrap();
// need to use strdup to allocate the result with malloc

View File

@@ -129,7 +129,7 @@ unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &dc_context_t, flags: libc::c_i
};
}
pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
pub unsafe fn dc_is_sending_locations_to_chat(
context: &dc_context_t,
chat_id: uint32_t,
) -> libc::c_int {

View File

@@ -469,7 +469,7 @@ unsafe fn jsondup(json: *const libc::c_char, tok: *mut jsmntok_t) -> *mut libc::
strdup(b"\x00" as *const u8 as *const libc::c_char)
}
unsafe extern "C" fn jsoneq(
unsafe fn jsoneq(
json: *const libc::c_char,
tok: *mut jsmntok_t,
s: *const libc::c_char,

View File

@@ -20,10 +20,7 @@ pub type dc_saxparser_starttag_cb_t = Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: *mut *mut libc::c_char) -> (),
>;
pub unsafe extern "C" fn dc_saxparser_init(
mut saxparser: *mut dc_saxparser_t,
userdata: *mut libc::c_void,
) {
pub unsafe fn dc_saxparser_init(mut saxparser: *mut dc_saxparser_t, userdata: *mut libc::c_void) {
(*saxparser).userdata = userdata;
(*saxparser).starttag_cb = Some(def_starttag_cb);
(*saxparser).endtag_cb = Some(def_endtag_cb);

View File

@@ -24,7 +24,7 @@ pub fn isdigit(mut _c: libc::c_int) -> libc::c_int {
}
}
pub unsafe extern "C" fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
let mut pstr: *const libc::c_char = to_encode;
if to_encode.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);

View File

@@ -266,7 +266,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li
}
/* remove all \r characters from string */
pub unsafe extern "C" fn dc_remove_cr_chars(buf: *mut libc::c_char) {
pub unsafe fn dc_remove_cr_chars(buf: *mut libc::c_char) {
/* search for first `\r` */
let mut p1: *const libc::c_char = buf;
while 0 != *p1 {

File diff suppressed because one or more lines are too long