diff --git a/Cargo.toml b/Cargo.toml index 80f68ab1d..bb6698ab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,8 @@ chrono = "0.4.6" [dev-dependencies] tempfile = "3.0" +pretty_assertions = "0.6.1" +pretty_env_logger = "0.3.0" [workspace] members = [ diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 7d55d79b1..1a345599e 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -481,6 +481,11 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char let arg1 = args.next().unwrap_or_default(); let arg1_c = CString::new(arg1).unwrap(); + let arg1_c_ptr = if arg1.is_empty() { + std::ptr::null() + } else { + arg1_c.as_ptr() + }; let arg2 = args.next().unwrap_or_default(); let arg2_c = CString::new(arg2).unwrap(); @@ -514,7 +519,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else if strcmp(cmd, b"open\x00" as *const u8 as *const libc::c_char) == 0i32 { if !arg1.is_empty() { dc_close(context); - ret = if 0 != dc_open(context, arg1_c.as_ptr(), 0 as *const libc::c_char) { + ret = if 0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char) { 2i32 as *mut libc::c_char } else { 1i32 as *mut libc::c_char @@ -613,7 +618,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char ) == 0i32 { if !arg1.is_empty() { - dc_imex(context, 12i32, arg1_c.as_ptr(), 0 as *const libc::c_char); + dc_imex(context, 12i32, arg1_c_ptr, 0 as *const libc::c_char); ret = 2i32 as *mut libc::c_char } else { ret = dc_strdup( @@ -665,7 +670,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char free(file_name as *mut libc::c_void); free(setup_code_0 as *mut libc::c_void); } else if strcmp(cmd, b"poke\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = if 0 != poke_spec(context, arg1_c.as_ptr()) { + ret = if 0 != poke_spec(context, arg1_c_ptr) { 2i32 as *mut libc::c_char } else { 1i32 as *mut libc::c_char @@ -694,7 +699,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char ret = 2i32 as *mut libc::c_char } else if strcmp(cmd, b"set\x00" as *const u8 as *const libc::c_char) == 0i32 { if !arg1.is_empty() { - ret = if 0 != dc_set_config(context, arg1_c.as_ptr(), arg2_c.as_ptr()) { + ret = if 0 != dc_set_config(context, arg1_c_ptr, arg2_c.as_ptr()) { 2i32 as *mut libc::c_char } else { 1i32 as *mut libc::c_char @@ -705,10 +710,10 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } } else if strcmp(cmd, b"get\x00" as *const u8 as *const libc::c_char) == 0i32 { if !arg1.is_empty() { - let val: *mut libc::c_char = dc_get_config(context, arg1_c.as_ptr()); + let val: *mut libc::c_char = dc_get_config(context, arg1_c_ptr); ret = dc_mprintf( b"%s=%s\x00" as *const u8 as *const libc::c_char, - arg1_c.as_ptr(), + arg1_c_ptr, val, ); free(val as *mut libc::c_void); @@ -739,7 +744,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char }; let chatlist: *mut dc_chatlist_t = - dc_get_chatlist(context, listflags, arg1_c.as_ptr(), 0i32 as uint32_t); + dc_get_chatlist(context, listflags, arg1_c_ptr, 0i32 as uint32_t); if !chatlist.is_null() { let mut i: libc::c_int; let cnt: libc::c_int = dc_chatlist_get_cnt(chatlist) as libc::c_int; @@ -943,7 +948,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else if strcmp(cmd, b"creategroup\x00" as *const u8 as *const libc::c_char) == 0i32 { if !arg1.is_empty() { let chat_id_1: libc::c_int = - dc_create_group_chat(context, 0i32, arg1_c.as_ptr()) as libc::c_int; + dc_create_group_chat(context, 0i32, arg1_c_ptr) as libc::c_int; ret = if chat_id_1 != 0i32 { dc_mprintf( b"Group#%lu created successfully.\x00" as *const u8 as *const libc::c_char, @@ -964,7 +969,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char { if !arg1.is_empty() { let chat_id_2: libc::c_int = - dc_create_group_chat(context, 1i32, arg1_c.as_ptr()) as libc::c_int; + dc_create_group_chat(context, 1i32, arg1_c_ptr) as libc::c_int; ret = if chat_id_2 != 0i32 { dc_mprintf( b"VerifiedGroup#%lu created successfully.\x00" as *const u8 @@ -1034,7 +1039,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else if strcmp(cmd, b"groupname\x00" as *const u8 as *const libc::c_char) == 0i32 { if !sel_chat.is_null() { if !arg1.is_empty() { - ret = if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c.as_ptr()) { + ret = if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { 2i32 as *mut libc::c_char } else { 1i32 as *mut libc::c_char @@ -1054,7 +1059,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char context, dc_chat_get_id(sel_chat), if !arg1.is_empty() { - arg1_c.as_ptr() + arg1_c_ptr } else { 0 as *mut libc::c_char }, @@ -1179,7 +1184,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else if strcmp(cmd, b"send\x00" as *const u8 as *const libc::c_char) == 0i32 { if !sel_chat.is_null() { if !arg1.is_empty() { - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), arg1_c.as_ptr()) { + if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { ret = dc_strdup(b"Message sent.\x00" as *const u8 as *const libc::c_char) } else { ret = @@ -1220,7 +1225,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char 60i32 }, ); - dc_msg_set_file(msg_0, arg1_c.as_ptr(), 0 as *const libc::c_char); + dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char); dc_msg_set_text(msg_0, arg2_c.as_ptr()); dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); dc_msg_unref(msg_0); @@ -1240,7 +1245,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else { 0i32 as libc::c_uint }, - arg1_c.as_ptr(), + arg1_c_ptr, ); if !msglist_0.is_null() { log_msglist(context, msglist_0); @@ -1259,7 +1264,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char if !sel_chat.is_null() { if !arg1.is_empty() { let draft_0: *mut dc_msg_t = dc_msg_new(context, 10i32); - dc_msg_set_text(draft_0, arg1_c.as_ptr()); + dc_msg_set_text(draft_0, arg1_c_ptr); dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0); dc_msg_unref(draft_0); ret = dc_strdup(b"Draft saved.\x00" as *const u8 as *const libc::c_char) @@ -1417,7 +1422,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } else { 0x2i32 }) as uint32_t, - arg1_c.as_ptr(), + arg1_c_ptr, ); if !contacts_0.is_null() { log_contactlist(context, contacts_0); @@ -1433,14 +1438,14 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char if !arg1.is_empty() && !arg2.is_empty() { let book: *mut libc::c_char = dc_mprintf( b"%s\n%s\x00" as *const u8 as *const libc::c_char, - arg1_c.as_ptr(), + arg1_c_ptr, arg2_c.as_ptr(), ); dc_add_address_book(context, book); ret = 2i32 as *mut libc::c_char; free(book as *mut libc::c_void); } else if !arg1.is_empty() { - ret = if 0 != dc_create_contact(context, 0 as *const libc::c_char, arg1_c.as_ptr()) { + ret = if 0 != dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) { 2i32 as *mut libc::c_char } else { 1i32 as *mut libc::c_char @@ -1544,7 +1549,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char } } else if strcmp(cmd, b"checkqr\x00" as *const u8 as *const libc::c_char) == 0i32 { if !arg1.is_empty() { - let res: *mut dc_lot_t = dc_check_qr(context, arg1_c.as_ptr()); + let res: *mut dc_lot_t = dc_check_qr(context, arg1_c_ptr); ret = dc_mprintf( b"state=%i, id=%i, text1=%s, text2=%s\x00" as *const u8 as *const libc::c_char, (*res).state as libc::c_int, @@ -1588,7 +1593,7 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char if 0 != dc_read_file( context, - arg1_c.as_ptr(), + arg1_c_ptr, &mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void, &mut buf_bytes, ) { diff --git a/examples/repl/main.rs b/examples/repl/main.rs index a358a5e24..61a5ed4cd 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -418,6 +418,8 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int } pub fn main() { + let _ = pretty_env_logger::try_init(); + let mut args: Vec<*mut libc::c_char> = Vec::new(); for arg in ::std::env::args() { args.push( diff --git a/src/aheader.rs b/src/aheader.rs index 180077a0a..a72d95977 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -47,6 +47,7 @@ impl str::FromStr for EncryptPreference { } /// Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header). +#[derive(Debug)] pub struct Aheader { pub addr: String, pub public_key: Key, diff --git a/src/context.rs b/src/context.rs index 6c9cdeba9..f92df66c9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -47,7 +47,7 @@ pub struct Context { unsafe impl std::marker::Send for Context {} unsafe impl std::marker::Sync for Context {} -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct RunningState { pub ongoing_running: bool, pub shall_stop_ongoing: bool, @@ -80,7 +80,7 @@ impl Default for RunningState { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct BobStatus { pub expects: i32, pub status: i32, diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 919480c06..df0d9f368 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -350,23 +350,27 @@ unsafe fn fingerprint_equals_sender( fingerprint: *const libc::c_char, contact_chat_id: uint32_t, ) -> libc::c_int { + if fingerprint.is_null() { + return 0; + } let mut fingerprint_equal: libc::c_int = 0i32; let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id); let contact: *mut dc_contact_t = dc_contact_new(context); if !(dc_array_get_cnt(contacts) != 1) { - let peerstate = Peerstate::from_addr( - context, - &context.sql.clone().read().unwrap(), - to_str((*contact).addr), - ); - if !(!dc_contact_load_from_db( + if !dc_contact_load_from_db( contact, &context.sql.clone().read().unwrap(), dc_array_get_id(contacts, 0i32 as size_t), - ) || peerstate.is_some()) - { - let peerstate = peerstate.as_ref().unwrap(); + ) { + return 0; + } + + if let Some(peerstate) = Peerstate::from_addr( + context, + &context.sql.clone().read().unwrap(), + to_str((*contact).addr), + ) { let fingerprint_normalized = dc_normalize_fingerprint(to_str(fingerprint)); if peerstate.public_key_fingerprint.is_some() && &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap() diff --git a/src/key.rs b/src/key.rs index 87b2a334e..99570a4ec 100644 --- a/src/key.rs +++ b/src/key.rs @@ -99,21 +99,17 @@ impl Key { match res { Ok(key) => Some(key), Err(err) => { - eprintln!("Invalid key bytes: {:?}\n{}", err, hex::encode(bytes)); + eprintln!("Invalid key bytes: {:?}", err); None } } } - pub fn from_binary( - data: *const libc::c_void, - len: libc::c_int, - key_type: KeyType, - ) -> Option { + pub fn from_binary(data: *const u8, len: libc::c_int, key_type: KeyType) -> Option { assert!(!data.is_null(), "missing data"); assert!(len > 0); - let bytes = unsafe { slice::from_raw_parts(data as *const u8, len as usize) }; + let bytes = unsafe { slice::from_raw_parts(data, len as usize) }; Self::from_slice(bytes, key_type) } @@ -124,9 +120,7 @@ impl Key { ) -> Option { assert!(!stmt.is_null(), "missing statement"); - let data = unsafe { - sqlite3_column_blob(stmt, index) as *mut libc::c_uchar as *const libc::c_void - }; + let data = unsafe { sqlite3_column_blob(stmt, index) as *const u8 }; let len = unsafe { sqlite3_column_bytes(stmt, index) }; Self::from_binary(data, len, key_type) diff --git a/src/peerstate.rs b/src/peerstate.rs index 21e058acb..c17959306 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::ffi::CString; +use std::fmt; use num_traits::FromPrimitive; @@ -30,6 +31,46 @@ pub struct Peerstate<'a> { pub degrade_event: Option, } +impl<'a> PartialEq for Peerstate<'a> { + fn eq(&self, other: &Peerstate) -> bool { + self.addr == other.addr + && self.last_seen == other.last_seen + && self.last_seen_autocrypt == other.last_seen_autocrypt + && self.prefer_encrypt == other.prefer_encrypt + && self.public_key == other.public_key + && self.public_key_fingerprint == other.public_key_fingerprint + && self.gossip_key == other.gossip_key + && self.gossip_timestamp == other.gossip_timestamp + && self.gossip_key_fingerprint == other.gossip_key_fingerprint + && self.verified_key == other.verified_key + && self.verified_key_fingerprint == other.verified_key_fingerprint + && self.to_save == other.to_save + && self.degrade_event == other.degrade_event + } +} + +impl<'a> Eq for Peerstate<'a> {} + +impl<'a> fmt::Debug for Peerstate<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Peerstate") + .field("addr", &self.addr) + .field("last_seen", &self.last_seen) + .field("last_seen_autocrypt", &self.last_seen_autocrypt) + .field("prefer_encrypt", &self.prefer_encrypt) + .field("public_key", &self.public_key) + .field("public_key_fingerprint", &self.public_key_fingerprint) + .field("gossip_key", &self.gossip_key) + .field("gossip_timestamp", &self.gossip_timestamp) + .field("gossip_key_fingerprint", &self.gossip_key_fingerprint) + .field("verified_key", &self.verified_key) + .field("verified_key_fingerprint", &self.verified_key_fingerprint) + .field("to_save", &self.to_save) + .field("degrade_event", &self.degrade_event) + .finish() + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] #[repr(u8)] pub enum ToSave { @@ -46,7 +87,7 @@ pub enum DegradeEvent { FingerprintChanged = 0x02, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum VerifiedKey { Gossip, Public, @@ -187,15 +228,12 @@ impl<'a> Peerstate<'a> { res.prefer_encrypt = EncryptPreference::from_i32(unsafe { sqlite3_column_int(stmt, 3) }).unwrap_or_default(); res.gossip_timestamp = unsafe { sqlite3_column_int(stmt, 5) } as u64; - res.public_key_fingerprint = Some(to_string(unsafe { - sqlite3_column_text(stmt, 7) as *const _ - })); - res.gossip_key_fingerprint = Some(to_string(unsafe { - sqlite3_column_text(stmt, 8) as *const _ - })); - res.verified_key_fingerprint = Some(to_string(unsafe { - sqlite3_column_text(stmt, 10) as *const _ - })); + let pkf = to_string(unsafe { sqlite3_column_text(stmt, 7) as *const _ }); + res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) }; + let gkf = to_string(unsafe { sqlite3_column_text(stmt, 8) as *const _ }); + res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) }; + let vkf = to_string(unsafe { sqlite3_column_text(stmt, 10) as *const _ }); + res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) }; if unsafe { sqlite3_column_type(stmt, 4) } != 5 { res.public_key = Key::from_stmt(stmt, 4, KeyType::Public); @@ -382,7 +420,7 @@ impl<'a> Peerstate<'a> { as *const libc::c_char, ) }; - let addr_c = CString::new(self.addr.as_ref().unwrap().as_bytes()).unwrap(); + let addr_c = to_cstring(self.addr.as_ref().unwrap()); unsafe { sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None); sqlite3_step(stmt); @@ -393,103 +431,104 @@ impl<'a> Peerstate<'a> { if self.to_save == Some(ToSave::All) || create { let stmt = unsafe { dc_sqlite3_prepare( - self.context,sql, - b"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, verified_key=?, verified_key_fingerprint=? WHERE addr=?;\x00" - as *const u8 as *const libc::c_char) + self.context, sql, + b"UPDATE acpeerstates \ + SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \ + public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, verified_key=?, verified_key_fingerprint=? \ + WHERE addr=?;\x00" + as *const u8 as *const libc::c_char + ) }; - unsafe { - sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64); - sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64); - sqlite3_bind_int64(stmt, 3, self.prefer_encrypt as sqlite3_int64); - } + unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) }; + unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) }; + unsafe { sqlite3_bind_int64(stmt, 3, self.prefer_encrypt as sqlite3_int64) }; - let addr_c = self.addr.as_ref().map(|addr| to_cstring(addr)); - let pub_bytes = self.public_key.as_ref().map(|k| k.to_bytes()); - let gossip_bytes = self.gossip_key.as_ref().map(|k| k.to_bytes()); - let ver_bytes = self.verified_key().map(|k| k.to_bytes()); + let pub_bytes = self + .public_key + .as_ref() + .map(|k| k.to_bytes()) + .unwrap_or_default(); + let gossip_bytes = self + .gossip_key + .as_ref() + .map(|k| k.to_bytes()) + .unwrap_or_default(); + let ver_bytes = self + .verified_key() + .as_ref() + .map(|k| k.to_bytes()) + .unwrap_or_default(); + + let pkc = self + .public_key_fingerprint + .as_ref() + .map(to_cstring) + .unwrap_or_default(); + + let pkc_ptr = if self.public_key_fingerprint.is_some() { + pkc.as_ptr() + } else { + std::ptr::null() + }; + + let gkc = self + .gossip_key_fingerprint + .as_ref() + .map(to_cstring) + .unwrap_or_default(); + + let gkc_ptr = if self.gossip_key_fingerprint.is_some() { + gkc.as_ptr() + } else { + std::ptr::null_mut() + }; + let vkc = self + .verified_key_fingerprint + .as_ref() + .map(to_cstring) + .unwrap_or_default(); + let vkc_ptr = if self.verified_key_fingerprint.is_some() { + vkc.as_ptr() + } else { + std::ptr::null_mut() + }; + let addr: String = self.addr.clone().unwrap_or_default(); + let addr_c = to_cstring(addr); unsafe { sqlite3_bind_blob( stmt, 4, - pub_bytes - .as_ref() - .map(|b| b.as_ptr()) - .unwrap_or_else(|| std::ptr::null()) as *const _, - pub_bytes.as_ref().map(|b| b.len()).unwrap_or_else(|| 0) as libc::c_int, - None, - ); - sqlite3_bind_int64(stmt, 5, self.gossip_timestamp as sqlite3_int64); + pub_bytes.as_ptr() as *const _, + pub_bytes.len() as libc::c_int, + SQLITE_TRANSIENT(), + ) + }; + unsafe { sqlite3_bind_int64(stmt, 5, self.gossip_timestamp as sqlite3_int64) }; + unsafe { sqlite3_bind_blob( stmt, 6, - gossip_bytes - .as_ref() - .map(|b| b.as_ptr()) - .unwrap_or_else(|| std::ptr::null()) as *const _, - gossip_bytes.as_ref().map(|b| b.len()).unwrap_or_else(|| 0) as libc::c_int, - None, - ); - let pkc = self - .public_key_fingerprint - .as_ref() - .map(|fp| to_cstring(fp)); - let gkc = self - .gossip_key_fingerprint - .as_ref() - .map(|fp| to_cstring(fp)); - - sqlite3_bind_text( - stmt, - 7, - pkc.map(|fp| fp.as_ptr()) - .unwrap_or_else(|| std::ptr::null()), - -1, - None, - ); - sqlite3_bind_text( - stmt, - 8, - gkc.map(|fp| fp.as_ptr()) - .unwrap_or_else(|| std::ptr::null()), - -1, - None, - ); + gossip_bytes.as_ptr() as *const _, + gossip_bytes.len() as libc::c_int, + SQLITE_TRANSIENT(), + ) + }; + unsafe { sqlite3_bind_text(stmt, 7, pkc_ptr as *const _, -1, SQLITE_TRANSIENT()) }; + unsafe { sqlite3_bind_text(stmt, 8, gkc_ptr as *const _, -1, SQLITE_TRANSIENT()) }; + unsafe { sqlite3_bind_blob( stmt, 9, - ver_bytes - .as_ref() - .map(|b| b.as_ptr()) - .unwrap_or_else(|| std::ptr::null()) as *const _, - ver_bytes.as_ref().map(|b| b.len()).unwrap_or_else(|| 0) as libc::c_int, - None, - ); + ver_bytes.as_ptr() as *const _, + ver_bytes.len() as libc::c_int, + SQLITE_TRANSIENT(), + ) + }; - let vkc = self - .verified_key_fingerprint - .as_ref() - .map(|fp| to_cstring(fp)); - - sqlite3_bind_text( - stmt, - 10, - vkc.map(|fp| fp.as_ptr()) - .unwrap_or_else(|| std::ptr::null()), - -1, - None, - ); - sqlite3_bind_text( - stmt, - 11, - addr_c - .map(|addr| addr.as_ptr()) - .unwrap_or_else(|| std::ptr::null()), - -1, - None, - ); - } + unsafe { sqlite3_bind_text(stmt, 10, vkc_ptr as *const _, -1, SQLITE_TRANSIENT()) }; + unsafe { sqlite3_bind_text(stmt, 11, addr_c.as_ptr(), -1, SQLITE_TRANSIENT()) }; if unsafe { sqlite3_step(stmt) } == 101 { success = true; @@ -506,10 +545,10 @@ impl<'a> Peerstate<'a> { let addr_c = self.addr.as_ref().map(|fp| to_cstring(fp)); + unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) }; + unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) }; + unsafe { sqlite3_bind_int64(stmt, 3, self.gossip_timestamp as sqlite3_int64) }; unsafe { - sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64); - sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64); - sqlite3_bind_int64(stmt, 3, self.gossip_timestamp as sqlite3_int64); sqlite3_bind_text( stmt, 4, @@ -517,9 +556,9 @@ impl<'a> Peerstate<'a> { .map(|addr| addr.as_ptr()) .unwrap_or_else(|| std::ptr::null()), -1, - None, - ); - } + SQLITE_TRANSIENT(), + ) + }; if unsafe { sqlite3_step(stmt) } == 101 { success = true; @@ -546,3 +585,84 @@ impl<'a> Peerstate<'a> { false } } + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + use std::ffi::CStr; + use tempfile::{tempdir, TempDir}; + + use crate::context::*; + + #[test] + fn test_peerstate_save_to_db() { + let ctx = unsafe { create_test_context() }; + let addr = "hello@mail.com"; + + let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap(); + + let mut peerstate = Peerstate { + context: &ctx.ctx, + addr: Some(addr.into()), + last_seen: 10, + last_seen_autocrypt: 11, + prefer_encrypt: EncryptPreference::Mutual, + public_key: Some(pub_key.clone()), + public_key_fingerprint: Some(pub_key.fingerprint()), + gossip_key: Some(pub_key.clone()), + gossip_timestamp: 12, + gossip_key_fingerprint: Some(pub_key.fingerprint()), + verified_key: VerifiedKey::Gossip, + verified_key_fingerprint: Some(pub_key.fingerprint()), + to_save: Some(ToSave::All), + degrade_event: None, + }; + + assert!( + peerstate.save_to_db(&ctx.ctx.sql.clone().read().unwrap(), true), + "failed to save" + ); + + let peerstate_new = + Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql.clone().read().unwrap(), addr.into()) + .expect("failed to load peerstate from db"); + + // clear to_save, as that is not persissted + peerstate.to_save = None; + assert_eq!(peerstate, peerstate_new); + } + + // TODO: don't copy this from stress.rs + #[allow(dead_code)] + struct TestContext { + ctx: Context, + dir: TempDir, + } + + unsafe extern "C" fn cb( + _context: &Context, + _event: Event, + _data1: uintptr_t, + _data2: uintptr_t, + ) -> uintptr_t { + 0 + } + + unsafe fn create_test_context() -> TestContext { + let mut ctx = dc_context_new(cb, std::ptr::null_mut(), std::ptr::null_mut()); + let dir = tempdir().unwrap(); + let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap(); + assert_eq!( + dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()), + 1, + "Failed to open {}", + CStr::from_ptr(dbfile.as_ptr() as *const libc::c_char) + .to_str() + .unwrap() + ); + + TestContext { ctx: ctx, dir: dir } + } +} diff --git a/tests/stress.rs b/tests/stress.rs index e4f11b519..8ab4f463f 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -2213,8 +2213,7 @@ fn test_encryption_decryption() { while j < 4096 / 40 { let bad_key = Key::from_binary( - &mut *bad_data.as_mut_ptr().offset(j as isize) as *mut libc::c_uchar - as *const libc::c_void, + &mut *bad_data.as_mut_ptr().offset(j as isize) as *const u8, 4096 / 2 + j, if 0 != j & 1 { KeyType::Public