diff --git a/.circleci/config.yml b/.circleci/config.yml index 1c3964763..f97c61b61 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -127,26 +127,26 @@ jobs: machine: True steps: - checkout - # - run: docker pull deltachat/doxygen + # - run: docker pull deltachat/doxygen - run: docker pull deltachat/coredeps - - run: - name: build docs, run tests and build wheels + - run: + name: build docs, run tests and build wheels command: ci_scripts/ci_run.sh environment: TESTS: 1 DOCS: 1 - - run: - name: copying docs and wheels to workspace + - run: + name: copying docs and wheels to workspace command: | mkdir -p workspace/python # cp -av docs workspace/c-docs cp -av python/.docker-tox/wheelhouse workspace/ cp -av python/doc/_build/ workspace/py-docs - - persist_to_workspace: - root: workspace - paths: + - persist_to_workspace: + root: workspace + paths: # - c-docs - py-docs - wheelhouse @@ -157,7 +157,7 @@ jobs: - checkout - attach_workspace: at: workspace - - run: ls -laR workspace + - run: ls -laR workspace - run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse @@ -175,12 +175,17 @@ workflows: requires: - cargo_fetch - # Linux Desktop + # Linux Desktop 64bit - test_x86_64-unknown-linux-gnu: requires: - cargo_fetch - # Linux Desktop + # Linux Desktop 32bit + # - test_i686-unknown-linux-gnu: + # requires: + # - cargo_fetch + + # Android 64bit # - test_aarch64-linux-android: # requires: # - cargo_fetch diff --git a/.gitignore b/.gitignore index 8545081e9..2fa6b7a8d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ python/.tox *.egg-info __pycache__ python/src/deltachat/capi*.so + +python/liveconfig* diff --git a/Cargo.toml b/Cargo.toml index a8cbd89b2..1c6c72668 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ hex = "0.3.2" sha2 = "0.8.0" rand = "0.6.5" smallvec = "0.6.9" -libsqlite3-sys = { version = "0.14.0", features = ["bundled", "min_sqlite_version_3_7_16"] } reqwest = "0.9.15" num-derive = "0.2.5" num-traits = "0.2.6" @@ -36,6 +35,12 @@ failure_derive = "0.1.5" rustyline = "4.1.0" lazy_static = "1.3.0" regex = "1.1.6" +rusqlite = { version = "0.19", features = ["bundled"] } +addr = "0.2.0" +r2d2_sqlite = "0.11.0" +r2d2 = "0.8.5" +strum = "0.15.0" +strum_macros = "0.15.0" [dev-dependencies] tempfile = "3.0" diff --git a/appveyor.yml b/appveyor.yml index 8e3866a34..23a513f60 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,10 @@ environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - TARGET: x86_64-pc-windows-msvc install: - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - rustup-init -yv --default-host %target% --default-toolchain none + - rustup-init -yv --default-toolchain nightly-2019-07-10 - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustc -vV - cargo -vV @@ -14,7 +13,7 @@ install: build: false test_script: -- cargo test --release + - cargo test --release cache: - target diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 7925c5de7..a477a59b1 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -10,6 +10,8 @@ #[macro_use] extern crate human_panic; +use std::str::FromStr; + use deltachat::*; // TODO: constants @@ -88,9 +90,13 @@ pub unsafe extern "C" fn dc_set_config( value: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(!key.is_null(), "invalid key"); let context = &*context; - context::dc_set_config(context, key, value) + match config::Config::from_str(dc_tools::as_str(key)) { + Ok(key) => context.set_config(key, as_opt_str(value)).is_ok() as libc::c_int, + Err(_) => 0, + } } #[no_mangle] @@ -99,9 +105,16 @@ pub unsafe extern "C" fn dc_get_config( key: *mut libc::c_char, ) -> *mut libc::c_char { assert!(!context.is_null()); + assert!(!key.is_null(), "invalid key"); let context = &*context; - context::dc_get_config(context, key) + match config::Config::from_str(dc_tools::as_str(key)) { + Ok(key) => { + let value = context.get_config(key).unwrap_or_default(); + into_cstring(value) + } + Err(_) => std::ptr::null_mut(), + } } #[no_mangle] @@ -411,7 +424,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id assert!(!context.is_null()); let context = &*context; - dc_chat::dc_marknoticed_chat(context, chat_id) + dc_chat::dc_marknoticed_chat(context, chat_id); } #[no_mangle] @@ -419,7 +432,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - dc_chat::dc_marknoticed_all_chats(context) + dc_chat::dc_marknoticed_all_chats(context); } #[no_mangle] @@ -460,7 +473,7 @@ pub unsafe extern "C" fn dc_archive_chat( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_archive_chat(context, chat_id, archive) + dc_chat::dc_archive_chat(context, chat_id, archive); } #[no_mangle] @@ -468,7 +481,8 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 assert!(!context.is_null()); let context = &*context; - dc_chat::dc_delete_chat(context, chat_id) + // TODO: update to indiciate public api success/failure of deletion + dc_chat::dc_delete_chat(context, chat_id); } #[no_mangle] @@ -641,7 +655,7 @@ pub unsafe extern "C" fn dc_markseen_msgs( assert!(!context.is_null()); let context = &*context; - dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt) + dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt as usize); } #[no_mangle] @@ -654,7 +668,7 @@ pub unsafe extern "C" fn dc_star_msgs( assert!(!context.is_null()); let context = &*context; - dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star) + dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star); } #[no_mangle] @@ -887,7 +901,7 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat( assert!(!context.is_null()); let context = &*context; - dc_location::dc_is_sending_locations_to_chat(context, chat_id) + dc_location::dc_is_sending_locations_to_chat(context, chat_id) as libc::c_int } #[no_mangle] @@ -928,7 +942,7 @@ pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - dc_location::dc_delete_all_locations(context) + dc_location::dc_delete_all_locations(context); } // dc_array_t @@ -1337,7 +1351,7 @@ pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg::dc_msg_t) -> lib #[no_mangle] pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { - dc_msg::dc_msg_is_setupmessage(msg) + dc_msg::dc_msg_is_setupmessage(msg) as libc::c_int } #[no_mangle] @@ -1524,3 +1538,15 @@ pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64 pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) { libc::free(s as *mut _) } + +fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> { + if s.is_null() { + return None; + } + + Some(dc_tools::as_str(s)) +} + +unsafe fn into_cstring(s: impl AsRef) -> *mut libc::c_char { + dc_tools::dc_strdup(dc_tools::to_cstring(s).as_ptr()) +} diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index ac78e053e..3138be865 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1,3 +1,6 @@ +use std::str::FromStr; + +use deltachat::config; use deltachat::constants::*; use deltachat::context::*; use deltachat::dc_array::*; @@ -12,9 +15,9 @@ use deltachat::dc_lot::*; use deltachat::dc_msg::*; use deltachat::dc_qr::*; use deltachat::dc_receive_imf::*; -use deltachat::dc_sqlite3::*; use deltachat::dc_tools::*; use deltachat::peerstate::*; +use deltachat::sql; use deltachat::types::*; use deltachat::x::*; use num_traits::FromPrimitive; @@ -25,65 +28,58 @@ use num_traits::FromPrimitive; pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 { info!(context, 0, "Resetting tables ({})...", bits); if 0 != bits & 1 { - dc_sqlite3_execute( - context, - &context.sql, - b"DELETE FROM jobs;\x00" as *const u8 as *const libc::c_char, - ); + sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]); info!(context, 0, "(1) Jobs reset."); } if 0 != bits & 2 { - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM acpeerstates;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM acpeerstates;", + params![], ); info!(context, 0, "(2) Peerstates reset."); } if 0 != bits & 4 { - dc_sqlite3_execute( - context, - &context.sql, - b"DELETE FROM keypairs;\x00" as *const u8 as *const libc::c_char, - ); + sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]); info!(context, 0, "(4) Private keypairs reset."); } if 0 != bits & 8 { - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM contacts WHERE id>9;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM contacts WHERE id>9;", + params![], ); - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM chats WHERE id>9;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM chats WHERE id>9;", + params![], ); - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM chats_contacts;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM chats_contacts;", + params![], ); - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM msgs WHERE id>9;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM msgs WHERE id>9;", + params![], ); - dc_sqlite3_execute( + sql::execute( context, &context.sql, - b"DELETE FROM config WHERE keyname LIKE \'imap.%\' OR keyname LIKE \'configured%\';\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - &context.sql, - b"DELETE FROM leftgrps;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';", + params![], ); + sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]); info!(context, 0, "(8) Rest but server config reset."); } - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MSGS_CHANGED, 0, 0); 1 } @@ -100,14 +96,7 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> &mut data_bytes, ) == 0i32) { - dc_receive_imf( - context, - data, - data_bytes, - b"import\x00" as *const u8 as *const libc::c_char, - 0i32 as uint32_t, - 0i32 as uint32_t, - ); + dc_receive_imf(context, data, data_bytes, "import", 0, 0); success = 1; } free(data as *mut libc::c_void); @@ -138,26 +127,19 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int /* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */ if !spec.is_null() { real_spec = dc_strdup(spec); - dc_sqlite3_set_config( - context, - &context.sql, - b"import_spec\x00" as *const u8 as *const libc::c_char, - real_spec, - ); + context + .sql + .set_config(context, "import_spec", Some(as_str(real_spec))); current_block = 7149356873433890176; } else { - real_spec = dc_sqlite3_get_config( - context, - &context.sql, - b"import_spec\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if real_spec.is_null() { + let rs = context.sql.get_config(context, "import_spec"); + if rs.is_none() { error!(context, 0, "Import: No file or folder given."); current_block = 8522321847195001863; } else { current_block = 7149356873433890176; } + real_spec = strdup(to_cstring(rs.unwrap_or_default()).as_ptr()); } match current_block { 8522321847195001863 => {} @@ -499,9 +481,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E }, "auth" => { if 0 == S_IS_AUTH { - let is_pw = - dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char); - if arg1 == as_str(is_pw) { + let is_pw = context + .get_config(config::Config::MailPw) + .unwrap_or_default(); + if arg1 == is_pw { S_IS_AUTH = 1; } else { println!("Bad password."); @@ -537,8 +520,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty(), "Argument missing."); let msg_id: u32 = arg1.parse().unwrap(); let msg: *mut dc_msg_t = dc_get_msg(context, msg_id); - if 0 != dc_msg_is_setupmessage(msg) { - let setupcodebegin: *mut libc::c_char = dc_msg_get_setupcodebegin(msg); + if dc_msg_is_setupmessage(msg) { + let setupcodebegin = dc_msg_get_setupcodebegin(msg); println!( "The setup code for setup message Msg#{} starts with: {}", msg_id, @@ -620,16 +603,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "set" => { ensure!(!arg1.is_empty(), "Argument missing."); - ensure!( - 0 != dc_set_config(context, arg1_c_ptr, arg2_c_ptr), - "Set config failed" - ); + let key = config::Config::from_str(&arg1)?; + let value = if arg2.is_empty() { None } else { Some(arg2) }; + context.set_config(key, value)?; } "get" => { ensure!(!arg1.is_empty(), "Argument missing."); - let val = dc_get_config(context, arg1_c_ptr); - println!("{}={}", arg1, to_string(val)); - free(val as *mut libc::c_void); + let key = config::Config::from_str(&arg1)?; + let val = context.get_config(key); + println!("{}={:?}", key, val); } "info" => { println!("{}", to_string(dc_get_info(context))); @@ -638,7 +620,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E dc_maybe_network(context); } "housekeeping" => { - dc_housekeeping(context); + sql::housekeeping(context); } "listchats" | "listarchived" | "chats" => { let listflags = if arg0 == "listarchived" { 0x01 } else { 0 }; @@ -714,7 +696,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E i -= 1 } } - if 0 != dc_is_sending_locations_to_chat(context, 0 as uint32_t) { + if dc_is_sending_locations_to_chat(context, 0 as uint32_t) { info!(context, 0, "Location streaming enabled."); } println!("{} chats", cnt); @@ -929,7 +911,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let seconds = arg1.parse().unwrap(); dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds); - println!("Locations will be sent to Chat#{} for {} seconds. Use \'setlocation \' to play around.", dc_chat_get_id(sel_chat), seconds); + println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation ' to play around.", dc_chat_get_id(sel_chat), seconds); } "setlocation" => { ensure!( diff --git a/examples/repl/main.rs b/examples/repl/main.rs index fc5c1433a..ea57e2d02 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -10,11 +10,14 @@ extern crate deltachat; extern crate failure; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate rusqlite; use std::borrow::Cow::{self, Borrowed, Owned}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; +use deltachat::config; use deltachat::constants::*; use deltachat::context::*; use deltachat::dc_configure::*; @@ -512,25 +515,20 @@ unsafe fn handle_cmd(line: &str, ctx: Arc>) -> Result { - let addr = dc_get_config( - &ctx.read().unwrap(), - b"addr\x00" as *const u8 as *const libc::c_char, - ); - if addr.is_null() || *addr.offset(0isize) as libc::c_int == 0i32 { - println!("oauth2: set addr first."); - } else { + if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) { let oauth2_url = dc_get_oauth2_url( &ctx.read().unwrap(), - as_str(addr), + &addr, "chat.delta:/com.b44t.messenger", ); if oauth2_url.is_none() { - println!("OAuth2 not available for {}.", to_string(addr)); + println!("OAuth2 not available for {}.", &addr); } else { println!("Open the following url, set mail_pw to the generated token and server_flags to 2:\n{}", oauth2_url.unwrap()); } + } else { + println!("oauth2: set addr first."); } - free(addr as *mut libc::c_void); } "clear" => { println!("\n\n\n"); diff --git a/examples/simple.rs b/examples/simple.rs index d7932b0f1..2213ec572 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock}; use std::{thread, time}; use tempfile::tempdir; +use deltachat::config; use deltachat::constants::Event; use deltachat::context::*; use deltachat::dc_chat::*; @@ -79,20 +80,14 @@ fn main() { println!("opening database {:?}", dbfile); - dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()); + assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1); println!("configuring"); - let pw = std::env::args().collect::>()[1].clone(); - dc_set_config( - &ctx, - CString::new("addr").unwrap().as_ptr(), - CString::new("d@testrun.org").unwrap().as_ptr(), - ); - dc_set_config( - &ctx, - CString::new("mail_pw").unwrap().as_ptr(), - CString::new(pw).unwrap().as_ptr(), - ); + let args = std::env::args().collect::>(); + assert_eq!(args.len(), 2, "missing password"); + let pw = args[1].clone(); + ctx.set_config(config::Config::Addr, Some("d@testrun.org")); + ctx.set_config(config::Config::MailPw, Some(&pw)); dc_configure(&ctx); thread::sleep(duration); @@ -127,8 +122,8 @@ fn main() { } dc_chatlist_unref(chats); - *running.clone().write().unwrap() = false; - println!("stopping threads"); + thread::sleep(duration); + // let msglist = dc_get_chat_msgs(&ctx, chat_id, 0, 0); // for i in 0..dc_array_get_cnt(msglist) { // let msg_id = dc_array_get_id(msglist, i); @@ -139,6 +134,9 @@ fn main() { // } // dc_array_unref(msglist); + println!("stopping threads"); + + *running.clone().write().unwrap() = false; deltachat::dc_job::dc_interrupt_imap_idle(&ctx); deltachat::dc_job::dc_interrupt_smtp_idle(&ctx); diff --git a/rust-toolchain b/rust-toolchain index cbba43503..3a7ebd2f9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-06-16 +nightly-2019-07-10 diff --git a/src/aheader.rs b/src/aheader.rs index a72d95977..35fcb25ca 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -1,5 +1,5 @@ use std::collections::BTreeMap; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::str::FromStr; use std::{fmt, str}; @@ -7,6 +7,7 @@ use mmime::mailimf_types::*; use crate::constants::*; use crate::dc_contact::*; +use crate::dc_tools::as_str; use crate::key::*; /// Possible values for encryption preference @@ -93,9 +94,7 @@ impl Aheader { 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 dc_addr_cmp(&test.addr, as_str(wanted_from)) { if fine_header.is_none() { fine_header = Some(test); } else { diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 000000000..b5a2e65a4 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,177 @@ +use strum::{EnumProperty, IntoEnumIterator}; +use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString}; + +use crate::constants::DC_VERSION_STR; +use crate::context::Context; +use crate::dc_job::*; +use crate::dc_stock::*; +use crate::dc_tools::*; +use crate::error::Error; +use crate::x::*; + +/// The available configuration keys. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty, +)] +#[strum(serialize_all = "snake_case")] +pub enum Config { + Addr, + MailServer, + MailUser, + MailPw, + MailPort, + SendServer, + SendUser, + SendPw, + SendPort, + ServerFlags, + #[strum(props(default = "INBOX"))] + ImapFolder, + Displayname, + Selfstatus, + Selfavatar, + #[strum(props(default = "1"))] + E2eeEnabled, + #[strum(props(default = "1"))] + MdnsEnabled, + InboxWatch, + #[strum(props(default = "1"))] + SentboxWatch, + #[strum(props(default = "1"))] + MvboxWatch, + #[strum(props(default = "1"))] + MvboxMove, + #[strum(props(default = "0"))] + ShowEmails, + SaveMimeHeaders, + ConfiguredAddr, + ConfiguredMailServer, + ConfiguredMailUser, + ConfiguredMailPw, + ConfiguredMailPort, + ConfiguredSendServer, + ConfiguredSendUser, + ConfiguredSendPw, + ConfiguredSendPort, + ConfiguredServerFlags, + Configured, + // Deprecated + #[strum(serialize = "sys.version")] + SysVersion, + #[strum(serialize = "sys.msgsize_max_recommended")] + SysMsgsizeMaxRecommended, + #[strum(serialize = "sys.config_keys")] + SysConfigKeys, +} + +impl Context { + /// Get a configuration key. Returns `None` if no value is set, and no default value found. + pub fn get_config(&self, key: Config) -> Option { + let value = match key { + Config::Selfavatar => { + let rel_path = self.sql.get_config(self, key); + rel_path.map(|p| { + let v = unsafe { dc_get_abs_path(self, to_cstring(p).as_ptr()) }; + let r = to_string(v); + unsafe { free(v as *mut _) }; + r + }) + } + Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()), + Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)), + Config::SysConfigKeys => Some(get_config_keys_string()), + _ => self.sql.get_config(self, key), + }; + + if value.is_some() { + return value; + } + + // Default values + match key { + Config::Selfstatus => { + let s = unsafe { dc_stock_str(self, 13) }; + let res = to_string(s); + unsafe { free(s as *mut _) }; + Some(res) + } + _ => key.get_str("default").map(|s| s.to_string()), + } + } + + /// Set the given config key. + /// If `None` is passed as a value the value is cleared and set to the deafult if there is one. + pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> { + match key { + Config::Selfavatar if value.is_some() => { + let rel_path = std::fs::canonicalize(value.unwrap())?; + self.sql + .set_config(self, key, Some(&rel_path.to_string_lossy())) + } + Config::InboxWatch => { + let ret = self.sql.set_config(self, key, value); + unsafe { dc_interrupt_imap_idle(self) }; + ret + } + Config::SentboxWatch => { + let ret = self.sql.set_config(self, key, value); + unsafe { dc_interrupt_sentbox_idle(self) }; + ret + } + Config::MvboxWatch => { + let ret = self.sql.set_config(self, key, value); + unsafe { dc_interrupt_mvbox_idle(self) }; + ret + } + Config::Selfstatus => { + let def = unsafe { dc_stock_str(self, 13) }; + let val = if value.is_none() || value.unwrap() == as_str(def) { + None + } else { + value + }; + + let ret = self.sql.set_config(self, key, val); + unsafe { free(def as *mut libc::c_void) }; + ret + } + _ => self.sql.set_config(self, key, value), + } + } +} + +/// Returns all available configuration keys concated together. +fn get_config_keys_string() -> String { + let keys = Config::iter().fold(String::new(), |mut acc, key| { + acc += key.as_ref(); + acc += " "; + acc + }); + + format!(" {} ", keys) +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::str::FromStr; + use std::string::ToString; + + #[test] + fn test_to_string() { + assert_eq!(Config::MailServer.to_string(), "mail_server"); + assert_eq!(Config::from_str("mail_server"), Ok(Config::MailServer)); + + assert_eq!(Config::SysConfigKeys.to_string(), "sys.config_keys"); + assert_eq!( + Config::from_str("sys.config_keys"), + Ok(Config::SysConfigKeys) + ); + } + + #[test] + fn test_default_prop() { + assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX")); + } +} diff --git a/src/context.rs b/src/context.rs index f564efc65..f21aea4f6 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,18 +6,16 @@ use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_jobthread::*; -use crate::dc_log::*; use crate::dc_loginparam::*; use crate::dc_lot::dc_lot_t; use crate::dc_move::*; use crate::dc_msg::*; use crate::dc_receive_imf::*; -use crate::dc_sqlite3::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::imap::*; use crate::key::*; use crate::smtp::*; +use crate::sql::Sql; use crate::types::*; use crate::x::*; @@ -26,7 +24,7 @@ pub struct Context { pub userdata: *mut libc::c_void, pub dbfile: Arc>, pub blobdir: Arc>, - pub sql: SQLite, + pub sql: Sql, pub inbox: Arc>, pub perform_inbox_jobs_needed: Arc>, pub probe_imap_network: Arc>, @@ -150,7 +148,7 @@ pub fn dc_context_new( cb, os_name: unsafe { dc_strdup_keep_null(os_name) }, running_state: Arc::new(RwLock::new(Default::default())), - sql: SQLite::new(), + sql: Sql::new(), smtp: Arc::new(Mutex::new(Smtp::new())), smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())), oauth2_critical: Arc::new(Mutex::new(())), @@ -186,7 +184,7 @@ unsafe fn cb_receive_imf( context: &Context, imf_raw_not_terminated: *const libc::c_char, imf_raw_bytes: size_t, - server_folder: *const libc::c_char, + server_folder: &str, server_uid: uint32_t, flags: uint32_t, ) { @@ -203,7 +201,7 @@ unsafe fn cb_receive_imf( unsafe fn cb_precheck_imf( context: &Context, rfc724_mid: *const libc::c_char, - server_folder: *const libc::c_char, + server_folder: &str, server_uid: uint32_t, ) -> libc::c_int { let mut rfc724_mid_exists: libc::c_int = 0i32; @@ -222,23 +220,23 @@ unsafe fn cb_precheck_imf( if *old_server_folder.offset(0isize) as libc::c_int == 0i32 && old_server_uid == 0i32 as libc::c_uint { - dc_log_info( + info!( context, - 0i32, - b"[move] detected bbc-self %s\x00" as *const u8 as *const libc::c_char, - rfc724_mid, + 0, + "[move] detected bbc-self {}", + as_str(rfc724_mid), ); mark_seen = 1i32 - } else if strcmp(old_server_folder, server_folder) != 0i32 { - dc_log_info( + } else if as_str(old_server_folder) != server_folder { + info!( context, - 0i32, - b"[move] detected moved message %s\x00" as *const u8 as *const libc::c_char, - rfc724_mid, + 0, + "[move] detected moved message {}", + as_str(rfc724_mid), ); dc_update_msg_move_state(context, rfc724_mid, DC_MOVE_STATE_STAY); } - if strcmp(old_server_folder, server_folder) != 0i32 || old_server_uid != server_uid { + if as_str(old_server_folder) != server_folder || old_server_uid != server_uid { dc_update_server_uid(context, rfc724_mid, server_folder, server_uid); } dc_do_heuristics_moves(context, server_folder, msg_id); @@ -257,7 +255,12 @@ unsafe fn cb_precheck_imf( } unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) { - dc_sqlite3_set_config(context, &context.sql, key, value); + let v = if value.is_null() { + None + } else { + Some(as_str(value)) + }; + context.sql.set_config(context, as_str(key), v); } /* * @@ -272,7 +275,11 @@ unsafe fn cb_get_config( key: *const libc::c_char, def: *const libc::c_char, ) -> *mut libc::c_char { - dc_sqlite3_get_config(context, &context.sql, key, def) + let res = context + .sql + .get_config(context, as_str(key)) + .unwrap_or_else(|| to_string(def)); + strdup(to_cstring(res).as_ptr()) } pub unsafe fn dc_context_unref(context: &mut Context) { @@ -355,358 +362,88 @@ pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char { dc_strdup(*context.blobdir.clone().read().unwrap()) } -pub unsafe fn dc_set_config( - context: &Context, - key: *const libc::c_char, - value: *const libc::c_char, -) -> libc::c_int { - let mut ret = 0; - let mut rel_path = 0 as *mut libc::c_char; - - if key.is_null() || 0 == is_settable_config_key(key) { - return 0; - } - if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 && !value.is_null() { - rel_path = dc_strdup(value); - if !(0 == dc_make_rel_and_copy(context, &mut rel_path)) { - ret = dc_sqlite3_set_config(context, &context.sql, key, rel_path) - } - } else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 { - ret = dc_sqlite3_set_config(context, &context.sql, key, value); - dc_interrupt_imap_idle(context); - } else if strcmp( - key, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - ret = dc_sqlite3_set_config(context, &context.sql, key, value); - dc_interrupt_sentbox_idle(context); - } else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 { - ret = dc_sqlite3_set_config(context, &context.sql, key, value); - dc_interrupt_mvbox_idle(context); - } else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 { - let def = dc_stock_str(context, 13); - ret = dc_sqlite3_set_config( - context, - &context.sql, - key, - if value.is_null() || strcmp(value, def) == 0 { - 0 as *const libc::c_char - } else { - value - }, - ); - free(def as *mut libc::c_void); - } else { - ret = dc_sqlite3_set_config(context, &context.sql, key, value); - } - free(rel_path as *mut libc::c_void); - ret -} - /* ****************************************************************************** * INI-handling, Information ******************************************************************************/ -unsafe fn is_settable_config_key(key: *const libc::c_char) -> libc::c_int { - let mut i = 0; - while i - < (::std::mem::size_of::<[*const libc::c_char; 33]>()) - .wrapping_div(::std::mem::size_of::<*mut libc::c_char>()) - { - if strcmp(key, config_keys[i as usize]) == 0 { - return 1; - } - i += 1 - } - 0 -} - -static mut config_keys: [*const libc::c_char; 33] = [ - b"addr\x00" as *const u8 as *const libc::c_char, - b"mail_server\x00" as *const u8 as *const libc::c_char, - b"mail_user\x00" as *const u8 as *const libc::c_char, - b"mail_pw\x00" as *const u8 as *const libc::c_char, - b"mail_port\x00" as *const u8 as *const libc::c_char, - b"send_server\x00" as *const u8 as *const libc::c_char, - b"send_user\x00" as *const u8 as *const libc::c_char, - b"send_pw\x00" as *const u8 as *const libc::c_char, - b"send_port\x00" as *const u8 as *const libc::c_char, - b"server_flags\x00" as *const u8 as *const libc::c_char, - b"imap_folder\x00" as *const u8 as *const libc::c_char, - b"displayname\x00" as *const u8 as *const libc::c_char, - b"selfstatus\x00" as *const u8 as *const libc::c_char, - b"selfavatar\x00" as *const u8 as *const libc::c_char, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - b"inbox_watch\x00" as *const u8 as *const libc::c_char, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - b"mvbox_watch\x00" as *const u8 as *const libc::c_char, - b"mvbox_move\x00" as *const u8 as *const libc::c_char, - b"show_emails\x00" as *const u8 as *const libc::c_char, - b"save_mime_headers\x00" as *const u8 as *const libc::c_char, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"configured_mail_server\x00" as *const u8 as *const libc::c_char, - b"configured_mail_user\x00" as *const u8 as *const libc::c_char, - b"configured_mail_pw\x00" as *const u8 as *const libc::c_char, - b"configured_mail_port\x00" as *const u8 as *const libc::c_char, - b"configured_send_server\x00" as *const u8 as *const libc::c_char, - b"configured_send_user\x00" as *const u8 as *const libc::c_char, - b"configured_send_pw\x00" as *const u8 as *const libc::c_char, - b"configured_send_port\x00" as *const u8 as *const libc::c_char, - b"configured_server_flags\x00" as *const u8 as *const libc::c_char, - b"configured\x00" as *const u8 as *const libc::c_char, -]; - -pub unsafe fn dc_get_config(context: &Context, key: *const libc::c_char) -> *mut libc::c_char { - let mut value = 0 as *mut libc::c_char; - if !key.is_null() - && *key.offset(0isize) as libc::c_int == 's' as i32 - && *key.offset(1isize) as libc::c_int == 'y' as i32 - && *key.offset(2isize) as libc::c_int == 's' as i32 - && *key.offset(3isize) as libc::c_int == '.' as i32 - { - return get_sys_config_str(key); - } - - if key.is_null() || 0 == is_gettable_config_key(key) { - return dc_strdup(b"\x00" as *const u8 as *const libc::c_char); - } - - if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 { - let rel_path: *mut libc::c_char = - dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char); - if !rel_path.is_null() { - value = dc_get_abs_path(context, rel_path); - free(rel_path as *mut libc::c_void); - } - } else { - value = dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char) - } - - if value.is_null() { - if strcmp(key, b"e2ee_enabled\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp(key, b"mdns_enabled\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp(key, b"imap_folder\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_strdup(b"INBOX\x00" as *const u8 as *const libc::c_char) - } else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp( - key, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp(key, b"mvbox_move\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1) - } else if strcmp(key, b"show_emails\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 0) - } else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 { - value = dc_stock_str(context, 13) - } else { - value = dc_mprintf(b"\x00" as *const u8 as *const libc::c_char) - } - } - - value -} - -unsafe fn is_gettable_config_key(key: *const libc::c_char) -> libc::c_int { - let mut i = 0; - while i - < (::std::mem::size_of::<[*const libc::c_char; 3]>()) - .wrapping_div(::std::mem::size_of::<*mut libc::c_char>()) - { - if strcmp(key, sys_config_keys[i as usize]) == 0 { - return 1; - } - i += 1 - } - - is_settable_config_key(key) -} - -// deprecated -static mut sys_config_keys: [*const libc::c_char; 3] = [ - b"sys.version\x00" as *const u8 as *const libc::c_char, - b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char, - b"sys.config_keys\x00" as *const u8 as *const libc::c_char, -]; - -unsafe fn get_sys_config_str(key: *const libc::c_char) -> *mut libc::c_char { - if strcmp(key, b"sys.version\x00" as *const u8 as *const libc::c_char) == 0 { - return dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char); - } else if strcmp( - key, - b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - return dc_mprintf( - b"%i\x00" as *const u8 as *const libc::c_char, - 24 * 1024 * 1024 / 4 * 3, - ); - } else if strcmp( - key, - b"sys.config_keys\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - return get_config_keys_str(); - } else { - return dc_strdup(0 as *const libc::c_char); - }; -} - -unsafe fn get_config_keys_str() -> *mut libc::c_char { - let mut ret = String::new(); - let mut i = 0; - while i - < (::std::mem::size_of::<[*const libc::c_char; 33]>()) - .wrapping_div(::std::mem::size_of::<*mut libc::c_char>()) - { - if !ret.is_empty() { - ret += " "; - } - ret += &to_string(config_keys[i as usize]); - i += 1 - } - - let mut i = 0; - while i - < (::std::mem::size_of::<[*const libc::c_char; 3]>()) - .wrapping_div(::std::mem::size_of::<*mut libc::c_char>()) - { - if !ret.is_empty() { - ret += " "; - } - ret += &to_string(sys_config_keys[i as usize]); - i += 1 - } - - strdup(to_cstring(ret).as_ptr()) -} - pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { let unset = "0"; - let l = dc_loginparam_new(); - let l2 = dc_loginparam_new(); - dc_loginparam_read( - context, - l, - &context.sql, - b"\x00" as *const u8 as *const libc::c_char, - ); - dc_loginparam_read( - context, - l2, - &context.sql, - b"configured_\x00" as *const u8 as *const libc::c_char, - ); - let displayname = dc_sqlite3_get_config( - context, - &context.sql, - b"displayname\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); + let l = dc_loginparam_read(context, &context.sql, ""); + let l2 = dc_loginparam_read(context, &context.sql, "configured_"); + let displayname = context.sql.get_config(context, "displayname"); let chats = dc_get_chat_cnt(context) as usize; let real_msgs = dc_get_real_msg_cnt(context) as usize; let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize; let contacts = dc_get_real_contact_cnt(context) as usize; - let is_configured = dc_sqlite3_get_config_int( - context, - &context.sql, - b"configured\x00" as *const u8 as *const libc::c_char, - 0, - ); - let dbversion = dc_sqlite3_get_config_int( - context, - &context.sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 0, - ); - let e2ee_enabled = dc_sqlite3_get_config_int( - context, - &context.sql, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - 1, - ); - let mdns_enabled = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1, - ); - let mut stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM keypairs;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_step(stmt); - let prv_key_cnt = sqlite3_column_int(stmt, 0); - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM acpeerstates;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_step(stmt); - let pub_key_cnt = sqlite3_column_int(stmt, 0); - sqlite3_finalize(stmt); - let fingerprint_str = - if let Some(key) = Key::from_self_public(context, (*l2).addr, &context.sql) { - key.fingerprint() - } else { - "".into() - }; + let is_configured = context + .sql + .get_config_int(context, "configured") + .unwrap_or_default(); + let dbversion = context + .sql + .get_config_int(context, "dbversion") + .unwrap_or_default(); + let e2ee_enabled = context + .sql + .get_config_int(context, "e2ee_enabled") + .unwrap_or_else(|| 1); + let mdns_enabled = context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1); - let l_readable_str = dc_loginparam_get_readable(l); - let l2_readable_str = dc_loginparam_get_readable(l2); - let inbox_watch = dc_sqlite3_get_config_int( + let prv_key_cnt: Option = context.sql.query_row_col( context, - &context.sql, - b"inbox_watch\x00" as *const u8 as *const libc::c_char, - 1, - ); - let sentbox_watch = dc_sqlite3_get_config_int( - context, - &context.sql, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - 1, - ); - let mvbox_watch = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_watch\x00" as *const u8 as *const libc::c_char, - 1, - ); - let mvbox_move = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_move\x00" as *const u8 as *const libc::c_char, - 1, - ); - let folders_configured = dc_sqlite3_get_config_int( - context, - &context.sql, - b"folders_configured\x00" as *const u8 as *const libc::c_char, + "SELECT COUNT(*) FROM keypairs;", + rusqlite::NO_PARAMS, 0, ); - let configured_sentbox_folder = dc_sqlite3_get_config( + + let pub_key_cnt: Option = context.sql.query_row_col( context, - &context.sql, - b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - let configured_mvbox_folder = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, + "SELECT COUNT(*) FROM acpeerstates;", + rusqlite::NO_PARAMS, + 0, ); + let fingerprint_str = if let Some(key) = Key::from_self_public(context, &l2.addr, &context.sql) + { + key.fingerprint() + } else { + "".into() + }; + + let l_readable_str = dc_loginparam_get_readable(&l); + let l2_readable_str = dc_loginparam_get_readable(&l2); + let inbox_watch = context + .sql + .get_config_int(context, "inbox_watch") + .unwrap_or_else(|| 1); + let sentbox_watch = context + .sql + .get_config_int(context, "sentbox_watch") + .unwrap_or_else(|| 1); + let mvbox_watch = context + .sql + .get_config_int(context, "mvbox_watch") + .unwrap_or_else(|| 1); + let mvbox_move = context + .sql + .get_config_int(context, "mvbox_move") + .unwrap_or_else(|| 1); + let folders_configured = context + .sql + .get_config_int(context, "folders_configured") + .unwrap_or_default(); + let configured_sentbox_folder = context + .sql + .get_config(context, "configured_sentbox_folder") + .unwrap_or_else(|| "".to_string()); + let configured_mvbox_folder = context + .sql + .get_config(context, "configured_mvbox_folder") + .unwrap_or_else(|| "".to_string()); + let res = format!( "deltachat_core_version=v{}\n\ sqlite_version={}\n\ @@ -737,7 +474,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { fingerprint={}\n\ level=awesome\n", as_str(DC_VERSION_STR as *const u8 as *const _), - as_str(libsqlite3_sys::SQLITE_VERSION as *const u8 as *const libc::c_char), + rusqlite::version(), sqlite3_threadsafe(), // arch (::std::mem::size_of::<*mut libc::c_void>()).wrapping_mul(8), @@ -756,36 +493,24 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { } else { unset }, - if !displayname.is_null() { - as_str(displayname) - } else { - unset - }, + displayname.unwrap_or_else(|| unset.into()), is_configured, - as_str(l_readable_str), - as_str(l2_readable_str), + l_readable_str, + l2_readable_str, inbox_watch, sentbox_watch, mvbox_watch, mvbox_move, folders_configured, - as_str(configured_sentbox_folder), - as_str(configured_mvbox_folder), + configured_sentbox_folder, + configured_mvbox_folder, mdns_enabled, e2ee_enabled, - prv_key_cnt, - pub_key_cnt, + prv_key_cnt.unwrap_or_default(), + pub_key_cnt.unwrap_or_default(), fingerprint_str, ); - dc_loginparam_unref(l); - dc_loginparam_unref(l2); - free(displayname as *mut libc::c_void); - free(l_readable_str as *mut libc::c_void); - free(l2_readable_str as *mut libc::c_void); - free(configured_sentbox_folder as *mut libc::c_void); - free(configured_mvbox_folder as *mut libc::c_void); - strdup(to_cstring(res).as_ptr()) } @@ -793,155 +518,106 @@ pub unsafe fn dc_get_version_str() -> *mut libc::c_char { dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char) } -pub unsafe fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t { +pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t { let show_deaddrop = 0; - let ret = dc_array_new(128 as size_t); - let mut stmt = 0 as *mut sqlite3_stmt; - if !ret.is_null() { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT m.id FROM msgs m LEFT JOIN contacts ct \ - ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \ - AND m.hidden=0 \ - AND m.chat_id>? \ - AND ct.blocked=0 \ - AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1, 10); - sqlite3_bind_int(stmt, 2, 9); - sqlite3_bind_int(stmt, 3, if 0 != show_deaddrop { 2 } else { 0 }); - while sqlite3_step(stmt) == 100 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t); - } - } - sqlite3_finalize(stmt); - ret + + context + .sql + .query_map( + "SELECT m.id FROM msgs m LEFT JOIN contacts ct \ + ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \ + AND m.hidden=0 \ + AND m.chat_id>? \ + AND ct.blocked=0 \ + AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;", + &[10, 9, if 0 != show_deaddrop { 2 } else { 0 }], + |row| row.get(0), + |rows| { + let ret = unsafe { dc_array_new(128 as size_t) }; + + for row in rows { + let id = row?; + unsafe { dc_array_add_id(ret, id) }; + } + Ok(ret) + }, + ) + .unwrap() } -pub unsafe fn dc_search_msgs( +pub fn dc_search_msgs( context: &Context, chat_id: uint32_t, query: *const libc::c_char, ) -> *mut dc_array_t { - let mut success = 0; - let ret = dc_array_new(100 as size_t); - let mut strLikeInText = 0 as *mut libc::c_char; - let mut strLikeBeg = 0 as *mut libc::c_char; - let mut real_query = 0 as *mut libc::c_char; - let mut stmt = 0 as *mut sqlite3_stmt; - - if !(ret.is_null() || query.is_null()) { - real_query = dc_strdup(query); - dc_trim(real_query); - if *real_query.offset(0isize) as libc::c_int == 0 { - success = 1 - } else { - strLikeInText = dc_mprintf( - b"%%%s%%\x00" as *const u8 as *const libc::c_char, - real_query, - ); - strLikeBeg = dc_mprintf(b"%s%%\x00" as *const u8 as *const libc::c_char, real_query); - if 0 != chat_id { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \ - AND m.hidden=0 \ - AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;\x00" - as *const u8 as *const libc::c_char - ); - sqlite3_bind_int(stmt, 1, chat_id as libc::c_int); - sqlite3_bind_text(stmt, 2, strLikeInText, -1, None); - sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None); - } else { - let show_deaddrop = 0; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \ - LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \ - AND (c.blocked=0 OR c.blocked=?) \ - AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;\x00" - as *const u8 as *const libc::c_char - ); - sqlite3_bind_int(stmt, 1, if 0 != show_deaddrop { 2 } else { 0 }); - sqlite3_bind_text(stmt, 2, strLikeInText, -1, None); - sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None); - } - while sqlite3_step(stmt) == 100 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t); - } - success = 1 - } + if query.is_null() { + return std::ptr::null_mut(); } - free(strLikeInText as *mut libc::c_void); - free(strLikeBeg as *mut libc::c_void); - free(real_query as *mut libc::c_void); - sqlite3_finalize(stmt); + let real_query = to_string(query).trim().to_string(); + if real_query.is_empty() { + return std::ptr::null_mut(); + } + let strLikeInText = format!("%{}%", &real_query); + let strLikeBeg = format!("{}%", &real_query); - if 0 != success { - ret + let query = if 0 != chat_id { + "SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \ + AND m.hidden=0 \ + AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;" } else { - if !ret.is_null() { - dc_array_unref(ret); - } - 0 as *mut dc_array_t + "SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \ + LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \ + AND (c.blocked=0 OR c.blocked=?) \ + AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;" + }; + + let ret = unsafe { dc_array_new(100 as size_t) }; + + let success = context + .sql + .query_map( + query, + params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg], + |row| row.get::<_, i32>(0), + |rows| { + for id in rows { + unsafe { dc_array_add_id(ret, id? as u32) }; + } + Ok(()) + }, + ) + .is_ok(); + + if success { + return ret; + } + + if !ret.is_null() { + unsafe { dc_array_unref(ret) }; + } + std::ptr::null_mut() +} + +pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef) -> bool { + folder_name.as_ref() == "INBOX" +} + +pub fn dc_is_sentbox(context: &Context, folder_name: impl AsRef) -> bool { + let sentbox_name = context.sql.get_config(context, "configured_sentbox_folder"); + if let Some(name) = sentbox_name { + name == folder_name.as_ref() + } else { + false } } -pub unsafe fn dc_is_inbox(_context: &Context, folder_name: *const libc::c_char) -> libc::c_int { - let mut is_inbox = 0; - if !folder_name.is_null() { - is_inbox = if strcasecmp( - b"INBOX\x00" as *const u8 as *const libc::c_char, - folder_name, - ) == 0 - { - 1 - } else { - 0 - } - } - is_inbox -} +pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef) -> bool { + let mvbox_name = context.sql.get_config(context, "configured_mvbox_folder"); -pub unsafe fn dc_is_sentbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int { - let sentbox_name = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - let mut is_sentbox = 0; - if !sentbox_name.is_null() && !folder_name.is_null() { - is_sentbox = if strcasecmp(sentbox_name, folder_name) == 0 { - 1 - } else { - 0 - } + if let Some(name) = mvbox_name { + name == folder_name.as_ref() + } else { + false } - free(sentbox_name as *mut libc::c_void); - is_sentbox -} - -pub unsafe fn dc_is_mvbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int { - let mvbox_name = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - let mut is_mvbox = 0; - if !mvbox_name.is_null() && !folder_name.is_null() { - is_mvbox = if strcasecmp(mvbox_name, folder_name) == 0 { - 1 - } else { - 0 - } - } - free(mvbox_name as *mut libc::c_void); - is_mvbox } diff --git a/src/dc_chat.rs b/src/dc_chat.rs index 33be18ee5..715e77fc2 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -4,12 +4,11 @@ use crate::dc_array::*; use crate::dc_chatlist::*; use crate::dc_contact::*; use crate::dc_job::*; -use crate::dc_log::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; +use crate::sql::{self, Sql}; use crate::types::*; use crate::x::*; @@ -101,106 +100,98 @@ pub unsafe fn dc_unblock_chat(context: &Context, chat_id: uint32_t) { dc_block_chat(context, chat_id, 0i32); } -pub unsafe fn dc_block_chat(context: &Context, chat_id: uint32_t, new_blocking: libc::c_int) { - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( +pub fn dc_block_chat(context: &Context, chat_id: u32, new_blocking: libc::c_int) -> bool { + sql::execute( context, &context.sql, - b"UPDATE chats SET blocked=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, new_blocking); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE chats SET blocked=? WHERE id=?;", + params![new_blocking, chat_id as i32], + ) + .is_ok() } -pub unsafe fn dc_chat_load_from_db(chat: *mut Chat, chat_id: uint32_t) -> bool { - let mut success = false; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(chat.is_null() || (*chat).magic != 0xc4a7c4a7u32) { - dc_chat_empty(chat); - stmt = dc_sqlite3_prepare( - (*chat).context, - &(*chat).context.sql, - b"SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \ - c.blocked, c.gossiped_timestamp, c.locations_send_until \ - FROM chats c WHERE c.id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1, chat_id as libc::c_int); +pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { + if chat.is_null() || unsafe { (*chat).magic != 0xc4a7c4a7u32 } { + return false; + } + unsafe { dc_chat_empty(chat) }; - if !(sqlite3_step(stmt) != 100i32) { - if !(0 == set_from_stmt(chat, stmt)) { - success = true + let context = unsafe { (*chat).context }; + + let res = context.sql.query_row( + "SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \ + c.blocked, c.gossiped_timestamp, c.locations_send_until \ + FROM chats c WHERE c.id=?;", + params![chat_id as i32], + |row| { + let c = unsafe { &mut *chat }; + + c.id = row.get(0)?; + c.type_0 = row.get(1)?; + c.name = { + let raw: String = row.get(2)?; + unsafe { strdup(to_cstring(raw).as_ptr()) } + }; + c.grpid = { + let raw: String = row.get(3)?; + unsafe { strdup(to_cstring(raw).as_ptr()) } + }; + + let packed: String = row.get(4)?; + unsafe { dc_param_set_packed((*chat).param, to_cstring(&packed).as_ptr()) }; + c.archived = row.get(5)?; + c.blocked = row.get(6)?; + c.gossiped_timestamp = row.get(7)?; + c.is_sending_locations = row.get(8)?; + + match c.id { + 1 => unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 8); + }, + 6 => unsafe { + free((*chat).name as *mut libc::c_void); + let tempname: *mut libc::c_char = dc_stock_str((*chat).context, 40); + (*chat).name = dc_mprintf( + b"%s (%i)\x00" as *const u8 as *const libc::c_char, + tempname, + dc_get_archived_cnt((*chat).context), + ); + free(tempname as *mut libc::c_void); + }, + 5 => unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 41); + }, + _ => { + if 0 != unsafe { dc_param_exists((*chat).param, 'K' as i32) } { + unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 2); + } + } + } } + Ok(()) + }, + ); + + match res { + Ok(_) => true, + Err(err) => { + error!( + context, + 0, "chat: failed to load from db {}: {:?}", chat_id, err + ); + false } } - sqlite3_finalize(stmt); - success -} - -unsafe fn set_from_stmt(mut chat: *mut Chat, row: *mut sqlite3_stmt) -> libc::c_int { - let mut row_offset: libc::c_int = 0i32; - if chat.is_null() || (*chat).magic != 0xc4a7c4a7u32 || row.is_null() { - return 0i32; - } - dc_chat_empty(chat); - let fresh0 = row_offset; - row_offset = row_offset + 1; - (*chat).id = sqlite3_column_int(row, fresh0) as uint32_t; - let fresh1 = row_offset; - row_offset = row_offset + 1; - (*chat).type_0 = sqlite3_column_int(row, fresh1); - let fresh2 = row_offset; - row_offset = row_offset + 1; - (*chat).name = dc_strdup(sqlite3_column_text(row, fresh2) as *mut libc::c_char); - let fresh3 = row_offset; - row_offset = row_offset + 1; - (*chat).grpid = dc_strdup(sqlite3_column_text(row, fresh3) as *mut libc::c_char); - let fresh4 = row_offset; - row_offset = row_offset + 1; - dc_param_set_packed( - (*chat).param, - sqlite3_column_text(row, fresh4) as *mut libc::c_char, - ); - let fresh5 = row_offset; - row_offset = row_offset + 1; - (*chat).archived = sqlite3_column_int(row, fresh5); - let fresh6 = row_offset; - row_offset = row_offset + 1; - (*chat).blocked = sqlite3_column_int(row, fresh6); - let fresh7 = row_offset; - row_offset = row_offset + 1; - (*chat).gossiped_timestamp = sqlite3_column_int64(row, fresh7) as i64; - let fresh8 = row_offset; - row_offset = row_offset + 1; - (*chat).is_sending_locations = - (sqlite3_column_int64(row, fresh8) as i64 > time()) as libc::c_int; - if (*chat).id == 1i32 as libc::c_uint { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 8i32) - } else if (*chat).id == 6i32 as libc::c_uint { - free((*chat).name as *mut libc::c_void); - let tempname: *mut libc::c_char = dc_stock_str((*chat).context, 40i32); - (*chat).name = dc_mprintf( - b"%s (%i)\x00" as *const u8 as *const libc::c_char, - tempname, - dc_get_archived_cnt((*chat).context), - ); - free(tempname as *mut libc::c_void); - } else if (*chat).id == 5i32 as libc::c_uint { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 41i32) - } else if 0 != dc_param_exists((*chat).param, 'K' as i32) { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 2i32) - } - row_offset } pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut chat_blocked: libc::c_int = 0i32; - let mut send_event: libc::c_int = 0i32; + let mut chat_id = 0; + let mut chat_blocked = 0; + let mut send_event = 0; dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); if 0 != chat_id { if 0 != chat_blocked { @@ -208,12 +199,9 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 send_event = 1i32 } } else if !dc_real_contact_exists(context, contact_id) && contact_id != 1i32 as libc::c_uint { - dc_log_warning( + warn!( context, - 0i32, - b"Cannot create chat, contact %i does not exist.\x00" as *const u8 - as *const libc::c_char, - contact_id as libc::c_int, + 0, "Cannot create chat, contact {} does not exist.", contact_id as libc::c_int, ); } else { dc_create_or_lookup_nchat_by_contact_id( @@ -224,7 +212,7 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 0 as *mut libc::c_int, ); if 0 != chat_id { - send_event = 1i32 + send_event = 1; } dc_scaleup_contact_origin(context, contact_id, 0x800i32); } @@ -241,26 +229,25 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( ret_chat_id: *mut uint32_t, ret_chat_blocked: *mut libc::c_int, ) { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut chat_blocked: libc::c_int = 0i32; + let mut chat_id = 0; + let mut chat_blocked = 0; let contact: *mut dc_contact_t; let chat_name: *mut libc::c_char; - let mut q: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + if !ret_chat_id.is_null() { - *ret_chat_id = 0i32 as uint32_t + *ret_chat_id = 0; } if !ret_chat_blocked.is_null() { - *ret_chat_blocked = 0i32 + *ret_chat_blocked = 0; } if !context.sql.is_open() { return; } - if contact_id == 0i32 as libc::c_uint { + if contact_id == 0 as libc::c_uint { return; } dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); - if chat_id != 0i32 as libc::c_uint { + if chat_id != 0 { if !ret_chat_id.is_null() { *ret_chat_id = chat_id } @@ -277,49 +264,37 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( } else { (*contact).addr }; - q = sqlite3_mprintf( - b"INSERT INTO chats (type, name, param, blocked, grpid) VALUES(%i, %Q, %Q, %i, %Q)\x00" - as *const u8 as *const libc::c_char, - 100i32, - chat_name, - if contact_id == 1i32 as libc::c_uint { - b"K=1\x00" as *const u8 as *const libc::c_char - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - create_blocked, - (*contact).addr, - ); - stmt = dc_sqlite3_prepare(context, &context.sql, q); - if !stmt.is_null() { - if !(sqlite3_step(stmt) != 101i32) { - chat_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"chats\x00" as *const u8 as *const libc::c_char, - b"grpid\x00" as *const u8 as *const libc::c_char, - (*contact).addr, - ); - sqlite3_free(q as *mut libc::c_void); - sqlite3_finalize(stmt); - q = sqlite3_mprintf( - b"INSERT INTO chats_contacts (chat_id, contact_id) VALUES(%i, %i)\x00" - as *const u8 as *const libc::c_char, - chat_id, - contact_id, - ); - stmt = dc_sqlite3_prepare(context, &context.sql, q); - if !(sqlite3_step(stmt) != 101i32) { - sqlite3_free(q as *mut libc::c_void); - q = 0 as *mut libc::c_char; - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt - } - } + + if sql::execute( + context, + &context.sql, + format!( + "INSERT INTO chats (type, name, param, blocked, grpid) VALUES({}, '{}', '{}', {}, '{}')", + 100, + as_str(chat_name), + if contact_id == 1 { "K=1" } else { "" }, + create_blocked, + as_str((*contact).addr), + ), + params![], + ).is_ok() { + chat_id = sql::get_rowid( + context, + &context.sql, + "chats", + "grpid", + as_str((*contact).addr), + ); + + sql::execute( + context, + &context.sql, + format!("INSERT INTO chats_contacts (chat_id, contact_id) VALUES({}, {})", chat_id, contact_id), + params![], + ); } } - sqlite3_free(q as *mut libc::c_void); - sqlite3_finalize(stmt); + dc_contact_unref(contact); if !ret_chat_id.is_null() { *ret_chat_id = chat_id @@ -329,39 +304,31 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( }; } -pub unsafe fn dc_lookup_real_nchat_by_contact_id( +pub fn dc_lookup_real_nchat_by_contact_id( context: &Context, contact_id: uint32_t, ret_chat_id: *mut uint32_t, ret_chat_blocked: *mut libc::c_int, ) { /* checks for "real" chats or self-chat */ - let stmt: *mut sqlite3_stmt; if !ret_chat_id.is_null() { - *ret_chat_id = 0i32 as uint32_t + unsafe { *ret_chat_id = 0 }; } if !ret_chat_blocked.is_null() { - *ret_chat_blocked = 0i32 + unsafe { *ret_chat_blocked = 0 }; } if !context.sql.is_open() { return; } - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - if !ret_chat_id.is_null() { - *ret_chat_id = sqlite3_column_int(stmt, 0i32) as uint32_t - } - if !ret_chat_blocked.is_null() { - *ret_chat_blocked = sqlite3_column_int(stmt, 1i32) - } + + if let Ok((id, blocked)) = context.sql.query_row( + "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", + params![contact_id as i32], + |row| Ok((row.get(0)?, row.get(1)?)), + ) { + unsafe { *ret_chat_id = id }; + unsafe { *ret_chat_blocked = blocked }; } - sqlite3_finalize(stmt); } pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { @@ -414,21 +381,15 @@ unsafe fn prepare_msg_common<'a>( { pathNfilename = dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char); if pathNfilename.is_null() { - dc_log_error( + error!( context, - 0i32, - b"Attachment missing for message of type #%i.\x00" as *const u8 - as *const libc::c_char, + 0, + "Attachment missing for message of type #{}.", (*msg).type_0 as libc::c_int, ); current_block = 2171833246886114521; } else if (*msg).state == 18i32 && 0 == dc_is_blobdir_path(context, pathNfilename) { - dc_log_error( - context, - 0i32, - b"Files must be created in the blob-directory.\x00" as *const u8 - as *const libc::c_char, - ); + error!(context, 0, "Files must be created in the blob-directory.",); current_block = 2171833246886114521; } else if 0 == dc_make_rel_and_copy(context, &mut pathNfilename) { current_block = 2171833246886114521; @@ -453,20 +414,20 @@ unsafe fn prepare_msg_common<'a>( dc_param_set((*msg).param, 'm' as i32, better_mime_0); free(better_mime_0 as *mut libc::c_void); } - dc_log_info( + info!( context, - 0i32, - b"Attaching \"%s\" for message type #%i.\x00" as *const u8 as *const libc::c_char, - pathNfilename, + 0, + "Attaching \"{}\" for message type #{}.", + as_str(pathNfilename), (*msg).type_0 as libc::c_int, ); current_block = 17281240262373992796; } } else { - dc_log_error( + error!( context, - 0i32, - b"Cannot send messages of type #%i.\x00" as *const u8 as *const libc::c_char, + 0, + "Cannot send messages of type #{}.", (*msg).type_0 as libc::c_int, ); current_block = 2171833246886114521; @@ -502,82 +463,63 @@ unsafe fn prepare_msg_raw( let mut do_guarantee_e2ee: libc::c_int; let e2ee_enabled: libc::c_int; let current_block: u64; - let mut parent_rfc724_mid: *mut libc::c_char = 0 as *mut libc::c_char; - let mut parent_references: *mut libc::c_char = 0 as *mut libc::c_char; - let mut parent_in_reply_to: *mut libc::c_char = 0 as *mut libc::c_char; - let mut new_rfc724_mid: *mut libc::c_char = 0 as *mut libc::c_char; - let mut new_references: *mut libc::c_char = 0 as *mut libc::c_char; - let mut new_in_reply_to: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut msg_id: uint32_t = 0i32 as uint32_t; - let mut to_id: uint32_t = 0i32 as uint32_t; - let mut location_id: uint32_t = 0i32 as uint32_t; + let mut parent_rfc724_mid = 0 as *mut libc::c_char; + let mut parent_references = 0 as *mut libc::c_char; + let mut parent_in_reply_to = 0 as *mut libc::c_char; + let mut new_rfc724_mid = 0 as *mut libc::c_char; + let mut new_references = 0 as *mut libc::c_char; + let mut new_in_reply_to = 0 as *mut libc::c_char; + let mut msg_id = 0; + let mut to_id = 0; + let mut location_id = 0; - if !((*chat).type_0 == 100i32 || (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32) { - dc_log_error( - context, - 0i32, - b"Cannot send to chat type #%i.\x00" as *const u8 as *const libc::c_char, - (*chat).type_0, - ); - } else if ((*chat).type_0 == 120i32 || (*chat).type_0 == 130i32) - && 0 == dc_is_contact_in_chat(context, (*chat).id, 1i32 as uint32_t) + if !((*chat).type_0 == 100 || (*chat).type_0 == 120 || (*chat).type_0 == 130) { + error!(context, 0, "Cannot send to chat type #{}.", (*chat).type_0,); + } else if ((*chat).type_0 == 120 || (*chat).type_0 == 130) + && 0 == dc_is_contact_in_chat(context, (*chat).id, 1 as uint32_t) { - dc_log_event( + log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, - 0i32, - b"Cannot send message; self not in group.\x00" as *const u8 as *const libc::c_char, + 0, + "Cannot send message; self not in group.", ); } else { - let from: *mut libc::c_char = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if from.is_null() { - dc_log_error( - context, - 0i32, - b"Cannot send message, not configured.\x00" as *const u8 as *const libc::c_char, - ); + let from = context.sql.get_config(context, "configured_addr"); + if from.is_none() { + error!(context, 0, "Cannot send message, not configured.",); } else { + let from_c = to_cstring(from.unwrap()); new_rfc724_mid = dc_create_outgoing_rfc724_mid( - if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { + if (*chat).type_0 == 120 || (*chat).type_0 == 130 { (*chat).grpid } else { 0 as *mut libc::c_char }, - from, + from_c.as_ptr(), ); - free(from as *mut libc::c_void); - if (*chat).type_0 == 100i32 { - stmt = dc_sqlite3_prepare( + + if (*chat).type_0 == 100 { + if let Some(id) = context.sql.query_row_col( context, - &context.sql, - b"SELECT contact_id FROM chats_contacts WHERE chat_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, (*chat).id as libc::c_int); - if sqlite3_step(stmt) != 100i32 { - dc_log_error( + "SELECT contact_id FROM chats_contacts WHERE chat_id=?;", + params![(*chat).id as i32], + 0, + ) { + to_id = id; + current_block = 5689316957504528238; + } else { + error!( context, - 0i32, - b"Cannot send message, contact for chat #%i not found.\x00" as *const u8 - as *const libc::c_char, + 0, + "Cannot send message, contact for chat #{} not found.", (*chat).id, ); current_block = 10477488590406205504; - } else { - to_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - current_block = 5689316957504528238; } } else { - if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { - if dc_param_get_int((*chat).param, 'U' as i32, 0i32) == 1i32 { + if (*chat).type_0 == 120 || (*chat).type_0 == 130 { + if dc_param_get_int((*chat).param, 'U' as i32, 0) == 1 { dc_param_set((*chat).param, 'U' as i32, 0 as *const libc::c_char); dc_chat_update_param(chat); } @@ -591,66 +533,67 @@ unsafe fn prepare_msg_raw( if we guarantee E2EE, and circumstances change so that E2EE is no longer available at a later point (reset, changed settings), we do not send the message out at all */ - do_guarantee_e2ee = 0i32; - e2ee_enabled = dc_sqlite3_get_config_int( - context, - &context.sql, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ); - if 0 != e2ee_enabled && dc_param_get_int((*msg).param, 'u' as i32, 0i32) == 0i32 - { - let mut can_encrypt: libc::c_int = 1i32; - let mut all_mutual: libc::c_int = 1i32; - stmt = - dc_sqlite3_prepare(context, &context.sql, - b"SELECT ps.prefer_encrypted, c.addr FROM chats_contacts cc LEFT JOIN contacts c ON cc.contact_id=c.id LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE cc.chat_id=? AND cc.contact_id>9;\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, (*chat).id as libc::c_int); - while sqlite3_step(stmt) == 100i32 { - if sqlite3_column_type(stmt, 0i32) == 5i32 { - dc_log_info( - context, - 0i32, - b"[autocrypt] no peerstate for %s\x00" as *const u8 - as *const libc::c_char, - sqlite3_column_text(stmt, 1i32), - ); - can_encrypt = 0i32; - all_mutual = 0i32 - } else { - let prefer_encrypted: libc::c_int = sqlite3_column_int(stmt, 0i32); - if prefer_encrypted != 1i32 { - dc_log_info( - context, - 0i32, - b"[autocrypt] peerstate for %s is %s\x00" as *const u8 - as *const libc::c_char, - sqlite3_column_text(stmt, 1i32), - if prefer_encrypted == 0i32 { - b"NOPREFERENCE\x00" as *const u8 as *const libc::c_char - } else { - b"RESET\x00" as *const u8 as *const libc::c_char - }, - ); - all_mutual = 0i32 + do_guarantee_e2ee = 0; + e2ee_enabled = context + .sql + .get_config_int(context, "e2ee_enabled") + .unwrap_or_else(|| 1); + if 0 != e2ee_enabled && dc_param_get_int((*msg).param, 'u' as i32, 0) == 0 { + let mut can_encrypt = 1; + let mut all_mutual = 1; + + let res = context.sql.query_row( + "SELECT ps.prefer_encrypted, c.addr \ + FROM chats_contacts cc \ + LEFT JOIN contacts c ON cc.contact_id=c.id \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ + WHERE cc.chat_id=? AND cc.contact_id>9;", + params![(*chat).id], + |row| { + let state: String = row.get(1)?; + + if let Some(prefer_encrypted) = row.get::<_, Option>(0)? { + if prefer_encrypted != 1 { + info!( + context, + 0, + "[autocrypt] peerstate for {} is {}", + state, + if prefer_encrypted == 0 { + "NOPREFERENCE" + } else { + "RESET" + }, + ); + all_mutual = 0; + } + } else { + info!(context, 0, "[autocrypt] no peerstate for {}", state,); + can_encrypt = 0; + all_mutual = 0; } + Ok(()) + }, + ); + match res { + Ok(_) => {} + Err(err) => { + warn!(context, 0, "chat: failed to load peerstates: {:?}", err); } } - sqlite3_finalize(stmt); + if 0 != can_encrypt { if 0 != all_mutual { - do_guarantee_e2ee = 1i32 + do_guarantee_e2ee = 1; } else if 0 != last_msg_in_chat_encrypted(context, &context.sql, (*chat).id) { - do_guarantee_e2ee = 1i32 + do_guarantee_e2ee = 1; } } } if 0 != do_guarantee_e2ee { - dc_param_set_int((*msg).param, 'c' as i32, 1i32); + dc_param_set_int((*msg).param, 'c' as i32, 1); } dc_param_set((*msg).param, 'e' as i32, 0 as *const libc::c_char); if 0 == dc_chat_is_self_talk(chat) @@ -670,7 +613,7 @@ unsafe fn prepare_msg_raw( let space: *mut libc::c_char; space = strchr(parent_references, ' ' as i32); if !space.is_null() { - *space = 0i32 as libc::c_char + *space = 0 as libc::c_char } } if !parent_references.is_null() @@ -707,108 +650,90 @@ unsafe fn prepare_msg_raw( // add independent location to database if 0 != dc_param_exists((*msg).param, DC_PARAM_SET_LATITUDE as libc::c_int) { - stmt = dc_sqlite3_prepare( + if sql::execute( context, &context.sql, - b"INSERT INTO locations \ - (timestamp,from_id,chat_id, latitude,longitude,independent)\ - VALUES (?,?,?, ?,?,1);\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int64(stmt, 1, timestamp as i64); - sqlite3_bind_int(stmt, 2, DC_CONTACT_ID_SELF as libc::c_int); - sqlite3_bind_int(stmt, 3, (*chat).id as libc::c_int); - sqlite3_bind_double( - stmt, - 4, - dc_param_get_float( - (*msg).param, - DC_PARAM_SET_LATITUDE as libc::c_int, - 0.0, - ), - ); - sqlite3_bind_double( - stmt, - 5, - dc_param_get_float( - (*msg).param, - DC_PARAM_SET_LONGITUDE as libc::c_int, - 0.0, - ), - ); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - - location_id = dc_sqlite3_get_rowid2( - context, - &context.sql, - b"locations\x00" as *const u8 as *const libc::c_char, - b"timestamp\x00" as *const u8 as *const libc::c_char, - timestamp as u64, - b"from_id\x00" as *const u8 as *const libc::c_char, - DC_CONTACT_ID_SELF as u32, - ); + "INSERT INTO locations \ + (timestamp,from_id,chat_id, latitude,longitude,independent)\ + VALUES (?,?,?, ?,?,1);", + params![ + timestamp, + DC_CONTACT_ID_SELF as i32, + (*chat).id as i32, + dc_param_get_float( + (*msg).param, + DC_PARAM_SET_LATITUDE as libc::c_int, + 0.0, + ), + dc_param_get_float( + (*msg).param, + DC_PARAM_SET_LONGITUDE as libc::c_int, + 0.0, + ), + ], + ) + .is_ok() + { + location_id = sql::get_rowid2( + context, + &context.sql, + "locations", + "timestamp", + timestamp, + "from_id", + DC_CONTACT_ID_SELF as i32, + ); + } } // add message to the database - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"INSERT INTO msgs (rfc724_mid, chat_id, from_id, to_id, timestamp, type, state, txt, param, hidden, mime_in_reply_to, mime_references, location_id) VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?);\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, new_rfc724_mid, -1i32, None); - sqlite3_bind_int(stmt, 2i32, (*chat).id as libc::c_int); - sqlite3_bind_int(stmt, 3i32, 1i32); - sqlite3_bind_int(stmt, 4i32, to_id as libc::c_int); - sqlite3_bind_int64(stmt, 5i32, timestamp as sqlite3_int64); - sqlite3_bind_int(stmt, 6i32, (*msg).type_0); - sqlite3_bind_int(stmt, 7i32, (*msg).state); - sqlite3_bind_text( - stmt, - 8i32, - if !(*msg).text.is_null() { - (*msg).text - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, - ); - sqlite3_bind_text(stmt, 9i32, (*(*msg).param).packed, -1i32, None); - sqlite3_bind_int(stmt, 10i32, (*msg).hidden); - sqlite3_bind_text(stmt, 11i32, new_in_reply_to, -1i32, None); - sqlite3_bind_text(stmt, 12i32, new_references, -1i32, None); - sqlite3_bind_int(stmt, 13i32, location_id as libc::c_int); - if sqlite3_step(stmt) != 101i32 { - dc_log_error( - context, - 0i32, - b"Cannot send message, cannot insert to database.\x00" as *const u8 - as *const libc::c_char, - (*chat).id, - ); - } else { - msg_id = dc_sqlite3_get_rowid( + if sql::execute( + context, + &context.sql, + "INSERT INTO msgs (rfc724_mid, chat_id, from_id, to_id, timestamp, type, state, txt, param, hidden, mime_in_reply_to, mime_references, location_id) VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?);", + params![ + as_str(new_rfc724_mid), + (*chat).id as i32, + 1i32, + to_id as i32, + timestamp, + (*msg).type_0, + (*msg).state, + if !(*msg).text.is_null() { Some(as_str((*msg).text)) } else { None }, + if (*(*msg).param).packed.is_null() { None } else { Some(as_str((*(*msg).param).packed)) }, + (*msg).hidden, + to_string(new_in_reply_to), + to_string(new_references), + location_id as i32, + ] + ).is_ok() { + msg_id = sql::get_rowid( context, &context.sql, - b"msgs\x00" as *const u8 as *const libc::c_char, - b"rfc724_mid\x00" as *const u8 as *const libc::c_char, - new_rfc724_mid, - ) + "msgs", + "rfc724_mid", + as_str(new_rfc724_mid), + ); + } else { + error!( + context, + 0, + "Cannot send message, cannot insert to database (chat #{}).", + (*chat).id, + ); } } } } } + free(parent_rfc724_mid as *mut libc::c_void); free(parent_in_reply_to as *mut libc::c_void); free(parent_references as *mut libc::c_void); free(new_rfc724_mid as *mut libc::c_void); free(new_in_reply_to as *mut libc::c_void); free(new_references as *mut libc::c_void); - sqlite3_finalize(stmt); msg_id } @@ -820,48 +745,52 @@ unsafe fn get_parent_mime_headers( parent_in_reply_to: *mut *mut libc::c_char, parent_references: *mut *mut libc::c_char, ) -> libc::c_int { - let mut success: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + let mut success = 0; + if !(chat.is_null() || parent_rfc724_mid.is_null() || parent_in_reply_to.is_null() || parent_references.is_null()) { - stmt = - dc_sqlite3_prepare( - (*chat).context, &(*chat).context.sql, - b"SELECT rfc724_mid, mime_in_reply_to, mime_references FROM msgs WHERE timestamp=(SELECT max(timestamp) FROM msgs WHERE chat_id=? AND from_id!=?);\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, (*chat).id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, 1i32); - if sqlite3_step(stmt) == 100i32 { - *parent_rfc724_mid = dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char); - *parent_in_reply_to = dc_strdup(sqlite3_column_text(stmt, 1i32) as *const libc::c_char); - *parent_references = dc_strdup(sqlite3_column_text(stmt, 2i32) as *const libc::c_char); - success = 1i32 - } - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; + success = (*chat) + .context + .sql + .query_row( + "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE timestamp=(SELECT max(timestamp) \ + FROM msgs WHERE chat_id=? AND from_id!=?);", + params![(*chat).id as i32, 1], + |row| { + *parent_rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); + *parent_in_reply_to = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); + *parent_references = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); + Ok(()) + }, + ) + .is_ok() as libc::c_int; + if 0 == success { - stmt = - dc_sqlite3_prepare( - (*chat).context, &(*chat).context.sql, - b"SELECT rfc724_mid, mime_in_reply_to, mime_references FROM msgs WHERE timestamp=(SELECT min(timestamp) FROM msgs WHERE chat_id=? AND from_id==?);\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, (*chat).id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, 1i32); - if sqlite3_step(stmt) == 100i32 { - *parent_rfc724_mid = - dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char); - *parent_in_reply_to = - dc_strdup(sqlite3_column_text(stmt, 1i32) as *const libc::c_char); - *parent_references = - dc_strdup(sqlite3_column_text(stmt, 2i32) as *const libc::c_char); - success = 1i32 - } + success = (*chat) + .context + .sql + .query_row( + "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE timestamp=(SELECT min(timestamp) \ + FROM msgs WHERE chat_id=? AND from_id==?);", + params![(*chat).id as i32, 1], + |row| { + *parent_rfc724_mid = + dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); + *parent_in_reply_to = + dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); + *parent_references = + dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); + Ok(()) + }, + ) + .is_ok() as libc::c_int; } } - sqlite3_finalize(stmt); success } @@ -878,49 +807,42 @@ pub unsafe fn dc_chat_is_self_talk(chat: *const Chat) -> libc::c_int { // TODO should return bool /rtn unsafe fn last_msg_in_chat_encrypted( context: &Context, - sql: &SQLite, + sql: &Sql, chat_id: uint32_t, ) -> libc::c_int { let mut last_is_encrypted: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt = - dc_sqlite3_prepare( - context, - sql, - b"SELECT param FROM msgs WHERE timestamp=(SELECT MAX(timestamp) FROM msgs WHERE chat_id=?) ORDER BY id DESC;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - let msg_param: *mut dc_param_t = dc_param_new(); - dc_param_set_packed( - msg_param, - sqlite3_column_text(stmt, 0i32) as *mut libc::c_char, - ); + let packed: Option = sql.query_row_col( + context, + "SELECT param \ + FROM msgs WHERE timestamp=(SELECT MAX(timestamp) FROM msgs WHERE chat_id=?) \ + ORDER BY id DESC;", + params![chat_id as i32], + 0, + ); + + if let Some(packed) = packed { + let msg_param = dc_param_new(); + let packed_c = to_cstring(packed); + dc_param_set_packed(msg_param, packed_c.as_ptr()); + if 0 != dc_param_exists(msg_param, 'c' as i32) { - last_is_encrypted = 1i32 + last_is_encrypted = 1; } dc_param_unref(msg_param); } - sqlite3_finalize(stmt); + last_is_encrypted } // TODO should return bool /rtn pub unsafe fn dc_chat_update_param(chat: *mut Chat) -> libc::c_int { - let success: libc::c_int; - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + sql::execute( (*chat).context, &(*chat).context.sql, - b"UPDATE chats SET param=? WHERE id=?\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, (*(*chat).param).packed, -1i32, None); - sqlite3_bind_int(stmt, 2i32, (*chat).id as libc::c_int); - success = if sqlite3_step(stmt) == 101i32 { - 1i32 - } else { - 0i32 - }; - sqlite3_finalize(stmt); - success + "UPDATE chats SET param=? WHERE id=?", + params![to_string((*(*chat).param).packed), (*chat).id as i32], + ) + .is_ok() as libc::c_int } pub unsafe fn dc_is_contact_in_chat( @@ -930,36 +852,23 @@ pub unsafe fn dc_is_contact_in_chat( ) -> libc::c_int { /* this function works for group and for normal chats, however, it is more useful for group chats. DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group chat (DC_CONTACT_ID_SELF is not added to normal chats) */ - let ret: libc::c_int; - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, contact_id as libc::c_int); - ret = if sqlite3_step(stmt) == 100i32 { - 1i32 - } else { - 0i32 - }; - - sqlite3_finalize(stmt); - ret + context + .sql + .exists( + "SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id=?;", + params![chat_id as i32, contact_id as i32], + ) + .unwrap_or_default() as libc::c_int } -pub unsafe fn dc_unarchive_chat(context: &Context, chat_id: uint32_t) { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( +pub fn dc_unarchive_chat(context: &Context, chat_id: u32) { + sql::execute( context, &context.sql, - b"UPDATE chats SET archived=0 WHERE id=?\x00" as *const u8 as *const libc::c_char, + "UPDATE chats SET archived=0 WHERE id=?", + params![chat_id as i32], ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); } pub unsafe fn dc_send_msg<'a>( @@ -968,20 +877,20 @@ pub unsafe fn dc_send_msg<'a>( msg: *mut dc_msg_t<'a>, ) -> uint32_t { if msg.is_null() { - return 0i32 as uint32_t; + return 0; } - if (*msg).state != 18i32 { + if (*msg).state != 18 { if 0 == prepare_msg_common(context, chat_id, msg) { - return 0i32 as uint32_t; + return 0; } } else { - if chat_id != 0i32 as libc::c_uint && chat_id != (*msg).chat_id { - return 0i32 as uint32_t; + if chat_id != 0 && chat_id != (*msg).chat_id { + return 0; } - dc_update_msg_state(context, (*msg).id, 20i32); + dc_update_msg_state(context, (*msg).id, 20); } if 0 == dc_job_send_msg(context, (*msg).id) { - return 0i32 as uint32_t; + return 0; } context.call_cb( Event::MSGS_CHANGED, @@ -994,19 +903,18 @@ pub unsafe fn dc_send_msg<'a>( } if 0 == chat_id { - let forwards: *mut libc::c_char = - dc_param_get((*msg).param, 'P' as i32, 0 as *const libc::c_char); + let forwards = dc_param_get((*msg).param, 'P' as i32, 0 as *const libc::c_char); if !forwards.is_null() { - let mut p: *mut libc::c_char = forwards; + let mut p = forwards; while 0 != *p { - let id: int32_t = strtol(p, &mut p, 10i32) as int32_t; + let id = strtol(p, &mut p, 10) as int32_t; if 0 == id { // avoid hanging if user tampers with db break; } else { - let copy: *mut dc_msg_t = dc_get_msg(context, id as uint32_t); + let copy = dc_get_msg(context, id as uint32_t); if !copy.is_null() { - dc_send_msg(context, 0i32 as uint32_t, copy); + dc_send_msg(context, 0 as uint32_t, copy); } dc_msg_unref(copy); } @@ -1025,11 +933,11 @@ pub unsafe fn dc_send_text_msg( chat_id: uint32_t, text_to_send: *const libc::c_char, ) -> uint32_t { - let mut msg: *mut dc_msg_t = dc_msg_new(context, 10i32); - let mut ret: uint32_t = 0i32 as uint32_t; - if !(chat_id <= 9i32 as libc::c_uint || text_to_send.is_null()) { + let mut msg = dc_msg_new(context, 10); + let mut ret = 0; + if !(chat_id <= 9 || text_to_send.is_null()) { (*msg).text = dc_strdup(text_to_send); - ret = dc_send_msg(context, chat_id, msg) + ret = dc_send_msg(context, chat_id, msg); } dc_msg_unref(msg); ret @@ -1048,7 +956,6 @@ pub unsafe fn dc_set_draft(context: &Context, chat_id: uint32_t, msg: *mut dc_ms unsafe fn set_draft_raw(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t) -> libc::c_int { let current_block: u64; // similar to as dc_set_draft() but does not emit an event - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; let mut pathNfilename: *mut libc::c_char = 0 as *mut libc::c_char; let prev_draft_msg_id: uint32_t; let mut sth_changed: libc::c_int = 0i32; @@ -1091,55 +998,43 @@ unsafe fn set_draft_raw(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t match current_block { 14513523936503887211 => {} _ => { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden) VALUES (?,?,?, ?,?,?,?,?);\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, 1i32); - sqlite3_bind_int64(stmt, 3i32, time() as sqlite3_int64); - sqlite3_bind_int(stmt, 4i32, (*msg).type_0); - sqlite3_bind_int(stmt, 5i32, 19i32); - sqlite3_bind_text( - stmt, - 6i32, - if !(*msg).text.is_null() { - (*msg).text - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, - ); - sqlite3_bind_text(stmt, 7i32, (*(*msg).param).packed, -1i32, None); - sqlite3_bind_int(stmt, 8i32, 1i32); - if !(sqlite3_step(stmt) != 101i32) { - sth_changed = 1i32 + if sql::execute( + context, + &context.sql, + "INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden) \ + VALUES (?,?,?, ?,?,?,?,?);", + params![ + chat_id as i32, 1, time(), (*msg).type_0, 19, + if !(*msg).text.is_null() { + as_str((*msg).text) + } else { + "" + }, + to_string((*(*msg).param).packed), + 1, + ] + ).is_ok() { + sth_changed = 1; } } } } - sqlite3_finalize(stmt); free(pathNfilename as *mut libc::c_void); sth_changed } -unsafe fn get_draft_msg_id(context: &Context, chat_id: uint32_t) -> uint32_t { - let mut draft_msg_id: uint32_t = 0i32 as uint32_t; - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM msgs WHERE chat_id=? AND state=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, 19i32); - if sqlite3_step(stmt) == 100i32 { - draft_msg_id = sqlite3_column_int(stmt, 0i32) as uint32_t - } - sqlite3_finalize(stmt); - draft_msg_id +fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 { + let draft_msg_id: i32 = context + .sql + .query_row_col( + context, + "SELECT id FROM msgs WHERE chat_id=? AND state=?;", + params![chat_id as i32, 19], + 0, + ) + .unwrap_or_default(); + + draft_msg_id as u32 } pub unsafe fn dc_get_draft(context: &Context, chat_id: uint32_t) -> *mut dc_msg_t { @@ -1167,194 +1062,198 @@ pub unsafe fn dc_get_chat_msgs( flags: uint32_t, marker1before: uint32_t, ) -> *mut dc_array_t { - //clock_t start = clock(); - let mut success: libc::c_int = 0i32; - let ret: *mut dc_array_t = dc_array_new(512i32 as size_t); - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut curr_id: uint32_t; - let mut curr_local_timestamp: i64; - let mut curr_day: libc::c_int; + let ret = dc_array_new(512); + assert!(!ret.is_null()); + let mut last_day = 0; let cnv_to_local = dc_gm2local_offset(); - if !ret.is_null() { - if chat_id == 1i32 as libc::c_uint { - let show_emails: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"show_emails\x00" as *const u8 as *const libc::c_char, - 0i32, - ); - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN chats ON m.chat_id=chats.id LEFT JOIN contacts ON m.from_id=contacts.id WHERE m.from_id!=1 AND m.from_id!=2 AND m.hidden=0 AND chats.blocked=2 AND contacts.blocked=0 AND m.msgrmsg>=? ORDER BY m.timestamp,m.id;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, if show_emails == 2i32 { 0i32 } else { 1i32 }); - } else if chat_id == 5i32 as libc::c_uint { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.starred=1 AND m.hidden=0 AND ct.blocked=0 ORDER BY m.timestamp,m.id;\x00" - as *const u8 as *const libc::c_char) - } else { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT m.id, m.timestamp FROM msgs m WHERE m.chat_id=? AND m.hidden=0 ORDER BY m.timestamp,m.id;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - } - while sqlite3_step(stmt) == 100i32 { - curr_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - if curr_id == marker1before { - dc_array_add_id(ret, 1i32 as uint32_t); + + let process_row = |row: &rusqlite::Row| Ok((row.get::<_, i32>(0)?, row.get::<_, i64>(1)?)); + let process_rows = |rows: rusqlite::MappedRows<_>| { + for row in rows { + let (curr_id, ts) = row?; + if curr_id as u32 == marker1before { + dc_array_add_id(ret, 1); } - if 0 != flags & 0x1i32 as libc::c_uint { - curr_local_timestamp = - (sqlite3_column_int64(stmt, 1i32) as i64 + cnv_to_local) as i64; - curr_day = (curr_local_timestamp / 86400) as libc::c_int; + if 0 != flags & 0x1 { + let curr_local_timestamp = ts + cnv_to_local; + let curr_day = (curr_local_timestamp / 86400) as libc::c_int; if curr_day != last_day { - dc_array_add_id(ret, 9i32 as uint32_t); - last_day = curr_day + dc_array_add_id(ret, 9); + last_day = curr_day; } } - dc_array_add_id(ret, curr_id); + dc_array_add_id(ret, curr_id as u32); } - success = 1i32 - } - sqlite3_finalize(stmt); - if 0 != success { - return ret; + Ok(()) + }; + + let success = if chat_id == 1 { + let show_emails = context + .sql + .get_config_int(context, "show_emails") + .unwrap_or_default(); + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + LEFT JOIN chats ON m.chat_id=chats.id \ + LEFT JOIN contacts ON m.from_id=contacts.id WHERE m.from_id!=1 \ + AND m.from_id!=2 \ + AND m.hidden=0 \ + AND chats.blocked=2 \ + AND contacts.blocked=0 \ + AND m.msgrmsg>=? \ + ORDER BY m.timestamp,m.id;", + params![if show_emails == 2 { 0 } else { 1 }], + process_row, + process_rows, + ) + } else if chat_id == 5 { + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.starred=1 \ + AND m.hidden=0 \ + AND ct.blocked=0 \ + ORDER BY m.timestamp,m.id;", + params![], + process_row, + process_rows, + ) + } else { + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + WHERE m.chat_id=? \ + AND m.hidden=0 \ + ORDER BY m.timestamp,m.id;", + params![chat_id as i32], + process_row, + process_rows, + ) + }; + + if success.is_ok() { + ret } else { if !ret.is_null() { dc_array_unref(ret); } - return 0 as *mut dc_array_t; - }; -} - -pub unsafe fn dc_get_msg_cnt(context: &Context, chat_id: uint32_t) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM msgs WHERE chat_id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32); + 0 as *mut dc_array_t } - - sqlite3_finalize(stmt); - ret } -pub unsafe fn dc_get_fresh_msg_cnt(context: &Context, chat_id: uint32_t) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM msgs WHERE state=10 AND hidden=0 AND chat_id=?;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32); - } - sqlite3_finalize(stmt); - ret -} - -pub unsafe fn dc_marknoticed_chat(context: &Context, chat_id: uint32_t) { - let check: *mut sqlite3_stmt; - let mut update: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - - check = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM msgs WHERE chat_id=? AND state=10;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(check, 1i32, chat_id as libc::c_int); - if !(sqlite3_step(check) != 100i32) { - update = dc_sqlite3_prepare( +pub fn dc_get_msg_cnt(context: &Context, chat_id: u32) -> libc::c_int { + context + .sql + .query_row_col( context, - &context.sql, - b"UPDATE msgs SET state=13 WHERE chat_id=? AND state=10;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(update, 1i32, chat_id as libc::c_int); - sqlite3_step(update); - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); - } - - sqlite3_finalize(check); - sqlite3_finalize(update); + "SELECT COUNT(*) FROM msgs WHERE chat_id=?;", + params![chat_id as i32], + 0, + ) + .unwrap_or_default() } -pub unsafe fn dc_marknoticed_all_chats(context: &Context) { - let check: *mut sqlite3_stmt; - let mut update: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; +pub fn dc_get_fresh_msg_cnt(context: &Context, chat_id: u32) -> libc::c_int { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM msgs \ + WHERE state=10 \ + AND hidden=0 \ + AND chat_id=?;", + params![chat_id as i32], + 0, + ) + .unwrap_or_default() +} - check = dc_sqlite3_prepare( +pub fn dc_marknoticed_chat(context: &Context, chat_id: u32) -> bool { + if !context + .sql + .exists( + "SELECT id FROM msgs WHERE chat_id=? AND state=10;", + params![chat_id as i32], + ) + .unwrap_or_default() + { + return false; + } + if sql::execute( context, &context.sql, - b"SELECT id FROM msgs WHERE state=10;\x00" as *const u8 as *const libc::c_char, - ); - if !(sqlite3_step(check) != 100i32) { - update = dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE msgs SET state=13 WHERE state=10;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_step(update); - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); + "UPDATE msgs \ + SET state=13 WHERE chat_id=? AND state=10;", + params![chat_id as i32], + ) + .is_err() + { + return false; } - - sqlite3_finalize(check); - sqlite3_finalize(update); + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + true } -pub unsafe fn dc_get_chat_media( +pub fn dc_marknoticed_all_chats(context: &Context) -> bool { + if !context + .sql + .exists( + "SELECT id FROM msgs \ + WHERE state=10;", + params![], + ) + .unwrap_or_default() + { + return false; + } + + if sql::execute( + context, + &context.sql, + "UPDATE msgs \ + SET state=13 WHERE state=10;", + params![], + ) + .is_err() + { + return false; + } + + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + + true +} + +pub fn dc_get_chat_media( context: &Context, chat_id: uint32_t, msg_type: libc::c_int, msg_type2: libc::c_int, msg_type3: libc::c_int, ) -> *mut dc_array_t { - let ret: *mut dc_array_t = dc_array_new(100i32 as size_t); - let stmt: *mut sqlite3_stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, msg_type); - sqlite3_bind_int( - stmt, - 3i32, - if msg_type2 > 0i32 { - msg_type2 - } else { - msg_type - }, - ); - sqlite3_bind_int( - stmt, - 4i32, - if msg_type3 > 0i32 { - msg_type3 - } else { - msg_type - }, - ); - while sqlite3_step(stmt) == 100i32 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0i32) as uint32_t); - } - sqlite3_finalize(stmt); - ret + context.sql.query_map( + "SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;", + params![ + chat_id as i32, + msg_type, + if msg_type2 > 0 { + msg_type2 + } else { + msg_type + }, if msg_type3 > 0 { + msg_type3 + } else { + msg_type + }, + ], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; + } + Ok(ret) + } + ).unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_next_media( @@ -1410,105 +1309,127 @@ pub unsafe fn dc_get_next_media( ret_msg_id } -pub unsafe fn dc_archive_chat(context: &Context, chat_id: uint32_t, archive: libc::c_int) { - if chat_id <= 9i32 as libc::c_uint || archive != 0i32 && archive != 1i32 { - return; +pub fn dc_archive_chat(context: &Context, chat_id: u32, archive: libc::c_int) -> bool { + if chat_id <= 9 || archive != 0 && archive != 1 { + return true; } if 0 != archive { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + if sql::execute( context, &context.sql, - b"UPDATE msgs SET state=13 WHERE chat_id=? AND state=10;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET state=13 WHERE chat_id=? AND state=10;", + params![chat_id as i32], + ) + .is_err() + { + return false; + } } - let stmt_0: *mut sqlite3_stmt = dc_sqlite3_prepare( + if sql::execute( context, &context.sql, - b"UPDATE chats SET archived=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt_0, 1i32, archive); - sqlite3_bind_int(stmt_0, 2i32, chat_id as libc::c_int); - sqlite3_step(stmt_0); - sqlite3_finalize(stmt_0); - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); -} - -pub unsafe fn dc_delete_chat(context: &Context, chat_id: uint32_t) { - /* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */ - let obj: *mut Chat = dc_chat_new(context); - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - if !(chat_id <= 9i32 as libc::c_uint) { - if dc_chat_load_from_db(obj, chat_id) { - q3 = sqlite3_mprintf( - b"DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=%i);\x00" - as *const u8 as *const libc::c_char, - chat_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { - sqlite3_free(q3 as *mut libc::c_void); - q3 = sqlite3_mprintf( - b"DELETE FROM msgs WHERE chat_id=%i;\x00" as *const u8 as *const libc::c_char, - chat_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { - sqlite3_free(q3 as *mut libc::c_void); - q3 = sqlite3_mprintf( - b"DELETE FROM chats_contacts WHERE chat_id=%i;\x00" as *const u8 - as *const libc::c_char, - chat_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { - sqlite3_free(q3 as *mut libc::c_void); - q3 = sqlite3_mprintf( - b"DELETE FROM chats WHERE id=%i;\x00" as *const u8 - as *const libc::c_char, - chat_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { - sqlite3_free(q3 as *mut libc::c_void); - q3 = 0 as *mut libc::c_char; - context.call_cb( - Event::MSGS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - dc_job_kill_action(context, 105i32); - dc_job_add(context, 105i32, 0i32, 0 as *const libc::c_char, 10i32); - } - } - } - } - } + "UPDATE chats SET archived=? WHERE id=?;", + params![archive, chat_id as i32], + ) + .is_err() + { + return false; } - dc_chat_unref(obj); - sqlite3_free(q3 as *mut libc::c_void); + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + + true } -pub unsafe fn dc_get_chat_contacts(context: &Context, chat_id: uint32_t) -> *mut dc_array_t { +pub fn dc_delete_chat(context: &Context, chat_id: u32) -> bool { + /* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */ + if chat_id <= 9 { + return false; + } + let obj = unsafe { dc_chat_new(context) }; + if !dc_chat_load_from_db(obj, chat_id) { + return false; + } + unsafe { dc_chat_unref(obj) }; + + if sql::execute( + context, + &context.sql, + "DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);", + params![chat_id as i32], + ) + .is_err() + { + return false; + } + if sql::execute( + context, + &context.sql, + "DELETE FROM msgs WHERE chat_id=?;", + params![chat_id as i32], + ) + .is_err() + { + return false; + } + if sql::execute( + context, + &context.sql, + "DELETE FROM chats_contacts WHERE chat_id=?;", + params![chat_id as i32], + ) + .is_err() + { + return false; + } + if sql::execute( + context, + &context.sql, + "DELETE FROM chats WHERE id=?;", + params![chat_id as i32], + ) + .is_err() + { + return false; + } + + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + + dc_job_kill_action(context, 105); + unsafe { dc_job_add(context, 105, 0, 0 as *const libc::c_char, 10) }; + + true +} + +pub fn dc_get_chat_contacts(context: &Context, chat_id: u32) -> *mut dc_array_t { /* Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a groupchat but the chats stays visible, moreover, this makes displaying lists easier) */ - let ret: *mut dc_array_t = dc_array_new(100i32 as size_t); - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(chat_id == 1i32 as libc::c_uint) { - /* we could also create a list for all contacts in the deaddrop by searching contacts belonging to chats with chats.blocked=2, however, currently this is not needed */ - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT cc.contact_id FROM chats_contacts cc LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=? ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - while sqlite3_step(stmt) == 100i32 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0i32) as uint32_t); - } + if chat_id == 1 { + return std::ptr::null_mut(); } - sqlite3_finalize(stmt); - ret + // we could also create a list for all contacts in the deaddrop by searching contacts belonging to chats with + // chats.blocked=2, however, currently this is not needed + + context + .sql + .query_map( + "SELECT cc.contact_id FROM chats_contacts cc \ + LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=? \ + ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;", + params![chat_id as i32], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; + + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; + } + + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_chat(context: &Context, chat_id: uint32_t) -> *mut Chat { @@ -1532,48 +1453,43 @@ pub unsafe fn dc_create_group_chat( context: &Context, verified: libc::c_int, chat_name: *const libc::c_char, -) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let draft_txt: *mut libc::c_char; - let mut draft_msg: *mut dc_msg_t = 0 as *mut dc_msg_t; - let grpid: *mut libc::c_char; - let stmt: *mut sqlite3_stmt; - if chat_name.is_null() || *chat_name.offset(0isize) as libc::c_int == 0i32 { - return 0i32 as uint32_t; +) -> u32 { + let mut chat_id = 0; + + if chat_name.is_null() || *chat_name.offset(0) as libc::c_int == 0 { + return 0; } - draft_txt = dc_stock_str_repl_string(context, 14i32, chat_name); - grpid = dc_create_id(); - stmt = dc_sqlite3_prepare( + + let draft_txt = dc_stock_str_repl_string(context, 14, chat_name); + let grpid = as_str(dc_create_id()); + + if sql::execute( context, &context.sql, - b"INSERT INTO chats (type, name, grpid, param) VALUES(?, ?, ?, \'U=1\');\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, if 0 != verified { 130i32 } else { 120i32 }); - sqlite3_bind_text(stmt, 2i32, chat_name, -1i32, None); - sqlite3_bind_text(stmt, 3i32, grpid, -1i32, None); - if !(sqlite3_step(stmt) != 101i32) { - chat_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"chats\x00" as *const u8 as *const libc::c_char, - b"grpid\x00" as *const u8 as *const libc::c_char, - grpid, - ); - if !(chat_id == 0i32 as libc::c_uint) { - if !(0 == dc_add_to_chat_contacts_table(context, chat_id, 1i32 as uint32_t)) { - draft_msg = dc_msg_new(context, 10i32); + "INSERT INTO chats (type, name, grpid, param) VALUES(?, ?, ?, \'U=1\');", + params![ + if verified != 0 { 130 } else { 120 }, + as_str(chat_name), + grpid + ], + ) + .is_ok() + { + chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid); + if chat_id != 0 { + if 0 != dc_add_to_chat_contacts_table(context, chat_id, 1) { + let draft_msg = dc_msg_new(context, 10); dc_msg_set_text(draft_msg, draft_txt); set_draft_raw(context, chat_id, draft_msg); + dc_msg_unref(draft_msg); } } } - sqlite3_finalize(stmt); + free(draft_txt as *mut libc::c_void); - dc_msg_unref(draft_msg); - free(grpid as *mut libc::c_void); + if 0 != chat_id { - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); } chat_id @@ -1582,96 +1498,86 @@ pub unsafe fn dc_create_group_chat( /* you MUST NOT modify this or the following strings */ // Context functions to work with chats // TODO should return bool /rtn -pub unsafe fn dc_add_to_chat_contacts_table( +pub fn dc_add_to_chat_contacts_table( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, ) -> libc::c_int { - /* add a contact to a chat; the function does not check the type or if any of the record exist or are already added to the chat! */ - let ret: libc::c_int; - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + // add a contact to a chat; the function does not check the type or if any of the record exist or are already + // added to the chat! + sql::execute( context, &context.sql, - b"INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, contact_id as libc::c_int); - ret = if sqlite3_step(stmt) == 101i32 { - 1i32 - } else { - 0i32 - }; - sqlite3_finalize(stmt); - ret + "INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)", + params![chat_id as i32, contact_id as i32], + ) + .is_ok() as libc::c_int } pub unsafe fn dc_add_contact_to_chat( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, ) -> libc::c_int { - dc_add_contact_to_chat_ex(context, chat_id, contact_id, 0i32) + dc_add_contact_to_chat_ex(context, chat_id, contact_id, 0) } // TODO should return bool /rtn pub unsafe fn dc_add_contact_to_chat_ex( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, flags: libc::c_int, ) -> libc::c_int { let mut current_block: u64; - let mut success: libc::c_int = 0i32; + let mut success: libc::c_int = 0; let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); let chat: *mut Chat = dc_chat_new(context); let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; - if !(contact.is_null() || chat_id <= 9i32 as libc::c_uint) { + + if !(contact.is_null() || chat_id <= 9 as libc::c_uint) { dc_reset_gossiped_timestamp(context, chat_id); /*this also makes sure, not contacts are added to special or normal chats*/ - if !(0i32 == real_group_exists(context, chat_id) - || !dc_real_contact_exists(context, contact_id) && contact_id != 1i32 as libc::c_uint + if !(0 == real_group_exists(context, chat_id) + || !dc_real_contact_exists(context, contact_id) && contact_id != 1 as libc::c_uint || !dc_chat_load_from_db(chat, chat_id)) { - if !(dc_is_contact_in_chat(context, chat_id, 1i32 as uint32_t) == 1i32) { - dc_log_event( + if !(dc_is_contact_in_chat(context, chat_id, 1 as uint32_t) == 1) { + log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, - 0i32, - b"Cannot add contact to group; self not in group.\x00" as *const u8 - as *const libc::c_char, + 0, + "Cannot add contact to group; self not in group.", ); } else { /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */ - if 0 != flags & 0x1i32 && dc_param_get_int((*chat).param, 'U' as i32, 0i32) == 1i32 - { + if 0 != flags & 0x1 && dc_param_get_int((*chat).param, 'U' as i32, 0) == 1 { dc_param_set((*chat).param, 'U' as i32, 0 as *const libc::c_char); dc_chat_update_param(chat); } - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if !(strcasecmp((*contact).addr, self_addr) == 0i32) { - /* ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly. if SELF is not in the group, members cannot be added at all. */ + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + if as_str((*contact).addr) != &self_addr { + // ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly. + // if SELF is not in the group, members cannot be added at all. + if 0 != dc_is_contact_in_chat(context, chat_id, contact_id) { - if 0 == flags & 0x1i32 { - success = 1i32; + if 0 == flags & 0x1 { + success = 1; current_block = 12326129973959287090; } else { current_block = 15125582407903384992; } } else { // else continue and send status mail - if (*chat).type_0 == 130i32 { - if dc_contact_is_verified(contact) != 2i32 { - dc_log_error(context, 0i32, - b"Only bidirectional verified contacts can be added to verified groups.\x00" - as *const u8 as - *const libc::c_char); + if (*chat).type_0 == 130 { + if dc_contact_is_verified(contact) != 2 { + error!( + context, 0, + "Only bidirectional verified contacts can be added to verified groups." + ); current_block = 12326129973959287090; } else { current_block = 13472856163611868459; @@ -1682,8 +1588,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( match current_block { 12326129973959287090 => {} _ => { - if 0i32 - == dc_add_to_chat_contacts_table(context, chat_id, contact_id) + if 0 == dc_add_to_chat_contacts_table(context, chat_id, contact_id) { current_block = 12326129973959287090; } else { @@ -1695,16 +1600,16 @@ pub unsafe fn dc_add_contact_to_chat_ex( match current_block { 12326129973959287090 => {} _ => { - if dc_param_get_int((*chat).param, 'U' as i32, 0i32) == 0i32 { - (*msg).type_0 = 10i32; + if dc_param_get_int((*chat).param, 'U' as i32, 0) == 0 { + (*msg).type_0 = 10; (*msg).text = dc_stock_system_msg( context, - 17i32, + 17, (*contact).addr, 0 as *const libc::c_char, - 1i32 as uint32_t, + 1 as uint32_t, ); - dc_param_set_int((*msg).param, 'S' as i32, 4i32); + dc_param_set_int((*msg).param, 'S' as i32, 4); dc_param_set((*msg).param, 'E' as i32, (*contact).addr); dc_param_set_int((*msg).param, 'F' as i32, flags); (*msg).id = dc_send_msg(context, chat_id, msg); @@ -1717,9 +1622,9 @@ pub unsafe fn dc_add_contact_to_chat_ex( context.call_cb( Event::MSGS_CHANGED, chat_id as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - success = 1i32 + success = 1; } } } @@ -1729,122 +1634,105 @@ pub unsafe fn dc_add_contact_to_chat_ex( dc_chat_unref(chat); dc_contact_unref(contact); dc_msg_unref(msg); - free(self_addr as *mut libc::c_void); success } // TODO should return bool /rtn -unsafe fn real_group_exists(context: &Context, chat_id: uint32_t) -> libc::c_int { +fn real_group_exists(context: &Context, chat_id: u32) -> libc::c_int { // check if a group or a verified group exists under the given ID - let stmt: *mut sqlite3_stmt; - let mut ret: libc::c_int = 0i32; - if !context.sql.is_open() || chat_id <= 9i32 as libc::c_uint { - return 0i32; + if !context.sql.is_open() || chat_id <= 9 { + return 02; } - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM chats WHERE id=? AND (type=120 OR type=130);\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - ret = 1i32 - } - sqlite3_finalize(stmt); - ret + + context + .sql + .exists( + "SELECT id FROM chats WHERE id=? AND (type=120 OR type=130);", + params![chat_id as i32], + ) + .unwrap_or_default() as libc::c_int } -pub unsafe fn dc_reset_gossiped_timestamp(context: &Context, chat_id: uint32_t) { +pub fn dc_reset_gossiped_timestamp(context: &Context, chat_id: u32) { dc_set_gossiped_timestamp(context, chat_id, 0); } -pub unsafe fn dc_set_gossiped_timestamp(context: &Context, chat_id: uint32_t, timestamp: i64) { - let stmt: *mut sqlite3_stmt; +pub fn dc_set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) { if 0 != chat_id { - dc_log_info( + info!( context, - 0i32, - b"set gossiped_timestamp for chat #%i to %i.\x00" as *const u8 as *const libc::c_char, - chat_id as libc::c_int, - timestamp as libc::c_int, + 0, "set gossiped_timestamp for chat #{} to {}.", chat_id, timestamp, ); - stmt = dc_sqlite3_prepare( + + sql::execute( context, &context.sql, - b"UPDATE chats SET gossiped_timestamp=? WHERE id=?;\x00" as *const u8 - as *const libc::c_char, + "UPDATE chats SET gossiped_timestamp=? WHERE id=?;", + params![timestamp, chat_id as i32], ); - sqlite3_bind_int64(stmt, 1i32, timestamp as sqlite3_int64); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); } else { - dc_log_info( + info!( context, - 0i32, - b"set gossiped_timestamp for all chats to %i.\x00" as *const u8 as *const libc::c_char, - timestamp as libc::c_int, + 0, "set gossiped_timestamp for all chats to {}.", timestamp, ); - stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"UPDATE chats SET gossiped_timestamp=?;\x00" as *const u8 as *const libc::c_char, + "UPDATE chats SET gossiped_timestamp=?;", + params![timestamp], ); - sqlite3_bind_int64(stmt, 1i32, timestamp as sqlite3_int64); } - sqlite3_step(stmt); - sqlite3_finalize(stmt); } // TODO should return bool /rtn pub unsafe fn dc_remove_contact_from_chat( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, ) -> libc::c_int { - let mut success: libc::c_int = 0i32; + let mut success: libc::c_int = 0; let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); let chat: *mut Chat = dc_chat_new(context); let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - if !(chat_id <= 9i32 as libc::c_uint - || contact_id <= 9i32 as libc::c_uint && contact_id != 1i32 as libc::c_uint) + + if !(chat_id <= 9 as libc::c_uint + || contact_id <= 9 as libc::c_uint && contact_id != 1 as libc::c_uint) { /* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */ /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */ - if !(0i32 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { - if !(dc_is_contact_in_chat(context, chat_id, 1i32 as uint32_t) == 1i32) { - dc_log_event( + if !(0 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { + if !(dc_is_contact_in_chat(context, chat_id, 1 as uint32_t) == 1) { + log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, - 0i32, - b"Cannot remove contact from chat; self not in group.\x00" as *const u8 - as *const libc::c_char, + 0, + "Cannot remove contact from chat; self not in group.", ); } else { /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */ if !contact.is_null() { - if dc_param_get_int((*chat).param, 'U' as i32, 0i32) == 0i32 { - (*msg).type_0 = 10i32; - if (*contact).id == 1i32 as libc::c_uint { + if dc_param_get_int((*chat).param, 'U' as i32, 0) == 0 { + (*msg).type_0 = 10; + if (*contact).id == 1 as libc::c_uint { dc_set_group_explicitly_left(context, (*chat).grpid); (*msg).text = dc_stock_system_msg( context, - 19i32, + 19, 0 as *const libc::c_char, 0 as *const libc::c_char, - 1i32 as uint32_t, + 1 as uint32_t, ) } else { (*msg).text = dc_stock_system_msg( context, - 18i32, + 18, (*contact).addr, 0 as *const libc::c_char, - 1i32 as uint32_t, + 1 as uint32_t, ) } - dc_param_set_int((*msg).param, 'S' as i32, 5i32); + dc_param_set_int((*msg).param, 'S' as i32, 5); dc_param_set((*msg).param, 'E' as i32, (*contact).addr); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( @@ -1854,24 +1742,21 @@ pub unsafe fn dc_remove_contact_from_chat( ); } } - q3 = sqlite3_mprintf( - b"DELETE FROM chats_contacts WHERE chat_id=%i AND contact_id=%i;\x00" - as *const u8 as *const libc::c_char, - chat_id, - contact_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); - success = 1i32 + if sql::execute( + context, + &context.sql, + "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?;", + params![chat_id as i32, contact_id as i32], + ) + .is_ok() + { + context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); + success = 1; } } } } - sqlite3_free(q3 as *mut libc::c_void); + dc_chat_unref(chat); dc_contact_unref(contact); dc_msg_unref(msg); @@ -1879,33 +1764,26 @@ pub unsafe fn dc_remove_contact_from_chat( success } -pub unsafe fn dc_set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { +pub fn dc_set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { if 0 == dc_is_group_explicitly_left(context, grpid) { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"INSERT INTO leftgrps (grpid) VALUES(?);\x00" as *const u8 as *const libc::c_char, + "INSERT INTO leftgrps (grpid) VALUES(?);", + params![as_str(grpid)], ); - sqlite3_bind_text(stmt, 1i32, grpid, -1i32, None); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - }; + } } // TODO should return bool /rtn -pub unsafe fn dc_is_group_explicitly_left( - context: &Context, - grpid: *const libc::c_char, -) -> libc::c_int { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM leftgrps WHERE grpid=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, grpid, -1i32, None); - let ret: libc::c_int = (sqlite3_step(stmt) == 100i32) as libc::c_int; - sqlite3_finalize(stmt); - ret +pub fn dc_is_group_explicitly_left(context: &Context, grpid: *const libc::c_char) -> libc::c_int { + context + .sql + .exists( + "SELECT id FROM leftgrps WHERE grpid=?;", + params![as_str(grpid)], + ) + .unwrap_or_default() as libc::c_int } // TODO should return bool /rtn @@ -1918,7 +1796,7 @@ pub unsafe fn dc_set_chat_name( let mut success: libc::c_int = 0i32; let chat: *mut Chat = dc_chat_new(context); let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; + if !(new_name.is_null() || *new_name.offset(0isize) as libc::c_int == 0i32 || chat_id <= 9i32 as libc::c_uint) @@ -1927,22 +1805,26 @@ pub unsafe fn dc_set_chat_name( if strcmp((*chat).name, new_name) == 0i32 { success = 1i32 } else if !(dc_is_contact_in_chat(context, chat_id, 1i32 as uint32_t) == 1i32) { - dc_log_event( + log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, - 0i32, - b"Cannot set chat name; self not in group\x00" as *const u8 - as *const libc::c_char, + 0, + "Cannot set chat name; self not in group", ); } else { /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */ - q3 = sqlite3_mprintf( - b"UPDATE chats SET name=%Q WHERE id=%i;\x00" as *const u8 - as *const libc::c_char, - new_name, - chat_id, - ); - if !(0 == dc_sqlite3_execute(context, &context.sql, q3)) { + if sql::execute( + context, + &context.sql, + format!( + "UPDATE chats SET name='{}' WHERE id={};", + as_str(new_name), + chat_id as i32 + ), + params![], + ) + .is_ok() + { if dc_param_get_int((*chat).param, 'U' as i32, 0i32) == 0i32 { (*msg).type_0 = 10i32; (*msg).text = dc_stock_system_msg( @@ -1972,7 +1854,6 @@ pub unsafe fn dc_set_chat_name( } } - sqlite3_free(q3 as *mut libc::c_void); dc_chat_unref(chat); dc_msg_unref(msg); @@ -1993,12 +1874,11 @@ pub unsafe fn dc_set_chat_profile_image( if !(chat_id <= 9i32 as libc::c_uint) { if !(0i32 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { if !(dc_is_contact_in_chat(context, chat_id, 1i32 as uint32_t) == 1i32) { - dc_log_event( + log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, - 0i32, - b"Cannot set chat profile image; self not in group.\x00" as *const u8 - as *const libc::c_char, + 0, + "Cannot set chat profile image; self not in group.", ); } else { /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */ @@ -2061,88 +1941,96 @@ pub unsafe fn dc_set_chat_profile_image( pub unsafe fn dc_forward_msgs( context: &Context, - msg_ids: *const uint32_t, + msg_ids: *const u32, msg_cnt: libc::c_int, - chat_id: uint32_t, + chat_id: u32, ) { - let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let chat: *mut Chat = dc_chat_new(context); - let contact: *mut dc_contact_t = dc_contact_new(context); - let created_db_entries: *mut carray = carray_new(16i32 as libc::c_uint); - let mut idsstr: *mut libc::c_char = 0 as *mut libc::c_char; - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut curr_timestamp: i64; - let original_param: *mut dc_param_t = dc_param_new(); - if !(msg_ids.is_null() || msg_cnt <= 0i32 || chat_id <= 9i32 as libc::c_uint) { - dc_unarchive_chat(context, chat_id); - if dc_chat_load_from_db(chat, chat_id) { - curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); - idsstr = dc_arr_to_string(msg_ids, msg_cnt); - q3 = sqlite3_mprintf( - b"SELECT id FROM msgs WHERE id IN(%s) ORDER BY timestamp,id\x00" as *const u8 - as *const libc::c_char, - idsstr, - ); - stmt = dc_sqlite3_prepare(context, &context.sql, q3); - loop { - if !(sqlite3_step(stmt) == 100i32) { - break; - } - let src_msg_id: libc::c_int = sqlite3_column_int(stmt, 0i32); - if !dc_msg_load_from_db(msg, context, src_msg_id as uint32_t) { - break; - } - dc_param_set_packed(original_param, (*(*msg).param).packed); - if (*msg).from_id != 1i32 as libc::c_uint { - dc_param_set_int((*msg).param, 'a' as i32, 1i32); - } - dc_param_set((*msg).param, 'c' as i32, 0 as *const libc::c_char); - dc_param_set((*msg).param, 'u' as i32, 0 as *const libc::c_char); - dc_param_set((*msg).param, 'S' as i32, 0 as *const libc::c_char); - let new_msg_id: uint32_t; - if (*msg).state == 18i32 { - let fresh9 = curr_timestamp; - curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh9); - let save_param: *mut dc_param_t = (*msg).param; - (*msg).param = original_param; - (*msg).id = src_msg_id as uint32_t; - let old_fwd: *mut libc::c_char = dc_param_get( - (*msg).param, - 'P' as i32, - b"\x00" as *const u8 as *const libc::c_char, - ); - let new_fwd: *mut libc::c_char = dc_mprintf( - b"%s %d\x00" as *const u8 as *const libc::c_char, - old_fwd, - new_msg_id, - ); - dc_param_set((*msg).param, 'P' as i32, new_fwd); - dc_msg_save_param_to_disk(msg); - free(new_fwd as *mut libc::c_void); - free(old_fwd as *mut libc::c_void); - (*msg).param = save_param - } else { - (*msg).state = 20i32; - let fresh10 = curr_timestamp; - curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); - dc_job_send_msg(context, new_msg_id); - } - carray_add( - created_db_entries, - chat_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); - carray_add( - created_db_entries, - new_msg_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); - } - } + if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= 9 { + return; } + + let msg = dc_msg_new_untyped(context); + let chat = dc_chat_new(context); + let contact = dc_contact_new(context); + let created_db_entries = carray_new(16); + let mut idsstr = 0 as *mut libc::c_char; + let mut curr_timestamp: i64; + + let original_param: *mut dc_param_t = dc_param_new(); + dc_unarchive_chat(context, chat_id); + if dc_chat_load_from_db(chat, chat_id) { + curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); + idsstr = dc_arr_to_string(msg_ids, msg_cnt); + + context + .sql + .query_map( + format!( + "SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id", + as_str(idsstr) + ), + params![], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + let src_msg_id = id?; + if !dc_msg_load_from_db(msg, context, src_msg_id as u32) { + break; + } + dc_param_set_packed(original_param, (*(*msg).param).packed); + if (*msg).from_id != 1i32 as libc::c_uint { + dc_param_set_int((*msg).param, 'a' as i32, 1i32); + } + dc_param_set((*msg).param, 'c' as i32, 0 as *const libc::c_char); + dc_param_set((*msg).param, 'u' as i32, 0 as *const libc::c_char); + dc_param_set((*msg).param, 'S' as i32, 0 as *const libc::c_char); + let new_msg_id: uint32_t; + if (*msg).state == 18i32 { + let fresh9 = curr_timestamp; + curr_timestamp = curr_timestamp + 1; + new_msg_id = prepare_msg_raw(context, chat, msg, fresh9); + let save_param: *mut dc_param_t = (*msg).param; + (*msg).param = original_param; + (*msg).id = src_msg_id as uint32_t; + let old_fwd: *mut libc::c_char = dc_param_get( + (*msg).param, + 'P' as i32, + b"\x00" as *const u8 as *const libc::c_char, + ); + let new_fwd: *mut libc::c_char = dc_mprintf( + b"%s %d\x00" as *const u8 as *const libc::c_char, + old_fwd, + new_msg_id, + ); + dc_param_set((*msg).param, 'P' as i32, new_fwd); + dc_msg_save_param_to_disk(msg); + free(new_fwd as *mut libc::c_void); + free(old_fwd as *mut libc::c_void); + (*msg).param = save_param + } else { + (*msg).state = 20i32; + let fresh10 = curr_timestamp; + curr_timestamp = curr_timestamp + 1; + new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); + dc_job_send_msg(context, new_msg_id); + } + carray_add( + created_db_entries, + chat_id as uintptr_t as *mut libc::c_void, + 0 as *mut libc::c_uint, + ); + carray_add( + created_db_entries, + new_msg_id as uintptr_t as *mut libc::c_void, + 0 as *mut libc::c_uint, + ); + } + Ok(()) + }, + ) + .unwrap(); // TODO: better error handling + } + if !created_db_entries.is_null() { let mut i = 0u32; let icnt = carray_count(created_db_entries); @@ -2159,9 +2047,7 @@ pub unsafe fn dc_forward_msgs( dc_contact_unref(contact); dc_msg_unref(msg); dc_chat_unref(chat); - sqlite3_finalize(stmt); free(idsstr as *mut libc::c_void); - sqlite3_free(q3 as *mut libc::c_void); dc_param_unref(original_param); } @@ -2188,32 +2074,33 @@ pub unsafe fn dc_chat_get_name(chat: *const Chat) -> *mut libc::c_char { pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *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 { return dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char); } - if (*chat).type_0 == 100i32 && 0 != dc_param_exists((*chat).param, 'K' as i32) { - ret = dc_stock_str((*chat).context, 50i32) - } else if (*chat).type_0 == 100i32 { - let r: libc::c_int; - let stmt: *mut sqlite3_stmt = - dc_sqlite3_prepare( - (*chat).context, &(*chat).context.sql, - b"SELECT c.addr FROM chats_contacts cc LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=?;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, (*chat).id as libc::c_int); - r = sqlite3_step(stmt); - if r == 100i32 { - ret = dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char) - } - sqlite3_finalize(stmt); - } else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { - let cnt: libc::c_int; - if (*chat).id == 1i32 as libc::c_uint { - ret = dc_stock_str((*chat).context, 8i32) + + let mut ret: *mut libc::c_char = std::ptr::null_mut(); + if (*chat).type_0 == 100 && 0 != dc_param_exists((*chat).param, 'K' as i32) { + ret = dc_stock_str((*chat).context, 50) + } else if (*chat).type_0 == 100 { + let ret_raw: String = (*chat) + .context + .sql + .query_row_col( + (*chat).context, + "SELECT c.addr FROM chats_contacts cc \ + LEFT JOIN contacts c ON c.id=cc.contact_id \ + WHERE cc.chat_id=?;", + params![(*chat).id as i32], + 0, + ) + .unwrap_or_else(|| "Err".into()); + ret = dc_strdup(to_cstring(ret_raw).as_ptr()); + } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { + if (*chat).id == 1 { + ret = dc_stock_str((*chat).context, 8) } else { - cnt = dc_get_chat_contact_cnt((*chat).context, (*chat).id); - ret = dc_stock_str_repl_int((*chat).context, 4i32, cnt) + let cnt = dc_get_chat_contact_cnt((*chat).context, (*chat).id); + ret = dc_stock_str_repl_int((*chat).context, 4, cnt) } } return if !ret.is_null() { @@ -2223,20 +2110,16 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { }; } -pub unsafe fn dc_get_chat_contact_cnt(context: &Context, chat_id: uint32_t) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM chats_contacts WHERE chat_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - ret = sqlite3_column_int(stmt, 0i32) - } - sqlite3_finalize(stmt); - ret +pub fn dc_get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM chats_contacts WHERE chat_id=?;", + params![chat_id as i32], + 0, + ) + .unwrap_or_default() } pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char { @@ -2324,23 +2207,21 @@ pub unsafe fn dc_chat_is_sending_locations(chat: *const Chat) -> libc::c_int { (*chat).is_sending_locations } -pub unsafe fn dc_get_chat_cnt(context: &Context) -> size_t { - let mut ret: size_t = 0i32 as size_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; +pub fn dc_get_chat_cnt(context: &Context) -> usize { if context.sql.is_open() { /* no database, no chats - this is no error (needed eg. for information) */ - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM chats WHERE id>9 AND blocked=0;\x00" as *const u8 - as *const libc::c_char, - ); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) as size_t - } + context + .sql + .query_row_col::<_, isize>( + context, + "SELECT COUNT(*) FROM chats WHERE id>9 AND blocked=0;", + params![], + 0, + ) + .unwrap_or_default() as usize + } else { + 0 } - sqlite3_finalize(stmt); - ret } pub unsafe fn dc_get_chat_id_by_grpid( @@ -2348,77 +2229,73 @@ pub unsafe fn dc_get_chat_id_by_grpid( grpid: *const libc::c_char, ret_blocked: *mut libc::c_int, ret_verified: *mut libc::c_int, -) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let stmt: *mut sqlite3_stmt; +) -> u32 { if !ret_blocked.is_null() { - *ret_blocked = 0i32 + *ret_blocked = 0; } if !ret_verified.is_null() { - *ret_verified = 0i32 + *ret_verified = 0; } - stmt = dc_sqlite3_prepare( + context + .sql + .query_row( + "SELECT id, blocked, type FROM chats WHERE grpid=?;", + params![as_str(grpid)], + |row| { + let chat_id = row.get(0)?; + if !ret_blocked.is_null() { + *ret_blocked = row.get(1)?; + } + if !ret_verified.is_null() { + let v: i32 = row.get(2)?; + *ret_verified = (v == 130) as libc::c_int; + } + Ok(chat_id) + }, + ) + .unwrap_or_default() +} + +pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc::c_char) { + if text.is_null() { + return; + } + let rfc724_mid = unsafe { + dc_create_outgoing_rfc724_mid( + 0 as *const libc::c_char, + b"@device\x00" as *const u8 as *const libc::c_char, + ) + }; + + if context.sql.execute( + "INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);", + params![ + chat_id as i32, + 2, + 2, + unsafe {dc_create_smeared_timestamp(context)}, + 10, + 13, + as_str(text), + as_str(rfc724_mid), + ] + ).is_err() { + unsafe { free(rfc724_mid as *mut libc::c_void) }; + return; + } + + let msg_id = sql::get_rowid( context, &context.sql, - b"SELECT id, blocked, type FROM chats WHERE grpid=?;\x00" as *const u8 - as *const libc::c_char, + "msgs", + "rfc724_mid", + as_str(rfc724_mid), ); - sqlite3_bind_text(stmt, 1i32, grpid, -1i32, None); - if sqlite3_step(stmt) == 100i32 { - chat_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - if !ret_blocked.is_null() { - *ret_blocked = sqlite3_column_int(stmt, 1i32) - } - if !ret_verified.is_null() { - *ret_verified = (sqlite3_column_int(stmt, 2i32) == 130i32) as libc::c_int - } - } - - sqlite3_finalize(stmt); - chat_id -} - -pub unsafe fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc::c_char) { - let msg_id: uint32_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let rfc724_mid: *mut libc::c_char = dc_create_outgoing_rfc724_mid( - 0 as *const libc::c_char, - b"@device\x00" as *const u8 as *const libc::c_char, + unsafe { free(rfc724_mid as *mut libc::c_void) }; + context.call_cb( + Event::MSGS_CHANGED, + chat_id as uintptr_t, + msg_id as uintptr_t, ); - if !text.is_null() { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, 2i32); - sqlite3_bind_int(stmt, 3i32, 2i32); - sqlite3_bind_int64( - stmt, - 4i32, - dc_create_smeared_timestamp(context) as sqlite3_int64, - ); - sqlite3_bind_int(stmt, 5i32, 10i32); - sqlite3_bind_int(stmt, 6i32, 13i32); - sqlite3_bind_text(stmt, 7i32, text, -1i32, None); - sqlite3_bind_text(stmt, 8i32, rfc724_mid, -1i32, None); - if !(sqlite3_step(stmt) != 101i32) { - msg_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"msgs\x00" as *const u8 as *const libc::c_char, - b"rfc724_mid\x00" as *const u8 as *const libc::c_char, - rfc724_mid, - ); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg_id as uintptr_t, - ); - } - } - free(rfc724_mid as *mut libc::c_void); - sqlite3_finalize(stmt); } diff --git a/src/dc_chatlist.rs b/src/dc_chatlist.rs index e4216c998..555416e3d 100644 --- a/src/dc_chatlist.rs +++ b/src/dc_chatlist.rs @@ -4,7 +4,6 @@ use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_lot::*; use crate::dc_msg::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; use crate::types::*; @@ -27,19 +26,14 @@ pub unsafe fn dc_get_chatlist<'a>( query_str: *const libc::c_char, query_id: uint32_t, ) -> *mut dc_chatlist_t<'a> { - let mut success: libc::c_int = 0i32; - let obj: *mut dc_chatlist_t = dc_chatlist_new(context); + let obj = dc_chatlist_new(context); - if !(0 == dc_chatlist_load_from_db(obj, listflags, query_str, query_id)) { - success = 1i32 + if 0 != dc_chatlist_load_from_db(obj, listflags, query_str, query_id) { + return obj; } - if 0 != success { - return obj; - } else { - dc_chatlist_unref(obj); - return 0 as *mut dc_chatlist_t; - }; + dc_chatlist_unref(obj); + return 0 as *mut dc_chatlist_t; } /** @@ -120,146 +114,173 @@ unsafe fn dc_chatlist_load_from_db( mut chatlist: *mut dc_chatlist_t, listflags: libc::c_int, query__: *const libc::c_char, - query_contact_id: uint32_t, + query_contact_id: u32, ) -> libc::c_int { - let current_block: u64; - //clock_t start = clock(); - let mut success: libc::c_int = 0i32; - let mut add_archived_link_item: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut strLikeCmd: *mut libc::c_char = 0 as *mut libc::c_char; - let mut query: *mut libc::c_char = 0 as *mut libc::c_char; - if !(chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32) { - dc_chatlist_empty(chatlist); - // select with left join and minimum: - // - the inner select must use `hidden` and _not_ `m.hidden` - // which would refer the outer select and take a lot of time - // - `GROUP BY` is needed several messages may have the same timestamp - // - the list starts with the newest chats - // nb: the query currently shows messages from blocked contacts in groups. - // however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs() - // (otherwise it would be hard to follow conversations, wa and tg do the same) - // for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not - // shown at all permanent in the chatlist. - if 0 != query_contact_id { - stmt = - dc_sqlite3_prepare( - (*chatlist).context, - &(*chatlist).context.sql, - b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00" - as *const u8 as *const libc::c_char - ); - sqlite3_bind_int(stmt, 1i32, query_contact_id as libc::c_int); - current_block = 3437258052017859086; - } else if 0 != listflags & 0x1i32 { - stmt = - dc_sqlite3_prepare( - (*chatlist).context, - &(*chatlist).context.sql, - b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=1 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00" - as *const u8 as *const libc::c_char); - current_block = 3437258052017859086; - } else if query__.is_null() { - if 0 == listflags & 0x2i32 { - let last_deaddrop_fresh_msg_id: uint32_t = - get_last_deaddrop_fresh_msg((*chatlist).context); - if last_deaddrop_fresh_msg_id > 0i32 as libc::c_uint { - dc_array_add_id((*chatlist).chatNlastmsg_ids, 1i32 as uint32_t); - dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id); - } - add_archived_link_item = 1i32 - } - stmt = - dc_sqlite3_prepare( - (*chatlist).context, - &(*chatlist).context.sql, - b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=0 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00" - as *const u8 as *const libc::c_char); - current_block = 3437258052017859086; - } else { - query = dc_strdup(query__); - dc_trim(query); - if *query.offset(0isize) as libc::c_int == 0i32 { - success = 1i32; - current_block = 15179736777190528364; - } else { - strLikeCmd = dc_mprintf(b"%%%s%%\x00" as *const u8 as *const libc::c_char, query); - stmt = - dc_sqlite3_prepare( - (*chatlist).context, - &(*chatlist).context.sql, - b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.name LIKE ? GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, strLikeCmd, -1i32, None); - current_block = 3437258052017859086; - } + if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 { + return 0; + } + dc_chatlist_empty(chatlist); + + let mut add_archived_link_item = 0; + + // select with left join and minimum: + // - the inner select must use `hidden` and _not_ `m.hidden` + // which would refer the outer select and take a lot of time + // - `GROUP BY` is needed several messages may have the same timestamp + // - the list starts with the newest chats + // nb: the query currently shows messages from blocked contacts in groups. + // however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs() + // (otherwise it would be hard to follow conversations, wa and tg do the same) + // for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not + // shown at all permanent in the chatlist. + + let process_row = |row: &rusqlite::Row| { + let chat_id: i32 = row.get(0)?; + // TODO: verify that it is okay for this to be Null + let msg_id: i32 = row.get(1).unwrap_or_default(); + + Ok((chat_id, msg_id)) + }; + + let process_rows = |rows: rusqlite::MappedRows<_>| { + for row in rows { + let (id1, id2) = row?; + + dc_array_add_id((*chatlist).chatNlastmsg_ids, id1 as u32); + dc_array_add_id((*chatlist).chatNlastmsg_ids, id2 as u32); } - match current_block { - 15179736777190528364 => {} - _ => { - while sqlite3_step(stmt) == 100i32 { - dc_array_add_id( - (*chatlist).chatNlastmsg_ids, - sqlite3_column_int(stmt, 0i32) as uint32_t, - ); - dc_array_add_id( - (*chatlist).chatNlastmsg_ids, - sqlite3_column_int(stmt, 1i32) as uint32_t, - ); - } - if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0i32 { - if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 - && 0 != listflags & 0x4i32 - { - dc_array_add_id((*chatlist).chatNlastmsg_ids, 7i32 as uint32_t); - dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t); - } - dc_array_add_id((*chatlist).chatNlastmsg_ids, 6i32 as uint32_t); - dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t); - } - (*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids).wrapping_div(2); - success = 1i32 + Ok(()) + }; + + // nb: the query currently shows messages from blocked contacts in groups. + // however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs() + // (otherwise it would be hard to follow conversations, wa and tg do the same) + // for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not + // shown at all permanent in the chatlist. + + let success = if query_contact_id != 0 { + // show chats shared with a given contact + (*chatlist).context.sql.query_map( + "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \ + ON c.id=m.chat_id \ + AND m.timestamp=( SELECT MAX(timestamp) \ + FROM msgs WHERE chat_id=c.id \ + AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \ + AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \ + GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;", + params![query_contact_id as i32], + process_row, + process_rows, + ) + } else if 0 != listflags & 0x1 { + // show archived chats + (*chatlist).context.sql.query_map( + "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \ + ON c.id=m.chat_id \ + AND m.timestamp=( SELECT MAX(timestamp) \ + FROM msgs WHERE chat_id=c.id \ + AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \ + AND c.blocked=0 AND c.archived=1 GROUP BY c.id \ + ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;", + params![], + process_row, + process_rows, + ) + } else if query__.is_null() { + // show normal chatlist + if 0 == listflags & 0x2 { + let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg((*chatlist).context); + if last_deaddrop_fresh_msg_id > 0 { + dc_array_add_id((*chatlist).chatNlastmsg_ids, 1); + dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id); } + add_archived_link_item = 1; + } + (*chatlist).context.sql.query_map( + "SELECT c.id, m.id FROM chats c \ + LEFT JOIN msgs m \ + ON c.id=m.chat_id \ + AND m.timestamp=( SELECT MAX(timestamp) \ + FROM msgs WHERE chat_id=c.id \ + AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \ + AND c.blocked=0 AND c.archived=0 \ + GROUP BY c.id \ + ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;", + params![], + process_row, + process_rows, + ) + } else { + let query = to_string(query__).trim().to_string(); + if query.is_empty() { + return 1; + } else { + let strLikeCmd = format!("%{}%", query); + (*chatlist).context.sql.query_map( + "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \ + ON c.id=m.chat_id \ + AND m.timestamp=( SELECT MAX(timestamp) \ + FROM msgs WHERE chat_id=c.id \ + AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \ + AND c.blocked=0 AND c.name LIKE ? \ + GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;", + params![strLikeCmd], + process_row, + process_rows, + ) + } + }; + + if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0 { + if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 && 0 != listflags & 0x4 { + dc_array_add_id((*chatlist).chatNlastmsg_ids, 7); + dc_array_add_id((*chatlist).chatNlastmsg_ids, 0); + } + dc_array_add_id((*chatlist).chatNlastmsg_ids, 6); + dc_array_add_id((*chatlist).chatNlastmsg_ids, 0); + } + (*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids) / 2; + + match success { + Ok(_) => 1, + Err(err) => { + error!( + (*chatlist).context, + 0, "chatlist: failed to load from database: {:?}", err + ); + 0 } } - sqlite3_finalize(stmt); - free(query as *mut libc::c_void); - free(strLikeCmd as *mut libc::c_void); - success } // Context functions to work with chatlist -pub unsafe fn dc_get_archived_cnt(context: &Context) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;\x00" as *const u8 - as *const libc::c_char, - ); - if sqlite3_step(stmt) == 100i32 { - ret = sqlite3_column_int(stmt, 0i32) - } - sqlite3_finalize(stmt); - ret +pub fn dc_get_archived_cnt(context: &Context) -> libc::c_int { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;", + params![], + 0, + ) + .unwrap_or_default() } -unsafe fn get_last_deaddrop_fresh_msg(context: &Context) -> uint32_t { - let mut ret: uint32_t = 0i32 as uint32_t; - let stmt: *mut sqlite3_stmt; - stmt = - dc_sqlite3_prepare( +fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 { + // we have an index over the state-column, this should be sufficient as there are typically only few fresh messages + context + .sql + .query_row_col( context, - &context.sql, - b"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.state=10 AND m.hidden=0 AND c.blocked=2 ORDER BY m.timestamp DESC, m.id DESC;\x00" - as *const u8 as *const libc::c_char); - /* we have an index over the state-column, this should be sufficient as there are typically only few fresh messages */ - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) as uint32_t - } - sqlite3_finalize(stmt); - ret + "SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ + WHERE m.state=10 \ + AND m.hidden=0 \ + AND c.blocked=2 \ + ORDER BY m.timestamp DESC, m.id DESC;", + params![], + 0, + ) + .unwrap_or_default() } pub unsafe fn dc_chatlist_get_cnt(chatlist: *const dc_chatlist_t) -> size_t { diff --git a/src/dc_configure.rs b/src/dc_configure.rs index 1ec517cde..5276159a5 100644 --- a/src/dc_configure.rs +++ b/src/dc_configure.rs @@ -1,12 +1,11 @@ +use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; + use crate::constants::Event; use crate::context::Context; use crate::dc_e2ee::*; use crate::dc_job::*; -use crate::dc_log::*; use crate::dc_loginparam::*; use crate::dc_saxparser::*; -use crate::dc_sqlite3::*; -use crate::dc_strencode::*; use crate::dc_tools::*; use crate::imap::*; use crate::oauth2::*; @@ -27,13 +26,12 @@ pub struct dc_imapfolder_t { * Thunderbird's Autoconfigure ******************************************************************************/ /* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */ -#[derive(Copy, Clone)] #[repr(C)] -pub struct moz_autoconfigure_t { - pub in_0: *const dc_loginparam_t, +pub struct moz_autoconfigure_t<'a> { + pub in_0: &'a dc_loginparam_t, pub in_emaildomain: *mut libc::c_char, pub in_emaillocalpart: *mut libc::c_char, - pub out: *mut dc_loginparam_t, + pub out: dc_loginparam_t, pub out_imap_set: libc::c_int, pub out_smtp_set: libc::c_int, pub tag_server: libc::c_int, @@ -43,11 +41,10 @@ pub struct moz_autoconfigure_t { /* ****************************************************************************** * Outlook's Autodiscover ******************************************************************************/ -#[derive(Copy, Clone)] #[repr(C)] -pub struct outlk_autodiscover_t { - pub in_0: *const dc_loginparam_t, - pub out: *mut dc_loginparam_t, +pub struct outlk_autodiscover_t<'a> { + pub in_0: &'a dc_loginparam_t, + pub out: dc_loginparam_t, pub out_imap_set: libc::c_int, pub out_smtp_set: libc::c_int, pub tag_config: libc::c_int, @@ -57,11 +54,9 @@ pub struct outlk_autodiscover_t { // connect pub unsafe fn dc_configure(context: &Context) { if 0 != dc_has_ongoing(context) { - dc_log_warning( + warn!( context, - 0i32, - b"There is already another ongoing process running.\x00" as *const u8 - as *const libc::c_char, + 0, "There is already another ongoing process running.", ); return; } @@ -78,38 +73,31 @@ pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int { 0 } } -pub unsafe fn dc_is_configured(context: &Context) -> libc::c_int { - return if 0 - != dc_sqlite3_get_config_int( - context, - &context.sql, - b"configured\x00" as *const u8 as *const libc::c_char, - 0i32, - ) { - 1i32 +pub fn dc_is_configured(context: &Context) -> libc::c_int { + if context + .sql + .get_config_int(context, "configured") + .unwrap_or_default() + > 0 + { + 1 } else { - 0i32 - }; + 0 + } } -pub unsafe fn dc_stop_ongoing_process(context: &Context) { + +pub fn dc_stop_ongoing_process(context: &Context) { let s_a = context.running_state.clone(); let mut s = s_a.write().unwrap(); if s.ongoing_running && !s.shall_stop_ongoing { - dc_log_info( - context, - 0i32, - b"Signaling the ongoing process to stop ASAP.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Signaling the ongoing process to stop ASAP.",); s.shall_stop_ongoing = true; } else { - dc_log_info( - context, - 0i32, - b"No ongoing process to stop.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "No ongoing process to stop.",); }; } + // the other dc_job_do_DC_JOB_*() functions are declared static in the c-file pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) { let flags: libc::c_int; @@ -118,21 +106,12 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j let mut imap_connected_here: libc::c_int = 0i32; let mut smtp_connected_here: libc::c_int = 0i32; let mut ongoing_allocated_here: libc::c_int = 0i32; - let mvbox_folder: *mut libc::c_char = 0 as *mut libc::c_char; - let mut param: *mut dc_loginparam_t = 0 as *mut dc_loginparam_t; - /* just a pointer inside param, must not be freed! */ - let mut param_domain: *mut libc::c_char; - let mut param_addr_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char; - let mut param_autoconfig: *mut dc_loginparam_t = 0 as *mut dc_loginparam_t; + let mut param_autoconfig = None; if !(0 == dc_alloc_ongoing(context)) { ongoing_allocated_here = 1i32; if !context.sql.is_open() { - dc_log_error( - context, - 0i32, - b"Cannot configure, database not opened.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "Cannot configure, database not opened.",); } else { context.inbox.read().unwrap().disconnect(context); context @@ -148,11 +127,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j .imap .disconnect(context); context.smtp.clone().lock().unwrap().disconnect(); - dc_log_info( - context, - 0i32, - b"Configure ...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Configure ...",); let s_a = context.running_state.clone(); let s = s_a.read().unwrap(); @@ -169,23 +144,12 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j }) as uintptr_t, 0i32 as uintptr_t, ); - param = dc_loginparam_new(); - dc_loginparam_read( - context, - param, - &context.sql, - b"\x00" as *const u8 as *const libc::c_char, - ); - if (*param).addr.is_null() { - dc_log_error( - context, - 0i32, - b"Please enter the email address.\x00" as *const u8 as *const libc::c_char, - ); + let mut param = dc_loginparam_read(context, &context.sql, ""); + if param.addr.is_empty() { + error!(context, 0, "Please enter an email address.",); } else { - dc_trim((*param).addr); - if 0 != (*param).server_flags & 0x2i32 { + if 0 != param.server_flags & 0x2 { // the used oauth2 addr may differ, check this. // if dc_get_oauth2_addr() is not available in the oauth2 implementation, // just use the given one. @@ -194,43 +158,37 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } else { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 10i32 < 1i32 { - 1i32 - } else if 10i32 > 999i32 { - 999i32 + (if 10 < 1 { + 1 + } else if 10 > 999 { + 999 } else { - 10i32 + 10 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - let oauth2_addr = dc_get_oauth2_addr( - context, - as_str((*param).addr), - as_str((*param).mail_pw), - ); - if oauth2_addr.is_some() { - free((*param).addr as *mut libc::c_void); - (*param).addr = strdup(to_cstring(oauth2_addr.unwrap()).as_ptr()); - dc_sqlite3_set_config( - context, - &context.sql, - b"addr\x00" as *const u8 as *const libc::c_char, - (*param).addr, - ); + if let Some(oauth2_addr) = + dc_get_oauth2_addr(context, ¶m.addr, ¶m.mail_pw) + .and_then(|e| e.parse().ok()) + { + param.addr = oauth2_addr; + context + .sql + .set_config(context, "addr", Some(param.addr.as_str())); } if s.shall_stop_ongoing { current_block = 2927484062889439186; } else { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 20i32 < 1i32 { - 1i32 - } else if 20i32 > 999i32 { - 999i32 + (if 20 < 1 { + 1 + } else if 20 > 999 { + 999 } else { - 20i32 + 20 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); current_block = 7746103178988627676; } @@ -241,75 +199,64 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - param_domain = strchr((*param).addr, '@' as i32); - if param_domain.is_null() - || *param_domain.offset(0isize) as libc::c_int == 0i32 - { - dc_log_error( - context, - 0i32, - b"Bad email-address.\x00" as *const u8 as *const libc::c_char, - ); + let parsed: addr::Result = param.addr.parse(); + if parsed.is_err() { + error!(context, 0, "Bad email-address."); } else { - param_domain = param_domain.offset(1isize); - param_addr_urlencoded = dc_urlencode((*param).addr); - if (*param).mail_pw.is_null() { - (*param).mail_pw = dc_strdup(0 as *const libc::c_char) - } + let parsed = parsed.unwrap(); + let param_domain = parsed.host(); + let param_addr_urlencoded = + utf8_percent_encode(¶m.addr, DEFAULT_ENCODE_SET) + .to_string(); + if !s.shall_stop_ongoing { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 200i32 < 1i32 { - 1i32 - } else if 200i32 > 999i32 { - 999i32 + (if 200 < 1 { + 1 + } else if 200 > 999 { + 999 } else { - 200i32 + 200 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); /* 2. Autoconfig **************************************************************************/ - if (*param).mail_server.is_null() - && (*param).mail_port == 0i32 - && (*param).send_server.is_null() - && (*param).send_port == 0i32 - && (*param).send_user.is_null() - && (*param).server_flags & !0x2i32 == 0i32 + if param.mail_server.is_empty() + && param.mail_port == 0 + && param.send_server.is_empty() + && param.send_port == 0 + && param.send_user.is_empty() + && param.server_flags & !0x2 == 0 { /*&¶m->mail_user ==NULL -- the user can enter a loginname which is used by autoconfig then */ /*&¶m->send_pw ==NULL -- the password cannot be auto-configured and is no criterion for autoconfig or not */ /* flags but OAuth2 avoid autoconfig */ - let keep_flags: libc::c_int = - (*param).server_flags & 0x2i32; + let keep_flags = param.server_flags & 0x2; /* A. Search configurations from the domain used in the email-address, prefer encrypted */ - if param_autoconfig.is_null() { - let url: - *mut libc::c_char = - dc_mprintf(b"https://autoconfig.%s/mail/config-v1.1.xml?emailaddress=%s\x00" - as - *const u8 - as - *const libc::c_char, - param_domain, - param_addr_urlencoded); + if param_autoconfig.is_none() { + let url = format!( + "https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", + param_domain, + param_addr_urlencoded + ); param_autoconfig = - moz_autoconfigure(context, url, param); - free(url as *mut libc::c_void); + moz_autoconfigure(context, &url, ¶m); if s.shall_stop_ongoing { current_block = 2927484062889439186; } else { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 300i32 < 1i32 { - 1i32 - } else if 300i32 > 999i32 { - 999i32 + (if 300 < 1 { + 1 + } else if 300 > 999 { + 999 } else { - 300i32 + 300 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); current_block = 13325891313334703151; } @@ -319,34 +266,29 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - if param_autoconfig.is_null() { + if param_autoconfig.is_none() { // the doc does not mention `emailaddress=`, however, Thunderbird adds it, see https://releases.mozilla.org/pub/thunderbird/ , which makes some sense - let url_0: - *mut libc::c_char = - dc_mprintf(b"https://%s/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=%s\x00" - as - *const u8 - as - *const libc::c_char, - param_domain, - param_addr_urlencoded); + let url = format!( + "https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}", + param_domain, + param_addr_urlencoded + ); param_autoconfig = - moz_autoconfigure(context, url_0, param); - free(url_0 as *mut libc::c_void); + moz_autoconfigure(context, &url, ¶m); if s.shall_stop_ongoing { current_block = 2927484062889439186; } else { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 310i32 < 1i32 { - 1i32 - } else if 310i32 > 999i32 { - 999i32 + (if 310 < 1 { + 1 + } else if 310 > 999 { + 999 } else { - 310i32 + 310 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); current_block = 5597585068398118923; } @@ -356,44 +298,29 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - let mut i: libc::c_int = 0i32; + let mut i: libc::c_int = 0; loop { - if !(i <= 1i32) { + if !(i <= 1) { current_block = 12961834331865314435; break; } - if param_autoconfig.is_null() { + if param_autoconfig.is_none() { /* Outlook uses always SSL but different domains */ - let url_1: - *mut libc::c_char = - dc_mprintf(b"https://%s%s/autodiscover/autodiscover.xml\x00" - as - *const u8 - as - *const libc::c_char, - if i - == - 0i32 - { - b"\x00" - as - *const u8 - as - *const libc::c_char - } else { - b"autodiscover.\x00" - as - *const u8 - as - *const libc::c_char - }, - param_domain); + let url = format!( + "https://{}{}/autodiscover/autodiscover.xml", + if i == 0 { + "" + } else { + "autodiscover." + }, + param_domain + ); param_autoconfig = outlk_autodiscover( - context, url_1, param, + context, &url, ¶m, ); - free(url_1 as *mut libc::c_void); + if s.shall_stop_ongoing { current_block = 2927484062889439186; @@ -401,17 +328,15 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } context.call_cb( Event::CONFIGURE_PROGRESS, - (if 320i32 + i * 10i32 < 1i32 { - 1i32 - } else if 320i32 + i * 10i32 - > 999i32 - { - 999i32 + (if 320 + i * 10 < 1 { + 1 + } else if 320 + i * 10 > 999 { + 999 } else { - 320i32 + i * 10i32 + 320 + i * 10 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); } i += 1 @@ -419,49 +344,33 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - if param_autoconfig.is_null() { - let url_2: - *mut libc::c_char = - dc_mprintf(b"http://autoconfig.%s/mail/config-v1.1.xml?emailaddress=%s\x00" - as - *const u8 - as - *const libc::c_char, - param_domain, - param_addr_urlencoded); + if param_autoconfig.is_none() { + let url = format!( + "http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", + param_domain, + param_addr_urlencoded + ); param_autoconfig = moz_autoconfigure( - context, url_2, param, + context, &url, ¶m, ); - free( - url_2 as *mut libc::c_void, - ); if s.shall_stop_ongoing { current_block = 2927484062889439186; } else { context.call_cb( - Event::CONFIGURE_PROGRESS, - (if 340i32 - < - 1i32 - { - 1i32 - } else if 340i32 - > - 999i32 - { - 999i32 - } else { - 340i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t - ); + Event::CONFIGURE_PROGRESS, + (if 340 < 1 { + 1 + } else if 340 > 999 { + 999 + } else { + 340 + }) + as uintptr_t, + 0, + ); current_block = 10778260831612459202; } @@ -473,54 +382,36 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j 2927484062889439186 => {} _ => { if param_autoconfig - .is_null() + .is_none() { // do not transfer the email-address unencrypted - let url_3: - *mut libc::c_char = - dc_mprintf(b"http://%s/.well-known/autoconfig/mail/config-v1.1.xml\x00" - as - *const u8 - as - *const libc::c_char, - param_domain); + let url = format!( + "http://{}/.well-known/autoconfig/mail/config-v1.1.xml", + param_domain + ); param_autoconfig = moz_autoconfigure( - context, url_3, - param, + context, &url, + ¶m, ); - free(url_3 - as - *mut libc::c_void); if s.shall_stop_ongoing { - current_block - = - 2927484062889439186; + current_block = + 2927484062889439186; } else { context.call_cb( - Event::CONFIGURE_PROGRESS, - (if 350i32 - < - 1i32 - { - 1i32 - } else if 350i32 - > - 999i32 - { - 999i32 - } else { - 350i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); - current_block - = - 5207889489643863322; + Event::CONFIGURE_PROGRESS, + if 350 < 1 { + 1 + } else if 350 > 999 { + 999 + } else { + 350 + }, + 0 + ); + current_block = + 5207889489643863322; } } else { current_block = @@ -532,25 +423,20 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j _ => { /* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */ if param_autoconfig - .is_null() + .is_none() { /* always SSL for Thunderbird's database */ - let url_4: - *mut libc::c_char = - dc_mprintf(b"https://autoconfig.thunderbird.net/v1.1/%s\x00" - as - *const u8 - as - *const libc::c_char, - param_domain); + let url = + format!("https://autoconfig.thunderbird.net/v1.1/{}", + param_domain + ); param_autoconfig - = - moz_autoconfigure(context, - url_4, - param); - free(url_4 - as - *mut libc::c_void); + = + moz_autoconfigure( + context, + &url, + ¶m + ); if s.shall_stop_ongoing { current_block @@ -558,25 +444,15 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j 2927484062889439186; } else { context.call_cb( - Event::CONFIGURE_PROGRESS, - (if 500i32 - < - 1i32 - { - 1i32 - } else if 500i32 - > - 999i32 - { - 999i32 - } else { - 500i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); + Event::CONFIGURE_PROGRESS, + if 500 < 1 { + 1 + } else if 500 > 999 { + 999 + } else { + 500 + }, + 0); current_block = 2798392256336243897; @@ -595,51 +471,37 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j _ => { - if !param_autoconfig.is_null() + if let Some(ref cfg) = param_autoconfig { - let r: - *mut libc::c_char = - dc_loginparam_get_readable(param_autoconfig); - dc_log_info(context, - 0i32, - b"Got autoconfig: %s\x00" - as - *const u8 - as - *const libc::c_char, - r); - free(r - as - *mut libc::c_void); - if !(*param_autoconfig).mail_user.is_null() + let r = dc_loginparam_get_readable(cfg); + info!( + context, + 0, + "Got autoconfig: {}", + r + ); + if !cfg.mail_user.is_empty() { - free((*param).mail_user - as - *mut libc::c_void); - (*param).mail_user - = - dc_strdup_keep_null((*param_autoconfig).mail_user) + param.mail_user = cfg.mail_user.clone(); } - (*param).mail_server + param.mail_server = cfg.mail_server.clone(); + param.mail_port = - dc_strdup_keep_null((*param_autoconfig).mail_server); - (*param).mail_port + cfg.mail_port; + param.send_server = - (*param_autoconfig).mail_port; - (*param).send_server + cfg.send_server.clone(); + param.send_port = - dc_strdup_keep_null((*param_autoconfig).send_server); - (*param).send_port + cfg.send_port; + param.send_user = - (*param_autoconfig).send_port; - (*param).send_user + cfg.send_user.clone(); + param.server_flags = - dc_strdup_keep_null((*param_autoconfig).send_user); - (*param).server_flags - = - (*param_autoconfig).server_flags + cfg.server_flags; } - (*param).server_flags + param.server_flags |= keep_flags; current_block @@ -663,160 +525,125 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - if (*param).mail_server.is_null() { - (*param).mail_server = dc_mprintf( - b"imap.%s\x00" as *const u8 - as *const libc::c_char, - param_domain, - ) + if param.mail_server.is_empty() { + param.mail_server = + format!("imap.{}", param_domain,) } - if (*param).mail_port == 0i32 { - (*param).mail_port = if 0 - != (*param).server_flags & (0x100i32 | 0x400i32) - { - 143i32 - } else { - 993i32 - } - } - if (*param).mail_user.is_null() { - (*param).mail_user = dc_strdup((*param).addr) - } - if (*param).send_server.is_null() - && !(*param).mail_server.is_null() - { - (*param).send_server = - dc_strdup((*param).mail_server); - if strncmp( - (*param).send_server, - b"imap.\x00" as *const u8 - as *const libc::c_char, - 5, - ) == 0i32 - { - memcpy( - (*param).send_server as *mut libc::c_void, - b"smtp\x00" as *const u8 - as *const libc::c_char - as *const libc::c_void, - 4, - ); - } - } - if (*param).send_port == 0i32 { - (*param).send_port = if 0 - != (*param).server_flags & 0x10000i32 - { - 587i32 - } else if 0 != (*param).server_flags & 0x40000i32 { - 25i32 - } else { - 465i32 - } - } - if (*param).send_user.is_null() - && !(*param).mail_user.is_null() - { - (*param).send_user = dc_strdup((*param).mail_user) - } - if (*param).send_pw.is_null() - && !(*param).mail_pw.is_null() - { - (*param).send_pw = dc_strdup((*param).mail_pw) - } - if 0 == dc_exactly_one_bit_set( - (*param).server_flags & (0x2i32 | 0x4i32), - ) { - (*param).server_flags &= !(0x2i32 | 0x4i32); - (*param).server_flags |= 0x4i32 - } - if 0 == dc_exactly_one_bit_set( - (*param).server_flags - & (0x100i32 | 0x200i32 | 0x400i32), - ) { - (*param).server_flags &= - !(0x100i32 | 0x200i32 | 0x400i32); - (*param).server_flags |= - if (*param).send_port == 143i32 { - 0x100i32 + if param.mail_port == 0 { + param.mail_port = + if 0 != param.server_flags & (0x100 | 0x400) { + 143 } else { - 0x200i32 + 993 } } - if 0 == dc_exactly_one_bit_set( - (*param).server_flags - & (0x10000i32 | 0x20000i32 | 0x40000i32), - ) { - (*param).server_flags &= - !(0x10000i32 | 0x20000i32 | 0x40000i32); - (*param).server_flags |= - if (*param).send_port == 587i32 { - 0x10000i32 - } else if (*param).send_port == 25i32 { - 0x40000i32 + if param.mail_user.is_empty() { + param.mail_user = param.addr.clone(); + } + if param.send_server.is_empty() + && !param.mail_server.is_empty() + { + param.send_server = param.mail_server.clone(); + if param.send_server.starts_with("imap.") { + param.send_server = param + .send_server + .replacen("imap", "smtp", 1); + } + } + if param.send_port == 0 { + param.send_port = + if 0 != param.server_flags & 0x10000 { + 587 + } else if 0 != param.server_flags & 0x40000 { + 25 } else { - 0x20000i32 + 465 } } + if param.send_user.is_empty() + && !param.mail_user.is_empty() + { + param.send_user = param.mail_user.clone(); + } + if param.send_pw.is_empty() && !param.mail_pw.is_empty() + { + param.send_pw = param.mail_pw.clone() + } + if 0 == dc_exactly_one_bit_set( + param.server_flags & (0x2 | 0x4), + ) { + param.server_flags &= !(0x2 | 0x4); + param.server_flags |= 0x4 + } + if 0 == dc_exactly_one_bit_set( + param.server_flags & (0x100 | 0x200 | 0x400), + ) { + param.server_flags &= !(0x100 | 0x200 | 0x400); + param.server_flags |= if param.send_port == 143 { + 0x100 + } else { + 0x200 + } + } + if 0 == dc_exactly_one_bit_set( + param.server_flags & (0x10000 | 0x20000 | 0x40000), + ) { + param.server_flags &= + !(0x10000 | 0x20000 | 0x40000); + param.server_flags |= if param.send_port == 587 { + 0x10000 + } else if param.send_port == 25 { + 0x40000 + } else { + 0x20000 + } + } /* do we have a complete configuration? */ - if (*param).addr.is_null() - || (*param).mail_server.is_null() - || (*param).mail_port == 0i32 - || (*param).mail_user.is_null() - || (*param).mail_pw.is_null() - || (*param).send_server.is_null() - || (*param).send_port == 0i32 - || (*param).send_user.is_null() - || (*param).send_pw.is_null() - || (*param).server_flags == 0i32 + if param.mail_server.is_empty() + || param.mail_port == 0 + || param.mail_user.is_empty() + || param.mail_pw.is_empty() + || param.send_server.is_empty() + || param.send_port == 0 + || param.send_user.is_empty() + || param.send_pw.is_empty() + || param.server_flags == 0 { - dc_log_error( - context, - 0i32, - b"Account settings incomplete.\x00" as *const u8 - as *const libc::c_char, - ); + error!(context, 0, "Account settings incomplete.",); } else if !s.shall_stop_ongoing { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 600i32 < 1i32 { - 1i32 - } else if 600i32 > 999i32 { - 999i32 + (if 600 < 1 { + 1 + } else if 600 > 999 { + 999 } else { - 600i32 + 600 }) as uintptr_t, - 0i32 as uintptr_t, + 0, ); /* try to connect to IMAP - if we did not got an autoconfig, do some further tries with different settings and username variations */ - let mut username_variation: libc::c_int = 0i32; + let mut username_variation = 0; loop { - if !(username_variation <= 1i32) { + if !(username_variation <= 1) { current_block = 14187386403465544025; break; } - let r_0: *mut libc::c_char = - dc_loginparam_get_readable(param); - dc_log_info( - context, - 0i32, - b"Trying: %s\x00" as *const u8 - as *const libc::c_char, - r_0, - ); - free(r_0 as *mut libc::c_void); + let r_0 = dc_loginparam_get_readable(¶m); + info!(context, 0, "Trying: {}", r_0,); + if 0 != context .inbox .read() .unwrap() - .connect(context, param) + .connect(context, ¶m) { current_block = 14187386403465544025; break; } - if !param_autoconfig.is_null() { + if !param_autoconfig.is_none() { current_block = 2927484062889439186; break; } @@ -827,39 +654,28 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } context.call_cb( Event::CONFIGURE_PROGRESS, - (if 650i32 + username_variation * 30i32 - < 1i32 + (if 650 + username_variation * 30 < 1 { + 1 + } else if 650 + username_variation * 30 + > 999 { - 1i32 - } else if 650i32 - + username_variation * 30i32 - > 999i32 - { - 999i32 + 999 } else { - 650i32 + username_variation * 30i32 + 650 + username_variation * 30 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - (*param).server_flags &= - !(0x100i32 | 0x200i32 | 0x400i32); - (*param).server_flags |= 0x100i32; - let r_1: *mut libc::c_char = - dc_loginparam_get_readable(param); - dc_log_info( - context, - 0i32, - b"Trying: %s\x00" as *const u8 - as *const libc::c_char, - r_1, - ); - free(r_1 as *mut libc::c_void); + param.server_flags &= !(0x100 | 0x200 | 0x400); + param.server_flags |= 0x100; + let r_1 = dc_loginparam_get_readable(¶m); + info!(context, 0, "Trying: {}", r_1,); + if 0 != context .inbox .read() .unwrap() - .connect(context, param) + .connect(context, ¶m) { current_block = 14187386403465544025; break; @@ -871,37 +687,27 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } context.call_cb( Event::CONFIGURE_PROGRESS, - (if 660i32 + username_variation * 30i32 - < 1i32 + (if 660 + username_variation * 30 < 1 { + 1 + } else if 660 + username_variation * 30 + > 999 { - 1i32 - } else if 660i32 - + username_variation * 30i32 - > 999i32 - { - 999i32 + 999 } else { - 660i32 + username_variation * 30i32 + 660 + username_variation * 30 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - (*param).mail_port = 143i32; - let r_2: *mut libc::c_char = - dc_loginparam_get_readable(param); - dc_log_info( - context, - 0i32, - b"Trying: %s\x00" as *const u8 - as *const libc::c_char, - r_2, - ); - free(r_2 as *mut libc::c_void); + param.mail_port = 143; + let r_2 = dc_loginparam_get_readable(¶m); + info!(context, 0, "Trying: {}", r_2,); + if 0 != context .inbox .read() .unwrap() - .connect(context, param) + .connect(context, ¶m) { current_block = 14187386403465544025; break; @@ -917,52 +723,55 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } context.call_cb( Event::CONFIGURE_PROGRESS, - (if 670i32 + username_variation * 30i32 - < 1i32 + (if 670 + username_variation * 30 < 1 { + 1 + } else if 670 + username_variation * 30 + > 999 { - 1i32 - } else if 670i32 - + username_variation * 30i32 - > 999i32 - { - 999i32 + 999 } else { - 670i32 + username_variation * 30i32 + 670 + username_variation * 30 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - (*param).server_flags &= - !(0x100i32 | 0x200i32 | 0x400i32); - (*param).server_flags |= 0x200i32; - (*param).mail_port = 993i32; - let mut at: *mut libc::c_char = - strchr((*param).mail_user, '@' as i32); - if !at.is_null() { - *at = 0i32 as libc::c_char + param.server_flags &= !(0x100 | 0x200 | 0x400); + param.server_flags |= 0x200; + param.mail_port = 993; + + if let Some(at) = param.mail_user.find('@') { + param.mail_user = param + .mail_user + .split_at(at) + .0 + .to_string(); } - at = strchr((*param).send_user, '@' as i32); - if !at.is_null() { - *at = 0i32 as libc::c_char + if let Some(at) = param.send_user.find('@') { + param.send_user = param + .send_user + .split_at(at) + .0 + .to_string(); } + username_variation += 1 } match current_block { 2927484062889439186 => {} _ => { - imap_connected_here = 1i32; + imap_connected_here = 1; if !s.shall_stop_ongoing { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 800i32 < 1i32 { - 1i32 - } else if 800i32 > 999i32 { - 999i32 + (if 800 < 1 { + 1 + } else if 800 > 999 { + 999 } else { - 800i32 + 800 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); /* try to connect to SMTP - if we did not got an autoconfig, the first try was SSL-465 and we do a second try with STARTTLS-587 */ if 0 == context @@ -970,9 +779,9 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j .clone() .lock() .unwrap() - .connect(context, param) + .connect(context, ¶m) { - if !param_autoconfig.is_null() { + if !param_autoconfig.is_none() { current_block = 2927484062889439186; } else if s.shall_stop_ongoing { @@ -981,106 +790,90 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } else { context.call_cb( Event::CONFIGURE_PROGRESS, - (if 850i32 < 1i32 { - 1i32 - } else if 850i32 > 999i32 { - 999i32 + (if 850 < 1 { + 1 + } else if 850 > 999 { + 999 } else { - 850i32 + 850 }) as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); - (*param).server_flags &= - !(0x10000i32 - | 0x20000i32 - | 0x40000i32); - (*param).server_flags |= - 0x10000i32; - (*param).send_port = 587i32; - let r_3: *mut libc::c_char = + param.server_flags &= !(0x10000 + | 0x20000 + | 0x40000); + param.server_flags |= 0x10000; + param.send_port = 587; + let r_3 = dc_loginparam_get_readable( - param, + ¶m, ); - dc_log_info( + info!( context, - 0i32, - b"Trying: %s\x00" - as *const u8 - as *const libc::c_char, - r_3, + 0, "Trying: {}", r_3, ); - free(r_3 as *mut libc::c_void); + if 0 == context .smtp .clone() .lock() .unwrap() - .connect(context, param) + .connect(context, ¶m) { if s.shall_stop_ongoing { current_block = 2927484062889439186; } else { - context.call_cb(Event::CONFIGURE_PROGRESS, - (if 860i32 + context.call_cb( + Event::CONFIGURE_PROGRESS, + (if 860 < - 1i32 + 1 { - 1i32 - } else if 860i32 + 1 + } else if 860 > - 999i32 + 999 { - 999i32 + 999 } else { - 860i32 + 860 }) as uintptr_t, - 0i32 + 0 as uintptr_t); - (*param) - .server_flags &= - !(0x10000i32 - | 0x20000i32 - | 0x40000i32); - (*param) - .server_flags |= - 0x10000i32; - (*param).send_port = - 25i32; - let r_4: - *mut libc::c_char = - dc_loginparam_get_readable(param); - dc_log_info(context, - 0i32, - b"Trying: %s\x00" - as - *const u8 - as - *const libc::c_char, - r_4); - free(r_4 - as - *mut libc::c_void); + param.server_flags &= + !(0x10000 + | 0x20000 + | 0x40000); + param.server_flags |= + 0x10000; + param.send_port = 25; + let r_4 = dc_loginparam_get_readable(¶m); + info!( + context, + 0, + "Trying: {}", + r_4 + ); + if 0 == context .smtp .clone() .lock() .unwrap() .connect( - context, param, + context, ¶m, ) { - current_block - = - 2927484062889439186; + current_block = + 2927484062889439186; } else { - current_block - = - 5083741289379115417; + current_block = + 5083741289379115417; } } } else { @@ -1094,52 +887,42 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - smtp_connected_here = 1i32; + smtp_connected_here = 1; if !s.shall_stop_ongoing { - context.call_cb(Event::CONFIGURE_PROGRESS, - (if 900i32 - < - 1i32 - { - 1i32 - } else if 900i32 - > - 999i32 - { - 999i32 - } else { - 900i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); - flags - = - if 0 - != - dc_sqlite3_get_config_int(context, &context.sql, - b"mvbox_watch\x00" - as - *const u8 - as - *const libc::c_char, - 1i32) - || - 0 - != - dc_sqlite3_get_config_int(context, &context.sql, - b"mvbox_move\x00" - as - *const u8 - as - *const libc::c_char, - 1i32) - { - 0x1i32 + context.call_cb( + Event::CONFIGURE_PROGRESS, + (if 900 < 1 { + 1 + } else if 900 > 999 { + 999 } else { - 0i32 + 900 + }) + as uintptr_t, + 0 as uintptr_t, + ); + flags = if 0 + != context + .sql + .get_config_int( + context, + "mvbox_watch", + ) + .unwrap_or_else( + || 1, + ) + || 0 != context + .sql + .get_config_int( + context, + "mvbox_move", + ) + .unwrap_or_else( + || 1, + ) { + 0x1 + } else { + 0 }; context @@ -1150,91 +933,93 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j context, flags, ); if !s.shall_stop_ongoing { - context.call_cb(Event::CONFIGURE_PROGRESS, - (if 910i32 - < - 1i32 - { - 1i32 - } else if 910i32 - > - 999i32 - { - 999i32 - } else { - 910i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); - dc_loginparam_write(context, param, - &context.sql, - b"configured_\x00" - as - *const u8 - as - *const libc::c_char); - dc_sqlite3_set_config_int(context, &context.sql, - b"configured\x00" - as - *const u8 - as - *const libc::c_char, - 1i32); + context.call_cb( + Event::CONFIGURE_PROGRESS, + (if 910 + < + 1 + { + 1 + } else if 910 + > + 999 + { + 999 + } else { + 910 + }) + as + uintptr_t, + 0 + as + uintptr_t + ); + dc_loginparam_write( + context, + ¶m, + &context.sql, + "configured_", + ); + context + .sql + .set_config_int( + context, + "configured", + 1, + ); if !s.shall_stop_ongoing { - context.call_cb(Event::CONFIGURE_PROGRESS, - (if 920i32 - < - 1i32 - { - 1i32 - } else if 920i32 - > - 999i32 - { - 999i32 - } else { - 920i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); + context.call_cb( + Event::CONFIGURE_PROGRESS, + (if 920 + < + 1 + { + 1 + } else if 920 + > + 999 + { + 999 + } else { + 920 + }) + as + uintptr_t, + 0 + as + uintptr_t + ); dc_ensure_secret_key_exists(context); - success = 1i32; - dc_log_info(context, - 0i32, - b"Configure completed.\x00" - as - *const u8 - as - *const libc::c_char); + success = 1; + info!( + context, + 0, + "Configure completed." + ); if !s.shall_stop_ongoing - { - context.call_cb(Event::CONFIGURE_PROGRESS, - (if 940i32 - < - 1i32 - { - 1i32 - } else if 940i32 - > - 999i32 - { - 999i32 - } else { - 940i32 - }) - as - uintptr_t, - 0i32 - as - uintptr_t); - } + { + context.call_cb( + Event::CONFIGURE_PROGRESS, + (if 940 + < + 1 + { + 1 + } else if 940 + > + 999 + { + 999 + } else { + 940 + }) + as + uintptr_t, + 0 + as + uintptr_t); + } } } } @@ -1261,17 +1046,15 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j if 0 != smtp_connected_here { context.smtp.clone().lock().unwrap().disconnect(); } - dc_loginparam_unref(param); - dc_loginparam_unref(param_autoconfig); - free(param_addr_urlencoded as *mut libc::c_void); + if 0 != ongoing_allocated_here { dc_free_ongoing(context); } - free(mvbox_folder as *mut libc::c_void); + context.call_cb( Event::CONFIGURE_PROGRESS, - (if 0 != success { 1000i32 } else { 0i32 }) as uintptr_t, - 0i32 as uintptr_t, + (if 0 != success { 1000 } else { 0 }) as uintptr_t, + 0 as uintptr_t, ); } @@ -1285,75 +1068,72 @@ pub unsafe fn dc_free_ongoing(context: &Context) { unsafe fn moz_autoconfigure( context: &Context, - url: *const libc::c_char, - param_in: *const dc_loginparam_t, -) -> *mut dc_loginparam_t { - let p: *mut libc::c_char; - let mut saxparser: dc_saxparser_t; - let xml_raw: *mut libc::c_char; - let mut moz_ac: moz_autoconfigure_t = moz_autoconfigure_t { - in_0: 0 as *const dc_loginparam_t, - in_emaildomain: 0 as *mut libc::c_char, - in_emaillocalpart: 0 as *mut libc::c_char, - out: 0 as *mut dc_loginparam_t, + url: &str, + param_in: &dc_loginparam_t, +) -> Option { + let mut moz_ac = moz_autoconfigure_t { + in_0: param_in, + in_emaildomain: std::ptr::null_mut(), + in_emaillocalpart: std::ptr::null_mut(), + out: dc_loginparam_new(), out_imap_set: 0, out_smtp_set: 0, tag_server: 0, tag_config: 0, }; - memset( - &mut moz_ac as *mut moz_autoconfigure_t as *mut libc::c_void, - 0i32, - ::std::mem::size_of::(), - ); - xml_raw = read_autoconf_file(context, url); - if !xml_raw.is_null() { - moz_ac.in_0 = param_in; - moz_ac.in_emaillocalpart = dc_strdup((*param_in).addr); - p = strchr(moz_ac.in_emaillocalpart, '@' as i32); - if !p.is_null() { - *p = 0i32 as libc::c_char; - moz_ac.in_emaildomain = dc_strdup(p.offset(1isize)); - moz_ac.out = dc_loginparam_new(); - saxparser = dc_saxparser_t { - starttag_cb: None, - endtag_cb: None, - text_cb: None, - userdata: 0 as *mut libc::c_void, - }; - dc_saxparser_init( - &mut saxparser, - &mut moz_ac as *mut moz_autoconfigure_t as *mut libc::c_void, - ); - dc_saxparser_set_tag_handler( - &mut saxparser, - Some(moz_autoconfigure_starttag_cb), - Some(moz_autoconfigure_endtag_cb), - ); - dc_saxparser_set_text_handler(&mut saxparser, Some(moz_autoconfigure_text_cb)); - dc_saxparser_parse(&mut saxparser, xml_raw); - if (*moz_ac.out).mail_server.is_null() - || (*moz_ac.out).mail_port == 0i32 - || (*moz_ac.out).send_server.is_null() - || (*moz_ac.out).send_port == 0i32 - { - let r: *mut libc::c_char = dc_loginparam_get_readable(moz_ac.out); - dc_log_warning( - context, - 0i32, - b"Bad or incomplete autoconfig: %s\x00" as *const u8 as *const libc::c_char, - r, - ); - free(r as *mut libc::c_void); - dc_loginparam_unref(moz_ac.out); - moz_ac.out = 0 as *mut dc_loginparam_t - } - } + + let xml_raw = read_autoconf_file(context, to_cstring(url).as_ptr()); + if xml_raw.is_null() { + return None; } + + moz_ac.in_emaillocalpart = dc_strdup(to_cstring(¶m_in.addr).as_ptr()); + let p = strchr(moz_ac.in_emaillocalpart, '@' as i32); + + if p.is_null() { + free(xml_raw as *mut libc::c_void); + free(moz_ac.in_emaildomain as *mut libc::c_void); + free(moz_ac.in_emaillocalpart as *mut libc::c_void); + return None; + } + + *p = 0 as libc::c_char; + moz_ac.in_emaildomain = dc_strdup(p.offset(1isize)); + let mut saxparser = dc_saxparser_t { + starttag_cb: None, + endtag_cb: None, + text_cb: None, + userdata: 0 as *mut libc::c_void, + }; + dc_saxparser_init( + &mut saxparser, + &mut moz_ac as *mut moz_autoconfigure_t as *mut libc::c_void, + ); + dc_saxparser_set_tag_handler( + &mut saxparser, + Some(moz_autoconfigure_starttag_cb), + Some(moz_autoconfigure_endtag_cb), + ); + dc_saxparser_set_text_handler(&mut saxparser, Some(moz_autoconfigure_text_cb)); + dc_saxparser_parse(&mut saxparser, xml_raw); + + if moz_ac.out.mail_server.is_empty() + || moz_ac.out.mail_port == 0 + || moz_ac.out.send_server.is_empty() + || moz_ac.out.send_port == 0 + { + let r = dc_loginparam_get_readable(&moz_ac.out); + warn!(context, 0, "Bad or incomplete autoconfig: {}", r,); + free(xml_raw as *mut libc::c_void); + free(moz_ac.in_emaildomain as *mut libc::c_void); + free(moz_ac.in_emaillocalpart as *mut libc::c_void); + return None; + } + free(xml_raw as *mut libc::c_void); free(moz_ac.in_emaildomain as *mut libc::c_void); free(moz_ac.in_emaillocalpart as *mut libc::c_void); - return moz_ac.out; + Some(moz_ac.out) } unsafe fn moz_autoconfigure_text_cb( @@ -1367,7 +1147,7 @@ unsafe fn moz_autoconfigure_text_cb( dc_str_replace( &mut val, b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char, - (*(*moz_ac).in_0).addr, + to_cstring(&(*moz_ac).in_0.addr).as_ptr(), ); dc_str_replace( &mut val, @@ -1379,54 +1159,50 @@ unsafe fn moz_autoconfigure_text_cb( b"%EMAILDOMAIN%\x00" as *const u8 as *const libc::c_char, (*moz_ac).in_emaildomain, ); - if (*moz_ac).tag_server == 1i32 { + if (*moz_ac).tag_server == 1 { match (*moz_ac).tag_config { 10 => { - free((*(*moz_ac).out).mail_server as *mut libc::c_void); - (*(*moz_ac).out).mail_server = val; + (*moz_ac).out.mail_server = to_string(val); val = 0 as *mut libc::c_char } - 11 => (*(*moz_ac).out).mail_port = dc_atoi_null_is_0(val), + 11 => (*moz_ac).out.mail_port = dc_atoi_null_is_0(val), 12 => { - free((*(*moz_ac).out).mail_user as *mut libc::c_void); - (*(*moz_ac).out).mail_user = val; + (*moz_ac).out.mail_user = to_string(val); val = 0 as *mut libc::c_char } 13 => { - if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x200i32 + if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x200 } - if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x100i32 + if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x100 } - if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x400i32 + if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x400 } } _ => {} } - } else if (*moz_ac).tag_server == 2i32 { + } else if (*moz_ac).tag_server == 2 { match (*moz_ac).tag_config { 10 => { - free((*(*moz_ac).out).send_server as *mut libc::c_void); - (*(*moz_ac).out).send_server = val; + (*moz_ac).out.send_server = to_string(val); val = 0 as *mut libc::c_char } - 11 => (*(*moz_ac).out).send_port = as_str(val).parse().unwrap_or_default(), + 11 => (*moz_ac).out.send_port = as_str(val).parse().unwrap_or_default(), 12 => { - free((*(*moz_ac).out).send_user as *mut libc::c_void); - (*(*moz_ac).out).send_user = val; + (*moz_ac).out.send_user = to_string(val); val = 0 as *mut libc::c_char } 13 => { - if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x20000i32 + if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x20000 } - if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x10000i32 + if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x10000 } - if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*(*moz_ac).out).server_flags |= 0x40000i32 + if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).out.server_flags |= 0x40000 } } _ => {} @@ -1439,21 +1215,21 @@ unsafe fn moz_autoconfigure_endtag_cb(userdata: *mut libc::c_void, tag: *const l if strcmp( tag, b"incomingserver\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - (*moz_ac).tag_server = 0i32; - (*moz_ac).tag_config = 0i32; - (*moz_ac).out_imap_set = 1i32 + (*moz_ac).tag_server = 0; + (*moz_ac).tag_config = 0; + (*moz_ac).out_imap_set = 1 } else if strcmp( tag, b"outgoingserver\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - (*moz_ac).tag_server = 0i32; - (*moz_ac).tag_config = 0i32; - (*moz_ac).out_smtp_set = 1i32 + (*moz_ac).tag_server = 0; + (*moz_ac).tag_config = 0; + (*moz_ac).out_smtp_set = 1 } else { - (*moz_ac).tag_config = 0i32 + (*moz_ac).tag_config = 0 }; } unsafe fn moz_autoconfigure_starttag_cb( @@ -1466,39 +1242,35 @@ unsafe fn moz_autoconfigure_starttag_cb( if strcmp( tag, b"incomingserver\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - (*moz_ac).tag_server = if (*moz_ac).out_imap_set == 0i32 + (*moz_ac).tag_server = if (*moz_ac).out_imap_set == 0 && { p1 = dc_attr_find(attr, b"type\x00" as *const u8 as *const libc::c_char); !p1.is_null() } - && strcasecmp(p1, b"imap\x00" as *const u8 as *const libc::c_char) == 0i32 + && strcasecmp(p1, b"imap\x00" as *const u8 as *const libc::c_char) == 0 { - 1i32 + 1 } else { - 0i32 + 0 }; - (*moz_ac).tag_config = 0i32 + (*moz_ac).tag_config = 0 } else if strcmp( tag, b"outgoingserver\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - (*moz_ac).tag_server = if (*moz_ac).out_smtp_set == 0i32 { - 2i32 - } else { - 0i32 - }; - (*moz_ac).tag_config = 0i32 - } else if strcmp(tag, b"hostname\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*moz_ac).tag_config = 10i32 - } else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*moz_ac).tag_config = 11i32 - } else if strcmp(tag, b"sockettype\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*moz_ac).tag_config = 13i32 - } else if strcmp(tag, b"username\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*moz_ac).tag_config = 12i32 + (*moz_ac).tag_server = if (*moz_ac).out_smtp_set == 0 { 2 } else { 0 }; + (*moz_ac).tag_config = 0 + } else if strcmp(tag, b"hostname\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).tag_config = 10 + } else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).tag_config = 11 + } else if strcmp(tag, b"sockettype\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).tag_config = 13 + } else if strcmp(tag, b"username\x00" as *const u8 as *const libc::c_char) == 0 { + (*moz_ac).tag_config = 12 }; } @@ -1521,30 +1293,30 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc: unsafe fn outlk_autodiscover( context: &Context, - url__: *const libc::c_char, - param_in: *const dc_loginparam_t, -) -> *mut dc_loginparam_t { + url__: &str, + param_in: &dc_loginparam_t, +) -> Option { let current_block: u64; let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char; - let mut url: *mut libc::c_char = dc_strdup(url__); - let mut outlk_ad: outlk_autodiscover_t = outlk_autodiscover_t { - in_0: 0 as *const dc_loginparam_t, - out: 0 as *mut dc_loginparam_t, + let mut url = dc_strdup(to_cstring(url__).as_ptr()); + let mut outlk_ad = outlk_autodiscover_t { + in_0: param_in, + out: dc_loginparam_new(), out_imap_set: 0, out_smtp_set: 0, tag_config: 0, config: [0 as *mut libc::c_char; 6], redirect: 0 as *mut libc::c_char, }; - let mut i: libc::c_int = 0; + let mut i = 0; loop { - if !(i < 10i32) { + if !(i < 10) { current_block = 11584701595673473500; break; } memset( &mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void, - 0i32, + 0, ::std::mem::size_of::(), ); xml_raw = read_autoconf_file(context, url); @@ -1552,8 +1324,6 @@ unsafe fn outlk_autodiscover( current_block = 3070887585260837332; break; } - outlk_ad.in_0 = param_in; - outlk_ad.out = dc_loginparam_new(); let mut saxparser: dc_saxparser_t = dc_saxparser_t { starttag_cb: None, endtag_cb: None, @@ -1579,29 +1349,27 @@ unsafe fn outlk_autodiscover( } free(url as *mut libc::c_void); url = dc_strdup(outlk_ad.config[5usize]); - dc_loginparam_unref(outlk_ad.out); + outlk_clean_config(&mut outlk_ad); free(xml_raw as *mut libc::c_void); xml_raw = 0 as *mut libc::c_char; - i += 1 + i += 1; } + match current_block { 11584701595673473500 => { - if (*outlk_ad.out).mail_server.is_null() - || (*outlk_ad.out).mail_port == 0i32 - || (*outlk_ad.out).send_server.is_null() - || (*outlk_ad.out).send_port == 0i32 + if outlk_ad.out.mail_server.is_empty() + || outlk_ad.out.mail_port == 0 + || outlk_ad.out.send_server.is_empty() + || outlk_ad.out.send_port == 0 { - let r: *mut libc::c_char = dc_loginparam_get_readable(outlk_ad.out); - dc_log_warning( - context, - 0i32, - b"Bad or incomplete autoconfig: %s\x00" as *const u8 as *const libc::c_char, - r, - ); - free(r as *mut libc::c_void); - dc_loginparam_unref(outlk_ad.out); - outlk_ad.out = 0 as *mut dc_loginparam_t + let r = dc_loginparam_get_readable(&outlk_ad.out); + warn!(context, 0, "Bad or incomplete autoconfig: {}", r,); + free(url as *mut libc::c_void); + free(xml_raw as *mut libc::c_void); + outlk_clean_config(&mut outlk_ad); + + return None; } } _ => {} @@ -1609,11 +1377,12 @@ unsafe fn outlk_autodiscover( free(url as *mut libc::c_void); free(xml_raw as *mut libc::c_void); outlk_clean_config(&mut outlk_ad); - return outlk_ad.out; + Some(outlk_ad.out) } + unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) { let mut i: libc::c_int = 0; - while i < 6i32 { + while i < 6 { free((*outlk_ad).config[i as usize] as *mut libc::c_void); (*outlk_ad).config[i as usize] = 0 as *mut libc::c_char; i += 1 @@ -1632,82 +1401,81 @@ unsafe fn outlk_autodiscover_text_cb( } unsafe fn outlk_autodiscover_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) { let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t; - if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0i32 { + if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 { if !(*outlk_ad).config[1usize].is_null() { let port: libc::c_int = dc_atoi_null_is_0((*outlk_ad).config[3usize]); let ssl_on: libc::c_int = (!(*outlk_ad).config[4usize].is_null() && strcasecmp( (*outlk_ad).config[4usize], b"on\x00" as *const u8 as *const libc::c_char, - ) == 0i32) as libc::c_int; + ) == 0) as libc::c_int; let ssl_off: libc::c_int = (!(*outlk_ad).config[4usize].is_null() && strcasecmp( (*outlk_ad).config[4usize], b"off\x00" as *const u8 as *const libc::c_char, - ) == 0i32) as libc::c_int; + ) == 0) as libc::c_int; if strcasecmp( (*outlk_ad).config[1usize], b"imap\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - && (*outlk_ad).out_imap_set == 0i32 + ) == 0 + && (*outlk_ad).out_imap_set == 0 { - (*(*outlk_ad).out).mail_server = dc_strdup_keep_null((*outlk_ad).config[2usize]); - (*(*outlk_ad).out).mail_port = port; + (*outlk_ad).out.mail_server = to_string((*outlk_ad).config[2]); + (*outlk_ad).out.mail_port = port; if 0 != ssl_on { - (*(*outlk_ad).out).server_flags |= 0x200i32 + (*outlk_ad).out.server_flags |= 0x200 } else if 0 != ssl_off { - (*(*outlk_ad).out).server_flags |= 0x400i32 + (*outlk_ad).out.server_flags |= 0x400 } - (*outlk_ad).out_imap_set = 1i32 + (*outlk_ad).out_imap_set = 1 } else if strcasecmp( (*outlk_ad).config[1usize], b"smtp\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - && (*outlk_ad).out_smtp_set == 0i32 + ) == 0 + && (*outlk_ad).out_smtp_set == 0 { - (*(*outlk_ad).out).send_server = dc_strdup_keep_null((*outlk_ad).config[2usize]); - (*(*outlk_ad).out).send_port = port; + (*outlk_ad).out.send_server = to_string((*outlk_ad).config[2]); + (*outlk_ad).out.send_port = port; if 0 != ssl_on { - (*(*outlk_ad).out).server_flags |= 0x20000i32 + (*outlk_ad).out.server_flags |= 0x20000 } else if 0 != ssl_off { - (*(*outlk_ad).out).server_flags |= 0x40000i32 + (*outlk_ad).out.server_flags |= 0x40000 } - (*outlk_ad).out_smtp_set = 1i32 + (*outlk_ad).out_smtp_set = 1 } } outlk_clean_config(outlk_ad); } - (*outlk_ad).tag_config = 0i32; + (*outlk_ad).tag_config = 0; } + unsafe fn outlk_autodiscover_starttag_cb( userdata: *mut libc::c_void, tag: *const libc::c_char, _attr: *mut *mut libc::c_char, ) { let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t; - if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0i32 { + if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 { outlk_clean_config(outlk_ad); - } else if strcmp(tag, b"type\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*outlk_ad).tag_config = 1i32 - } else if strcmp(tag, b"server\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*outlk_ad).tag_config = 2i32 - } else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*outlk_ad).tag_config = 3i32 - } else if strcmp(tag, b"ssl\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*outlk_ad).tag_config = 4i32 - } else if strcmp(tag, b"redirecturl\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*outlk_ad).tag_config = 5i32 + } else if strcmp(tag, b"type\x00" as *const u8 as *const libc::c_char) == 0 { + (*outlk_ad).tag_config = 1 + } else if strcmp(tag, b"server\x00" as *const u8 as *const libc::c_char) == 0 { + (*outlk_ad).tag_config = 2 + } else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 { + (*outlk_ad).tag_config = 3 + } else if strcmp(tag, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 { + (*outlk_ad).tag_config = 4 + } else if strcmp(tag, b"redirecturl\x00" as *const u8 as *const libc::c_char) == 0 { + (*outlk_ad).tag_config = 5 }; } pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int { if 0 != dc_has_ongoing(context) { - dc_log_warning( + warn!( context, - 0i32, - b"There is already another ongoing process running.\x00" as *const u8 - as *const libc::c_char, + 0, "There is already another ongoing process running.", ); - return 0i32; + return 0; } let s_a = context.running_state.clone(); let mut s = s_a.write().unwrap(); @@ -1718,35 +1486,26 @@ pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int { 1 } -pub unsafe fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_int { - let mut ret_connected: libc::c_int = 0i32; - let param: *mut dc_loginparam_t = dc_loginparam_new(); +pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_int { + let mut ret_connected = 0; + if imap.is_connected() { - ret_connected = 1i32 - } else if dc_sqlite3_get_config_int( - context, - &context.sql, - b"configured\x00" as *const u8 as *const libc::c_char, - 0i32, - ) == 0i32 + ret_connected = 1 + } else if context + .sql + .get_config_int(context, "configured") + .unwrap_or_default() + == 0 { - dc_log_warning( - context, - 0i32, - b"Not configured, cannot connect.\x00" as *const u8 as *const libc::c_char, - ); + warn!(context, 0, "Not configured, cannot connect.",); } else { - dc_loginparam_read( - context, - param, - &context.sql, - b"configured_\x00" as *const u8 as *const libc::c_char, - ); - /*the trailing underscore is correct*/ - if !(0 == imap.connect(context, param)) { - ret_connected = 2i32; + let param = dc_loginparam_read(context, &context.sql, "configured_"); + // the trailing underscore is correct + + if 0 != imap.connect(context, ¶m) { + ret_connected = 2; } } - dc_loginparam_unref(param); + ret_connected } diff --git a/src/dc_contact.rs b/src/dc_contact.rs index 78858389a..e35a40eb4 100644 --- a/src/dc_contact.rs +++ b/src/dc_contact.rs @@ -1,19 +1,20 @@ use crate::aheader::EncryptPreference; +use crate::config; use crate::constants::Event; use crate::context::Context; -use crate::context::*; use crate::dc_array::*; use crate::dc_e2ee::*; -use crate::dc_log::*; use crate::dc_loginparam::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; use crate::key::*; use crate::peerstate::*; +use crate::sql::{self, Sql}; use crate::types::*; use crate::x::*; +const DC_GCL_VERIFIED_ONLY: u32 = 0x01; + #[derive(Copy, Clone)] #[repr(C)] pub struct dc_contact_t<'a> { @@ -27,17 +28,17 @@ pub struct dc_contact_t<'a> { pub origin: libc::c_int, } -pub unsafe fn dc_marknoticed_contact(context: &Context, contact_id: uint32_t) { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( +pub fn dc_marknoticed_contact(context: &Context, contact_id: u32) { + if sql::execute( context, &context.sql, - b"UPDATE msgs SET state=13 WHERE from_id=? AND state=10;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); + "UPDATE msgs SET state=13 WHERE from_id=? AND state=10;", + params![contact_id as i32], + ) + .is_ok() + { + context.call_cb(Event::MSGS_CHANGED, 0, 0); + } } /// Returns false if addr is an invalid address, otherwise true. @@ -65,45 +66,30 @@ pub unsafe fn dc_lookup_contact_id_by_addr( context: &Context, addr: *const libc::c_char, ) -> uint32_t { - let mut contact_id: libc::c_int = 0i32; - let mut addr_normalized: *mut libc::c_char = 0 as *mut libc::c_char; - let mut addr_self: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(addr.is_null() || *addr.offset(0isize) as libc::c_int == 0i32) { - addr_normalized = dc_addr_normalize(addr); - addr_self = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if strcasecmp(addr_normalized, addr_self) == 0i32 { - contact_id = 1i32 - } else { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_text( - stmt, - 1i32, - addr_normalized as *const libc::c_char, - -1i32, - None, - ); - sqlite3_bind_int(stmt, 2i32, 9i32); - sqlite3_bind_int(stmt, 3i32, 0x100i32); - if sqlite3_step(stmt) == 100i32 { - contact_id = sqlite3_column_int(stmt, 0i32) - } - } + if addr.is_null() || *addr.offset(0) as libc::c_int == 0 { + return 0; } - sqlite3_finalize(stmt); - free(addr_normalized as *mut libc::c_void); - free(addr_self as *mut libc::c_void); - contact_id as uint32_t + let addr_normalized_c = dc_addr_normalize(addr); + let addr_normalized = as_str(addr_normalized_c); + let addr_self = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + + let contact_id = if addr_normalized == addr_self { + 1 + } else { + context.sql.query_row_col( + context, + "SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;", + params![addr_normalized, 9, 0x100], + 0 + ).unwrap_or_default() + }; + free(addr_normalized_c as *mut libc::c_void); + + contact_id } pub unsafe fn dc_addr_normalize(addr: *const libc::c_char) -> *mut libc::c_char { @@ -124,6 +110,16 @@ pub unsafe fn dc_addr_normalize(addr: *const libc::c_char) -> *mut libc::c_char addr_normalized } +pub fn dc_addr_normalize_safe(addr: &str) -> &str { + let norm = addr.trim(); + + if norm.starts_with("mailto:") { + return &norm[7..]; + } + + norm +} + pub unsafe fn dc_create_contact( context: &Context, name: *const libc::c_char, @@ -153,60 +149,44 @@ pub unsafe fn dc_create_contact( } pub unsafe fn dc_block_contact(context: &Context, contact_id: uint32_t, new_blocking: libc::c_int) { - let current_block: u64; - let mut send_event: libc::c_int = 0i32; - let contact: *mut dc_contact_t = dc_contact_new(context); - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(contact_id <= 9i32 as libc::c_uint) { - if dc_contact_load_from_db(contact, &context.sql, contact_id) - && (*contact).blocked != new_blocking + if contact_id <= 9 { + return; + } + + let contact = dc_contact_new(context); + + if dc_contact_load_from_db(contact, &context.sql, contact_id) + && (*contact).blocked != new_blocking + { + if sql::execute( + context, + &context.sql, + "UPDATE contacts SET blocked=? WHERE id=?;", + params![new_blocking, contact_id as i32], + ) + .is_ok() { - stmt = dc_sqlite3_prepare( + // also (un)block all chats with _only_ this contact - we do not delete them to allow a + // non-destructive blocking->unblocking. + // (Maybe, beside normal chats (type=100) we should also block group chats with only this user. + // However, I'm not sure about this point; it may be confusing if the user wants to add other people; + // this would result in recreating the same group...) + if sql::execute( context, &context.sql, - b"UPDATE contacts SET blocked=? WHERE id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, new_blocking); - sqlite3_bind_int(stmt, 2i32, contact_id as libc::c_int); - if sqlite3_step(stmt) != 101i32 { - current_block = 5249903830285462583; - } else { - sqlite3_finalize(stmt); - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"UPDATE chats SET blocked=? WHERE type=? AND id IN (SELECT chat_id FROM chats_contacts WHERE contact_id=?);\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, new_blocking); - sqlite3_bind_int(stmt, 2i32, 100i32); - sqlite3_bind_int(stmt, 3i32, contact_id as libc::c_int); - if sqlite3_step(stmt) != 101i32 { - current_block = 5249903830285462583; - } else { - dc_marknoticed_contact(context, contact_id); - send_event = 1i32; - current_block = 15652330335145281839; - } - } - } else { - current_block = 15652330335145281839; - } - match current_block { - 5249903830285462583 => {} - _ => { - if 0 != send_event { - context.call_cb( - Event::CONTACTS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - } + "UPDATE chats SET blocked=? WHERE type=? AND id IN (SELECT chat_id FROM chats_contacts WHERE contact_id=?);", + params![new_blocking, 100, contact_id as i32], + ).is_ok() { + dc_marknoticed_contact(context, contact_id); + context.call_cb( + Event::CONTACTS_CHANGED, + 0, + 0, + ); } } } - sqlite3_finalize(stmt); + dc_contact_unref(contact); } @@ -285,52 +265,44 @@ pub unsafe fn dc_contact_empty(mut contact: *mut dc_contact_t) { /* contacts with at least this origin value start a new "normal" chat, defaults to off */ pub unsafe fn dc_contact_load_from_db( contact: *mut dc_contact_t, - sql: &SQLite, - contact_id: uint32_t, + sql: &Sql, + contact_id: u32, ) -> bool { - let current_block: u64; - let mut success = false; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint) { - dc_contact_empty(contact); - if contact_id == 1i32 as libc::c_uint { - (*contact).id = contact_id; - (*contact).name = dc_stock_str((*contact).context, 2i32); - (*contact).addr = dc_sqlite3_get_config( - (*contact).context, - sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - current_block = 5143058163439228106; - } else { - stmt = - dc_sqlite3_prepare( - (*contact).context,sql, - b"SELECT c.name, c.addr, c.origin, c.blocked, c.authname FROM contacts c WHERE c.id=?;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - if sqlite3_step(stmt) != 100i32 { - current_block = 12908855840294526070; - } else { - (*contact).id = contact_id; - (*contact).name = dc_strdup(sqlite3_column_text(stmt, 0i32) as *mut libc::c_char); - (*contact).addr = dc_strdup(sqlite3_column_text(stmt, 1i32) as *mut libc::c_char); - (*contact).origin = sqlite3_column_int(stmt, 2i32); - (*contact).blocked = sqlite3_column_int(stmt, 3i32); - (*contact).authname = - dc_strdup(sqlite3_column_text(stmt, 4i32) as *mut libc::c_char); - current_block = 5143058163439228106; - } - } - match current_block { - 12908855840294526070 => {} - _ => success = true, - } + if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { + return false; } - sqlite3_finalize(stmt); - success + dc_contact_empty(contact); + + if contact_id == 1 as libc::c_uint { + (*contact).id = contact_id; + (*contact).name = dc_stock_str((*contact).context, 2); + (*contact).addr = dc_strdup( + to_cstring( + (*contact) + .context + .sql + .get_config((*contact).context, "configured_addr") + .unwrap_or_default(), + ) + .as_ptr(), + ); + true + } else { + sql.query_row( + "SELECT c.name, c.addr, c.origin, c.blocked, c.authname FROM contacts c WHERE c.id=?;", + params![contact_id as i32], + |row| { + (*contact).id = contact_id; + (*contact).name = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); + (*contact).addr = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); + (*contact).origin = row.get(2)?; + (*contact).blocked = row.get(3)?; + (*contact).authname = dc_strdup(to_cstring(row.get::<_, String>(4)?).as_ptr()); + Ok(()) + } + ).is_ok() + } } pub unsafe fn dc_is_contact_blocked(context: &Context, contact_id: uint32_t) -> bool { @@ -347,190 +319,137 @@ pub unsafe fn dc_is_contact_blocked(context: &Context, contact_id: uint32_t) -> } /*can be NULL*/ -pub unsafe fn dc_add_or_lookup_contact( +pub fn dc_add_or_lookup_contact( context: &Context, name: *const libc::c_char, addr__: *const libc::c_char, origin: libc::c_int, mut sth_modified: *mut libc::c_int, ) -> uint32_t { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut row_id: uint32_t = 0i32 as uint32_t; - let mut dummy: libc::c_int = 0i32; - let mut addr: *mut libc::c_char = 0 as *mut libc::c_char; - let mut addr_self: *mut libc::c_char = 0 as *mut libc::c_char; - let mut row_name: *mut libc::c_char = 0 as *mut libc::c_char; - let mut row_addr: *mut libc::c_char = 0 as *mut libc::c_char; - let mut row_authname: *mut libc::c_char = 0 as *mut libc::c_char; + let mut dummy = 0; + if sth_modified.is_null() { - sth_modified = &mut dummy + sth_modified = &mut dummy; } - *sth_modified = 0i32; - if !(addr__.is_null() || origin <= 0i32) { - addr = dc_addr_normalize(addr__); - addr_self = dc_sqlite3_get_config( + unsafe { *sth_modified = 0 }; + + if addr__.is_null() || origin <= 0 { + return 0; + } + + let addr_c = unsafe { dc_addr_normalize(addr__) }; + let addr = as_str(addr_c); + let addr_self = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + + if addr == addr_self { + return 1; + } + + if !unsafe { dc_may_be_valid_addr(addr_c) } { + warn!( context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, + 0, + "Bad address \"{}\" for contact \"{}\".", + addr, + if !name.is_null() { + as_str(name) + } else { + "" + }, ); - if strcasecmp(addr, addr_self) == 0i32 { - row_id = 1i32 as uint32_t - } else if !dc_may_be_valid_addr(addr) { - dc_log_warning( - context, - 0i32, - b"Bad address \"%s\" for contact \"%s\".\x00" as *const u8 as *const libc::c_char, - addr, - if !name.is_null() { - name - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - ); - } else { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, addr as *const libc::c_char, -1i32, None); - if sqlite3_step(stmt) == 100i32 { - let row_origin: libc::c_int; - let mut update_addr: libc::c_int = 0i32; - let mut update_name: libc::c_int = 0i32; - let mut update_authname: libc::c_int = 0i32; - row_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - row_name = dc_strdup(sqlite3_column_text(stmt, 1i32) as *mut libc::c_char); - row_addr = dc_strdup(sqlite3_column_text(stmt, 2i32) as *mut libc::c_char); - row_origin = sqlite3_column_int(stmt, 3i32); - row_authname = dc_strdup(sqlite3_column_text(stmt, 4i32) as *mut libc::c_char); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int { - if 0 != *row_name.offset(0isize) { - if origin >= row_origin && strcmp(name, row_name) != 0i32 { - update_name = 1i32 - } - } else { - update_name = 1i32 + return 0; + } + + let mut update_addr = false; + let mut update_name = false; + let mut update_authname = false; + let mut row_id = 0; + + if let Ok((id, row_name, row_addr, row_origin, row_authname)) = context.sql.query_row( + "SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;", + params![addr], + |row| { + let row_id = row.get(0)?; + let row_name: String = row.get(1)?; + let row_addr: String = row.get(2)?; + let row_origin = row.get(3)?; + let row_authname: String = row.get(4)?; + + if !name.is_null() && 0 != unsafe { *name.offset(0) as libc::c_int } { + if !row_name.is_empty() { + if origin >= row_origin && as_str(name) != row_name { + update_name = true; } - if origin == 0x10i32 && strcmp(name, row_authname) != 0i32 { - update_authname = 1i32 - } - } - if origin >= row_origin && strcmp(addr, row_addr) != 0i32 { - update_addr = 1i32 - } - if 0 != update_name - || 0 != update_authname - || 0 != update_addr - || origin > row_origin - { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text( - stmt, - 1i32, - if 0 != update_name { name } else { row_name }, - -1i32, - None, - ); - sqlite3_bind_text( - stmt, - 2i32, - if 0 != update_addr { addr } else { row_addr }, - -1i32, - None, - ); - sqlite3_bind_int( - stmt, - 3i32, - if origin > row_origin { - origin - } else { - row_origin - }, - ); - sqlite3_bind_text( - stmt, - 4i32, - if 0 != update_authname { - name - } else { - row_authname - }, - -1i32, - None, - ); - sqlite3_bind_int(stmt, 5i32, row_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if 0 != update_name { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, name, -1i32, None); - sqlite3_bind_int(stmt, 2i32, 100i32); - sqlite3_bind_int(stmt, 3i32, row_id as libc::c_int); - sqlite3_step(stmt); - } - *sth_modified = 1i32 } } else { - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( + update_name = true; + } + if origin == 0x10 && !name.is_null() && as_str(name) != row_authname { + update_authname = true; + } + Ok((row_id, row_name, row_addr, row_origin, row_authname)) + }, + ) { + row_id = id; + if origin >= row_origin && addr != row_addr { + update_addr = true; + } + if update_name || update_authname || update_addr || origin > row_origin { + sql::execute( + context, + &context.sql, + "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;", + params![ + if update_name { + to_string(name) + } else { + row_name + }, + if update_addr { addr } else { &row_addr }, + if origin > row_origin { + origin + } else { + row_origin + }, + if update_authname { + to_string(name) + } else { + row_authname + }, + row_id + ], + ); + + if update_name { + sql::execute( context, &context.sql, - b"INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);\x00" as *const u8 - as *const libc::c_char, + "UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);", + params![to_string(name), 100, row_id] ); - sqlite3_bind_text( - stmt, - 1i32, - if !name.is_null() { - name - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, - ); - sqlite3_bind_text(stmt, 2i32, addr, -1i32, None); - sqlite3_bind_int(stmt, 3i32, origin); - if sqlite3_step(stmt) == 101i32 { - row_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"contacts\x00" as *const u8 as *const libc::c_char, - b"addr\x00" as *const u8 as *const libc::c_char, - addr, - ); - *sth_modified = 2i32 - } else { - dc_log_error( - context, - 0i32, - b"Cannot add contact.\x00" as *const u8 as *const libc::c_char, - ); - } } + unsafe { *sth_modified = 1 }; + } + } else { + if sql::execute( + context, + &context.sql, + "INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);", + params![to_string(name), addr, origin,], + ) + .is_ok() + { + row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr); + unsafe { *sth_modified = 2 }; + } else { + error!(context, 0, "Cannot add contact."); } } - free(addr as *mut libc::c_void); - free(addr_self as *mut libc::c_void); - free(row_addr as *mut libc::c_void); - free(row_name as *mut libc::c_void); - free(row_authname as *mut libc::c_void); - sqlite3_finalize(stmt); + + unsafe { free(addr_c as *mut libc::c_void) }; row_id } @@ -607,143 +526,120 @@ pub unsafe fn dc_normalize_name(full_name: *mut libc::c_char) { }; } -pub unsafe fn dc_get_contacts( +pub fn dc_get_contacts( context: &Context, - listflags: uint32_t, + listflags: u32, query: *const libc::c_char, ) -> *mut dc_array_t { - let current_block: u64; - let self_addr: *mut libc::c_char; - let mut self_name: *mut libc::c_char = 0 as *mut libc::c_char; - let mut self_name2: *mut libc::c_char = 0 as *mut libc::c_char; - let mut add_self: libc::c_int = 0i32; - let ret: *mut dc_array_t = dc_array_new(100i32 as size_t); - let mut s3strLikeCmd: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if 0 != listflags & 0x1i32 as libc::c_uint || !query.is_null() { - s3strLikeCmd = sqlite3_mprintf( - b"%%%s%%\x00" as *const u8 as *const libc::c_char, - if !query.is_null() { - query - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - ); - if s3strLikeCmd.is_null() { - current_block = 7597307149762829253; - } else { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT c.id FROM contacts c LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.addr!=?1 AND c.id>?2 AND c.origin>=?3 AND c.blocked=0 AND (c.name LIKE ?4 OR c.addr LIKE ?5) AND (1=?6 OR LENGTH(ps.verified_key_fingerprint)!=0) ORDER BY LOWER(c.name||c.addr),c.id;\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, self_addr, -1i32, None); - sqlite3_bind_int(stmt, 2i32, 9i32); - sqlite3_bind_int(stmt, 3i32, 0x100i32); - sqlite3_bind_text(stmt, 4i32, s3strLikeCmd, -1i32, None); - sqlite3_bind_text(stmt, 5i32, s3strLikeCmd, -1i32, None); - sqlite3_bind_int( - stmt, - 6i32, - if 0 != listflags & 0x1i32 as libc::c_uint { - 0i32 - } else { - 1i32 + let mut add_self = false; + let ret = unsafe { dc_array_new(100) }; + + if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || !query.is_null() { + let s3strLikeCmd = format!("%{}%", if !query.is_null() { as_str(query) } else { "" }); + eprintln!("query '{}'", &s3strLikeCmd); + context + .sql + .query_map( + "SELECT c.id FROM contacts c \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ + WHERE c.addr!=?1 \ + AND c.id>?2 \ + AND c.origin>=?3 \ + AND c.blocked=0 \ + AND (c.name LIKE ?4 OR c.addr LIKE ?5) \ + AND (1=?6 OR LENGTH(ps.verified_key_fingerprint)!=0) \ + ORDER BY LOWER(c.name||c.addr),c.id;", + params![ + self_addr, + 9, + 0x100, + &s3strLikeCmd, + &s3strLikeCmd, + if 0 != listflags & 0x1 { 0 } else { 1 }, + ], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; + } + Ok(()) }, - ); - self_name = dc_sqlite3_get_config( - context, - &context.sql, - b"displayname\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - self_name2 = dc_stock_str(context, 2i32); - if query.is_null() - || 0 != dc_str_contains(self_addr, query) - || 0 != dc_str_contains(self_name, query) - || 0 != dc_str_contains(self_name2, query) - { - add_self = 1i32 - } - current_block = 15768484401365413375; + ) + .unwrap(); // TODO: Better error handling + + let self_name = context + .sql + .get_config(context, "displayname") + .unwrap_or_default(); + + let self_name2 = unsafe { dc_stock_str(context, 2) }; + + if query.is_null() + || self_addr.contains(as_str(query)) + || self_name.contains(as_str(query)) + || 0 != unsafe { dc_str_contains(self_name2, query) } + { + add_self = true; } + unsafe { free(self_name2 as *mut _) }; } else { - stmt = - dc_sqlite3_prepare( - context,&context.sql, - b"SELECT id FROM contacts WHERE addr!=?1 AND id>?2 AND origin>=?3 AND blocked=0 ORDER BY LOWER(name||addr),id;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, self_addr, -1i32, None); - sqlite3_bind_int(stmt, 2i32, 9i32); - sqlite3_bind_int(stmt, 3i32, 0x100i32); - add_self = 1i32; - current_block = 15768484401365413375; - } - match current_block { - 7597307149762829253 => {} - _ => { - while sqlite3_step(stmt) == 100i32 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0i32) as uint32_t); + add_self = true; + + context.sql.query_map( + "SELECT id FROM contacts WHERE addr!=?1 AND id>?2 AND origin>=?3 AND blocked=0 ORDER BY LOWER(name||addr),id;", + params![self_addr, 9, 0x100], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; + } + Ok(()) } - if 0 != listflags & 0x2i32 as libc::c_uint && 0 != add_self { - dc_array_add_id(ret, 1i32 as uint32_t); - } - } + ).unwrap(); // TODO: better error handling } - sqlite3_finalize(stmt); - sqlite3_free(s3strLikeCmd as *mut libc::c_void); - free(self_addr as *mut libc::c_void); - free(self_name as *mut libc::c_void); - free(self_name2 as *mut libc::c_void); + if 0 != listflags & 0x2 && add_self { + unsafe { dc_array_add_id(ret, 1) }; + } ret } -pub unsafe fn dc_get_blocked_cnt(context: &Context) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt; - - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, 9i32); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) - } - - sqlite3_finalize(stmt); - ret +pub fn dc_get_blocked_cnt(context: &Context) -> libc::c_int { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0", + params![9], + 0, + ) + .unwrap_or_default() } -pub unsafe fn dc_get_blocked_contacts(context: &Context) -> *mut dc_array_t { - let ret: *mut dc_array_t = dc_array_new(100i32 as size_t); - let stmt: *mut sqlite3_stmt; +pub fn dc_get_blocked_contacts(context: &Context) -> *mut dc_array_t { + context + .sql + .query_map( + "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", + params![9], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, 9i32); - while sqlite3_step(stmt) == 100i32 { - dc_array_add_id(ret, sqlite3_column_int(stmt, 0i32) as uint32_t); - } + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; + } - sqlite3_finalize(stmt); - ret + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_contact_encrinfo( @@ -751,23 +647,18 @@ pub unsafe fn dc_get_contact_encrinfo( contact_id: uint32_t, ) -> *mut libc::c_char { let mut ret = String::new(); - let loginparam: *mut dc_loginparam_t = dc_loginparam_new(); - let contact: *mut dc_contact_t = dc_contact_new(context); + let contact = dc_contact_new(context); - let mut fingerprint_self: *mut libc::c_char = 0 as *mut libc::c_char; - let mut fingerprint_other_verified: *mut libc::c_char = 0 as *mut libc::c_char; - let mut fingerprint_other_unverified: *mut libc::c_char = 0 as *mut libc::c_char; + let mut fingerprint_self = 0 as *mut libc::c_char; + let mut fingerprint_other_verified = 0 as *mut libc::c_char; + let mut fingerprint_other_unverified = 0 as *mut libc::c_char; let mut p: *mut libc::c_char; if !(!dc_contact_load_from_db(contact, &context.sql, contact_id)) { let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); - dc_loginparam_read( - context, - loginparam, - &context.sql, - b"configured_\x00" as *const u8 as *const libc::c_char, - ); - let mut self_key = Key::from_self_public(context, (*loginparam).addr, &context.sql); + let loginparam = dc_loginparam_read(context, &context.sql, "configured_"); + + let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); if peerstate.is_some() && peerstate.as_ref().and_then(|p| p.peek_key(0)).is_some() { let peerstate = peerstate.as_ref().unwrap(); @@ -783,7 +674,7 @@ pub unsafe fn dc_get_contact_encrinfo( free(p as *mut libc::c_void); if self_key.is_none() { dc_ensure_secret_key_exists(context); - self_key = Key::from_self_public(context, (*loginparam).addr, &context.sql); + self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); } p = dc_stock_str(context, 30i32); ret += &format!(" {}:", as_str(p)); @@ -800,12 +691,10 @@ pub unsafe fn dc_get_contact_encrinfo( .peek_key(0) .map(|k| k.formatted_fingerprint_c()) .unwrap_or(std::ptr::null_mut()); - if peerstate.addr.is_some() - && as_str((*loginparam).addr) < peerstate.addr.as_ref().unwrap().as_str() - { + if peerstate.addr.is_some() && &loginparam.addr < peerstate.addr.as_ref().unwrap() { cat_fingerprint( &mut ret, - to_string((*loginparam).addr), + &loginparam.addr, fingerprint_self, 0 as *const libc::c_char, ); @@ -824,26 +713,23 @@ pub unsafe fn dc_get_contact_encrinfo( ); cat_fingerprint( &mut ret, - to_string((*loginparam).addr), + &loginparam.addr, fingerprint_self, 0 as *const libc::c_char, ); } - } else if 0 == (*loginparam).server_flags & 0x400i32 - && 0 == (*loginparam).server_flags & 0x40000i32 - { - p = dc_stock_str(context, 27i32); + } else if 0 == loginparam.server_flags & 0x400 && 0 == loginparam.server_flags & 0x40000 { + p = dc_stock_str(context, 27); ret += as_str(p); free(p as *mut libc::c_void); } else { - p = dc_stock_str(context, 28i32); + p = dc_stock_str(context, 28); ret += as_str(p); free(p as *mut libc::c_void); } } dc_contact_unref(contact); - dc_loginparam_unref(loginparam); free(fingerprint_self as *mut libc::c_void); free(fingerprint_other_verified as *mut libc::c_void); @@ -873,7 +759,7 @@ unsafe fn cat_fingerprint( && 0 != *fingerprint_verified.offset(0isize) as libc::c_int && !fingerprint_unverified.is_null() && 0 != *fingerprint_unverified.offset(0isize) as libc::c_int - && strcmp(fingerprint_verified, fingerprint_unverified) != 0i32 + && strcmp(fingerprint_verified, fingerprint_unverified) != 0 { *ret += &format!( "\n\n{} (alternative):\n{}", @@ -883,49 +769,52 @@ unsafe fn cat_fingerprint( } } -pub unsafe fn dc_delete_contact(context: &Context, contact_id: uint32_t) -> bool { - let mut success = false; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(contact_id <= 9i32 as libc::c_uint) { - stmt = dc_sqlite3_prepare( +pub fn dc_delete_contact(context: &Context, contact_id: u32) -> bool { + if contact_id <= 9 { + return false; + } + + let count_contacts: i32 = context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;", + params![contact_id as i32], + 0, + ) + .unwrap_or_default(); + + let count_msgs: i32 = if count_contacts > 0 { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;", + params![contact_id as i32, contact_id as i32], + 0, + ) + .unwrap_or_default() + } else { + 0 + }; + + if count_msgs > 0 { + if sql::execute( context, &context.sql, - b"SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32 || sqlite3_column_int(stmt, 0i32) >= 1i32) { - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, contact_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32 || sqlite3_column_int(stmt, 0i32) >= 1i32) { - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"DELETE FROM contacts WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - if !(sqlite3_step(stmt) != 101i32) { - context.call_cb( - Event::CONTACTS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - success = true - } - } + "DELETE FROM contacts WHERE id=?;", + params![contact_id as i32], + ) + .is_ok() + { + context.call_cb(Event::CONTACTS_CHANGED, 0, 0); + true + } else { + false } + } else { + false } - sqlite3_finalize(stmt); - - success } pub unsafe fn dc_get_contact(context: &Context, contact_id: uint32_t) -> *mut dc_contact_t { @@ -1006,22 +895,20 @@ pub unsafe fn dc_get_first_name(full_name: *const libc::c_char) -> *mut libc::c_ first_name } -pub unsafe fn dc_contact_get_profile_image(contact: *const dc_contact_t) -> *mut libc::c_char { - let mut selfavatar: *mut libc::c_char = 0 as *mut libc::c_char; - let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; - if !(contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint) { - if (*contact).id == 1i32 as libc::c_uint { - selfavatar = dc_get_config( - (*contact).context, - b"selfavatar\x00" as *const u8 as *const libc::c_char, - ); - if !selfavatar.is_null() && 0 != *selfavatar.offset(0isize) as libc::c_int { - image_abs = dc_strdup(selfavatar) - } +pub fn dc_contact_get_profile_image(contact: *const dc_contact_t) -> *mut libc::c_char { + let mut image_abs = 0 as *mut libc::c_char; + + if contact.is_null() || unsafe { (*contact).magic != 0xc047ac7 } { + return image_abs; + } + + if unsafe { (*contact).id } == 1 { + let context = unsafe { (*contact) }.context; + if let Some(avatar) = context.get_config(config::Config::Selfavatar) { + image_abs = unsafe { dc_strdup(to_cstring(avatar).as_ptr()) }; } } // TODO: else get image_abs from contact param - free(selfavatar as *mut libc::c_void); image_abs } @@ -1096,79 +983,67 @@ pub unsafe fn dc_contact_is_verified_ex<'a>( } // Working with e-mail-addresses -pub unsafe fn dc_addr_cmp(addr1: *const libc::c_char, addr2: *const libc::c_char) -> libc::c_int { - let norm1: *mut libc::c_char = dc_addr_normalize(addr1); - let norm2: *mut libc::c_char = dc_addr_normalize(addr2); - let ret: libc::c_int = strcasecmp(addr1, addr2); - free(norm1 as *mut libc::c_void); - free(norm2 as *mut libc::c_void); - ret +pub fn dc_addr_cmp(addr1: impl AsRef, addr2: impl AsRef) -> bool { + let norm1 = dc_addr_normalize_safe(addr1.as_ref()); + let norm2 = dc_addr_normalize_safe(addr2.as_ref()); + + norm1 == norm2 } -pub unsafe fn dc_addr_equals_self(context: &Context, addr: *const libc::c_char) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let mut normalized_addr: *mut libc::c_char = 0 as *mut libc::c_char; - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; +pub fn dc_addr_equals_self(context: &Context, addr: *const libc::c_char) -> libc::c_int { + let mut ret = 0; + if !addr.is_null() { - normalized_addr = dc_addr_normalize(addr); - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if !self_addr.is_null() { - ret = if strcasecmp(normalized_addr, self_addr) == 0i32 { - 1i32 - } else { - 0i32 - } + let normalized_addr = unsafe { dc_addr_normalize(addr) }; + if let Some(self_addr) = context.sql.get_config(context, "configured_addr") { + ret = (as_str(normalized_addr) == self_addr) as libc::c_int; } + unsafe { free(normalized_addr as *mut libc::c_void) }; } - free(self_addr as *mut libc::c_void); - free(normalized_addr as *mut libc::c_void); + ret } pub unsafe fn dc_addr_equals_contact( context: &Context, - addr: *const libc::c_char, - contact_id: uint32_t, + addr: impl AsRef, + contact_id: u32, ) -> bool { + if addr.as_ref().is_empty() { + return false; + } + + let contact = dc_contact_new(context); let mut addr_are_equal = false; - if !addr.is_null() { - let contact: *mut dc_contact_t = dc_contact_new(context); - if dc_contact_load_from_db(contact, &context.sql, contact_id) { - if !(*contact).addr.is_null() { - let normalized_addr: *mut libc::c_char = dc_addr_normalize(addr); - if strcasecmp((*contact).addr, normalized_addr) == 0i32 { - addr_are_equal = true; - } - free(normalized_addr as *mut libc::c_void); + + if dc_contact_load_from_db(contact, &context.sql, contact_id) { + if !(*contact).addr.is_null() { + let normalized_addr = dc_addr_normalize_safe(addr.as_ref()); + if as_str((*contact).addr) == normalized_addr { + addr_are_equal = true; } } dc_contact_unref(contact); } + addr_are_equal } // Context functions to work with contacts -pub unsafe fn dc_get_real_contact_cnt(context: &Context) -> size_t { - let mut ret: size_t = 0i32 as size_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if context.sql.is_open() { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM contacts WHERE id>?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, 9i32); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) as size_t - } +pub fn dc_get_real_contact_cnt(context: &Context) -> usize { + if !context.sql.is_open() { + return 0; } - sqlite3_finalize(stmt); - ret + + context + .sql + .query_row_col::<_, isize>( + context, + "SELECT COUNT(*) FROM contacts WHERE id>?;", + params![9], + 0, + ) + .unwrap_or_default() as usize } pub unsafe fn dc_get_contact_origin( @@ -1195,40 +1070,28 @@ pub unsafe fn dc_get_contact_origin( ret } -pub unsafe fn dc_real_contact_exists(context: &Context, contact_id: uint32_t) -> bool { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut ret = false; - if !(!context.sql.is_open() || contact_id <= 9i32 as libc::c_uint) { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM contacts WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, contact_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - ret = true - } +pub fn dc_real_contact_exists(context: &Context, contact_id: u32) -> bool { + if !context.sql.is_open() || contact_id <= 9 { + return false; } - sqlite3_finalize(stmt); - ret + + context + .sql + .exists( + "SELECT id FROM contacts WHERE id=?;", + params![contact_id as i32], + ) + .unwrap_or_default() } -pub unsafe fn dc_scaleup_contact_origin( - context: &Context, - contact_id: uint32_t, - origin: libc::c_int, -) { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE contacts SET origin=? WHERE id=? AND origin bool { + context + .sql + .execute( + "UPDATE contacts SET origin=? WHERE id=? AND origin {} _ => { - let addr = CStr::from_ptr(addr).to_str().unwrap(); - let aheader = Aheader::new(addr.into(), public_key, prefer_encrypt); + let aheader = Aheader::new(addr, public_key, prefer_encrypt); let rendered = CString::new(aheader.to_string()).unwrap(); mailimf_fields_add( @@ -503,13 +489,13 @@ unsafe fn new_data_part( ******************************************************************************/ unsafe fn load_or_generate_self_public_key( context: &Context, - self_addr: *const libc::c_char, + self_addr: impl AsRef, _random_data_mime: *mut mailmime, ) -> Option { /* avoid double creation (we unlock the database during creation) */ - static mut s_in_key_creation: libc::c_int = 0i32; + static mut s_in_key_creation: libc::c_int = 0; - let mut key = Key::from_self_public(context, self_addr, &context.sql); + let mut key = Key::from_self_public(context, &self_addr, &context.sql); if key.is_some() { return key; } @@ -521,46 +507,35 @@ unsafe fn load_or_generate_self_public_key( let key_creation_here = 1; s_in_key_creation = 1; - let start: libc::clock_t = clock(); - dc_log_info( + let start = clock(); + info!( context, - 0i32, - b"Generating keypair with %i bits, e=%i ...\x00" as *const u8 as *const libc::c_char, - 2048i32, - 65537i32, + 0, "Generating keypair with {} bits, e={} ...", 2048, 65537, ); - if let Some((public_key, private_key)) = dc_pgp_create_keypair(self_addr) { + if let Some((public_key, private_key)) = dc_pgp_create_keypair(&self_addr) { if !dc_key_save_self_keypair( context, &public_key, &private_key, - self_addr, + &self_addr, 1i32, &context.sql, ) { /*set default*/ - dc_log_warning( - context, - 0i32, - b"Cannot save keypair.\x00" as *const u8 as *const libc::c_char, - ); + warn!(context, 0, "Cannot save keypair.",); } else { - dc_log_info( + info!( context, - 0i32, - b"Keypair generated in %.3f s.\x00" as *const u8 as *const libc::c_char, - clock().wrapping_sub(start) as libc::c_double / 1000000i32 as libc::c_double, + 0, + "Keypair generated in {:.3}s.", + clock().wrapping_sub(start) as libc::c_double / 1000000 as libc::c_double, ); } key = Some(public_key); } else { - dc_log_warning( - context, - 0i32, - b"Cannot create keypair.\x00" as *const u8 as *const libc::c_char, - ); + warn!(context, 0, "Cannot create keypair."); } if 0 != key_creation_here { @@ -583,7 +558,6 @@ pub unsafe fn dc_e2ee_decrypt( let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message); let mut message_time = 0; let mut from: *mut libc::c_char = 0 as *mut libc::c_char; - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; let mut private_keyring = Keyring::default(); let mut public_keyring_for_validate = Keyring::default(); let mut gossip_headers: *mut mailimf_fields = 0 as *mut mailimf_fields; @@ -612,28 +586,23 @@ pub unsafe fn dc_e2ee_decrypt( if let Some(ref mut peerstate) = peerstate { if let Some(ref header) = autocryptheader { - peerstate.apply_header(&header, message_time as u64); + peerstate.apply_header(&header, message_time); peerstate.save_to_db(&context.sql, false); - } else if message_time as u64 > peerstate.last_seen_autocrypt + } else if message_time > peerstate.last_seen_autocrypt && 0 == contains_report(in_out_message) { - peerstate.degrade_encryption(message_time as u64); + peerstate.degrade_encryption(message_time); peerstate.save_to_db(&context.sql, false); } } else if let Some(ref header) = autocryptheader { - let p = Peerstate::from_header(context, header, message_time as u64); + let p = Peerstate::from_header(context, header, message_time); p.save_to_db(&context.sql, true); peerstate = Some(p); } } /* load private key for decryption */ - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if !self_addr.is_null() { + let self_addr = context.sql.get_config(context, "configured_addr"); + if let Some(self_addr) = self_addr { if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) { if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 { peerstate = Peerstate::from_addr(&context, &context.sql, as_str(from)); @@ -681,7 +650,6 @@ pub unsafe fn dc_e2ee_decrypt( } free(from as *mut libc::c_void); - free(self_addr as *mut libc::c_void); } unsafe fn update_gossip_peerstates( @@ -722,10 +690,10 @@ unsafe fn update_gossip_peerstates( let mut peerstate = Peerstate::from_addr(context, &context.sql, &header.addr); if let Some(ref mut peerstate) = peerstate { - peerstate.apply_gossip(header, message_time as u64); + peerstate.apply_gossip(header, message_time); peerstate.save_to_db(&context.sql, false); } else { - let p = Peerstate::from_gossip(context, header, message_time as u64); + let p = Peerstate::from_gossip(context, header, message_time); p.save_to_db(&context.sql, true); peerstate = Some(p); } @@ -737,12 +705,11 @@ unsafe fn update_gossip_peerstates( gossipped_addr.insert(header.addr.clone()); } else { - dc_log_info( + 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(), + 0, + "Ignoring gossipped \"{}\" as the address is not in To/Cc list.", + &header.addr, ); } } @@ -1103,25 +1070,18 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int { (this is to gain some extra-random-seed by the message content and the timespan between program start and message sending) */ let mut success: libc::c_int = 0i32; - let self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if self_addr.is_null() { - dc_log_warning( + let self_addr = context.sql.get_config(context, "configured_addr"); + if self_addr.is_none() { + warn!( context, - 0i32, - b"Cannot ensure secret key if context is not configured.\x00" as *const u8 - as *const libc::c_char, + 0, "Cannot ensure secret key if context is not configured.", ); - } else if load_or_generate_self_public_key(context, self_addr, 0 as *mut mailmime).is_some() { + } else if load_or_generate_self_public_key(context, self_addr.unwrap(), 0 as *mut mailmime) + .is_some() + { /*no random text data for seeding available*/ - success = 1i32 + success = 1; } - free(self_addr as *mut libc::c_void); - success } diff --git a/src/dc_imex.rs b/src/dc_imex.rs index 172b7790d..f55bcdfc1 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -1,5 +1,6 @@ use std::ffi::CString; +use failure::format_err; use mmime::mailmime_content::*; use mmime::mmapstring::*; use mmime::other::*; @@ -11,14 +12,13 @@ use crate::dc_chat::*; use crate::dc_configure::*; use crate::dc_e2ee::*; use crate::dc_job::*; -use crate::dc_log::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; use crate::key::*; use crate::pgp::*; +use crate::sql::{self, Sql}; use crate::types::*; use crate::x::*; @@ -50,13 +50,11 @@ pub unsafe fn dc_imex_has_backup( let dir_name = as_path(dir_name); let dir_iter = std::fs::read_dir(dir_name); if dir_iter.is_err() { - dc_log_info( + info!( context, - 0i32, - b"Backup check: Cannot open directory \"%s\".\x00" as *const u8 as *const libc::c_char, - CString::new(format!("{}", dir_name.display())) - .unwrap() - .as_ptr(), + 0, + "Backup check: Cannot open directory \"{}\".\x00", + dir_name.display(), ); return 0 as *mut libc::c_char; } @@ -69,14 +67,11 @@ pub unsafe fn dc_imex_has_backup( let name = dirent.file_name(); let name = name.to_string_lossy(); if name.starts_with("delta-chat") && name.ends_with(".bak") { - let mut sql = SQLite::new(); - if sql.open(context, &path, 0x1i32) { - let curr_backup_time = dc_sqlite3_get_config_int( - context, - &mut sql, - b"backup_time\x00" as *const u8 as *const libc::c_char, - 0i32, - ) as u64; + let sql = Sql::new(); + if sql.open(context, &path, 0x1) { + let curr_backup_time = + sql.get_config_int(context, "backup_time") + .unwrap_or_default() as u64; if curr_backup_time > newest_backup_time { newest_backup_path = Some(path); newest_backup_time = curr_backup_time; @@ -91,12 +86,7 @@ pub unsafe fn dc_imex_has_backup( Some(path) => match path.to_c_string() { Ok(cstr) => dc_strdup(cstr.as_ptr()), Err(err) => { - dc_log_error( - context, - 0i32, - b"Invalid backup filename: %s\x00" as *const u8 as *const libc::c_char, - CString::new(format!("{}", err)).unwrap().as_ptr(), - ); + error!(context, 0, "Invalid backup filename: {}", err); std::ptr::null_mut() } }, @@ -173,12 +163,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { if !(msg_id == 0i32 as libc::c_uint) { dc_msg_unref(msg); msg = 0 as *mut dc_msg_t; - dc_log_info( - context, - 0i32, - b"Wait for setup message being sent ...\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Wait for setup message being sent ...",); loop { if context .running_state @@ -202,13 +187,8 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { match current_block { 6116957410927263949 => {} _ => { - dc_log_info( - context, - 0i32, - b"... setup message sent.\x00" as *const u8 - as *const libc::c_char, - ); - success = 1i32 + info!(context, 0, "... setup message sent.",); + success = 1; } } } @@ -236,7 +216,6 @@ pub unsafe extern "C" fn dc_render_setup_file( passphrase: *const libc::c_char, ) -> *mut libc::c_char { let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; let mut passphrase_begin: [libc::c_char; 8] = [0; 8]; let mut ret_setupfilecontent: *mut libc::c_char = 0 as *mut libc::c_char; @@ -245,19 +224,15 @@ pub unsafe extern "C" fn dc_render_setup_file( passphrase_begin[2usize] = 0i32 as libc::c_char; /* create the payload */ if !(0 == dc_ensure_secret_key_exists(context)) { - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); let curr_private_key = Key::from_self_private(context, self_addr, &context.sql); - let e2ee_enabled: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + let e2ee_enabled = context + .sql + .get_config_int(context, "e2ee_enabled") + .unwrap_or_else(|| 1); let headers = if 0 != e2ee_enabled { Some(("Autocrypt-Prefer-Encrypt", "mutual")) @@ -311,8 +286,6 @@ pub unsafe extern "C" fn dc_render_setup_file( } sqlite3_finalize(stmt); - free(self_addr as *mut libc::c_void); - ret_setupfilecontent } @@ -355,7 +328,7 @@ pub unsafe fn dc_continue_key_transfer( if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) { msg = dc_get_msg(context, msg_id); if msg.is_null() - || 0 == dc_msg_is_setupmessage(msg) + || !dc_msg_is_setupmessage(msg) || { filename = dc_msg_get_file(msg); filename.is_null() @@ -400,101 +373,83 @@ pub unsafe fn dc_continue_key_transfer( } // TODO should return bool /rtn -unsafe fn set_self_key( +fn set_self_key( context: &Context, armored_c: *const libc::c_char, set_default: libc::c_int, ) -> libc::c_int { - let mut success = 0; - - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; - assert!(!armored_c.is_null(), "invalid buffer"); let armored = as_str(armored_c); - if let Some((private_key, public_key, header)) = - Key::from_armored_string(armored, KeyType::Private) - .and_then(|(k, h)| if k.verify() { Some((k, h)) } else { None }) - .and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h))) - { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"DELETE FROM keypairs WHERE public_key=? OR private_key=?;\x00" as *const u8 - as *const libc::c_char, - ); - let pub_bytes = public_key.to_bytes(); - sqlite3_bind_blob( - stmt, - 1, - pub_bytes.as_ptr() as *const _, - pub_bytes.len() as libc::c_int, - None, - ); - let priv_bytes = private_key.to_bytes(); - sqlite3_bind_blob( - stmt, - 2, - priv_bytes.as_ptr() as *const _, - priv_bytes.len() as libc::c_int, - None, - ); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if 0 != set_default { - dc_sqlite3_execute( - context, - &context.sql, - b"UPDATE keypairs SET is_default=0;\x00" as *const u8 as *const libc::c_char, - ); - } - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if !dc_key_save_self_keypair( - context, - &public_key, - &private_key, - self_addr, - set_default, - &context.sql, - ) { - error!(context, 0, "Cannot save keypair.",); - } else { - let prefer_encrypt = header.get("Autocrypt-Prefer-Encrypt"); + let keys = Key::from_armored_string(armored, KeyType::Private) + .and_then(|(k, h)| if k.verify() { Some((k, h)) } else { None }) + .and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h))); - if let Some(prefer_encrypt) = prefer_encrypt { - if prefer_encrypt == "nopreference" { - dc_sqlite3_set_config_int( - context, - &context.sql, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - 0i32, - ); - } else if prefer_encrypt == "mutual" { - dc_sqlite3_set_config_int( - context, - &context.sql, - b"e2ee_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ); - } - } - success = 1; + if keys.is_none() { + error!(context, 0, "File does not contain a valid private key.",); + return 0; + } + + let (private_key, public_key, header) = keys.unwrap(); + let preferencrypt = header.get("Autocrypt-Prefer-Encrypt"); + + if sql::execute( + context, + &context.sql, + "DELETE FROM keypairs WHERE public_key=? OR private_key=?;", + params![public_key.to_bytes(), private_key.to_bytes()], + ) + .is_err() + { + return 0; + } + + if 0 != set_default { + if sql::execute( + context, + &context.sql, + "UPDATE keypairs SET is_default=0;", + params![], + ) + .is_err() + { + return 0; } } else { error!(context, 0, "File does not contain a private key.",); } - sqlite3_finalize(stmt); - free(self_addr as *mut libc::c_void); + let self_addr = context.sql.get_config(context, "configured_addr"); - success + if self_addr.is_none() { + error!(context, 0, "Missing self addr"); + return 0; + } + + if !dc_key_save_self_keypair( + context, + &public_key, + &private_key, + self_addr.unwrap(), + set_default, + &context.sql, + ) { + error!(context, 0, "Cannot save keypair."); + return 0; + } + + match preferencrypt.map(|s| s.as_str()) { + Some("") => 0, + Some("nopreference") => context + .sql + .set_config_int(context, "e2ee_enabled", 0) + .is_ok() as libc::c_int, + Some("mutual") => context + .sql + .set_config_int(context, "e2ee_enabled", 1) + .is_ok() as libc::c_int, + _ => 1, + } } pub unsafe fn dc_decrypt_setup_file( @@ -600,32 +555,21 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) param1 = dc_param_get((*job).param, 'E' as i32, 0 as *const libc::c_char); param2 = dc_param_get((*job).param, 'F' as i32, 0 as *const libc::c_char); if param1.is_null() { - dc_log_error( - context, - 0i32, - b"No Import/export dir/file given.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "No Import/export dir/file given.",); } else { - dc_log_info( - context, - 0i32, - b"Import/export process started.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Import/export process started.",); context.call_cb(Event::IMEX_PROGRESS, 10i32 as uintptr_t, 0i32 as uintptr_t); if !context.sql.is_open() { - dc_log_error( - context, - 0i32, - b"Import/export: Database not opened.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "Import/export: Database not opened.",); } else { if what == 1i32 || what == 11i32 { /* before we export anything, make sure the private key exists */ if 0 == dc_ensure_secret_key_exists(context) { - dc_log_error(context, 0i32, - b"Import/export: Cannot create private key or private key not available.\x00" - as *const u8 as - *const libc::c_char); + error!( + context, + 0, + "Import/export: Cannot create private key or private key not available.", + ); current_block = 3568988166330621280; } else { dc_create_folder(context, param1); @@ -672,12 +616,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) match current_block { 3568988166330621280 => {} _ => { - dc_log_info( - context, - 0i32, - b"Import/export completed.\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Import/export completed.",); success = 1i32 } } @@ -717,12 +656,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) match current_block { 3568988166330621280 => {} _ => { - dc_log_info( - context, - 0i32, - b"Import/export completed.\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Import/export completed.",); success = 1i32 } } @@ -762,12 +696,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) match current_block { 3568988166330621280 => {} _ => { - dc_log_info( - context, - 0i32, - b"Import/export completed.\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Import/export completed.",); success = 1i32 } } @@ -807,12 +736,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) match current_block { 3568988166330621280 => {} _ => { - dc_log_info( - context, - 0i32, - b"Import/export completed.\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Import/export completed.",); success = 1i32 } } @@ -842,65 +766,68 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) // TODO should return bool /rtn unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char) -> libc::c_int { - let current_block: u64; - let mut success: libc::c_int = 0i32; - let mut processed_files_cnt: libc::c_int = 0i32; - let total_files_cnt: libc::c_int; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut pathNfilename: *mut libc::c_char = 0 as *mut libc::c_char; - let repl_from: *mut libc::c_char = 0 as *mut libc::c_char; - let repl_to: *mut libc::c_char = 0 as *mut libc::c_char; - dc_log_info( + info!( context, - 0i32, - b"Import \"%s\" to \"%s\".\x00" as *const u8 as *const libc::c_char, - backup_to_import, - context.get_dbfile(), + 0, + "Import \"{}\" to \"{}\".", + as_str(backup_to_import), + as_str(context.get_dbfile()), ); if 0 != dc_is_configured(context) { - dc_log_error( + error!(context, 0, "Cannot import backups to accounts in use."); + return 0; + } + &context.sql.close(&context); + dc_delete_file(context, context.get_dbfile()); + if 0 != dc_file_exist(context, context.get_dbfile()) { + error!( context, - 0i32, - b"Cannot import backups to accounts in use.\x00" as *const u8 as *const libc::c_char, + 0, "Cannot import backups: Cannot delete the old file.", ); - } else { - &context.sql.close(&context); - dc_delete_file(context, context.get_dbfile()); - if 0 != dc_file_exist(context, context.get_dbfile()) { - dc_log_error( - context, - 0i32, - b"Cannot import backups: Cannot delete the old file.\x00" as *const u8 - as *const libc::c_char, - ); - } else if !(0 == dc_copy_file(context, backup_to_import, context.get_dbfile())) { - /* error already logged */ - /* re-open copied database file */ - if context.sql.open(&context, as_path(context.get_dbfile()), 0) { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM backup_blobs;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_step(stmt); - total_files_cnt = sqlite3_column_int(stmt, 0i32); - info!( - context, - 0, "***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt - ); - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT file_name, file_content FROM backup_blobs ORDER BY id;\x00" - as *const u8 as *const libc::c_char, - ); - loop { - if !(sqlite3_step(stmt) == 100i32) { - current_block = 10891380440665537214; + return 0; + } + + if 0 == dc_copy_file(context, backup_to_import, context.get_dbfile()) { + return 0; + } + /* error already logged */ + /* re-open copied database file */ + if !context.sql.open(&context, as_path(context.get_dbfile()), 0) { + return 0; + } + + let total_files_cnt = context + .sql + .query_row_col::<_, isize>(context, "SELECT COUNT(*) FROM backup_blobs;", params![], 0) + .unwrap_or_default() as usize; + info!( + context, + 0, "***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt, + ); + + context + .sql + .query_map( + "SELECT file_name, file_content FROM backup_blobs ORDER BY id;", + params![], + |row| { + let name: String = row.get(0)?; + let blob: Vec = row.get(1)?; + + Ok((name, blob)) + }, + |files| { + let mut loop_success = true; + let mut processed_files_cnt = 0; + + for file in files { + if file.is_err() { + loop_success = false; break; } + let (file_name, file_blob) = file.unwrap(); + if context .running_state .clone() @@ -908,84 +835,48 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char .unwrap() .shall_stop_ongoing { - current_block = 8648553629232744886; + loop_success = false; break; } processed_files_cnt += 1; - let mut permille: libc::c_int = processed_files_cnt * 1000i32 / total_files_cnt; - if permille < 10i32 { - permille = 10i32 + let mut permille = processed_files_cnt * 1000 / total_files_cnt; + if permille < 10 { + permille = 10 } - if permille > 990i32 { - permille = 990i32 + if permille > 990 { + permille = 990 } - context.call_cb( - Event::IMEX_PROGRESS, - permille as uintptr_t, - 0i32 as uintptr_t, - ); - let file_name: *const libc::c_char = - sqlite3_column_text(stmt, 0i32) as *const libc::c_char; - let file_bytes: libc::c_int = sqlite3_column_bytes(stmt, 1i32); - let file_content: *const libc::c_void = sqlite3_column_blob(stmt, 1i32); - if !(file_bytes > 0i32 && !file_content.is_null()) { + context.call_cb(Event::IMEX_PROGRESS, permille as uintptr_t, 0); + if file_blob.is_empty() { continue; } - free(pathNfilename as *mut libc::c_void); - pathNfilename = dc_mprintf( - b"%s/%s\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - file_name, - ); - if !(0 - == dc_write_file( - context, - pathNfilename, - file_content, - file_bytes as size_t, - )) - { + + let pathNfilename = format!("{}/{}", as_str(context.get_blobdir()), file_name); + if dc_write_file_safe(context, &pathNfilename, &file_blob) { continue; } - dc_log_error( + + error!( context, - 0i32, - b"Storage full? Cannot write file %s with %i bytes.\x00" as *const u8 - as *const libc::c_char, - pathNfilename, - file_bytes, + 0, + "Storage full? Cannot write file {} with {} bytes.", + &pathNfilename, + file_blob.len(), ); - /* otherwise the user may believe the stuff is imported correctly, but there are files missing ... */ - current_block = 8648553629232744886; + // otherwise the user may believe the stuff is imported correctly, but there are files missing ... + loop_success = false; break; } - match current_block { - 8648553629232744886 => {} - _ => { - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - dc_sqlite3_execute( - context, - &context.sql, - b"DROP TABLE backup_blobs;\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_try_execute( - context, - &context.sql, - b"VACUUM;\x00" as *const u8 as *const libc::c_char, - ); - success = 1i32 - } - } - } - } - } - free(pathNfilename as *mut libc::c_void); - free(repl_from as *mut libc::c_void); - free(repl_to as *mut libc::c_void); - sqlite3_finalize(stmt); - success + if !loop_success { + return Err(format_err!("fail").into()); + } + sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; + sql::try_execute(context, &context.sql, "VACUUM;"); + Ok(()) + }, + ) + .is_ok() as libc::c_int } /******************************************************************************* @@ -996,15 +887,9 @@ The macro avoids weird values of 0% or 100% while still working. */ // TODO should return bool /rtn unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int { let mut current_block: u64; - let mut success: libc::c_int = 0i32; - let mut closed: libc::c_int; + let mut success: libc::c_int = 0; - let mut curr_pathNfilename: *mut libc::c_char = 0 as *mut libc::c_char; - let mut buf: *mut libc::c_void = 0 as *mut libc::c_void; - let mut buf_bytes: size_t = 0i32 as size_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut delete_dest_file: libc::c_int = 0i32; - let mut dest_sql: Option = None; + let mut delete_dest_file: libc::c_int = 0; // get a fine backup file name (the name includes the date so that multiple backup instances are possible) // FIXME: we should write to a temporary file first and rename it on success. this would guarantee the backup is complete. however, currently it is not clear it the import exists in the long run (may be replaced by a restore-from-imap) let now = time(); @@ -1014,23 +899,16 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ let buffer = to_cstring(res); let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer.as_ptr()); if dest_pathNfilename.is_null() { - dc_log_error( - context, - 0i32, - b"Cannot get backup file name.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "Cannot get backup file name.",); return success; } - dc_housekeeping(context); - dc_sqlite3_try_execute( - context, - &context.sql, - b"VACUUM;\x00" as *const u8 as *const libc::c_char, - ); - context.sql.close(&context); - closed = 1i32; + sql::housekeeping(context); + + sql::try_execute(context, &context.sql, "VACUUM;"); + context.sql.close(context); + let mut closed = true; info!( context, 0, @@ -1040,24 +918,25 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ ); if !(0 == dc_copy_file(context, context.get_dbfile(), dest_pathNfilename)) { context.sql.open(&context, as_path(context.get_dbfile()), 0); - closed = 0i32; + closed = false; /* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */ /*for logging only*/ - let sql = SQLite::new(); + let sql = Sql::new(); if sql.open(context, as_path(dest_pathNfilename), 0) { - if 0 == dc_sqlite3_table_exists( - context, - &sql, - b"backup_blobs\x00" as *const u8 as *const libc::c_char, - ) { - if 0 == - dc_sqlite3_execute(context, &sql, - b"CREATE TABLE backup_blobs (id INTEGER PRIMARY KEY, file_name, file_content);\x00" - as *const u8 as - *const libc::c_char) { - /* error already logged */ - current_block = 11487273724841241105; - } else { current_block = 14648156034262866959; } + if !sql.table_exists("backup_blobs") { + if sql::execute( + context, + &sql, + "CREATE TABLE backup_blobs (id INTEGER PRIMARY KEY, file_name, file_content);", + params![], + ) + .is_err() + { + /* error already logged */ + current_block = 11487273724841241105; + } else { + current_block = 14648156034262866959; + } } else { current_block = 14648156034262866959; } @@ -1068,12 +947,11 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ let dir = std::path::Path::new(as_str(context.get_blobdir())); let dir_handle = std::fs::read_dir(dir); if dir_handle.is_err() { - dc_log_error( + error!( context, - 0i32, - b"Backup: Cannot get info for blob-directory \"%s\".\x00" as *const u8 - as *const libc::c_char, - context.get_blobdir(), + 0, + "Backup: Cannot get info for blob-directory \"{}\".", + as_str(context.get_blobdir()), ); } else { let dir_handle = dir_handle.unwrap(); @@ -1092,144 +970,115 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ ); } else { let dir_handle = dir_handle.unwrap(); - stmt = dc_sqlite3_prepare( - context, - &sql, - b"INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);\x00" - as *const u8 as - *const libc::c_char - ); - let mut processed_files_cnt = 0; - for entry in dir_handle { - if entry.is_err() { - current_block = 2631791190359682872; - break; - } - let entry = entry.unwrap(); - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - delete_dest_file = 1; - current_block = 11487273724841241105; - break; - } else { - processed_files_cnt += 1; - let mut permille = - processed_files_cnt * 1000 / total_files_cnt; - if permille < 10 { - permille = 10; - } - if permille > 990 { - permille = 990; - } - context.call_cb( - Event::IMEX_PROGRESS, - permille as uintptr_t, - 0i32 as uintptr_t, - ); - - let name_f = entry.file_name(); - let name = name_f.to_string_lossy(); - if name.starts_with("delt-chat") && name.ends_with(".bak") { - continue; - } else { - info!(context, 0, "EXPORTing filename={}", name); - free(curr_pathNfilename as *mut libc::c_void); - let name_c = to_cstring(name); - curr_pathNfilename = dc_mprintf( - b"%s/%s\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - name_c.as_ptr(), - ); - free(buf); - if 0 == dc_read_file( - context, - curr_pathNfilename, - &mut buf, - &mut buf_bytes, - ) || buf.is_null() - || buf_bytes <= 0 - { - continue; + sql.prepare( + "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);", + move |mut stmt| { + let mut processed_files_cnt = 0; + for entry in dir_handle { + if entry.is_err() { + current_block = 2631791190359682872; + break; } - sqlite3_bind_text( - stmt, - 1i32, - name_c.as_ptr(), - -1i32, - None, - ); - sqlite3_bind_blob( - stmt, - 2i32, - buf, - buf_bytes as libc::c_int, - None, - ); - if sqlite3_step(stmt) != 101i32 { - dc_log_error( - context, - 0i32, - b"Disk full? Cannot add file \"%s\" to backup.\x00" - as *const u8 - as *const libc::c_char, - curr_pathNfilename, - ); - /* this is not recoverable! writing to the sqlite database should work! */ + let entry = entry.unwrap(); + if context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { + delete_dest_file = 1; current_block = 11487273724841241105; break; } else { - sqlite3_reset(stmt); + processed_files_cnt += 1; + let mut permille = + processed_files_cnt * 1000 / total_files_cnt; + if permille < 10 { + permille = 10; + } + if permille > 990 { + permille = 990; + } + context.call_cb( + Event::IMEX_PROGRESS, + permille as uintptr_t, + 0 as uintptr_t, + ); + + let name_f = entry.file_name(); + let name = name_f.to_string_lossy(); + if name.starts_with("delta-chat") && name.ends_with(".bak") + { + continue; + } else { + info!(context, 0, "EXPORTing filename={}", name); + let curr_pathNfilename = format!( + "{}/{}", + as_str(context.get_blobdir()), + name + ); + + if let Some(buf) = + dc_read_file_safe(context, &curr_pathNfilename) + { + if buf.is_empty() { + continue; + } + if stmt.execute(params![name, buf]).is_err() { + error!( + context, + 0, + "Disk full? Cannot add file \"{}\" to backup.", + &curr_pathNfilename, + ); + /* this is not recoverable! writing to the sqlite database should work! */ + current_block = 11487273724841241105; + break; + } + } else { + continue; + } + } } } + Ok(()) } - } + ).unwrap(); } } else { - info!(context, 0, "Backup: No files to copy."); + info!(context, 0, "Backup: No files to copy.",); current_block = 2631791190359682872; } match current_block { 11487273724841241105 => {} _ => { - dc_sqlite3_set_config_int( - context, - &sql, - b"backup_time\x00" as *const u8 as *const libc::c_char, - now as int32_t, - ); - context.call_cb( - Event::IMEX_FILE_WRITTEN, - dest_pathNfilename as uintptr_t, - 0i32 as uintptr_t, - ); - success = 1i32 + if sql + .set_config_int(context, "backup_time", now as i32) + .is_ok() + { + context.call_cb( + Event::IMEX_FILE_WRITTEN, + dest_pathNfilename as uintptr_t, + 0, + ); + success = 1; + } } } } } } } - dest_sql = Some(sql); } - if 0 != closed { + if closed { context.sql.open(&context, as_path(context.get_dbfile()), 0); } - sqlite3_finalize(stmt); - if let Some(sql) = dest_sql.take() { - sql.close(&context); - } if 0 != delete_dest_file { dc_delete_file(context, dest_pathNfilename); } free(dest_pathNfilename as *mut libc::c_void); - free(curr_pathNfilename as *mut libc::c_void); - free(buf); success } @@ -1244,12 +1093,12 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> Maybe we should make the "default" key handlong also a little bit smarter (currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */ - let mut imported_cnt: libc::c_int = 0i32; + let mut imported_cnt: libc::c_int = 0; let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char; let mut set_default: libc::c_int; let mut buf: *mut libc::c_char = 0 as *mut libc::c_char; - let mut buf_bytes: size_t = 0i32 as size_t; + let mut buf_bytes: size_t = 0 as size_t; // a pointer inside buf, MUST NOT be free()'d let mut private_key: *const libc::c_char; let mut buf2: *mut libc::c_char = 0 as *mut libc::c_char; @@ -1259,11 +1108,11 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let dir = std::path::Path::new(as_str(dir_name)); let dir_handle = std::fs::read_dir(dir); if dir_handle.is_err() { - dc_log_error( + error!( context, - 0i32, - b"Import: Cannot open directory \"%s\".\x00" as *const u8 as *const libc::c_char, - dir_name, + 0, + "Import: Cannot open directory \"{}\".", + as_str(dir_name), ); } else { let dir_handle = dir_handle.unwrap(); @@ -1277,7 +1126,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let name_c = to_cstring(name_f.to_string_lossy()); suffix = dc_get_filesuffix_lc(name_c.as_ptr()); if suffix.is_null() - || strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0i32 + || strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0 { continue; } @@ -1287,12 +1136,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> dir_name, name_c.as_ptr(), ); - dc_log_info( - context, - 0i32, - b"Checking: %s\x00" as *const u8 as *const libc::c_char, - path_plus_name, - ); + info!(context, 0, "Checking: {}", as_str(path_plus_name)); free(buf as *mut libc::c_void); buf = 0 as *mut libc::c_char; if 0 == dc_read_file( @@ -1316,7 +1160,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> ) && strcmp( buf2_headerline, b"-----BEGIN PGP PUBLIC KEY BLOCK-----\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { private_key = strstr( buf, @@ -1327,19 +1171,18 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> continue; } } - set_default = 1i32; + set_default = 1; if !strstr( name_c.as_ptr(), b"legacy\x00" as *const u8 as *const libc::c_char, ) .is_null() { - dc_log_info( + info!( context, - 0i32, - b"Treating \"%s\" as a legacy private key.\x00" as *const u8 - as *const libc::c_char, - path_plus_name, + 0, + "Treating \"{}\" as a legacy private key.", + as_str(path_plus_name), ); set_default = 0i32 } @@ -1349,11 +1192,11 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> imported_cnt += 1 } if imported_cnt == 0i32 { - dc_log_error( + error!( context, - 0i32, - b"No private keys found in \"%s\".\x00" as *const u8 as *const libc::c_char, - dir_name, + 0, + "No private keys found in \"{}\".", + as_str(dir_name), ); } } @@ -1369,45 +1212,52 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> // TODO should return bool /rtn unsafe fn export_self_keys(context: &Context, dir: *const libc::c_char) -> libc::c_int { - let mut success: libc::c_int = 0i32; - let mut export_errors: libc::c_int = 0i32; - let mut id: libc::c_int; - let mut is_default: libc::c_int; - let stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id, public_key, private_key, is_default FROM keypairs;\x00" as *const u8 - as *const libc::c_char, - ); - if !stmt.is_null() { - while sqlite3_step(stmt) == 100i32 { - id = sqlite3_column_int(stmt, 0i32); - let public_key = Key::from_stmt(stmt, 1, KeyType::Public); - let private_key = Key::from_stmt(stmt, 2, KeyType::Private); + let mut export_errors = 0; - is_default = sqlite3_column_int(stmt, 3i32); - if let Some(key) = public_key { - if 0 == export_key_to_asc_file(context, dir, id, &key, is_default) { - export_errors += 1 + context + .sql + .query_map( + "SELECT id, public_key, private_key, is_default FROM keypairs;", + params![], + |row| { + let id = row.get(0)?; + let public_key_blob: Vec = row.get(1)?; + let public_key = Key::from_slice(&public_key_blob, KeyType::Public); + let private_key_blob: Vec = row.get(2)?; + let private_key = Key::from_slice(&private_key_blob, KeyType::Private); + let is_default = row.get(3)?; + + Ok((id, public_key, private_key, is_default)) + }, + |keys| { + for key_pair in keys { + let (id, public_key, private_key, is_default) = key_pair?; + if let Some(key) = public_key { + if 0 == export_key_to_asc_file(context, dir, id, &key, is_default) { + export_errors += 1; + } + } else { + export_errors += 1; + } + if let Some(key) = private_key { + if 0 == export_key_to_asc_file(context, dir, id, &key, is_default) { + export_errors += 1; + } + } else { + export_errors += 1; + } } - } else { - export_errors += 1; - } - if let Some(key) = private_key { - if 0 == export_key_to_asc_file(context, dir, id, &key, is_default) { - export_errors += 1 - } - } else { - export_errors += 1; - } - } - if export_errors == 0i32 { - success = 1i32 - } + + Ok(()) + }, + ) + .unwrap(); + + if export_errors == 0 { + 1 + } else { + 0 } - sqlite3_finalize(stmt); - - success } /******************************************************************************* @@ -1445,20 +1295,10 @@ unsafe fn export_key_to_asc_file( id, ) } - dc_log_info( - context, - 0i32, - b"Exporting key %s\x00" as *const u8 as *const libc::c_char, - file_name, - ); + info!(context, 0, "Exporting key {}", as_str(file_name),); dc_delete_file(context, file_name); if !key.write_asc_to_file(file_name, context) { - dc_log_error( - context, - 0i32, - b"Cannot write key to %s\x00" as *const u8 as *const libc::c_char, - file_name, - ); + error!(context, 0, "Cannot write key to {}", as_str(file_name),); } else { context.call_cb( Event::IMEX_FILE_WRITTEN, diff --git a/src/dc_job.rs b/src/dc_job.rs index b665b1acd..b65d31595 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -12,15 +12,14 @@ use crate::dc_configure::*; use crate::dc_imex::*; use crate::dc_jobthread::*; use crate::dc_location::*; -use crate::dc_log::*; use crate::dc_loginparam::*; use crate::dc_mimefactory::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_sqlite3::*; use crate::dc_tools::*; use crate::imap::*; use crate::keyhistory::*; +use crate::sql; use crate::types::*; use crate::x::*; @@ -49,132 +48,113 @@ pub struct dc_job_t { } pub unsafe fn dc_perform_imap_jobs(context: &Context) { - dc_log_info( - context, - 0i32, - b"INBOX-jobs started...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "INBOX-jobs started...",); let probe_imap_network = *context.probe_imap_network.clone().read().unwrap(); *context.probe_imap_network.write().unwrap() = 0; *context.perform_inbox_jobs_needed.write().unwrap() = 0; dc_job_perform(context, 100, probe_imap_network); - dc_log_info( - context, - 0i32, - b"INBOX-jobs ended.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "INBOX-jobs ended.",); } -unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) { - let mut select_stmt: *mut sqlite3_stmt; - let mut job = dc_job_t { - job_id: 0, - action: 0, - foreign_id: 0, - desired_timestamp: 0, - added_timestamp: 0, - tries: 0, - param: 0 as *mut dc_param_t, - try_again: 0, - pending_error: 0 as *mut libc::c_char, - }; - job.param = dc_param_new(); - if probe_network == 0i32 { - select_stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries FROM jobs WHERE thread=? AND desired_timestamp<=? ORDER BY action DESC, added_timestamp;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int64(select_stmt, 1i32, thread as sqlite3_int64); - sqlite3_bind_int64(select_stmt, 2i32, time() as sqlite3_int64); +unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) { + let query = if probe_network == 0 { + // processing for first-try and after backoff-timeouts: + // process jobs in the order they were added. + "SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries \ + FROM jobs WHERE thread=? AND desired_timestamp<=? ORDER BY action DESC, added_timestamp;" } else { - select_stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries FROM jobs WHERE thread=? AND tries>0 ORDER BY desired_timestamp, action DESC;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int64(select_stmt, 1i32, thread as sqlite3_int64); - } - while sqlite3_step(select_stmt) == 100i32 { - job.job_id = sqlite3_column_int(select_stmt, 0i32) as uint32_t; - job.action = sqlite3_column_int(select_stmt, 1i32); - job.foreign_id = sqlite3_column_int(select_stmt, 2i32) as uint32_t; - dc_param_set_packed( - job.param, - sqlite3_column_text(select_stmt, 3i32) as *mut libc::c_char, - ); - job.added_timestamp = sqlite3_column_int64(select_stmt, 4i32) as i64; - job.desired_timestamp = sqlite3_column_int64(select_stmt, 5i32) as i64; - job.tries = sqlite3_column_int(select_stmt, 6i32); - dc_log_info( - context, - 0i32, - b"%s-job #%i, action %i started...\x00" as *const u8 as *const libc::c_char, - if thread == 100i32 { - b"INBOX\x00" as *const u8 as *const libc::c_char - } else { - b"SMTP\x00" as *const u8 as *const libc::c_char + // processing after call to dc_maybe_network(): + // process _all_ pending jobs that failed before + // in the order of their backoff-times. + "SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries \ + FROM jobs WHERE thread=? AND tries>0 ORDER BY desired_timestamp, action DESC;" + }; + + let params_no_probe = params![thread as i64, time()]; + let params_probe = params![thread as i64]; + let params: &[&dyn rusqlite::ToSql] = if probe_network == 0 { + params_no_probe + } else { + params_probe + }; + + let jobs: Vec = context + .sql + .query_map( + query, + params, + |row| { + let job = dc_job_t { + job_id: row.get(0)?, + action: row.get(1)?, + foreign_id: row.get(2)?, + desired_timestamp: row.get(5)?, + added_timestamp: row.get(4)?, + tries: row.get(6)?, + param: dc_param_new(), + try_again: 0, + pending_error: 0 as *mut libc::c_char, + }; + + let packed: String = row.get(3)?; + dc_param_set_packed(job.param, to_cstring(packed).as_ptr()); + Ok(job) }, - job.job_id as libc::c_int, - job.action as libc::c_int, + |jobs| { + jobs.collect::, _>>() + .map_err(Into::into) + }, + ) + .unwrap_or_default(); + + for mut job in jobs { + info!( + context, + 0, + "{}-job #{}, action {} started...", + if thread == 100 { "INBOX" } else { "SMTP" }, + job.job_id, + job.action, ); - if 900i32 == job.action || 910i32 == job.action { + + // some configuration jobs are "exclusive": + // - they are always executed in the imap-thread and the smtp-thread is suspended during execution + // - they may change the database handle change the database handle; we do not keep old pointers therefore + // - they can be re-executed one time AT_ONCE, but they are not save in the database for later execution + if 900 == job.action || 910 == job.action { dc_job_kill_action(context, job.action); - sqlite3_finalize(select_stmt); - select_stmt = 0 as *mut sqlite3_stmt; dc_jobthread_suspend(context, &context.sentbox_thread.clone().read().unwrap(), 1); dc_jobthread_suspend(context, &context.mvbox_thread.clone().read().unwrap(), 1); - dc_suspend_smtp_thread(context, 1i32); + dc_suspend_smtp_thread(context, 1); } - let mut tries: libc::c_int = 0i32; - while tries <= 1i32 { - job.try_again = 0i32; + + let mut tries = 0; + while tries <= 1 { + // this can be modified by a job using dc_job_try_again_later() + job.try_again = 0; + match job.action { - 5901 => { - dc_job_do_DC_JOB_SEND(context, &mut job); - } - 110 => { - dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context, &mut job); - } - 130 => { - dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context, &mut job); - } - 120 => { - dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context, &mut job); - } - 200 => { - dc_job_do_DC_JOB_MOVE_MSG(context, &mut job); - } - 5011 => { - dc_job_do_DC_JOB_SEND(context, &mut job); - } - 900 => { - dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &mut job); - } - 910 => { - dc_job_do_DC_JOB_IMEX_IMAP(context, &mut job); - } - 5005 => { - dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &mut job); - } - 5007 => { - dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job); - } - 105 => { - dc_housekeeping(context); - } + 5901 => dc_job_do_DC_JOB_SEND(context, &mut job), + 110 => dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context, &mut job), + 130 => dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context, &mut job), + 120 => dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context, &mut job), + 200 => dc_job_do_DC_JOB_MOVE_MSG(context, &mut job), + 5011 => dc_job_do_DC_JOB_SEND(context, &mut job), + 900 => dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &mut job), + 910 => dc_job_do_DC_JOB_IMEX_IMAP(context, &mut job), + 5005 => dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &mut job), + 5007 => dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job), + 105 => sql::housekeeping(context), _ => {} } - if job.try_again != -1i32 { + if job.try_again != -1 { break; } tries += 1 } - if 900i32 == job.action || 910i32 == job.action { + if 900 == job.action || 910 == job.action { dc_jobthread_suspend( context, &mut context.sentbox_thread.clone().read().unwrap(), @@ -185,57 +165,50 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: &mut context.mvbox_thread.clone().read().unwrap(), 0, ); - dc_suspend_smtp_thread(context, 0i32); + dc_suspend_smtp_thread(context, 0); break; - } else if job.try_again == 2i32 { - dc_log_info( + } else if job.try_again == 2 { + // just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready + info!( context, - 0i32, - b"%s-job #%i not yet ready and will be delayed.\x00" as *const u8 - as *const libc::c_char, - if thread == 100i32 { - b"INBOX\x00" as *const u8 as *const libc::c_char - } else { - b"SMTP\x00" as *const u8 as *const libc::c_char - }, - job.job_id as libc::c_int, + 0, + "{}-job #{} not yet ready and will be delayed.", + if thread == 100 { "INBOX" } else { "SMTP" }, + job.job_id ); - } else if job.try_again == -1i32 || job.try_again == 3i32 { - let tries_0: libc::c_int = job.tries + 1i32; - if tries_0 < 17i32 { - job.tries = tries_0; - let time_offset = get_backoff_time_offset(tries_0); + } else if job.try_again == -1 || job.try_again == 3 { + let tries = job.tries + 1; + if tries < 17 { + job.tries = tries; + let time_offset = get_backoff_time_offset(tries); job.desired_timestamp = job.added_timestamp + time_offset; dc_job_update(context, &mut job); - dc_log_info(context, 0i32, - b"%s-job #%i not succeeded on try #%i, retry in ADD_TIME+%i (in %i seconds).\x00" - as *const u8 as *const libc::c_char, - if thread == 100i32 { - b"INBOX\x00" as *const u8 as - *const libc::c_char - } else { - b"SMTP\x00" as *const u8 as - *const libc::c_char - }, job.job_id as libc::c_int, tries_0, - time_offset, - job.added_timestamp + time_offset - - time()); - if thread == 5000i32 && tries_0 < 17i32 - 1i32 { + info!( + context, + 0, + "{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).", + if thread == 100 { "INBOX" } else { "SMTP" }, + job.job_id as libc::c_int, + tries, + time_offset, + job.added_timestamp + time_offset - time() + ); + if thread == 5000 && tries < 17 - 1 { context .smtp_state .clone() .0 .lock() .unwrap() - .perform_jobs_needed = 2i32; + .perform_jobs_needed = 2; } } else { - if job.action == 5901i32 { + if job.action == 5901 { dc_set_msg_failed(context, job.foreign_id, job.pending_error); } dc_job_delete(context, &mut job); } - if !(0 != probe_network) { + if 0 == probe_network { continue; } // on dc_maybe_network() we stop trying here; @@ -246,21 +219,16 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: } else { dc_job_delete(context, &mut job); } + dc_param_unref(job.param); + free(job.pending_error as *mut libc::c_void); } - dc_param_unref(job.param); - free(job.pending_error as *mut libc::c_void); - sqlite3_finalize(select_stmt); } -unsafe fn dc_job_delete(context: &Context, job: &dc_job_t) { - let delete_stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"DELETE FROM jobs WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(delete_stmt, 1i32, job.job_id as libc::c_int); - sqlite3_step(delete_stmt); - sqlite3_finalize(delete_stmt); +fn dc_job_delete(context: &Context, job: &dc_job_t) -> bool { + context + .sql + .execute("DELETE FROM jobs WHERE id=?;", params![job.job_id as i32]) + .is_ok() } /* ****************************************************************************** @@ -279,20 +247,21 @@ unsafe fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 { seconds as i64 } -unsafe fn dc_job_update(context: &Context, job: &dc_job_t) { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( +fn dc_job_update(context: &Context, job: &dc_job_t) -> bool { + sql::execute( context, &context.sql, - b"UPDATE jobs SET desired_timestamp=?, tries=?, param=? WHERE id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int64(stmt, 1i32, job.desired_timestamp as sqlite3_int64); - sqlite3_bind_int64(stmt, 2i32, job.tries as sqlite3_int64); - sqlite3_bind_text(stmt, 3i32, (*job.param).packed, -1i32, None); - sqlite3_bind_int(stmt, 4i32, job.job_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE jobs SET desired_timestamp=?, tries=?, param=? WHERE id=?;", + params![ + job.desired_timestamp, + job.tries as i64, + as_str(unsafe { (*job.param).packed }), + job.job_id as i32, + ], + ) + .is_ok() } + unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: libc::c_int) { context.smtp_state.0.lock().unwrap().suspended = suspend; if 0 != suspend { @@ -310,18 +279,12 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { let mut buf: *mut libc::c_void = 0 as *mut libc::c_void; let mut buf_bytes: size_t = 0i32 as size_t; let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + /* connect to SMTP server, if not yet done */ if !context.smtp.lock().unwrap().is_connected() { - let loginparam: *mut dc_loginparam_t = dc_loginparam_new(); - dc_loginparam_read( - context, - loginparam, - &context.sql, - b"configured_\x00" as *const u8 as *const libc::c_char, - ); - let connected = context.smtp.lock().unwrap().connect(context, loginparam); - dc_loginparam_unref(loginparam); + let loginparam = dc_loginparam_read(context, &context.sql, "configured_"); + let connected = context.smtp.lock().unwrap().connect(context, &loginparam); + if 0 == connected { dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char); current_block = 14216916617354591294; @@ -335,21 +298,11 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { 13109137661213826276 => { filename = dc_param_get(job.param, 'f' as i32, 0 as *const libc::c_char); if filename.is_null() { - dc_log_warning( - context, - 0i32, - b"Missing file name for job %d\x00" as *const u8 as *const libc::c_char, - job.job_id, - ); + warn!(context, 0, "Missing file name for job {}", job.job_id,); } else if !(0 == dc_read_file(context, filename, &mut buf, &mut buf_bytes)) { recipients = dc_param_get(job.param, 'R' as i32, 0 as *const libc::c_char); if recipients.is_null() { - dc_log_warning( - context, - 0i32, - b"Missing recipients for job %d\x00" as *const u8 as *const libc::c_char, - job.job_id, - ); + warn!(context, 0, "Missing recipients for job {}", job.job_id,); } else { let recipients_list = std::ffi::CStr::from_ptr(recipients) .to_str() @@ -368,11 +321,10 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { before the generated mime was sent out */ if 0 != job.foreign_id { if 0 == dc_msg_exists(context, job.foreign_id) { - dc_log_warning( + warn!( context, - 0i32, - b"Message %i for job %i does not exist\x00" as *const u8 - as *const libc::c_char, + 0, + "Message {} for job {} does not exist", job.foreign_id, job.job_id, ); @@ -404,18 +356,15 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { dc_delete_file(context, filename); if 0 != job.foreign_id { dc_update_msg_state(context, job.foreign_id, 26i32); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT chat_id FROM msgs WHERE id=?\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, job.foreign_id as libc::c_int); - let chat_id: libc::c_int = if sqlite3_step(stmt) == 100i32 { - sqlite3_column_int(stmt, 0i32) - } else { - 0i32 - }; + let chat_id: i32 = context + .sql + .query_row_col( + context, + "SELECT chat_id FROM msgs WHERE id=?", + params![job.foreign_id as i32], + 0, + ) + .unwrap_or_default(); context.call_cb( Event::MSG_DELIVERED, chat_id as uintptr_t, @@ -430,11 +379,11 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { } _ => {} } - sqlite3_finalize(stmt); free(recipients as *mut libc::c_void); free(buf); free(filename as *mut libc::c_void); } + // this value does not increase the number of tries pub unsafe fn dc_job_try_again_later( job: &mut dc_job_t, @@ -448,8 +397,7 @@ pub unsafe fn dc_job_try_again_later( unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) { let mut current_block: u64; - let msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let mut dest_folder: *mut libc::c_char = 0 as *mut libc::c_char; + let msg = dc_msg_new_untyped(context); let mut dest_uid: uint32_t = 0i32 as uint32_t; let inbox = context.inbox.read().unwrap(); @@ -468,30 +416,24 @@ unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) { match current_block { 2473556513754201174 => { if dc_msg_load_from_db(msg, context, job.foreign_id) { - if dc_sqlite3_get_config_int( - context, - &context.sql, - b"folders_configured\x00" as *const u8 as *const libc::c_char, - 0i32, - ) < 3i32 + if context + .sql + .get_config_int(context, "folders_configured") + .unwrap_or_default() + < 3 { inbox.configure_folders(context, 0x1i32); } - dest_folder = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); + let dest_folder = context.sql.get_config(context, "configured_mvbox_folder"); - if !dest_folder.is_null() { + if let Some(dest_folder) = dest_folder { let server_folder = as_str((*msg).server_folder); match inbox.mv( context, server_folder, (*msg).server_uid, - as_str(dest_folder), + &dest_folder, &mut dest_uid, ) as libc::c_uint { @@ -502,7 +444,7 @@ unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) { dc_update_server_uid( context, (*msg).rfc724_mid, - dest_folder, + &dest_folder, dest_uid, ); } @@ -518,7 +460,7 @@ unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) { dc_update_server_uid( context, (*msg).rfc724_mid, - dest_folder, + &dest_folder, dest_uid, ); } @@ -534,18 +476,17 @@ unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) { } _ => {} } - free(dest_folder as *mut libc::c_void); + dc_msg_unref(msg); } + /* ****************************************************************************** * IMAP-jobs ******************************************************************************/ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int { - let ret_connected: libc::c_int; - - ret_connected = unsafe { dc_connect_to_configured_imap(context, inbox) }; - if !(0 == ret_connected) { - inbox.set_watch_folder(b"INBOX\x00" as *const u8 as *const libc::c_char); + let ret_connected = dc_connect_to_configured_imap(context, inbox); + if 0 != ret_connected { + inbox.set_watch_folder("INBOX".into()); } ret_connected } @@ -554,7 +495,6 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_ let current_block: u64; let folder: *mut libc::c_char = dc_param_get(job.param, 'Z' as i32, 0 as *const libc::c_char); let uid: uint32_t = dc_param_get_int(job.param, 'z' as i32, 0i32) as uint32_t; - let mut dest_folder: *mut libc::c_char = 0 as *mut libc::c_char; let mut dest_uid: uint32_t = 0i32 as uint32_t; let inbox = context.inbox.read().unwrap(); @@ -576,23 +516,16 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_ dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char); } if 0 != dc_param_get_int(job.param, 'M' as i32, 0i32) { - if dc_sqlite3_get_config_int( - context, - &context.sql, - b"folders_configured\x00" as *const u8 as *const libc::c_char, - 0i32, - ) < 3i32 + if context + .sql + .get_config_int(context, "folders_configured") + .unwrap_or_default() + < 3 { inbox.configure_folders(context, 0x1i32); } - dest_folder = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if !dest_folder.is_null() { - let dest_folder = as_str(dest_folder); + let dest_folder = context.sql.get_config(context, "configured_mvbox_folder"); + if let Some(dest_folder) = dest_folder { if 1 == inbox.mv(context, folder, uid, dest_folder, &mut dest_uid) as libc::c_uint { @@ -604,8 +537,8 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_ _ => {} } free(folder as *mut libc::c_void); - free(dest_folder as *mut libc::c_void); } + unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_job_t) { let mut current_block: u64; let msg: *mut dc_msg_t = dc_msg_new_untyped(context); @@ -636,12 +569,10 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_ } _ => { if 0 != dc_param_get_int((*msg).param, 'r' as i32, 0i32) - && 0 != dc_sqlite3_get_config_int( - context, - &context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ) + && 0 != context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1) { let folder = CStr::from_ptr((*msg).server_folder).to_str().unwrap(); @@ -692,12 +623,10 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_ } _ => { if 0 != dc_param_get_int((*msg).param, 'r' as i32, 0i32) - && 0 != dc_sqlite3_get_config_int( - context, - &context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ) + && 0 != context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1) { let folder = CStr::from_ptr((*msg).server_folder).to_str().unwrap(); @@ -805,12 +734,11 @@ unsafe fn dc_add_smtp_job( (*mimefactory).rfc724_mid, ); if pathNfilename.is_null() { - dc_log_error( + error!( context, - 0i32, - b"Could not find free file name for message with ID <%s>.\x00" as *const u8 - as *const libc::c_char, - (*mimefactory).rfc724_mid, + 0, + "Could not find free file name for message with ID <{}>.", + to_string((*mimefactory).rfc724_mid), ); } else if 0 == dc_write_file( @@ -820,12 +748,12 @@ unsafe fn dc_add_smtp_job( (*(*mimefactory).out).len, ) { - dc_log_error( + error!( context, - 0i32, - b"Could not write message <%s> to \"%s\".\x00" as *const u8 as *const libc::c_char, - (*mimefactory).rfc724_mid, - pathNfilename, + 0, + "Could not write message <{}> to \"{}\".", + to_string((*mimefactory).rfc724_mid), + as_str(pathNfilename), ); } else { recipients = dc_str_from_clist( @@ -862,55 +790,41 @@ pub unsafe fn dc_job_add( delay_seconds: libc::c_int, ) { let timestamp = time(); - let stmt: *mut sqlite3_stmt; - let thread: libc::c_int; - if action >= 100i32 && action < 100i32 + 1000i32 { - thread = 100i32 - } else if action >= 5000i32 && action < 5000i32 + 1000i32 { - thread = 5000i32 + let thread = if action >= 100 && action < 100 + 1000 { + 100 + } else if action >= 5000 && action < 5000 + 1000 { + 5000 } else { return; - } - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO jobs (added_timestamp, thread, action, foreign_id, param, desired_timestamp) VALUES (?,?,?,?,?,?);\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int64(stmt, 1i32, timestamp as sqlite3_int64); - sqlite3_bind_int(stmt, 2i32, thread); - sqlite3_bind_int(stmt, 3i32, action); - sqlite3_bind_int(stmt, 4i32, foreign_id); - sqlite3_bind_text( - stmt, - 5i32, - if !param.is_null() { - param - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, + }; + + sql::execute( + context, + &context.sql, + "INSERT INTO jobs (added_timestamp, thread, action, foreign_id, param, desired_timestamp) VALUES (?,?,?,?,?,?);", + params![ + timestamp, + thread, + action, + foreign_id, + if !param.is_null() { + as_str(param) + } else { + "" + }, + (timestamp + delay_seconds as i64) + ] ); - sqlite3_bind_int64( - stmt, - 6i32, - (timestamp + delay_seconds as i64) as sqlite3_int64, - ); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - if thread == 100i32 { + + if thread == 100 { dc_interrupt_imap_idle(context); } else { dc_interrupt_smtp_idle(context); - }; + } } + pub unsafe fn dc_interrupt_smtp_idle(context: &Context) { - dc_log_info( - context, - 0i32, - b"Interrupting SMTP-idle...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Interrupting SMTP-idle...",); let &(ref lock, ref cvar) = &*context.smtp_state.clone(); let mut state = lock.lock().unwrap(); @@ -921,11 +835,7 @@ pub unsafe fn dc_interrupt_smtp_idle(context: &Context) { } pub unsafe fn dc_interrupt_imap_idle(context: &Context) { - dc_log_info( - context, - 0i32, - b"Interrupting IMAP-IDLE...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Interrupting IMAP-IDLE...",); *context.perform_inbox_jobs_needed.write().unwrap() = 1; context.inbox.read().unwrap().interrupt_idle(); @@ -943,11 +853,9 @@ unsafe fn dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context: &Context, job: &mut dc_jo { /* eg. device messages have no Message-ID */ if dc_rfc724_mid_cnt(context, (*msg).rfc724_mid) != 1i32 { - dc_log_info( + info!( context, - 0i32, - b"The message is deleted from the server when all parts are deleted.\x00" - as *const u8 as *const libc::c_char, + 0, "The message is deleted from the server when all parts are deleted.", ); delete_from_server = 0i32 } @@ -991,57 +899,43 @@ unsafe fn dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context: &Context, job: &mut dc_jo } /* delete all pending jobs with the given action */ -pub unsafe fn dc_job_kill_action(context: &Context, action: libc::c_int) { - let stmt = dc_sqlite3_prepare( +pub fn dc_job_kill_action(context: &Context, action: libc::c_int) -> bool { + sql::execute( context, &context.sql, - b"DELETE FROM jobs WHERE action=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, action); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "DELETE FROM jobs WHERE action=?;", + params![action], + ) + .is_ok() } pub unsafe fn dc_perform_imap_fetch(context: &Context) { let inbox = context.inbox.read().unwrap(); + let start = clock(); - let start: libc::clock_t = clock(); if 0 == connect_to_inbox(context, &inbox) { return; } - if dc_sqlite3_get_config_int( - context, - &context.sql, - b"inbox_watch\x00" as *const u8 as *const libc::c_char, - 1i32, - ) == 0i32 + if context + .sql + .get_config_int(context, "inbox_watch") + .unwrap_or_else(|| 1) + == 0 { - dc_log_info( - context, - 0i32, - b"INBOX-watch disabled.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "INBOX-watch disabled.",); return; } - dc_log_info( - context, - 0i32, - b"INBOX-fetch started...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "INBOX-fetch started...",); inbox.fetch(context); if inbox.should_reconnect() { - dc_log_info( - context, - 0i32, - b"INBOX-fetch aborted, starting over...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "INBOX-fetch aborted, starting over...",); inbox.fetch(context); } - dc_log_info( + info!( context, - 0i32, - b"INBOX-fetch done in %.0f ms.\x00" as *const u8 as *const libc::c_char, - clock().wrapping_sub(start) as libc::c_double * 1000.0f64 / 1000000i32 as libc::c_double, + 0, + "INBOX-fetch done in {:.4} ms.", + clock().wrapping_sub(start) as libc::c_double * 1000.0f64 / 1000000 as libc::c_double, ); } @@ -1063,12 +957,10 @@ pub fn dc_perform_imap_idle(context: &Context) { } pub unsafe fn dc_perform_mvbox_fetch(context: &Context) { - let use_network: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_watch\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + let use_network = context + .sql + .get_config_int(context, "mvbox_watch") + .unwrap_or_else(|| 1); dc_jobthread_fetch( context, &mut context.mvbox_thread.clone().write().unwrap(), @@ -1077,12 +969,11 @@ pub unsafe fn dc_perform_mvbox_fetch(context: &Context) { } pub unsafe fn dc_perform_mvbox_idle(context: &Context) { - let use_network: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_watch\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + let use_network = context + .sql + .get_config_int(context, "mvbox_watch") + .unwrap_or_else(|| 1); + dc_jobthread_idle( context, &context.mvbox_thread.clone().read().unwrap(), @@ -1095,12 +986,10 @@ pub unsafe fn dc_interrupt_mvbox_idle(context: &Context) { } pub unsafe fn dc_perform_sentbox_fetch(context: &Context) { - let use_network: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + let use_network = context + .sql + .get_config_int(context, "sentbox_watch") + .unwrap_or_else(|| 1); dc_jobthread_fetch( context, &mut context.sentbox_thread.clone().write().unwrap(), @@ -1109,12 +998,10 @@ pub unsafe fn dc_perform_sentbox_fetch(context: &Context) { } pub unsafe fn dc_perform_sentbox_idle(context: &Context) { - let use_network: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"sentbox_watch\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + let use_network = context + .sql + .get_config_int(context, "sentbox_watch") + .unwrap_or_else(|| 1); dc_jobthread_idle( context, &context.sentbox_thread.clone().read().unwrap(), @@ -1136,28 +1023,16 @@ pub unsafe fn dc_perform_smtp_jobs(context: &Context) { state.perform_jobs_needed = 0; if 0 != state.suspended { - dc_log_info( - context, - 0i32, - b"SMTP-jobs suspended.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "SMTP-jobs suspended.",); return; } state.doing_jobs = 1; probe_smtp_network }; - dc_log_info( - context, - 0, - b"SMTP-jobs started...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "SMTP-jobs started...",); dc_job_perform(context, 5000, probe_smtp_network); - dc_log_info( - context, - 0i32, - b"SMTP-jobs ended.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "SMTP-jobs ended."); { let &(ref lock, _) = &*context.smtp_state.clone(); @@ -1168,21 +1043,15 @@ pub unsafe fn dc_perform_smtp_jobs(context: &Context) { } pub unsafe fn dc_perform_smtp_idle(context: &Context) { - dc_log_info( - context, - 0i32, - b"SMTP-idle started...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "SMTP-idle started...",); { let &(ref lock, ref cvar) = &*context.smtp_state.clone(); let mut state = lock.lock().unwrap(); if state.perform_jobs_needed == 1 { - dc_log_info( + info!( context, - 0, - b"SMTP-idle will not be started because of waiting jobs.\x00" as *const u8 - as *const libc::c_char, + 0, "SMTP-idle will not be started because of waiting jobs.", ); } else { let dur = get_next_wakeup_time(context, 5000); @@ -1200,37 +1069,30 @@ pub unsafe fn dc_perform_smtp_idle(context: &Context) { } } - dc_log_info( - context, - 0i32, - b"SMTP-idle ended.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "SMTP-idle ended.",); } unsafe fn get_next_wakeup_time(context: &Context, thread: libc::c_int) -> Duration { - let stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT MIN(desired_timestamp) FROM jobs WHERE thread=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1, thread); + let t: i64 = context + .sql + .query_row_col( + context, + "SELECT MIN(desired_timestamp) FROM jobs WHERE thread=?;", + params![thread], + 0, + ) + .unwrap_or_default(); let mut wakeup_time = Duration::new(10 * 60, 0); - - if sqlite3_step(stmt) == 100 { - let t = sqlite3_column_int(stmt, 0) as i64; - let now = time(); - if t > 0 { - if t > now { - wakeup_time = Duration::new((t - now) as u64, 0); - } else { - wakeup_time = Duration::new(0, 0); - } + let now = time(); + if t > 0 { + if t > now { + wakeup_time = Duration::new((t - now) as u64, 0); + } else { + wakeup_time = Duration::new(0, 0); } } - sqlite3_finalize(stmt); wakeup_time } @@ -1249,22 +1111,16 @@ pub unsafe fn dc_maybe_network(context: &Context) { dc_interrupt_sentbox_idle(context); } -pub unsafe fn dc_job_action_exists(context: &Context, action: libc::c_int) -> libc::c_int { - let job_exists: libc::c_int; - let stmt; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM jobs WHERE action=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, action); - job_exists = (sqlite3_step(stmt) == 100i32) as libc::c_int; - sqlite3_finalize(stmt); - return job_exists; +pub fn dc_job_action_exists(context: &Context, action: libc::c_int) -> bool { + context + .sql + .exists("SELECT id FROM jobs WHERE action=?;", params![action]) + .unwrap_or_default() } + /* special case for DC_JOB_SEND_MSG_TO_SMTP */ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int { - let mut success: libc::c_int = 0i32; + let mut success = 0; let mut mimefactory = dc_mimefactory_t { from_addr: 0 as *mut libc::c_char, from_displayname: 0 as *mut libc::c_char, @@ -1290,11 +1146,9 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in dc_mimefactory_init(&mut mimefactory, context); /* load message data */ if 0 == dc_mimefactory_load_msg(&mut mimefactory, msg_id) || mimefactory.from_addr.is_null() { - dc_log_warning( + warn!( context, - 0i32, - b"Cannot load data to send, maybe the message is deleted in between.\x00" as *const u8 - as *const libc::c_char, + 0, "Cannot load data to send, maybe the message is deleted in between.", ); } else { // no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed() @@ -1345,9 +1199,16 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in /* create message */ if 0 == dc_mimefactory_render(&mut mimefactory) { dc_set_msg_failed(context, msg_id, mimefactory.error); - } else if 0 != dc_param_get_int((*mimefactory.msg).param, 'c' as i32, 0i32) + } else if 0 != dc_param_get_int((*mimefactory.msg).param, 'c' as i32, 0) && 0 == mimefactory.out_encrypted { + warn!( + context, + 0, + "e2e encryption unavailable {} - {}", + msg_id, + dc_param_get_int((*mimefactory.msg).param, 'c' as i32, 0), + ); dc_set_msg_failed( context, msg_id, @@ -1396,9 +1257,10 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in 0 as *const libc::c_char, 0 as *const libc::c_char, ); - success = dc_add_smtp_job(context, 5901i32, &mut mimefactory) + success = dc_add_smtp_job(context, 5901i32, &mut mimefactory); } } dc_mimefactory_empty(&mut mimefactory); - return success; + + success } diff --git a/src/dc_jobthread.rs b/src/dc_jobthread.rs index f1779c738..c37394f4b 100644 --- a/src/dc_jobthread.rs +++ b/src/dc_jobthread.rs @@ -2,11 +2,8 @@ use std::sync::{Arc, Condvar, Mutex}; use crate::context::Context; use crate::dc_configure::*; -use crate::dc_sqlite3::*; use crate::imap::Imap; -use crate::types::*; use crate::x::*; -use std::ffi::CString; #[repr(C)] pub struct dc_jobthread_t { @@ -132,40 +129,32 @@ pub unsafe fn dc_jobthread_fetch( ******************************************************************************/ unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int { - let mut ret_connected: libc::c_int; - let mut mvbox_name: *mut libc::c_char = 0 as *mut libc::c_char; - if jobthread.imap.is_connected() { - ret_connected = 1; - } else { - ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap); - if !(0 == ret_connected) { - if dc_sqlite3_get_config_int( - context, - &context.sql, - b"folders_configured\x00" as *const u8 as *const libc::c_char, - 0, - ) < 3 - { - jobthread.imap.configure_folders(context, 0x1); - } - mvbox_name = dc_sqlite3_get_config( - context, - &context.sql, - CString::new(&jobthread.folder_config_name[..]) - .unwrap() - .as_ptr(), - 0 as *const libc::c_char, - ); - if mvbox_name.is_null() { - jobthread.imap.disconnect(context); - ret_connected = 0; - } else { - jobthread.imap.set_watch_folder(mvbox_name); - } + return 1; + } + + let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap); + + if !(0 == ret_connected) { + if context + .sql + .get_config_int(context, "folders_configured") + .unwrap_or_default() + < 3 + { + jobthread.imap.configure_folders(context, 0x1); + } + + if let Some(mvbox_name) = context + .sql + .get_config(context, jobthread.folder_config_name) + { + jobthread.imap.set_watch_folder(mvbox_name); + } else { + jobthread.imap.disconnect(context); + ret_connected = 0; } } - free(mvbox_name as *mut libc::c_void); ret_connected } diff --git a/src/dc_location.rs b/src/dc_location.rs index 6d1082713..e9924ae45 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -3,13 +3,12 @@ use crate::context::*; use crate::dc_array::*; use crate::dc_chat::*; use crate::dc_job::*; -use crate::dc_log::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_saxparser::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; +use crate::sql; use crate::types::*; use crate::x::*; @@ -43,361 +42,295 @@ pub unsafe fn dc_send_locations_to_chat( chat_id: uint32_t, seconds: libc::c_int, ) { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; let now = time(); let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t; let mut stock_str: *mut libc::c_char = 0 as *mut libc::c_char; - let is_sending_locations_before: libc::c_int; + let is_sending_locations_before: bool; if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) { is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id); - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE chats SET locations_send_begin=?, locations_send_until=? WHERE id=?\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int64( - stmt, - 1i32, - (if 0 != seconds { now } else { 0 }) as sqlite3_int64, - ); - sqlite3_bind_int64( - stmt, - 2i32, - (if 0 != seconds { - now + seconds as i64 - } else { - 0 - }) as sqlite3_int64, - ); - sqlite3_bind_int(stmt, 3i32, chat_id as libc::c_int); - sqlite3_step(stmt); - if 0 != seconds && 0 == is_sending_locations_before { - msg = dc_msg_new(context, 10i32); - (*msg).text = dc_stock_system_msg( - context, - 64i32, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0i32 as uint32_t, - ); - dc_param_set_int((*msg).param, 'S' as i32, 8i32); - dc_send_msg(context, chat_id, msg); - } else if 0 == seconds && 0 != is_sending_locations_before { - stock_str = dc_stock_system_msg( - context, - 65i32, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0i32 as uint32_t, - ); - dc_add_device_msg(context, chat_id, stock_str); - } - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); - if 0 != seconds { - schedule_MAYBE_SEND_LOCATIONS(context, 0i32); - dc_job_add( - context, - 5007i32, - chat_id as libc::c_int, - 0 as *const libc::c_char, - seconds + 1i32, + if sql::execute( + context, + &context.sql, + "UPDATE chats \ + SET locations_send_begin=?, \ + locations_send_until=? \ + WHERE id=?", + params![ + if 0 != seconds { now } else { 0 }, + if 0 != seconds { + now + seconds as i64 + } else { + 0 + }, + chat_id as i32, + ], + ) + .is_ok() + { + if 0 != seconds && !is_sending_locations_before { + msg = dc_msg_new(context, 10i32); + (*msg).text = dc_stock_system_msg( + context, + 64, + 0 as *const libc::c_char, + 0 as *const libc::c_char, + 0, + ); + dc_param_set_int((*msg).param, 'S' as i32, 8i32); + dc_send_msg(context, chat_id, msg); + } else if 0 == seconds && is_sending_locations_before { + stock_str = dc_stock_system_msg( + context, + 65i32, + 0 as *const libc::c_char, + 0 as *const libc::c_char, + 0i32 as uint32_t, + ); + dc_add_device_msg(context, chat_id, stock_str); + } + context.call_cb( + Event::CHAT_MODIFIED, + chat_id as uintptr_t, + 0i32 as uintptr_t, ); + if 0 != seconds { + schedule_MAYBE_SEND_LOCATIONS(context, 0i32); + dc_job_add( + context, + 5007i32, + chat_id as libc::c_int, + 0 as *const libc::c_char, + seconds + 1i32, + ); + } } } free(stock_str as *mut libc::c_void); dc_msg_unref(msg); - sqlite3_finalize(stmt); } /******************************************************************************* * job to send locations out to all chats that want them ******************************************************************************/ unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: libc::c_int) { - if 0 != flags & 0x1i32 || 0 == dc_job_action_exists(context, 5005i32) { - dc_job_add(context, 5005i32, 0i32, 0 as *const libc::c_char, 60i32); + if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) { + dc_job_add(context, 5005, 0, 0 as *const libc::c_char, 60); }; } -pub unsafe fn dc_is_sending_locations_to_chat(context: &Context, chat_id: uint32_t) -> libc::c_int { - let mut is_sending_locations: libc::c_int = 0i32; - let stmt: *mut sqlite3_stmt; - - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int( - stmt, - 1i32, - if chat_id == 0i32 as libc::c_uint { - 1i32 - } else { - 0i32 - }, - ); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); - sqlite3_bind_int64(stmt, 3i32, time() as sqlite3_int64); - if !(sqlite3_step(stmt) != 100i32) { - is_sending_locations = 1i32 - } - - sqlite3_finalize(stmt); - - is_sending_locations +pub fn dc_is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool { + context + .sql + .exists( + "SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;", + params![if chat_id == 0 { 1 } else { 0 }, chat_id as i32, time()], + ) + .unwrap_or_default() } -pub unsafe fn dc_set_location( +pub fn dc_set_location( context: &Context, latitude: libc::c_double, longitude: libc::c_double, accuracy: libc::c_double, ) -> libc::c_int { - let mut stmt_chats: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut stmt_insert: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut continue_streaming: libc::c_int = 0i32; - if latitude == 0.0f64 && longitude == 0.0f64 { - continue_streaming = 1i32 - } else { - stmt_chats = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM chats WHERE locations_send_until>?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int64(stmt_chats, 1i32, time() as sqlite3_int64); - while sqlite3_step(stmt_chats) == 100i32 { - let chat_id: uint32_t = sqlite3_column_int(stmt_chats, 0i32) as uint32_t; - stmt_insert = - dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO locations (latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_double(stmt_insert, 1i32, latitude); - sqlite3_bind_double(stmt_insert, 2i32, longitude); - sqlite3_bind_double(stmt_insert, 3i32, accuracy); - sqlite3_bind_int64(stmt_insert, 4i32, time() as sqlite3_int64); - sqlite3_bind_int(stmt_insert, 5i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt_insert, 6i32, 1i32); - sqlite3_step(stmt_insert); - continue_streaming = 1i32 - } - if 0 != continue_streaming { - context.call_cb( - Event::LOCATION_CHANGED, - 1i32 as uintptr_t, - 0i32 as uintptr_t, - ); - schedule_MAYBE_SEND_LOCATIONS(context, 0i32); - } + if latitude == 0.0 && longitude == 0.0 { + return 1; } - sqlite3_finalize(stmt_chats); - sqlite3_finalize(stmt_insert); - continue_streaming + context.sql.query_map( + "SELECT id FROM chats WHERE locations_send_until>?;", + params![time()], |row| row.get::<_, i32>(0), + |chats| { + let mut continue_streaming = false; + + for chat in chats { + let chat_id = chat?; + context.sql.execute( + "INSERT INTO locations \ + (latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);", + params![ + latitude, + longitude, + accuracy, + time(), + chat_id, + 1, + ] + )?; + continue_streaming = true; + } + if continue_streaming { + context.call_cb(Event::LOCATION_CHANGED, 1, 0); + }; + unsafe { schedule_MAYBE_SEND_LOCATIONS(context, 0) }; + Ok(continue_streaming as libc::c_int) + } + ).unwrap_or_default() } -pub unsafe fn dc_get_locations( +pub fn dc_get_locations( context: &Context, chat_id: uint32_t, contact_id: uint32_t, timestamp_from: i64, mut timestamp_to: i64, ) -> *mut dc_array_t { - let ret: *mut dc_array_t = dc_array_new_typed(1i32, 500i32 as size_t); - let stmt: *mut sqlite3_stmt; - if timestamp_to == 0 { timestamp_to = time() + 10; } - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \ - m.id, l.from_id, l.chat_id, m.txt \ - FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \ - AND (? OR l.from_id=?) \ - AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \ - ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int( - stmt, - 1i32, - if chat_id == 0i32 as libc::c_uint { - 1i32 - } else { - 0i32 - }, - ); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); - sqlite3_bind_int( - stmt, - 3i32, - if contact_id == 0i32 as libc::c_uint { - 1i32 - } else { - 0i32 - }, - ); - sqlite3_bind_int(stmt, 4i32, contact_id as libc::c_int); - sqlite3_bind_int(stmt, 5i32, timestamp_from as libc::c_int); - sqlite3_bind_int(stmt, 6i32, timestamp_to as libc::c_int); - while sqlite3_step(stmt) == 100i32 { - let mut loc: *mut _dc_location = - calloc(1, ::std::mem::size_of::<_dc_location>()) as *mut _dc_location; - if loc.is_null() { - break; - } - (*loc).location_id = sqlite3_column_double(stmt, 0i32) as uint32_t; - (*loc).latitude = sqlite3_column_double(stmt, 1i32); - (*loc).longitude = sqlite3_column_double(stmt, 2i32); - (*loc).accuracy = sqlite3_column_double(stmt, 3i32); - (*loc).timestamp = sqlite3_column_int64(stmt, 4i32) as i64; - (*loc).independent = sqlite3_column_int(stmt, 5i32) as uint32_t; - (*loc).msg_id = sqlite3_column_int(stmt, 6i32) as uint32_t; - (*loc).contact_id = sqlite3_column_int(stmt, 7i32) as uint32_t; - (*loc).chat_id = sqlite3_column_int(stmt, 8i32) as uint32_t; - if 0 != (*loc).msg_id { - let txt: *const libc::c_char = sqlite3_column_text(stmt, 9i32) as *const libc::c_char; - if 0 != is_marker(txt) { - (*loc).marker = strdup(txt) - } - } - dc_array_add_ptr(ret, loc as *mut libc::c_void); - } + context + .sql + .query_map( + "SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \ + m.id, l.from_id, l.chat_id, m.txt \ + FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \ + AND (? OR l.from_id=?) \ + AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \ + ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;", + params![ + if chat_id == 0 { 1 } else { 0 }, + chat_id as i32, + if contact_id == 0 { 1 } else { 0 }, + contact_id as i32, + timestamp_from, + timestamp_to, + ], + |row| unsafe { + let mut loc: *mut _dc_location = + calloc(1, ::std::mem::size_of::<_dc_location>()) as *mut _dc_location; + assert!(!loc.is_null(), "allocation failed"); - sqlite3_finalize(stmt); + (*loc).location_id = row.get(0)?; + (*loc).latitude = row.get(1)?; + (*loc).longitude = row.get(2)?; + (*loc).accuracy = row.get(3)?; + (*loc).timestamp = row.get(4)?; + (*loc).independent = row.get(5)?; + (*loc).msg_id = row.get(6)?; + (*loc).contact_id = row.get(7)?; + (*loc).chat_id = row.get(8)?; - ret + if 0 != (*loc).msg_id { + let txt: String = row.get(9)?; + let txt_c = to_cstring(txt); + if 0 != is_marker(txt_c.as_ptr()) { + (*loc).marker = strdup(txt_c.as_ptr()); + } + } + Ok(loc) + }, + |locations| { + let ret = unsafe { dc_array_new_typed(1, 500) }; + + for location in locations { + unsafe { dc_array_add_ptr(ret, location? as *mut libc::c_void) }; + } + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } // TODO should be bool /rtn unsafe fn is_marker(txt: *const libc::c_char) -> libc::c_int { if !txt.is_null() { let len: libc::c_int = dc_utf8_strlen(txt) as libc::c_int; - if len == 1i32 && *txt.offset(0isize) as libc::c_int != ' ' as i32 { - return 1i32; + if len == 1 && *txt.offset(0isize) as libc::c_int != ' ' as i32 { + return 1; } } 0 } -pub unsafe fn dc_delete_all_locations(context: &Context) { - let stmt: *mut sqlite3_stmt; - - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"DELETE FROM locations;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_step(stmt); - context.call_cb( - Event::LOCATION_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - - sqlite3_finalize(stmt); +pub fn dc_delete_all_locations(context: &Context) -> bool { + if sql::execute(context, &context.sql, "DELETE FROM locations;", params![]).is_err() { + return false; + } + context.call_cb(Event::LOCATION_CHANGED, 0, 0); + true } -pub unsafe fn dc_get_location_kml( +pub fn dc_get_location_kml( context: &Context, chat_id: uint32_t, last_added_location_id: *mut uint32_t, ) -> *mut libc::c_char { - let mut success: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt; - let self_addr: *mut libc::c_char; + let mut success: libc::c_int = 0; let now = time(); - let locations_send_begin: i64; - let locations_send_until: i64; - let locations_last_sent: i64; - let mut location_count: libc::c_int = 0i32; + let mut location_count: libc::c_int = 0; let mut ret = String::new(); - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - locations_send_begin = sqlite3_column_int64(stmt, 0i32) as i64; - locations_send_until = sqlite3_column_int64(stmt, 1i32) as i64; - locations_last_sent = sqlite3_column_int64(stmt, 2i32) as i64; - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + if let Ok((locations_send_begin, locations_send_until, locations_last_sent)) = context.sql.query_row( + "SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;", + params![chat_id as i32], |row| { + let send_begin: i64 = row.get(0)?; + let send_until: i64 = row.get(1)?; + let last_sent: i64 = row.get(2)?; + + Ok((send_begin, send_until, last_sent)) + } + ) { if !(locations_send_begin == 0 || now > locations_send_until) { ret += &format!( "\n\n\n", - to_string(self_addr), + self_addr, ); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id, latitude, longitude, accuracy, timestamp\ - FROM locations WHERE from_id=? \ - AND timestamp>=? \ - AND (timestamp>=? OR timestamp=(SELECT MAX(timestamp) FROM locations WHERE from_id=?)) \ - AND independent=0 \ - GROUP BY timestamp \ - ORDER BY timestamp;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, 1i32); - sqlite3_bind_int64(stmt, 2i32, locations_send_begin as sqlite3_int64); - sqlite3_bind_int64(stmt, 3i32, locations_last_sent as sqlite3_int64); - sqlite3_bind_int(stmt, 4i32, 1i32); - while sqlite3_step(stmt) == 100i32 { - let location_id: uint32_t = sqlite3_column_int(stmt, 0i32) as uint32_t; - let latitude = sqlite3_column_double(stmt, 1i32); - let longitude = sqlite3_column_double(stmt, 2i32); - let accuracy = sqlite3_column_double(stmt, 3i32); - let timestamp = get_kml_timestamp(sqlite3_column_int64(stmt, 4i32) as i64); - ret += &format!( - "{}{},{}\n\x00", - as_str(timestamp), - accuracy, - longitude, - latitude - ); - location_count += 1; - if !last_added_location_id.is_null() { - *last_added_location_id = location_id + context.sql.query_map( + "SELECT id, latitude, longitude, accuracy, timestamp\ + FROM locations WHERE from_id=? \ + AND timestamp>=? \ + AND (timestamp>=? OR timestamp=(SELECT MAX(timestamp) FROM locations WHERE from_id=?)) \ + AND independent=0 \ + GROUP BY timestamp \ + ORDER BY timestamp;", + params![1, locations_send_begin, locations_last_sent, 1], + |row| { + let location_id: i32 = row.get(0)?; + let latitude: f64 = row.get(1)?; + let longitude: f64 = row.get(2)?; + let accuracy: f64 = row.get(3)?; + let timestamp = unsafe { get_kml_timestamp(row.get(4)?) }; + + Ok((location_id, latitude, longitude, accuracy, timestamp)) + }, + |rows| { + for row in rows { + let (location_id, latitude, longitude, accuracy, timestamp) = row?; + ret += &format!( + "{}{},{}\n\x00", + as_str(timestamp), + accuracy, + longitude, + latitude + ); + location_count += 1; + if !last_added_location_id.is_null() { + unsafe { *last_added_location_id = location_id as u32 }; + } + unsafe { free(timestamp as *mut libc::c_void) }; + } + Ok(()) } - free(timestamp as *mut libc::c_void); - } - if !(location_count == 0) { - ret += "\n"; - success = 1; - } + ).unwrap(); // TODO: better error handling } } - sqlite3_finalize(stmt); - free(self_addr as *mut libc::c_void); + if location_count > 0 { + ret += "\n"; + success = 1; + } if 0 != success { - strdup(to_cstring(ret).as_ptr()) + unsafe { strdup(to_cstring(ret).as_ptr()) } } else { 0 as *mut libc::c_char } @@ -445,94 +378,83 @@ pub unsafe fn dc_get_message_kml( ret } -pub unsafe fn dc_set_kml_sent_timestamp(context: &Context, chat_id: uint32_t, timestamp: i64) { - let stmt = dc_sqlite3_prepare( +pub fn dc_set_kml_sent_timestamp(context: &Context, chat_id: u32, timestamp: i64) -> bool { + sql::execute( context, &context.sql, - b"UPDATE chats SET locations_last_sent=? WHERE id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int64(stmt, 1i32, timestamp as sqlite3_int64); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE chats SET locations_last_sent=? WHERE id=?;", + params![timestamp, chat_id as i32], + ) + .is_ok() } -pub unsafe fn dc_set_msg_location_id(context: &Context, msg_id: uint32_t, location_id: uint32_t) { - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( +pub fn dc_set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> bool { + sql::execute( context, &context.sql, - b"UPDATE msgs SET location_id=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int64(stmt, 1i32, location_id as sqlite3_int64); - sqlite3_bind_int(stmt, 2i32, msg_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET location_id=? WHERE id=?;", + params![location_id, msg_id as i32], + ) + .is_ok() } pub unsafe fn dc_save_locations( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, locations: *const dc_array_t, independent: libc::c_int, -) -> uint32_t { - let mut stmt_test: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut stmt_insert: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut newest_timestamp = 0; - let mut newest_location_id: uint32_t = 0i32 as uint32_t; - if !(chat_id <= 9i32 as libc::c_uint || locations.is_null()) { - stmt_test = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM locations WHERE timestamp=? AND from_id=?\x00" as *const u8 - as *const libc::c_char, - ); - stmt_insert = dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO locations\ - (timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \ - VALUES (?,?,?,?,?,?,?);\x00" as *const u8 as *const libc::c_char, - ); - let mut i = 0; - while i < dc_array_get_cnt(locations) { - let location: *mut dc_location_t = - dc_array_get_ptr(locations, i as size_t) as *mut dc_location_t; - sqlite3_reset(stmt_test); - sqlite3_bind_int64(stmt_test, 1i32, (*location).timestamp as sqlite3_int64); - sqlite3_bind_int(stmt_test, 2i32, contact_id as libc::c_int); - if independent | sqlite3_step(stmt_test) != 100i32 { - sqlite3_reset(stmt_insert); - sqlite3_bind_int64(stmt_insert, 1i32, (*location).timestamp as sqlite3_int64); - sqlite3_bind_int(stmt_insert, 2i32, contact_id as libc::c_int); - sqlite3_bind_int(stmt_insert, 3i32, chat_id as libc::c_int); - sqlite3_bind_double(stmt_insert, 4i32, (*location).latitude); - sqlite3_bind_double(stmt_insert, 5i32, (*location).longitude); - sqlite3_bind_double(stmt_insert, 6i32, (*location).accuracy); - sqlite3_bind_double(stmt_insert, 7i32, independent as libc::c_double); - sqlite3_step(stmt_insert); - } - if (*location).timestamp > newest_timestamp { - newest_timestamp = (*location).timestamp; - newest_location_id = dc_sqlite3_get_rowid2( - context, - &context.sql, - b"locations\x00" as *const u8 as *const libc::c_char, - b"timestamp\x00" as *const u8 as *const libc::c_char, - (*location).timestamp as uint64_t, - b"from_id\x00" as *const u8 as *const libc::c_char, - contact_id, - ) - } - i += 1 - } +) -> u32 { + if chat_id <= 9 || locations.is_null() { + return 0; } - sqlite3_finalize(stmt_test); - sqlite3_finalize(stmt_insert); - newest_location_id + context + .sql + .prepare2( + "SELECT id FROM locations WHERE timestamp=? AND from_id=?", + "INSERT INTO locations\ + (timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \ + VALUES (?,?,?,?,?,?,?);", + |mut stmt_test, mut stmt_insert, conn| { + let mut newest_timestamp = 0; + let mut newest_location_id = 0; + + for i in 0..dc_array_get_cnt(locations) { + let location = dc_array_get_ptr(locations, i as size_t) as *mut dc_location_t; + + let exists = + stmt_test.exists(params![(*location).timestamp, contact_id as i32])?; + + if 0 != independent || !exists { + stmt_insert.execute(params![ + (*location).timestamp, + contact_id as i32, + chat_id as i32, + (*location).latitude, + (*location).longitude, + (*location).accuracy, + independent, + ])?; + + if (*location).timestamp > newest_timestamp { + newest_timestamp = (*location).timestamp; + newest_location_id = sql::get_rowid2_with_conn( + context, + conn, + "locations", + "timestamp", + (*location).timestamp, + "from_id", + contact_id as i32, + ); + } + } + } + Ok(newest_location_id) + }, + ) + .unwrap_or_default() } pub unsafe fn dc_kml_parse( @@ -550,12 +472,9 @@ pub unsafe fn dc_kml_parse( }; if content_bytes > (1 * 1024 * 1024) { - dc_log_warning( + warn!( context, - 0, - b"A kml-files with %i bytes is larger than reasonably expected.\x00" as *const u8 - as *const libc::c_char, - content_bytes, + 0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes, ); } else { content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int); @@ -616,15 +535,15 @@ unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _l (*kml).curr.timestamp = time(); } } - } else if 0 != (*kml).tag & 0x10i32 { + } else if 0 != (*kml).tag & 0x10 { let mut comma: *mut libc::c_char = strchr(val, ',' as i32); if !comma.is_null() { let longitude: *mut libc::c_char = val; let latitude: *mut libc::c_char = comma.offset(1isize); - *comma = 0i32 as libc::c_char; + *comma = 0 as libc::c_char; comma = strchr(latitude, ',' as i32); if !comma.is_null() { - *comma = 0i32 as libc::c_char + *comma = 0 as libc::c_char } (*kml).curr.latitude = dc_atof(latitude); (*kml).curr.longitude = dc_atof(longitude) @@ -636,8 +555,8 @@ unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _l unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) { let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t; - if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0i32 { - if 0 != (*kml).tag & 0x1i32 + if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 { + if 0 != (*kml).tag & 0x1 && 0 != (*kml).curr.timestamp && 0. != (*kml).curr.latitude && 0. != (*kml).curr.longitude @@ -647,7 +566,7 @@ unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) { *location = (*kml).curr; dc_array_add_ptr((*kml).locations, location as *mut libc::c_void); } - (*kml).tag = 0i32 + (*kml).tag = 0 }; } @@ -660,34 +579,34 @@ unsafe fn kml_starttag_cb( attr: *mut *mut libc::c_char, ) { let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t; - if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0i32 { + if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0 { let addr: *const libc::c_char = dc_attr_find(attr, b"addr\x00" as *const u8 as *const libc::c_char); if !addr.is_null() { (*kml).addr = dc_strdup(addr) } - } else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0i32 { - (*kml).tag = 0x1i32; + } else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 { + (*kml).tag = 0x1; (*kml).curr.timestamp = 0; - (*kml).curr.latitude = 0i32 as libc::c_double; + (*kml).curr.latitude = 0 as libc::c_double; (*kml).curr.longitude = 0.0f64; (*kml).curr.accuracy = 0.0f64 - } else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0i32 - && 0 != (*kml).tag & 0x1i32 + } else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0 + && 0 != (*kml).tag & 0x1 { - (*kml).tag = 0x1i32 | 0x2i32 - } else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0i32 - && 0 != (*kml).tag & 0x2i32 + (*kml).tag = 0x1 | 0x2 + } else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0 + && 0 != (*kml).tag & 0x2 { - (*kml).tag = 0x1i32 | 0x2i32 | 0x4i32 - } else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0i32 - && 0 != (*kml).tag & 0x1i32 + (*kml).tag = 0x1 | 0x2 | 0x4 + } else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0 + && 0 != (*kml).tag & 0x1 { - (*kml).tag = 0x1i32 | 0x8i32 - } else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0i32 - && 0 != (*kml).tag & 0x8i32 + (*kml).tag = 0x1 | 0x8 + } else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0 + && 0 != (*kml).tag & 0x8 { - (*kml).tag = 0x1i32 | 0x8i32 | 0x10i32; + (*kml).tag = 0x1 | 0x8 | 0x10; let accuracy: *const libc::c_char = dc_attr_find(attr, b"accuracy\x00" as *const u8 as *const libc::c_char); if !accuracy.is_null() { @@ -706,130 +625,124 @@ pub unsafe fn dc_kml_unref(kml: *mut dc_kml_t) { } pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mut dc_job_t) { - let stmt_chats: *mut sqlite3_stmt; - let mut stmt_locations: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; let now = time(); - let mut continue_streaming: libc::c_int = 1i32; - dc_log_info( + let mut continue_streaming: libc::c_int = 1; + info!( context, - 0i32, - b" ----------------- MAYBE_SEND_LOCATIONS -------------- \x00" as *const u8 - as *const libc::c_char, + 0, " ----------------- MAYBE_SEND_LOCATIONS -------------- ", ); - stmt_chats = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id, locations_send_begin, locations_last_sent \ - FROM chats \ - WHERE locations_send_until>?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int64(stmt_chats, 1i32, now as sqlite3_int64); - while sqlite3_step(stmt_chats) == 100i32 { - let chat_id: uint32_t = sqlite3_column_int(stmt_chats, 0i32) as uint32_t; - let locations_send_begin = sqlite3_column_int64(stmt_chats, 1i32) as i64; - let locations_last_sent = sqlite3_column_int64(stmt_chats, 2i32) as i64; - continue_streaming = 1i32; - // be a bit tolerant as the timer may not align exactly with time(NULL) - if now - locations_last_sent < (60 - 3) { - continue; - } - if stmt_locations.is_null() { - stmt_locations = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id \ - FROM locations \ - WHERE from_id=? \ - AND timestamp>=? \ - AND timestamp>? \ - AND independent=0 \ - ORDER BY timestamp;\x00" as *const u8 as *const libc::c_char, - ); - } else { - sqlite3_reset(stmt_locations); - } - sqlite3_bind_int(stmt_locations, 1i32, 1i32); - sqlite3_bind_int64(stmt_locations, 2i32, locations_send_begin as sqlite3_int64); - sqlite3_bind_int64(stmt_locations, 3i32, locations_last_sent as sqlite3_int64); - // if there is no new location, there's nothing to send. - // however, maybe we want to bypass this test eg. 15 minutes - if sqlite3_step(stmt_locations) != 100i32 { - continue; - } - // pending locations are attached automatically to every message, - // so also to this empty text message. - // DC_CMD_LOCATION is only needed to create a nicer subject. - // - // for optimisation and to avoid flooding the sending queue, - // we could sending these messages only if we're really online. - // the easiest way to determine this, is to check for an empty message queue. - // (might not be 100%, however, as positions are sent combined later - // and dc_set_location() is typically called periodically, this is ok) - let mut msg: *mut dc_msg_t = dc_msg_new(context, 10i32); - (*msg).hidden = 1i32; - dc_param_set_int((*msg).param, 'S' as i32, 9i32); - dc_send_msg(context, chat_id, msg); - dc_msg_unref(msg); - } + + context + .sql + .query_map( + "SELECT id, locations_send_begin, locations_last_sent \ + FROM chats \ + WHERE locations_send_until>?;", + params![now], + |row| { + let chat_id: i32 = row.get(0)?; + let locations_send_begin: i64 = row.get(1)?; + let locations_last_sent: i64 = row.get(2)?; + continue_streaming = 1; + + // be a bit tolerant as the timer may not align exactly with time(NULL) + if now - locations_last_sent < (60 - 3) { + Ok(None) + } else { + Ok(Some((chat_id, locations_send_begin, locations_last_sent))) + } + }, + |rows| { + context.sql.prepare( + "SELECT id \ + FROM locations \ + WHERE from_id=? \ + AND timestamp>=? \ + AND timestamp>? \ + AND independent=0 \ + ORDER BY timestamp;", + |mut stmt_locations| { + for (chat_id, locations_send_begin, locations_last_sent) in + rows.filter_map(|r| match r { + Ok(Some(v)) => Some(v), + _ => None, + }) + { + // TODO: do I need to reset? + if !stmt_locations + .exists(params![1, locations_send_begin, locations_last_sent,]) + .unwrap_or_default() + { + // if there is no new location, there's nothing to send. + // however, maybe we want to bypass this test eg. 15 minutes + continue; + } + // pending locations are attached automatically to every message, + // so also to this empty text message. + // DC_CMD_LOCATION is only needed to create a nicer subject. + // + // for optimisation and to avoid flooding the sending queue, + // we could sending these messages only if we're really online. + // the easiest way to determine this, is to check for an empty message queue. + // (might not be 100%, however, as positions are sent combined later + // and dc_set_location() is typically called periodically, this is ok) + let mut msg = dc_msg_new(context, 10); + (*msg).hidden = 1; + dc_param_set_int((*msg).param, 'S' as i32, 9); + dc_send_msg(context, chat_id as u32, msg); + dc_msg_unref(msg); + } + Ok(()) + }, + ) + }, + ) + .unwrap(); // TODO: Better error handling + if 0 != continue_streaming { - schedule_MAYBE_SEND_LOCATIONS(context, 0x1i32); + schedule_MAYBE_SEND_LOCATIONS(context, 0x1); } - sqlite3_finalize(stmt_chats); - sqlite3_finalize(stmt_locations); } pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut dc_job_t) { // this function is called when location-streaming _might_ have ended for a chat. // the function checks, if location-streaming is really ended; // if so, a device-message is added if not yet done. - let chat_id: uint32_t = (*job).foreign_id; - let locations_send_begin: i64; - let locations_send_until: i64; - let mut stmt; - let mut stock_str: *mut libc::c_char = 0 as *mut libc::c_char; - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - locations_send_begin = sqlite3_column_int64(stmt, 0i32) as i64; - locations_send_until = sqlite3_column_int64(stmt, 1i32) as i64; - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if !(locations_send_begin != 0 && time() <= locations_send_until) { + + let chat_id = (*job).foreign_id; + let mut stock_str = 0 as *mut libc::c_char; + + if let Ok((send_begin, send_until)) = context.sql.query_row( + "SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?", + params![chat_id as i32], + |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)), + ) { + if !(send_begin != 0 && time() <= send_until) { // still streaming - // may happen as several calls to dc_send_locations_to_chat() // do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs - if !(locations_send_begin == 0 && locations_send_until == 0) { + if !(send_begin == 0 && send_until == 0) { // not streaming, device-message already sent - stmt = - dc_sqlite3_prepare( + if context.sql.execute( + "UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?", + params![chat_id as i32], + ).is_ok() { + stock_str = dc_stock_system_msg( context, - &context.sql, - b"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_step(stmt); - stock_str = dc_stock_system_msg( - context, - 65i32, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0i32 as uint32_t, - ); - dc_add_device_msg(context, chat_id, stock_str); - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); + 65, + 0 as *const libc::c_char, + 0 as *const libc::c_char, + 0, + ); + dc_add_device_msg(context, chat_id, stock_str); + context.call_cb( + Event::CHAT_MODIFIED, + chat_id as usize, + 0, + ); + } } } } - sqlite3_finalize(stmt); free(stock_str as *mut libc::c_void); } diff --git a/src/dc_log.rs b/src/dc_log.rs deleted file mode 100644 index aa060f438..000000000 --- a/src/dc_log.rs +++ /dev/null @@ -1,142 +0,0 @@ -use crate::constants::Event; -use crate::context::Context; -use crate::dc_tools::*; -use crate::types::*; -use crate::x::*; - -pub unsafe extern "C" fn dc_log_event( - context: &Context, - event_code: Event, - data1: libc::c_int, - msg: *const libc::c_char, - va: ... -) { - log_vprintf(context, event_code, data1, msg, va); -} - -/* Asynchronous "Thread-errors" are reported by the dc_log_error() -function. These errors must be shown to the user by a bubble or so. - -"Normal" errors are usually returned by a special value (null or so) and are -usually not reported using dc_log_error() - its up to the caller to -decide, what should be reported or done. However, these "Normal" errors -are usually logged by dc_log_warning(). */ -unsafe fn log_vprintf( - context: &Context, - event: Event, - data1: libc::c_int, - msg_format: *const libc::c_char, - va_0: ::std::ffi::VaList, -) { - let msg: *mut libc::c_char; - if !msg_format.is_null() { - let mut tempbuf: [libc::c_char; 1025] = [0; 1025]; - vsnprintf( - tempbuf.as_mut_ptr(), - 1024i32 as libc::c_ulong, - msg_format, - va_0, - ); - msg = dc_strdup(tempbuf.as_mut_ptr()) - } else { - msg = dc_mprintf( - b"event #%i\x00" as *const u8 as *const libc::c_char, - event as libc::c_int, - ) - } - context.call_cb(event, data1 as uintptr_t, msg as uintptr_t); - free(msg as *mut libc::c_void); -} - -pub unsafe extern "C" fn dc_log_event_seq( - context: &Context, - event_code: Event, - sequence_start: *mut libc::c_int, - msg: *const libc::c_char, - va_0: ... -) { - if sequence_start.is_null() { - return; - } - log_vprintf(context, event_code, *sequence_start, msg, va_0); - *sequence_start = 0i32; -} - -pub unsafe extern "C" fn dc_log_error( - context: &Context, - data1: libc::c_int, - msg: *const libc::c_char, - va_1: ... -) { - log_vprintf(context, Event::ERROR, data1, msg, va_1); -} - -pub unsafe extern "C" fn dc_log_warning( - context: &Context, - data1: libc::c_int, - msg: *const libc::c_char, - va_2: ... -) { - log_vprintf(context, Event::WARNING, data1, msg, va_2); -} - -pub unsafe extern "C" fn dc_log_info( - context: &Context, - data1: libc::c_int, - msg: *const libc::c_char, - va_3: ... -) { - log_vprintf(context, Event::INFO, data1, msg, va_3); -} - -#[macro_export] -macro_rules! info { - ($ctx:expr, $data1:expr, $msg:expr) => { - info!($ctx, $data1, $msg,) - }; - ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{ - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($crate::constants::Event::INFO, $data1 as uintptr_t, - formatted_c.as_ptr() as uintptr_t) - }}; -} - -#[macro_export] -macro_rules! warn { - ($ctx:expr, $data1:expr, $msg:expr) => { - warn!($ctx, $data1, $msg,) - }; - ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t) - }; -} - -#[macro_export] -macro_rules! error { - ($ctx:expr, $data1:expr, $msg:expr) => { - error!($ctx, $data1, $msg,) - }; - ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($crate::constants::Event::ERROR, $data1 as uintptr_t, - formatted_c.as_ptr() as uintptr_t) - }; -} - -#[macro_export] -macro_rules! log_event { - ($ctx:expr, $data1:expr, $msg:expr) => { - log_event!($ctx, $data1, $msg,) - }; - ($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($event, $data1 as uintptr_t, - formatted_c.as_ptr() as uintptr_t) - }; -} diff --git a/src/dc_loginparam.rs b/src/dc_loginparam.rs index 11d29f784..d231f134e 100644 --- a/src/dc_loginparam.rs +++ b/src/dc_loginparam.rs @@ -1,283 +1,168 @@ -use crate::context::Context; -use crate::dc_sqlite3::*; -use crate::dc_tools::*; -use crate::types::*; -use crate::x::*; +use std::borrow::Cow; -#[derive(Copy, Clone)] -#[repr(C)] +use crate::context::Context; +use crate::sql::Sql; + +#[derive(Default, Debug)] pub struct dc_loginparam_t { - pub addr: *mut libc::c_char, - pub mail_server: *mut libc::c_char, - pub mail_user: *mut libc::c_char, - pub mail_pw: *mut libc::c_char, + pub addr: String, + pub mail_server: String, + pub mail_user: String, + pub mail_pw: String, pub mail_port: i32, - pub send_server: *mut libc::c_char, - pub send_user: *mut libc::c_char, - pub send_pw: *mut libc::c_char, + pub send_server: String, + pub send_user: String, + pub send_pw: String, pub send_port: i32, pub server_flags: i32, } -pub unsafe fn dc_loginparam_new() -> *mut dc_loginparam_t { - let loginparam: *mut dc_loginparam_t; - loginparam = calloc(1, ::std::mem::size_of::()) as *mut dc_loginparam_t; - assert!(!loginparam.is_null()); - - loginparam -} - -pub unsafe fn dc_loginparam_unref(loginparam: *mut dc_loginparam_t) { - if loginparam.is_null() { - return; +impl dc_loginparam_t { + pub fn addr_str(&self) -> &str { + self.addr.as_str() } - dc_loginparam_empty(loginparam); - free(loginparam as *mut libc::c_void); } -/* clears all data and frees its memory. All pointers are NULL after this function is called. */ -pub unsafe fn dc_loginparam_empty(mut loginparam: *mut dc_loginparam_t) { - if loginparam.is_null() { - return; - } - free((*loginparam).addr as *mut libc::c_void); - (*loginparam).addr = 0 as *mut libc::c_char; - free((*loginparam).mail_server as *mut libc::c_void); - (*loginparam).mail_server = 0 as *mut libc::c_char; - (*loginparam).mail_port = 0i32; - free((*loginparam).mail_user as *mut libc::c_void); - (*loginparam).mail_user = 0 as *mut libc::c_char; - free((*loginparam).mail_pw as *mut libc::c_void); - (*loginparam).mail_pw = 0 as *mut libc::c_char; - free((*loginparam).send_server as *mut libc::c_void); - (*loginparam).send_server = 0 as *mut libc::c_char; - (*loginparam).send_port = 0i32; - free((*loginparam).send_user as *mut libc::c_void); - (*loginparam).send_user = 0 as *mut libc::c_char; - free((*loginparam).send_pw as *mut libc::c_void); - (*loginparam).send_pw = 0 as *mut libc::c_char; - (*loginparam).server_flags = 0i32; +pub fn dc_loginparam_new() -> dc_loginparam_t { + Default::default() } -pub unsafe fn dc_loginparam_read( +pub fn dc_loginparam_read( context: &Context, - loginparam: *mut dc_loginparam_t, - sql: &SQLite, - prefix: *const libc::c_char, -) { - let mut key: *mut libc::c_char = 0 as *mut libc::c_char; - dc_loginparam_empty(loginparam); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"addr\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).addr = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_server\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).mail_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_port\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).mail_port = dc_sqlite3_get_config_int(context, sql, key, 0i32); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_user\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).mail_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_pw\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).mail_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_server\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).send_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_port\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).send_port = dc_sqlite3_get_config_int(context, sql, key, 0i32); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_user\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).send_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_pw\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).send_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"server_flags\x00" as *const u8 as *const libc::c_char, - ); - (*loginparam).server_flags = dc_sqlite3_get_config_int(context, sql, key, 0i32); - sqlite3_free(key as *mut libc::c_void); -} + sql: &Sql, + prefix: impl AsRef, +) -> dc_loginparam_t { + let prefix = prefix.as_ref(); -pub unsafe fn dc_loginparam_write( - context: &Context, - loginparam: *const dc_loginparam_t, - sql: &SQLite, - prefix: *const libc::c_char, -) { - let mut key: *mut libc::c_char = 0 as *mut libc::c_char; - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"addr\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).addr); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_server\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_server); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_port\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config_int(context, sql, key, (*loginparam).mail_port); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_user\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_user); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"mail_pw\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_pw); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_server\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).send_server); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_port\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config_int(context, sql, key, (*loginparam).send_port); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_user\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).send_user); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"send_pw\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config(context, sql, key, (*loginparam).send_pw); - sqlite3_free(key as *mut libc::c_void); - key = sqlite3_mprintf( - b"%s%s\x00" as *const u8 as *const libc::c_char, - prefix, - b"server_flags\x00" as *const u8 as *const libc::c_char, - ); - dc_sqlite3_set_config_int(context, sql, key, (*loginparam).server_flags); - sqlite3_free(key as *mut libc::c_void); -} + let key = format!("{}addr", prefix); + let addr = sql + .get_config(context, key) + .unwrap_or_default() + .trim() + .to_string(); -pub unsafe fn dc_loginparam_get_readable(loginparam: *const dc_loginparam_t) -> *mut libc::c_char { - let unset: *const libc::c_char = b"0\x00" as *const u8 as *const libc::c_char; - let pw: *const libc::c_char = b"***\x00" as *const u8 as *const libc::c_char; - if loginparam.is_null() { - return dc_strdup(0 as *const libc::c_char); + let key = format!("{}mail_server", prefix); + let mail_server = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}mail_port", prefix); + let mail_port = sql.get_config_int(context, key).unwrap_or_default(); + + let key = format!("{}mail_user", prefix); + let mail_user = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}mail_pw", prefix); + let mail_pw = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}send_server", prefix); + let send_server = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}send_port", prefix); + let send_port = sql.get_config_int(context, key).unwrap_or_default(); + + let key = format!("{}send_user", prefix); + let send_user = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}send_pw", prefix); + let send_pw = sql.get_config(context, key).unwrap_or_default(); + + let key = format!("{}server_flags", prefix); + let server_flags = sql.get_config_int(context, key).unwrap_or_default(); + + dc_loginparam_t { + addr: addr.to_string(), + mail_server, + mail_user, + mail_pw, + mail_port, + send_server, + send_user, + send_pw, + send_port, + server_flags, } - let flags_readable: *mut libc::c_char = get_readable_flags((*loginparam).server_flags); - let ret: *mut libc::c_char = dc_mprintf( - b"%s %s:%s:%s:%i %s:%s:%s:%i %s\x00" as *const u8 as *const libc::c_char, - if !(*loginparam).addr.is_null() { - (*loginparam).addr - } else { - unset - }, - if !(*loginparam).mail_user.is_null() { - (*loginparam).mail_user - } else { - unset - }, - if !(*loginparam).mail_pw.is_null() { +} + +pub fn dc_loginparam_write( + context: &Context, + loginparam: &dc_loginparam_t, + sql: &Sql, + prefix: impl AsRef, +) { + let prefix = prefix.as_ref(); + + let key = format!("{}addr", prefix); + sql.set_config(context, key, Some(&loginparam.addr)); + + let key = format!("{}mail_server", prefix); + sql.set_config(context, key, Some(&loginparam.mail_server)); + + let key = format!("{}mail_port", prefix); + sql.set_config_int(context, key, loginparam.mail_port); + + let key = format!("{}mail_user", prefix); + sql.set_config(context, key, Some(&loginparam.mail_user)); + + let key = format!("{}mail_pw", prefix); + sql.set_config(context, key, Some(&loginparam.mail_pw)); + + let key = format!("{}send_server", prefix); + sql.set_config(context, key, Some(&loginparam.send_server)); + + let key = format!("{}send_port", prefix); + sql.set_config_int(context, key, loginparam.send_port); + + let key = format!("{}send_user", prefix); + sql.set_config(context, key, Some(&loginparam.send_user)); + + let key = format!("{}send_pw", prefix); + sql.set_config(context, key, Some(&loginparam.send_pw)); + + let key = format!("{}server_flags", prefix); + sql.set_config_int(context, key, loginparam.server_flags); +} + +fn unset_empty(s: &String) -> Cow { + if s.is_empty() { + Cow::Owned("unset".to_string()) + } else { + Cow::Borrowed(s) + } +} + +pub fn dc_loginparam_get_readable(loginparam: &dc_loginparam_t) -> String { + let unset = "0"; + let pw = "***"; + + let flags_readable = get_readable_flags(loginparam.server_flags); + + format!( + "{} {}:{}:{}:{} {}:{}:{}:{} {}", + unset_empty(&loginparam.addr), + unset_empty(&loginparam.mail_user), + if !loginparam.mail_pw.is_empty() { pw } else { unset }, - if !(*loginparam).mail_server.is_null() { - (*loginparam).mail_server - } else { - unset - }, - (*loginparam).mail_port, - if !(*loginparam).send_user.is_null() { - (*loginparam).send_user - } else { - unset - }, - if !(*loginparam).send_pw.is_null() { + unset_empty(&loginparam.mail_server), + loginparam.mail_port, + unset_empty(&loginparam.send_user), + if !loginparam.send_pw.is_empty() { pw } else { unset }, - if !(*loginparam).send_server.is_null() { - (*loginparam).send_server - } else { - unset - }, - (*loginparam).send_port, + unset_empty(&loginparam.send_server), + loginparam.send_port, flags_readable, - ); - free(flags_readable as *mut libc::c_void); - - ret + ) } -fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char { +fn get_readable_flags(flags: i32) -> String { let mut res = String::new(); for bit in 0..31 { if 0 != flags & 1 << bit { - let mut flag_added: libc::c_int = 0; + let mut flag_added = 0; if 1 << bit == 0x2 { res += "OAUTH2 "; flag_added = 1; @@ -319,5 +204,5 @@ fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char { res += "0"; } - unsafe { strdup(to_cstring(res).as_ptr()) } + res } diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 27cae7569..d1eb9dcc7 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -14,10 +14,8 @@ use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_e2ee::*; use crate::dc_location::*; -use crate::dc_log::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_tools::*; @@ -100,7 +98,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) { mmap_string_free((*factory).out); (*factory).out = 0 as *mut MMAPString } - (*factory).out_encrypted = 0i32; + (*factory).out_encrypted = 0; (*factory).loaded = DC_MF_NOTHING_LOADED; free((*factory).error as *mut libc::c_void); (*factory).error = 0 as *mut libc::c_char; @@ -111,158 +109,192 @@ pub unsafe fn dc_mimefactory_load_msg( mut factory: *mut dc_mimefactory_t, msg_id: uint32_t, ) -> libc::c_int { - let mut success: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(factory.is_null() || msg_id <= 9i32 as libc::c_uint || !(*factory).msg.is_null()) { - /*call empty() before */ - let context = (*factory).context; - (*factory).recipients_names = clist_new(); - (*factory).recipients_addr = clist_new(); - (*factory).msg = dc_msg_new_untyped(context); - (*factory).chat = dc_chat_new(context); - if dc_msg_load_from_db((*factory).msg, context, msg_id) - && dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id) - { - load_from(factory); - (*factory).req_mdn = 0i32; - if 0 != dc_chat_is_self_talk((*factory).chat) { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void, + if factory.is_null() || msg_id <= 9 || !(*factory).msg.is_null() { + info!((*factory).context, 0, "mimefactory: null"); + return 0; + } + + let mut success = 0; + + /*call empty() before */ + let context = (*factory).context; + (*factory).recipients_names = clist_new(); + (*factory).recipients_addr = clist_new(); + (*factory).msg = dc_msg_new_untyped(context); + (*factory).chat = dc_chat_new(context); + if dc_msg_load_from_db((*factory).msg, context, msg_id) + && dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id) + { + info!(context, 0, "mimefactory: loaded msg and chat",); + load_from(factory); + (*factory).req_mdn = 0; + if 0 != dc_chat_is_self_talk((*factory).chat) { + info!(context, 0, "mimefactory: selftalk"); + + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + dc_strdup((*factory).from_addr) as *mut libc::c_void, + ); + } else { + info!(context, 0, "mimefactory: query map"); + context + .sql + .query_map( + "SELECT c.authname, c.addr \ + FROM chats_contacts cc \ + LEFT JOIN contacts c ON cc.contact_id=c.id \ + WHERE cc.chat_id=? AND cc.contact_id>9;", + params![(*(*factory).msg).chat_id as i32], + |row| { + let authname: String = row.get(0)?; + let addr: String = row.get(1)?; + Ok((authname, addr)) + }, + |rows| { + info!(context, 0, "mimefactory: processing rows"); + for row in rows { + let (authname, addr) = row?; + let addr_c = to_cstring(addr); + if clist_search_string_nocase( + (*factory).recipients_addr, + addr_c.as_ptr(), + ) == 0 + { + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + if !authname.is_empty() { + dc_strdup(to_cstring(authname).as_ptr()) + } else { + 0 as *mut libc::c_char + } as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + dc_strdup(addr_c.as_ptr()) as *mut libc::c_void, + ); + } + } + Ok(()) + }, + ) + .unwrap(); + + let command = dc_param_get_int((*(*factory).msg).param, 'S' as i32, 0); + if command == 5 { + let email_to_remove_c = dc_param_get( + (*(*factory).msg).param, + 'E' as i32, + 0 as *const libc::c_char, ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - dc_strdup((*factory).from_addr) as *mut libc::c_void, - ); - } else { - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT c.authname, c.addr FROM chats_contacts cc LEFT JOIN contacts c ON cc.contact_id=c.id WHERE cc.chat_id=? AND cc.contact_id>9;\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).chat_id as libc::c_int); - while sqlite3_step(stmt) == 100i32 { - let authname: *const libc::c_char = - sqlite3_column_text(stmt, 0i32) as *const libc::c_char; - let addr: *const libc::c_char = - sqlite3_column_text(stmt, 1i32) as *const libc::c_char; - if clist_search_string_nocase((*factory).recipients_addr, addr) == 0i32 { + let email_to_remove = to_string(email_to_remove_c); + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + + if !email_to_remove.is_empty() && email_to_remove != self_addr { + if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c) + == 0 + { clist_insert_after( (*factory).recipients_names, (*(*factory).recipients_names).last, - (if !authname.is_null() && 0 != *authname.offset(0isize) as libc::c_int - { - dc_strdup(authname) - } else { - 0 as *mut libc::c_char - }) as *mut libc::c_void, + 0 as *mut libc::c_void, ); clist_insert_after( (*factory).recipients_addr, (*(*factory).recipients_addr).last, - dc_strdup(addr) as *mut libc::c_void, + email_to_remove_c as *mut libc::c_void, ); } } - sqlite3_finalize(stmt); - let command: libc::c_int = - dc_param_get_int((*(*factory).msg).param, 'S' as i32, 0i32); - if command == 5i32 { - let email_to_remove: *mut libc::c_char = dc_param_get( - (*(*factory).msg).param, - 'E' as i32, - 0 as *const libc::c_char, - ); - let self_addr: *mut libc::c_char = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if !email_to_remove.is_null() && strcasecmp(email_to_remove, self_addr) != 0i32 - { - if clist_search_string_nocase((*factory).recipients_addr, email_to_remove) - == 0i32 - { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - 0 as *mut libc::c_void, - ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - email_to_remove as *mut libc::c_void, - ); - } - } - free(self_addr as *mut libc::c_void); - } - if command != 6i32 - && command != 7i32 - && 0 != dc_sqlite3_get_config_int( - context, - &context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ) - { - (*factory).req_mdn = 1i32 - } } - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - (*factory).in_reply_to = - dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char); - (*factory).references = - dc_strdup(sqlite3_column_text(stmt, 1i32) as *const libc::c_char) + if command != 6 + && command != 7 + && 0 != context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1) + { + (*factory).req_mdn = 1 } - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - success = 1i32; - (*factory).loaded = DC_MF_MSG_LOADED; - (*factory).timestamp = (*(*factory).msg).timestamp_sort; - (*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid) } - if 0 != success { - (*factory).increation = dc_msg_is_increation((*factory).msg) + info!(context, 0, "mimefactory: loading in reply to"); + + let row = context.sql.query_row( + "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", + params![(*(*factory).msg).id as i32], + |row| { + let in_reply_to: String = row.get(0)?; + let references: String = row.get(1)?; + + Ok((in_reply_to, references)) + }, + ); + match row { + Ok((in_reply_to, references)) => { + (*factory).in_reply_to = dc_strdup(to_cstring(in_reply_to).as_ptr()); + (*factory).references = dc_strdup(to_cstring(references).as_ptr()); + } + Err(err) => { + error!( + context, + 0, "mimefactory: failed to load mime_in_reply_to: {:?}", err + ); + } } + + success = 1; + (*factory).loaded = DC_MF_MSG_LOADED; + (*factory).timestamp = (*(*factory).msg).timestamp_sort; + (*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid) } - sqlite3_finalize(stmt); - return success; + if 0 != success { + (*factory).increation = dc_msg_is_increation((*factory).msg) + } + + success } unsafe fn load_from(mut factory: *mut dc_mimefactory_t) { - (*factory).from_addr = dc_sqlite3_get_config( - (*factory).context, - &(*factory).context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, + let context = (*factory).context; + (*factory).from_addr = strdup( + to_cstring( + context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(), + ) + .as_ptr(), ); - (*factory).from_displayname = dc_sqlite3_get_config( - (*factory).context, - &(*factory).context.sql, - b"displayname\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, + (*factory).from_displayname = strdup( + to_cstring( + context + .sql + .get_config(context, "displayname") + .unwrap_or_default(), + ) + .as_ptr(), ); - (*factory).selfstatus = dc_sqlite3_get_config( - (*factory).context, - &(*factory).context.sql, - b"selfstatus\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, + (*factory).selfstatus = strdup( + to_cstring( + context + .sql + .get_config(context, "selfstatus") + .unwrap_or_default(), + ) + .as_ptr(), ); if (*factory).selfstatus.is_null() { - (*factory).selfstatus = dc_stock_str((*factory).context, 13i32) + (*factory).selfstatus = dc_stock_str((*factory).context, 13) }; } @@ -270,61 +302,63 @@ pub unsafe fn dc_mimefactory_load_mdn( mut factory: *mut dc_mimefactory_t, msg_id: uint32_t, ) -> libc::c_int { - let mut success: libc::c_int = 0i32; - let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t; - if !factory.is_null() { - (*factory).recipients_names = clist_new(); - (*factory).recipients_addr = clist_new(); - (*factory).msg = dc_msg_new_untyped((*factory).context); - if !(0 - == dc_sqlite3_get_config_int( - (*factory).context, + if factory.is_null() { + return 0; + } + + let mut success = 0; + let mut contact = 0 as *mut dc_contact_t; + + (*factory).recipients_names = clist_new(); + (*factory).recipients_addr = clist_new(); + (*factory).msg = dc_msg_new_untyped((*factory).context); + if 0 != (*factory) + .context + .sql + .get_config_int((*factory).context, "mdns_enabled") + .unwrap_or_else(|| 1) + { + // MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ... + contact = dc_contact_new((*factory).context); + if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id) + || !dc_contact_load_from_db( + contact, &(*factory).context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, + (*(*factory).msg).from_id, )) { - /* MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ... */ - contact = dc_contact_new((*factory).context); - if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id) - || !dc_contact_load_from_db( - contact, - &(*factory).context.sql, - (*(*factory).msg).from_id, - )) - { - if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9i32 as libc::c_uint) { - /* Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs() */ - if !((*(*factory).msg).from_id <= 9i32 as libc::c_uint) { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - (if !(*contact).authname.is_null() - && 0 != *(*contact).authname.offset(0isize) as libc::c_int - { - dc_strdup((*contact).authname) - } else { - 0 as *mut libc::c_char - }) as *mut libc::c_void, - ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - dc_strdup((*contact).addr) as *mut libc::c_void, - ); - load_from(factory); - (*factory).timestamp = dc_create_smeared_timestamp((*factory).context); - (*factory).rfc724_mid = dc_create_outgoing_rfc724_mid( - 0 as *const libc::c_char, - (*factory).from_addr, - ); - success = 1i32; - (*factory).loaded = DC_MF_MDN_LOADED - } + if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9 as libc::c_uint) { + // Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs() + if !((*(*factory).msg).from_id <= 9 as libc::c_uint) { + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + (if !(*contact).authname.is_null() + && 0 != *(*contact).authname.offset(0isize) as libc::c_int + { + dc_strdup((*contact).authname) + } else { + 0 as *mut libc::c_char + }) as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + dc_strdup((*contact).addr) as *mut libc::c_void, + ); + load_from(factory); + (*factory).timestamp = dc_create_smeared_timestamp((*factory).context); + (*factory).rfc724_mid = dc_create_outgoing_rfc724_mid( + 0 as *const libc::c_char, + (*factory).from_addr, + ); + success = 1; + (*factory).loaded = DC_MF_MDN_LOADED } } } } + dc_contact_unref(contact); success @@ -339,15 +373,15 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let mut message_text: *mut libc::c_char = 0 as *mut libc::c_char; let mut message_text2: *mut libc::c_char = 0 as *mut libc::c_char; let mut subject_str: *mut libc::c_char = 0 as *mut libc::c_char; - let mut afwd_email: libc::c_int = 0i32; - let mut col: libc::c_int = 0i32; - let mut success: libc::c_int = 0i32; - let mut parts: libc::c_int = 0i32; - let mut e2ee_guaranteed: libc::c_int = 0i32; - let mut min_verified: libc::c_int = 0i32; + let mut afwd_email: libc::c_int = 0; + let mut col: libc::c_int = 0; + let mut success: libc::c_int = 0; + let mut parts: libc::c_int = 0; + let mut e2ee_guaranteed: libc::c_int = 0; + let mut min_verified: libc::c_int = 0; // 1=add Autocrypt-header (needed eg. for handshaking), 2=no Autocrypte-header (used for MDN) - let mut force_plaintext: libc::c_int = 0i32; - let mut do_gossip: libc::c_int = 0i32; + let mut force_plaintext: libc::c_int = 0; + let mut do_gossip: libc::c_int = 0; let mut grpimage: *mut libc::c_char = 0 as *mut libc::c_char; let mut e2ee_helper = dc_e2ee_helper_t { encryption_successfull: 0, @@ -382,7 +416,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let mut to: *mut mailimf_address_list = 0 as *mut mailimf_address_list; if !(*factory).recipients_names.is_null() && !(*factory).recipients_addr.is_null() - && (*(*factory).recipients_addr).count > 0i32 + && (*(*factory).recipients_addr).count > 0 { let mut iter1: *mut clistiter; let mut iter2: *mut clistiter; @@ -505,7 +539,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let msg: *mut dc_msg_t = (*factory).msg; let mut meta_part: *mut mailmime = 0 as *mut mailmime; let mut placeholdertext: *mut libc::c_char = 0 as *mut libc::c_char; - if (*chat).type_0 == 130i32 { + if (*chat).type_0 == 130 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -513,23 +547,23 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: strdup(b"1\x00" as *const u8 as *const libc::c_char), ), ); - force_plaintext = 0i32; - e2ee_guaranteed = 1i32; - min_verified = 2i32 + force_plaintext = 0; + e2ee_guaranteed = 1; + min_verified = 2 } else { - force_plaintext = dc_param_get_int((*(*factory).msg).param, 'u' as i32, 0i32); - if force_plaintext == 0i32 { - e2ee_guaranteed = dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32) + force_plaintext = dc_param_get_int((*(*factory).msg).param, 'u' as i32, 0); + if force_plaintext == 0 { + e2ee_guaranteed = dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0) } } if (*chat).gossiped_timestamp == 0 || ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time() { - do_gossip = 1i32 + do_gossip = 1 } /* build header etc. */ - let command: libc::c_int = dc_param_get_int((*msg).param, 'S' as i32, 0i32); - if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { + let command: libc::c_int = dc_param_get_int((*msg).param, 'S' as i32, 0); + if (*chat).type_0 == 120 || (*chat).type_0 == 130 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -544,7 +578,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: dc_encode_header_words((*chat).name), ), ); - if command == 5i32 { + if command == 5 { let email_to_remove: *mut libc::c_char = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char); if !email_to_remove.is_null() { @@ -559,8 +593,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); } - } else if command == 4i32 { - do_gossip = 1i32; + } else if command == 4 { + do_gossip = 1; let email_to_add: *mut libc::c_char = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char); if !email_to_add.is_null() { @@ -576,13 +610,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ); grpimage = dc_param_get((*chat).param, 'i' as i32, 0 as *const libc::c_char) } - if 0 != dc_param_get_int((*msg).param, 'F' as i32, 0i32) & 0x1i32 { - dc_log_info( + if 0 != dc_param_get_int((*msg).param, 'F' as i32, 0) & 0x1 { + info!( (*msg).context, - 0i32, - b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00" - as *const u8 as *const libc::c_char, - b"vg-member-added\x00" as *const u8 as *const libc::c_char, + 0, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + "vg-member-added", ); mailimf_fields_add( imf_fields, @@ -592,7 +625,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); } - } else if command == 2i32 { + } else if command == 2 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -606,7 +639,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ), ); - } else if command == 3i32 { + } else if command == 3 { grpimage = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char); if grpimage.is_null() { mailimf_fields_add( @@ -619,7 +652,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } } } - if command == 8i32 { + if command == 8 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -630,7 +663,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); } - if command == 6i32 { + if command == 6 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -638,18 +671,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: strdup(b"v1\x00" as *const u8 as *const libc::c_char), ), ); - placeholdertext = dc_stock_str((*factory).context, 43i32) + placeholdertext = dc_stock_str((*factory).context, 43) } - if command == 7i32 { + if command == 7 { let step: *mut libc::c_char = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char); if !step.is_null() { - dc_log_info( + info!( (*msg).context, - 0i32, - b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00" - as *const u8 as *const libc::c_char, - step, + 0, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + as_str(step), ); mailimf_fields_add( imf_fields, @@ -667,12 +699,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: if strcmp( step, b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 || strcmp( step, b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { strdup( b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char, @@ -718,7 +750,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } if !grpimage.is_null() { let mut meta: *mut dc_msg_t = dc_msg_new_untyped((*factory).context); - (*meta).type_0 = 20i32; + (*meta).type_0 = 20; dc_param_set((*meta).param, 'f' as i32, grpimage); let mut filename_as_sent: *mut libc::c_char = 0 as *mut libc::c_char; meta_part = build_body_file( @@ -737,8 +769,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } dc_msg_unref(meta); } - if (*msg).type_0 == 41i32 || (*msg).type_0 == 40i32 || (*msg).type_0 == 50i32 { - if (*msg).type_0 == 41i32 { + if (*msg).type_0 == 41 || (*msg).type_0 == 40 || (*msg).type_0 == 50 { + if (*msg).type_0 == 41 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -747,8 +779,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); } - let duration_ms: libc::c_int = dc_param_get_int((*msg).param, 'd' as i32, 0i32); - if duration_ms > 0i32 { + let duration_ms: libc::c_int = dc_param_get_int((*msg).param, 'd' as i32, 0); + if duration_ms > 0 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -813,18 +845,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: free(fwdhint as *mut libc::c_void); free(placeholdertext as *mut libc::c_void); /* add attachment part */ - if (*msg).type_0 == 20i32 - || (*msg).type_0 == 21i32 - || (*msg).type_0 == 40i32 - || (*msg).type_0 == 41i32 - || (*msg).type_0 == 50i32 - || (*msg).type_0 == 60i32 + if (*msg).type_0 == 20 + || (*msg).type_0 == 21 + || (*msg).type_0 == 40 + || (*msg).type_0 == 41 + || (*msg).type_0 == 50 + || (*msg).type_0 == 60 { if 0 == is_file_size_okay(msg) { let error: *mut libc::c_char = dc_mprintf( b"Message exceeds the recommended %i MB.\x00" as *const u8 as *const libc::c_char, - 24i32 * 1024i32 * 1024i32 / 4i32 * 3i32 / 1000i32 / 1000i32, + 24 * 1024 * 1024 / 4 * 3 / 1000 / 1000, ); set_error(factory, error); free(error as *mut libc::c_void); @@ -844,7 +876,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: match current_block { 11328123142868406523 => {} _ => { - if parts == 0i32 { + if parts == 0 { set_error( factory, b"Empty message.\x00" as *const u8 as *const libc::c_char, @@ -887,8 +919,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } } - if 0 != dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) { - let mut last_added_location_id: uint32_t = 0i32 as uint32_t; + if dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) { + let mut last_added_location_id: uint32_t = 0 as uint32_t; let kml_file: *mut libc::c_char = dc_get_location_kml( (*msg).context, (*msg).chat_id, @@ -943,12 +975,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: mailmime_add_part(message, multipart); let p1: *mut libc::c_char; let p2: *mut libc::c_char; - if 0 != dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32) { - p1 = dc_stock_str((*factory).context, 24i32) + if 0 != dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0) { + p1 = dc_stock_str((*factory).context, 24) } else { - p1 = dc_msg_get_summarytext((*factory).msg, 32i32) + p1 = dc_msg_get_summarytext((*factory).msg, 32) } - p2 = dc_stock_str_repl_string((*factory).context, 32i32, p1); + p2 = dc_stock_str_repl_string((*factory).context, 32, p1); message_text = dc_mprintf(b"%s\r\n\x00" as *const u8 as *const libc::c_char, p2); free(p2 as *mut libc::c_void); free(p1 as *mut libc::c_void); @@ -968,7 +1000,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let mach_mime_part: *mut mailmime = mailmime_new_empty(content_type_0, mime_fields_0); mailmime_set_body_text(mach_mime_part, message_text2, strlen(message_text2)); mailmime_add_part(multipart, mach_mime_part); - force_plaintext = 2i32; + force_plaintext = 2; current_block = 9952640327414195044; } else { set_error( @@ -983,7 +1015,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: if (*factory).loaded as libc::c_uint == DC_MF_MDN_LOADED as libc::c_int as libc::c_uint { - let e: *mut libc::c_char = dc_stock_str((*factory).context, 31i32); + let e: *mut libc::c_char = dc_stock_str((*factory).context, 31); subject_str = dc_mprintf(b"Chat: %s\x00" as *const u8 as *const libc::c_char, e); free(e as *mut libc::c_void); @@ -1019,7 +1051,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: 0 as *mut mailimf_optional_field, ), ); - if force_plaintext != 2i32 { + if force_plaintext != 2 { dc_e2ee_encrypt( (*factory).context, (*factory).recipients_addr, @@ -1032,14 +1064,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ); } if 0 != e2ee_helper.encryption_successfull { - (*factory).out_encrypted = 1i32; + (*factory).out_encrypted = 1; if 0 != do_gossip { - (*factory).out_gossiped = 1i32 + (*factory).out_gossiped = 1 } } (*factory).out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char); mailmime_write_mem((*factory).out, &mut col, message); - success = 1i32 + success = 1 } } } @@ -1063,15 +1095,15 @@ unsafe fn get_subject( let context = (*chat).context; let ret: *mut libc::c_char; let raw_subject: *mut libc::c_char = - dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32i32, context); + dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32, context); let fwd: *const libc::c_char = if 0 != afwd_email { b"Fwd: \x00" as *const u8 as *const libc::c_char } else { b"\x00" as *const u8 as *const libc::c_char }; - if dc_param_get_int((*msg).param, 'S' as i32, 0i32) == 6i32 { - ret = dc_stock_str(context, 42i32) - } else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { + if dc_param_get_int((*msg).param, 'S' as i32, 0) == 6 { + ret = dc_stock_str(context, 42) + } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { ret = dc_mprintf( b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char, (*chat).name, @@ -1135,7 +1167,7 @@ unsafe fn build_body_file( let mut filename_to_send: *mut libc::c_char = 0 as *mut libc::c_char; let mut filename_encoded: *mut libc::c_char = 0 as *mut libc::c_char; if !pathNfilename.is_null() { - if (*msg).type_0 == 41i32 { + if (*msg).type_0 == 41 { let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0); let suffix = if !suffix.is_null() { @@ -1147,9 +1179,9 @@ unsafe fn build_body_file( .format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix)) .to_string(); filename_to_send = strdup(to_cstring(res).as_ptr()); - } else if (*msg).type_0 == 40i32 { + } else if (*msg).type_0 == 40 { filename_to_send = dc_get_filename(pathNfilename) - } else if (*msg).type_0 == 20i32 || (*msg).type_0 == 21i32 { + } else if (*msg).type_0 == 20 || (*msg).type_0 == 21 { if base_name.is_null() { base_name = b"image\x00" as *const u8 as *const libc::c_char } @@ -1162,7 +1194,7 @@ unsafe fn build_body_file( b"dat\x00" as *const u8 as *const libc::c_char }, ) - } else if (*msg).type_0 == 50i32 { + } else if (*msg).type_0 == 50 { filename_to_send = dc_mprintf( b"video.%s\x00" as *const u8 as *const libc::c_char, if !suffix.is_null() { @@ -1178,14 +1210,14 @@ unsafe fn build_body_file( if suffix.is_null() { mimetype = dc_strdup(b"application/octet-stream\x00" as *const u8 as *const libc::c_char) - } else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0i32 { + } else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0 { mimetype = dc_strdup(b"image/png\x00" as *const u8 as *const libc::c_char) - } else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0i32 + } else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0 + || strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0 + || strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0 { mimetype = dc_strdup(b"image/jpeg\x00" as *const u8 as *const libc::c_char) - } else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0i32 { + } else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0 { mimetype = dc_strdup(b"image/gif\x00" as *const u8 as *const libc::c_char) } else { mimetype = @@ -1228,7 +1260,7 @@ unsafe fn build_body_file( 0 as *mut libc::c_char, 0 as *mut libc::c_char, 0 as *mut libc::c_char, - 0i32 as size_t, + 0 as size_t, mailmime_parameter_new( strdup( b"filename*\x00" as *const u8 as *const libc::c_char, @@ -1285,12 +1317,12 @@ unsafe fn build_body_file( ******************************************************************************/ unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> libc::c_int { - let mut file_size_okay: libc::c_int = 1i32; + let mut file_size_okay: libc::c_int = 1; let pathNfilename: *mut libc::c_char = dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char); let bytes: uint64_t = dc_get_filebytes((*msg).context, pathNfilename); - if bytes > (49i32 * 1024i32 * 1024i32 / 4i32 * 3i32) as libc::c_ulonglong { - file_size_okay = 0i32 + if bytes > (49 * 1024 * 1024 / 4 * 3) as libc::c_ulonglong { + file_size_okay = 0; } free(pathNfilename as *mut libc::c_void); diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index e8d28b1e3..d6d57afb5 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -15,7 +15,6 @@ use crate::context::Context; use crate::dc_contact::*; use crate::dc_e2ee::*; use crate::dc_location::*; -use crate::dc_log::*; use crate::dc_param::*; use crate::dc_simplify::*; use crate::dc_stock::*; @@ -531,11 +530,9 @@ unsafe fn dc_mimeparser_parse_mime_recursive( b"rfc822-headers\x00" as *const u8 as *const libc::c_char, ) == 0i32 { - dc_log_info( + info!( (*mimeparser).context, - 0i32, - b"Protected headers found in text/rfc822-headers attachment: Will be ignored.\x00" - as *const u8 as *const libc::c_char, + 0, "Protected headers found in text/rfc822-headers attachment: Will be ignored.", ); return 0i32; } @@ -549,11 +546,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive( ) != MAILIMF_NO_ERROR as libc::c_int || (*mimeparser).header_protected.is_null() { - dc_log_warning( - (*mimeparser).context, - 0i32, - b"Protected headers parsing error.\x00" as *const u8 as *const libc::c_char, - ); + warn!((*mimeparser).context, 0, "Protected headers parsing error.",); } else { hash_header( &mut (*mimeparser).header, @@ -562,9 +555,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive( ); } } else { - dc_log_info((*mimeparser).context, 0i32, - b"Protected headers found in MIME header: Will be ignored as we already found an outer one.\x00" - as *const u8 as *const libc::c_char); + info!( + (*mimeparser).context, + 0, + "Protected headers found in MIME header: Will be ignored as we already found an outer one." + ); } } match (*mime).mm_type { @@ -756,10 +751,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive( } } if plain_cnt == 1i32 && html_cnt == 1i32 { - dc_log_warning((*mimeparser).context, 0i32, - b"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted.\x00" - as *const u8 as - *const libc::c_char); + warn!( + (*mimeparser).context, + 0i32, + "HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted." + ); skip_part = html_part } cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; @@ -1213,14 +1209,12 @@ unsafe fn dc_mimeparser_add_single_part_if_known( current_block = 17788412896529399552; } } else { - dc_log_warning( + warn!( mimeparser.context, - 0i32, - b"Cannot convert %i bytes from \"%s\" to \"utf-8\".\x00" - as *const u8 - as *const libc::c_char, + 0, + "Cannot convert {} bytes from \"{}\" to \"utf-8\".", decoded_data_bytes as libc::c_int, - charset, + as_str(charset), ); current_block = 17788412896529399552; } diff --git a/src/dc_move.rs b/src/dc_move.rs index c48008899..69005cc8d 100644 --- a/src/dc_move.rs +++ b/src/dc_move.rs @@ -2,44 +2,44 @@ use crate::constants::*; use crate::context::*; use crate::dc_job::*; use crate::dc_msg::*; -use crate::dc_sqlite3::*; -use crate::types::*; -pub unsafe fn dc_do_heuristics_moves( - context: &Context, - folder: *const libc::c_char, - msg_id: uint32_t, -) { - // for already seen messages, folder may be different from msg->folder - let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t; - let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_move\x00" as *const u8 as *const libc::c_char, - 1i32, - ) == 0i32) +pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) { + if context + .sql + .get_config_int(context, "mvbox_move") + .unwrap_or_else(|| 1) + == 0 { - if !(0 == dc_is_inbox(context, folder) && 0 == dc_is_sentbox(context, folder)) { - msg = dc_msg_new_load(context, msg_id); - if !(0 != dc_msg_is_setupmessage(msg)) { - // do not move setup messages; - // there may be a non-delta device that wants to handle it - if 0 != dc_is_mvbox(context, folder) { - dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY); - } else if 0 != (*msg).is_dc_message { - dc_job_add( - context, - 200i32, - (*msg).id as libc::c_int, - 0 as *const libc::c_char, - 0i32, - ); - dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING); - } - } - } + return; } - sqlite3_finalize(stmt); + + if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) { + return; + } + + let msg = dc_msg_new_load(context, msg_id); + if dc_msg_is_setupmessage(msg) { + // do not move setup messages; + // there may be a non-delta device that wants to handle it + dc_msg_unref(msg); + return; + } + + if dc_is_mvbox(context, folder) { + dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY); + } + + // 1 = dc message, 2 = reply to dc message + if 0 != (*msg).is_dc_message { + dc_job_add( + context, + 200, + (*msg).id as libc::c_int, + 0 as *const libc::c_char, + 0, + ); + dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING); + } + dc_msg_unref(msg); } diff --git a/src/dc_msg.rs b/src/dc_msg.rs index eb4cee832..66e6ccd52 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -3,14 +3,13 @@ use crate::context::*; use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_job::*; -use crate::dc_log::*; use crate::dc_lot::dc_lot_t; use crate::dc_lot::*; use crate::dc_param::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_tools::*; use crate::pgp::*; +use crate::sql; use crate::types::*; use crate::x::*; @@ -44,174 +43,172 @@ pub struct dc_msg_t<'a> { } // handle messages -pub unsafe fn dc_get_msg_info(context: &Context, msg_id: uint32_t) -> *mut libc::c_char { - let e2ee_errors: libc::c_int; - let w: libc::c_int; - let h: libc::c_int; - let duration: libc::c_int; - let mut stmt: *mut sqlite3_stmt; - let msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let contact_from: *mut dc_contact_t = dc_contact_new(context); - let mut rawtxt: *mut libc::c_char = 0 as *mut libc::c_char; +pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_char { + let msg = dc_msg_new_untyped(context); + let contact_from = dc_contact_new(context); let mut p: *mut libc::c_char; let mut ret = String::new(); dc_msg_load_from_db(msg, context, msg_id); dc_contact_load_from_db(contact_from, &context.sql, (*msg).from_id); - stmt = dc_sqlite3_prepare( + + let rawtxt: Option = context.sql.query_row_col( context, - &context.sql, - b"SELECT txt_raw FROM msgs WHERE id=?;\x00" as *const u8 as *const libc::c_char, + "SELECT txt_raw FROM msgs WHERE id=?;", + params![msg_id as i32], + 0, ); - sqlite3_bind_int(stmt, 1, msg_id as libc::c_int); - if sqlite3_step(stmt) != 100 { + + if rawtxt.is_none() { ret += &format!("Cannot load message #{}.", msg_id as usize); - } else { - rawtxt = dc_strdup(sqlite3_column_text(stmt, 0) as *mut libc::c_char); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - dc_trim(rawtxt); - dc_truncate_str(rawtxt, 100000); - p = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); - ret += &format!("Sent: {}", as_str(p)); + dc_msg_unref(msg); + dc_contact_unref(contact_from); + return strdup(to_cstring(ret).as_ptr()); + } + let rawtxt = rawtxt.unwrap(); + let rawtxt = dc_truncate_str(rawtxt.trim(), 100000); - free(p as *mut libc::c_void); - p = dc_contact_get_name_n_addr(contact_from); - ret += &format!(" by {}", to_string(p)); + let fts = dc_timestamp_to_str_safe(dc_msg_get_timestamp(msg)); + ret += &format!("Sent: {}", fts); + p = dc_contact_get_name_n_addr(contact_from); + ret += &format!(" by {}", to_string(p)); + free(p as *mut libc::c_void); + ret += "\n"; + + if (*msg).from_id != 1 as libc::c_uint { + p = dc_timestamp_to_str(if 0 != (*msg).timestamp_rcvd { + (*msg).timestamp_rcvd + } else { + (*msg).timestamp_sort + }); + ret += &format!("Received: {}", as_str(p)); free(p as *mut libc::c_void); ret += "\n"; - if (*msg).from_id != 1 as libc::c_uint { - p = dc_timestamp_to_str(if 0 != (*msg).timestamp_rcvd { - (*msg).timestamp_rcvd - } else { - (*msg).timestamp_sort - }); - ret += &format!("Received: {}", as_str(p)); - free(p as *mut libc::c_void); - ret += "\n"; - } - if !((*msg).from_id == 2 as libc::c_uint || (*msg).to_id == 2 as libc::c_uint) { - // device-internal message, no further details needed - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT contact_id, timestamp_sent FROM msgs_mdns WHERE msg_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1, msg_id as libc::c_int); - while sqlite3_step(stmt) == 100 { - p = dc_timestamp_to_str(sqlite3_column_int64(stmt, 1) as i64); - ret += &format!("Read: {}", as_str(p)); - free(p as *mut libc::c_void); - let contact = dc_contact_new(context); - dc_contact_load_from_db( - contact, - &context.sql, - sqlite3_column_int64(stmt, 0) as uint32_t, - ); - p = dc_contact_get_name_n_addr(contact); - ret += &format!(" by {}", as_str(p)); - free(p as *mut libc::c_void); - dc_contact_unref(contact); - ret += "\n"; - } - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - ret += "State: "; - match (*msg).state { - 10 => ret += "Fresh", - 13 => ret += "Noticed", - 16 => ret += "Seen", - 26 => ret += "Delivered", - 24 => ret += "Failed", - 28 => ret += "Read", - 20 => ret += "Pending", - 18 => ret += "Preparing", - _ => ret += &format!("{}", (*msg).state), - } - - if dc_msg_has_location(msg) { - ret += ", Location sent"; - } - p = 0 as *mut libc::c_char; - e2ee_errors = dc_param_get_int((*msg).param, 'e' as i32, 0); - if 0 != e2ee_errors { - if 0 != e2ee_errors & 0x2 { - p = dc_strdup( - b"Encrypted, no valid signature\x00" as *const u8 as *const libc::c_char, - ) - } - } else if 0 != dc_param_get_int((*msg).param, 'c' as i32, 0) { - p = dc_strdup(b"Encrypted\x00" as *const u8 as *const libc::c_char) - } - if !p.is_null() { - ret += &format!(", {}", as_str(p)); - free(p as *mut libc::c_void); - } - ret += "\n"; - p = dc_param_get((*msg).param, 'L' as i32, 0 as *const libc::c_char); - if !p.is_null() { - ret += &format!("Error: {}", as_str(p)); - free(p as *mut libc::c_void); - } - p = dc_msg_get_file(msg); - if !p.is_null() && 0 != *p.offset(0isize) as libc::c_int { - ret += &format!( - "\nFile: {}, {}, bytes\n", - as_str(p), - dc_get_filebytes(context, p) as libc::c_int, - ); - } - free(p as *mut libc::c_void); - if (*msg).type_0 != 10 { - ret += "Type: "; - match (*msg).type_0 { - 40 => ret += "Audio", - 60 => ret += "File", - 21 => ret += "GIF", - 20 => ret += "Image", - 50 => ret += "Video", - 41 => ret += "Voice", - _ => ret += &format!("{}", (*msg).type_0), - } - ret += "\n"; - p = dc_msg_get_filemime(msg); - ret += &format!("Mimetype: {}\n", as_str(p)); - free(p as *mut libc::c_void); - } - w = dc_param_get_int((*msg).param, 'w' as i32, 0); - h = dc_param_get_int((*msg).param, 'h' as i32, 0); - if w != 0 || h != 0 { - ret += &format!("Dimension: {} x {}\n", w, h,); - } - duration = dc_param_get_int((*msg).param, 'd' as i32, 0); - if duration != 0 { - ret += &format!("Duration: {} ms\n", duration,); - } - if !rawtxt.is_null() && 0 != *rawtxt.offset(0) as libc::c_int { - ret += &format!("\n{}\n", as_str(rawtxt)); - } - if !(*msg).rfc724_mid.is_null() && 0 != *(*msg).rfc724_mid.offset(0) as libc::c_int { - ret += &format!("\nMessage-ID: {}", (*msg).rfc724_mid as libc::c_int); - } - if !(*msg).server_folder.is_null() - && 0 != *(*msg).server_folder.offset(0) as libc::c_int - { - ret += &format!( - "\nLast seen as: {}/{}", - to_string((*msg).server_folder), - (*msg).server_uid as libc::c_int, - ); - } - } } - sqlite3_finalize(stmt); + if (*msg).from_id == 2 || (*msg).to_id == 2 { + // device-internal message, no further details needed + dc_msg_unref(msg); + dc_contact_unref(contact_from); + return strdup(to_cstring(ret).as_ptr()); + } + + context + .sql + .query_map( + "SELECT contact_id, timestamp_sent FROM msgs_mdns WHERE msg_id=?;", + params![msg_id as i32], + |row| { + let contact_id: i32 = row.get(0)?; + let ts: i64 = row.get(1)?; + Ok((contact_id, ts)) + }, + |rows| { + for row in rows { + let (contact_id, ts) = row?; + let fts = dc_timestamp_to_str_safe(ts); + ret += &format!("Read: {}", fts); + + let contact = dc_contact_new(context); + dc_contact_load_from_db(contact, &context.sql, contact_id as u32); + + p = dc_contact_get_name_n_addr(contact); + ret += &format!(" by {}", as_str(p)); + free(p as *mut libc::c_void); + dc_contact_unref(contact); + + ret += "\n"; + } + Ok(()) + }, + ) + .unwrap(); // TODO: better error handling + + ret += "State: "; + match (*msg).state { + 10 => ret += "Fresh", + 13 => ret += "Noticed", + 16 => ret += "Seen", + 26 => ret += "Delivered", + 24 => ret += "Failed", + 28 => ret += "Read", + 20 => ret += "Pending", + 18 => ret += "Preparing", + _ => ret += &format!("{}", (*msg).state), + } + + if dc_msg_has_location(msg) { + ret += ", Location sent"; + } + + let e2ee_errors = dc_param_get_int((*msg).param, 'e' as i32, 0); + + if 0 != e2ee_errors { + if 0 != e2ee_errors & 0x2 { + ret += ", Encrypted, no valid signature"; + } + } else if 0 != dc_param_get_int((*msg).param, 'c' as i32, 0) { + ret += ", Encrypted"; + } + + ret += "\n"; + p = dc_param_get((*msg).param, 'L' as i32, 0 as *const libc::c_char); + if !p.is_null() { + ret += &format!("Error: {}", as_str(p)); + free(p as *mut libc::c_void); + } + p = dc_msg_get_file(msg); + if !p.is_null() && 0 != *p.offset(0isize) as libc::c_int { + ret += &format!( + "\nFile: {}, {}, bytes\n", + as_str(p), + dc_get_filebytes(context, p) as libc::c_int, + ); + } + free(p as *mut libc::c_void); + + if (*msg).type_0 != 10 { + ret += "Type: "; + match (*msg).type_0 { + 40 => ret += "Audio", + 60 => ret += "File", + 21 => ret += "GIF", + 20 => ret += "Image", + 50 => ret += "Video", + 41 => ret += "Voice", + _ => ret += &format!("{}", (*msg).type_0), + } + ret += "\n"; + p = dc_msg_get_filemime(msg); + ret += &format!("Mimetype: {}\n", as_str(p)); + free(p as *mut libc::c_void); + } + let w = dc_param_get_int((*msg).param, 'w' as i32, 0); + let h = dc_param_get_int((*msg).param, 'h' as i32, 0); + if w != 0 || h != 0 { + ret += &format!("Dimension: {} x {}\n", w, h,); + } + let duration = dc_param_get_int((*msg).param, 'd' as i32, 0); + if duration != 0 { + ret += &format!("Duration: {} ms\n", duration,); + } + if !rawtxt.is_empty() { + ret += &format!("\n{}\n", rawtxt); + } + if !(*msg).rfc724_mid.is_null() && 0 != *(*msg).rfc724_mid.offset(0) as libc::c_int { + ret += &format!("\nMessage-ID: {}", (*msg).rfc724_mid as libc::c_int); + } + if !(*msg).server_folder.is_null() && 0 != *(*msg).server_folder.offset(0) as libc::c_int { + ret += &format!( + "\nLast seen as: {}/{}", + to_string((*msg).server_folder), + (*msg).server_uid as libc::c_int, + ); + } + dc_msg_unref(msg); dc_contact_unref(contact_from); - free(rawtxt as *mut libc::c_void); - strdup(to_cstring(ret).as_ptr()) } @@ -429,130 +426,78 @@ pub unsafe fn dc_msg_get_timestamp(msg: *const dc_msg_t) -> i64 { }; } -pub unsafe fn dc_msg_load_from_db<'a>( - msg: *mut dc_msg_t<'a>, - context: &'a Context, - id: uint32_t, -) -> bool { - let mut success = false; - let mut stmt = 0 as *mut sqlite3_stmt; - if !msg.is_null() { - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT m.id,rfc724_mid,m.mime_in_reply_to,m.server_folder,m.server_uid,m.move_state,m.chat_id, m.from_id,m.to_id,m.timestamp,m.timestamp_sent,m.timestamp_rcvd, m.type,m.state,m.msgrmsg,m.txt, m.param,m.starred,m.hidden,m.location_id, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=?;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - if !(0 == dc_msg_set_from_stmt(msg, stmt, 0i32)) { - /* also calls dc_msg_empty() */ +pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: u32) -> bool { + if msg.is_null() { + return false; + } + + let res = context.sql.query_row( + "SELECT \ + m.id,rfc724_mid,m.mime_in_reply_to,m.server_folder,m.server_uid,m.move_state,m.chat_id, \ + m.from_id,m.to_id,m.timestamp,m.timestamp_sent,m.timestamp_rcvd, m.type,m.state,m.msgrmsg,m.txt, \ + m.param,m.starred,m.hidden,m.location_id, c.blocked \ + FROM msgs m \ + LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=?;", + params![id as i32], + |row| { + unsafe { (*msg).context = context; - success = true + dc_msg_empty(msg); + + (*msg).id = row.get::<_, i32>(0)? as u32; + (*msg).rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); + (*msg).in_reply_to = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); + (*msg).server_folder = dc_strdup(to_cstring(row.get::<_, String>(3)?).as_ptr()); + (*msg).server_uid = row.get(4)?; + (*msg).move_state = row.get(5)?; + (*msg).chat_id = row.get(6)?; + (*msg).from_id = row.get(7)?; + (*msg).to_id = row.get(8)?; + (*msg).timestamp_sort = row.get(9)?; + (*msg).timestamp_sent = row.get(10)?; + (*msg).timestamp_rcvd = row.get(11)?; + (*msg).type_0 = row.get(12)?; + (*msg).state = row.get(13)?; + (*msg).is_dc_message = row.get(14)?; + (*msg).text = dc_strdup(to_cstring(row.get::<_, String>(15).unwrap_or_default()).as_ptr()); + dc_param_set_packed( + (*msg).param, + to_cstring(row.get::<_, String>(16)?).as_ptr() + ); + (*msg).starred = row.get(17)?; + (*msg).hidden = row.get(18)?; + (*msg).location_id = row.get(19)?; + (*msg).chat_blocked = row.get(20)?; + if (*msg).chat_blocked == 2 { + dc_truncate_n_unwrap_str((*msg).text, 256, 0); + } } + Ok(()) + } + ); + + match res { + Ok(_) => true, + Err(err) => { + error!(context, 0, "msg: load from db failed: {:?}", err); + false } } - sqlite3_finalize(stmt); - - success -} - -// TODO always returns 1, should be void /rtn -unsafe fn dc_msg_set_from_stmt( - mut msg: *mut dc_msg_t, - row: *mut sqlite3_stmt, - mut row_offset: libc::c_int, -) -> libc::c_int { - dc_msg_empty(msg); - let fresh0 = row_offset; - row_offset = row_offset + 1; - (*msg).id = sqlite3_column_int(row, fresh0) as uint32_t; - let fresh1 = row_offset; - row_offset = row_offset + 1; - (*msg).rfc724_mid = dc_strdup(sqlite3_column_text(row, fresh1) as *mut libc::c_char); - let fresh2 = row_offset; - row_offset = row_offset + 1; - (*msg).in_reply_to = dc_strdup(sqlite3_column_text(row, fresh2) as *mut libc::c_char); - let fresh3 = row_offset; - row_offset = row_offset + 1; - (*msg).server_folder = dc_strdup(sqlite3_column_text(row, fresh3) as *mut libc::c_char); - let fresh4 = row_offset; - row_offset = row_offset + 1; - (*msg).server_uid = sqlite3_column_int(row, fresh4) as uint32_t; - let fresh5 = row_offset; - row_offset = row_offset + 1; - (*msg).move_state = sqlite3_column_int(row, fresh5) as dc_move_state_t; - let fresh6 = row_offset; - row_offset = row_offset + 1; - (*msg).chat_id = sqlite3_column_int(row, fresh6) as uint32_t; - let fresh7 = row_offset; - row_offset = row_offset + 1; - (*msg).from_id = sqlite3_column_int(row, fresh7) as uint32_t; - let fresh8 = row_offset; - row_offset = row_offset + 1; - (*msg).to_id = sqlite3_column_int(row, fresh8) as uint32_t; - let fresh9 = row_offset; - row_offset = row_offset + 1; - (*msg).timestamp_sort = sqlite3_column_int64(row, fresh9) as i64; - let fresh10 = row_offset; - row_offset = row_offset + 1; - (*msg).timestamp_sent = sqlite3_column_int64(row, fresh10) as i64; - let fresh11 = row_offset; - row_offset = row_offset + 1; - (*msg).timestamp_rcvd = sqlite3_column_int64(row, fresh11) as i64; - let fresh12 = row_offset; - row_offset = row_offset + 1; - (*msg).type_0 = sqlite3_column_int(row, fresh12); - let fresh13 = row_offset; - row_offset = row_offset + 1; - (*msg).state = sqlite3_column_int(row, fresh13); - let fresh14 = row_offset; - row_offset = row_offset + 1; - (*msg).is_dc_message = sqlite3_column_int(row, fresh14); - let fresh15 = row_offset; - row_offset = row_offset + 1; - (*msg).text = dc_strdup(sqlite3_column_text(row, fresh15) as *mut libc::c_char); - let fresh16 = row_offset; - row_offset = row_offset + 1; - dc_param_set_packed( - (*msg).param, - sqlite3_column_text(row, fresh16) as *mut libc::c_char, - ); - let fresh17 = row_offset; - row_offset = row_offset + 1; - (*msg).starred = sqlite3_column_int(row, fresh17); - let fresh18 = row_offset; - row_offset = row_offset + 1; - (*msg).hidden = sqlite3_column_int(row, fresh18); - let fresh19 = row_offset; - row_offset = row_offset + 1; - (*msg).location_id = sqlite3_column_int(row, fresh19) as uint32_t; - let fresh20 = row_offset; - (*msg).chat_blocked = sqlite3_column_int(row, fresh20); - if (*msg).chat_blocked == 2i32 { - dc_truncate_n_unwrap_str((*msg).text, 256i32, 0i32); - } - - 1 } pub unsafe fn dc_get_mime_headers(context: &Context, msg_id: uint32_t) -> *mut libc::c_char { - let mut eml = 0 as *mut libc::c_char; - let stmt: *mut sqlite3_stmt; - - stmt = dc_sqlite3_prepare( + let headers: Option = context.sql.query_row_col( context, - &context.sql, - b"SELECT mime_headers FROM msgs WHERE id=?;\x00" as *const u8 as *const libc::c_char, + "SELECT mime_headers FROM msgs WHERE id=?;", + params![msg_id as i32], + 0, ); - sqlite3_bind_int(stmt, 1i32, msg_id as libc::c_int); - if sqlite3_step(stmt) == 100i32 { - eml = dc_strdup_keep_null(sqlite3_column_text(stmt, 0i32) as *const libc::c_char) + + if let Some(headers) = headers { + dc_strdup_keep_null(to_cstring(headers).as_ptr()) + } else { + std::ptr::null_mut() } - - sqlite3_finalize(stmt); - - eml } pub unsafe fn dc_delete_msgs(context: &Context, msg_ids: *const uint32_t, msg_cnt: libc::c_int) { @@ -579,104 +524,90 @@ pub unsafe fn dc_delete_msgs(context: &Context, msg_ids: *const uint32_t, msg_cn }; } -pub unsafe fn dc_update_msg_chat_id(context: &Context, msg_id: uint32_t, chat_id: uint32_t) { - let stmt = dc_sqlite3_prepare( +pub fn dc_update_msg_chat_id(context: &Context, msg_id: u32, chat_id: u32) -> bool { + sql::execute( context, &context.sql, - b"UPDATE msgs SET chat_id=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, msg_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET chat_id=? WHERE id=?;", + params![chat_id as i32, msg_id as i32], + ) + .is_ok() } -pub unsafe fn dc_markseen_msgs(context: &Context, msg_ids: *const uint32_t, msg_cnt: libc::c_int) { - let mut i: libc::c_int; - let mut send_event: libc::c_int = 0i32; - let mut curr_state: libc::c_int; - let mut curr_blocked: libc::c_int; - let mut stmt = 0 as *mut sqlite3_stmt; - if !(msg_ids.is_null() || msg_cnt <= 0i32) { - stmt = - dc_sqlite3_prepare(context, &context.sql, - b"SELECT m.state, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=? AND m.chat_id>9\x00" - as *const u8 as *const libc::c_char); - i = 0i32; - while i < msg_cnt { - sqlite3_reset(stmt); - sqlite3_bind_int(stmt, 1i32, *msg_ids.offset(i as isize) as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - curr_state = sqlite3_column_int(stmt, 0i32); - curr_blocked = sqlite3_column_int(stmt, 1i32); - if curr_blocked == 0i32 { - if curr_state == 10i32 || curr_state == 13i32 { - dc_update_msg_state(context, *msg_ids.offset(i as isize), 16i32); - dc_log_info( - context, - 0i32, - b"Seen message #%i.\x00" as *const u8 as *const libc::c_char, - *msg_ids.offset(i as isize), - ); - dc_job_add( - context, - 130i32, - *msg_ids.offset(i as isize) as libc::c_int, - 0 as *const libc::c_char, - 0i32, - ); - send_event = 1i32 +pub fn dc_markseen_msgs(context: &Context, msg_ids: *const u32, msg_cnt: usize) -> bool { + if msg_ids.is_null() || msg_cnt <= 0 { + return false; + } + context.sql.prepare( + "SELECT m.state, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=? AND m.chat_id>9", + |mut stmt| { + let mut send_event = false; + + for i in 0..msg_cnt { + // TODO: do I need to reset? + let id = unsafe { *msg_ids.offset(i as isize) }; + if let Ok((curr_state, curr_blocked)) = stmt + .query_row(params![id as i32], |row| { + Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) + }) + { + if curr_blocked == 0 { + if curr_state == 10 || curr_state == 13 { + dc_update_msg_state(context, id, 16); + info!(context, 0, "Seen message #{}.", id); + + unsafe { dc_job_add( + context, + 130, + id as i32, + 0 as *const libc::c_char, + 0, + ) }; + send_event = true; + } + } else if curr_state == 10 { + dc_update_msg_state(context, id, 13); + send_event = true; } - } else if curr_state == 10i32 { - dc_update_msg_state(context, *msg_ids.offset(i as isize), 13i32); - send_event = 1i32 } } - i += 1 - } - if 0 != send_event { - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); + if send_event { + context.call_cb(Event::MSGS_CHANGED, 0, 0); + } + Ok(()) } - } - sqlite3_finalize(stmt); + ).is_ok() } -pub unsafe fn dc_update_msg_state(context: &Context, msg_id: uint32_t, state: libc::c_int) { - let stmt = dc_sqlite3_prepare( +pub fn dc_update_msg_state(context: &Context, msg_id: uint32_t, state: libc::c_int) -> bool { + sql::execute( context, &context.sql, - b"UPDATE msgs SET state=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, state); - sqlite3_bind_int(stmt, 2i32, msg_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET state=? WHERE id=?;", + params![state, msg_id as i32], + ) + .is_ok() } -pub unsafe fn dc_star_msgs( +pub fn dc_star_msgs( context: &Context, - msg_ids: *const uint32_t, + msg_ids: *const u32, msg_cnt: libc::c_int, star: libc::c_int, -) { - if msg_ids.is_null() || msg_cnt <= 0i32 || star != 0i32 && star != 1i32 { - return; +) -> bool { + if msg_ids.is_null() || msg_cnt <= 0 || star != 0 && star != 1 { + return false; } - let stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE msgs SET starred=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - let mut i: libc::c_int = 0i32; - while i < msg_cnt { - sqlite3_reset(stmt); - sqlite3_bind_int(stmt, 1i32, star); - sqlite3_bind_int(stmt, 2i32, *msg_ids.offset(i as isize) as libc::c_int); - sqlite3_step(stmt); - i += 1 - } - sqlite3_finalize(stmt); + context + .sql + .prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt| { + for i in 0..msg_cnt { + stmt.execute(params![star, unsafe { *msg_ids.offset(i as isize) as i32 }])?; + } + Ok(()) + }) + .is_ok() } pub unsafe fn dc_get_msg<'a>(context: &'a Context, msg_id: uint32_t) -> *mut dc_msg_t<'a> { @@ -753,14 +684,12 @@ pub unsafe fn dc_msg_get_sort_timestamp(msg: *const dc_msg_t) -> i64 { } pub unsafe fn dc_msg_get_text(msg: *const dc_msg_t) -> *mut libc::c_char { - let ret: *mut libc::c_char; if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { return dc_strdup(0 as *const libc::c_char); } - ret = dc_strdup((*msg).text); - dc_truncate_str(ret, 30000i32); - ret + let res = dc_truncate_str(as_str((*msg).text), 30000); + dc_strdup(to_cstring(res).as_ptr()) } pub unsafe fn dc_msg_get_filename(msg: *const dc_msg_t) -> *mut libc::c_char { @@ -1034,19 +963,19 @@ pub unsafe fn dc_msg_is_increation(msg: *const dc_msg_t) -> libc::c_int { && (*msg).state == 18i32) as libc::c_int } -// TODO should return bool /rtn -pub unsafe fn dc_msg_is_setupmessage(msg: *const dc_msg_t) -> libc::c_int { +pub unsafe fn dc_msg_is_setupmessage(msg: *const dc_msg_t) -> bool { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint || (*msg).type_0 != DC_MSG_FILE as libc::c_int { - return 0i32; + return false; } - return if dc_param_get_int((*msg).param, 'S' as i32, 0i32) == 6i32 { - 1i32 + + if dc_param_get_int((*msg).param, 'S' as i32, 0) == 6 { + true } else { - 0i32 - }; + true + } } pub unsafe fn dc_msg_get_setupcodebegin(msg: *const dc_msg_t) -> *mut libc::c_char { @@ -1058,7 +987,7 @@ pub unsafe fn dc_msg_get_setupcodebegin(msg: *const dc_msg_t) -> *mut libc::c_ch // just a pointer inside buf, MUST NOT be free()'d let mut buf_setupcodebegin: *const libc::c_char = 0 as *const libc::c_char; let mut ret: *mut libc::c_char = 0 as *mut libc::c_char; - if !(0 == dc_msg_is_setupmessage(msg)) { + if dc_msg_is_setupmessage(msg) { filename = dc_msg_get_file(msg); if !(filename.is_null() || *filename.offset(0isize) as libc::c_int == 0i32) { if !(0 @@ -1153,19 +1082,18 @@ pub unsafe fn dc_msg_latefiling_mediasize( }; } -pub unsafe fn dc_msg_save_param_to_disk(msg: *mut dc_msg_t) { +pub unsafe fn dc_msg_save_param_to_disk(msg: *mut dc_msg_t) -> bool { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { - return; + return false; } - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + + sql::execute( (*msg).context, &(*msg).context.sql, - b"UPDATE msgs SET param=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, (*(*msg).param).packed, -1i32, None); - sqlite3_bind_int(stmt, 2i32, (*msg).id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET param=? WHERE id=?;", + params![as_str((*(*msg).param).packed), (*msg).id as i32], + ) + .is_ok() } pub unsafe fn dc_msg_new_load<'a>(context: &'a Context, msg_id: uint32_t) -> *mut dc_msg_t<'a> { @@ -1176,27 +1104,20 @@ pub unsafe fn dc_msg_new_load<'a>(context: &'a Context, msg_id: uint32_t) -> *mu pub unsafe fn dc_delete_msg_from_db(context: &Context, msg_id: uint32_t) { let msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; if dc_msg_load_from_db(msg, context, msg_id) { - stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"DELETE FROM msgs WHERE id=?;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM msgs WHERE id=?;", + params![(*msg).id as i32], ); - sqlite3_bind_int(stmt, 1i32, (*msg).id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"DELETE FROM msgs_mdns WHERE msg_id=?;\x00" as *const u8 as *const libc::c_char, + "DELETE FROM msgs_mdns WHERE msg_id=?;", + params![(*msg).id as i32], ); - sqlite3_bind_int(stmt, 1i32, (*msg).id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt } - sqlite3_finalize(stmt); dc_msg_unref(msg); } @@ -1208,318 +1129,271 @@ The value is also used for CC:-summaries */ // Context functions to work with messages pub unsafe fn dc_msg_exists(context: &Context, msg_id: uint32_t) -> libc::c_int { - let mut msg_exists = 0; - let mut stmt = 0 as *mut sqlite3_stmt; + if msg_id <= 9 { + return 0; + } - if msg_id > 9 { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT chat_id FROM msgs WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, msg_id as libc::c_int); + let chat_id: Option = context.sql.query_row_col( + context, + "SELECT chat_id FROM msgs WHERE id=?;", + params![msg_id as i32], + 0, + ); - if sqlite3_step(stmt) == 100i32 { - let chat_id: uint32_t = sqlite3_column_int(stmt, 0i32) as uint32_t; - if chat_id != 3i32 as libc::c_uint { - msg_exists = 1i32 - } + if let Some(chat_id) = chat_id { + if chat_id != 3 { + return 1; } } - sqlite3_finalize(stmt); - msg_exists + 0 } -pub unsafe fn dc_update_msg_move_state( +pub fn dc_update_msg_move_state( context: &Context, rfc724_mid: *const libc::c_char, state: dc_move_state_t, -) { +) -> bool { // we update the move_state for all messages belonging to a given Message-ID // so that the state stay intact when parts are deleted - let stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"UPDATE msgs SET move_state=? WHERE rfc724_mid=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, state as libc::c_int); - sqlite3_bind_text(stmt, 2i32, rfc724_mid, -1i32, None); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + "UPDATE msgs SET move_state=? WHERE rfc724_mid=?;", + params![state as i32, as_str(rfc724_mid)], + ) + .is_ok() } pub unsafe fn dc_set_msg_failed(context: &Context, msg_id: uint32_t, error: *const libc::c_char) { let mut msg = dc_msg_new_untyped(context); - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + if dc_msg_load_from_db(msg, context, msg_id) { - if 18i32 == (*msg).state || 20i32 == (*msg).state || 26i32 == (*msg).state { - (*msg).state = 24i32 + if 18 == (*msg).state || 20 == (*msg).state || 26 == (*msg).state { + (*msg).state = 24 } if !error.is_null() { dc_param_set((*msg).param, 'L' as i32, error); - dc_log_error( - context, - 0i32, - b"%s\x00" as *const u8 as *const libc::c_char, - error, - ); + error!(context, 0, "{}", as_str(error),); } - stmt = dc_sqlite3_prepare( + + if sql::execute( context, &context.sql, - b"UPDATE msgs SET state=?, param=? WHERE id=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, (*msg).state); - sqlite3_bind_text(stmt, 2i32, (*(*msg).param).packed, -1i32, None); - sqlite3_bind_int(stmt, 3i32, msg_id as libc::c_int); - sqlite3_step(stmt); - context.call_cb( - Event::MSG_FAILED, - (*msg).chat_id as uintptr_t, - msg_id as uintptr_t, - ); + "UPDATE msgs SET state=?, param=? WHERE id=?;", + params![(*msg).state, as_str((*(*msg).param).packed), msg_id as i32], + ) + .is_ok() + { + context.call_cb( + Event::MSG_FAILED, + (*msg).chat_id as uintptr_t, + msg_id as uintptr_t, + ); + } } - sqlite3_finalize(stmt); + dc_msg_unref(msg); } /* returns 1 if an event should be send */ pub unsafe fn dc_mdn_from_ext( context: &Context, - from_id: uint32_t, + from_id: u32, rfc724_mid: *const libc::c_char, timestamp_sent: i64, - ret_chat_id: *mut uint32_t, - ret_msg_id: *mut uint32_t, + ret_chat_id: *mut u32, + ret_msg_id: *mut u32, ) -> libc::c_int { - let chat_type: libc::c_int; - let msg_state: libc::c_int; - let mdn_already_in_table: libc::c_int; - let ist_cnt: libc::c_int; - let soll_cnt: libc::c_int; - let mut read_by_all: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(from_id <= 9i32 as libc::c_uint + if from_id <= 9 || rfc724_mid.is_null() || ret_chat_id.is_null() || ret_msg_id.is_null() - || *ret_chat_id != 0i32 as libc::c_uint - || *ret_msg_id != 0i32 as libc::c_uint) + || *ret_chat_id != 0 + || *ret_msg_id != 0 { - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT m.id, c.id, c.type, m.state FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id WHERE rfc724_mid=? AND from_id=1 ORDER BY m.id;\x00" - as *const u8 as *const libc::c_char - ); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - if !(sqlite3_step(stmt) != 100i32) { - *ret_msg_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - *ret_chat_id = sqlite3_column_int(stmt, 1i32) as uint32_t; - chat_type = sqlite3_column_int(stmt, 2i32); - msg_state = sqlite3_column_int(stmt, 3i32); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if !(msg_state != 18i32 && msg_state != 20i32 && msg_state != 26i32) { - /* eg. already marked as MDNS_RCVD. however, it is importent, that the message ID is set above as this will allow the caller eg. to move the message away */ - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT contact_id FROM msgs_mdns WHERE msg_id=? AND contact_id=?;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, *ret_msg_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, from_id as libc::c_int); - mdn_already_in_table = if sqlite3_step(stmt) == 100i32 { - 1i32 - } else { - 0i32 - }; - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - if 0 == mdn_already_in_table { - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO msgs_mdns (msg_id, contact_id, timestamp_sent) VALUES (?, ?, ?);\x00" - as *const u8 as - *const libc::c_char); - sqlite3_bind_int(stmt, 1i32, *ret_msg_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, from_id as libc::c_int); - sqlite3_bind_int64(stmt, 3i32, timestamp_sent as sqlite3_int64); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt - } - // Normal chat? that's quite easy. - if chat_type == 100i32 { - dc_update_msg_state(context, *ret_msg_id, 28i32); - read_by_all = 1i32 - } else { - /* send event about new state */ - stmt = dc_sqlite3_prepare( + return 0; + } + + let mut read_by_all = 0; + + if let Ok((msg_id, chat_id, chat_type, msg_state)) = context.sql.query_row( + "SELECT m.id, c.id, c.type, m.state FROM msgs m \ + LEFT JOIN chats c ON m.chat_id=c.id \ + WHERE rfc724_mid=? AND from_id=1 \ + ORDER BY m.id;", + params![as_str(rfc724_mid)], + |row| { + Ok(( + row.get::<_, i32>(0)?, + row.get::<_, i32>(1)?, + row.get::<_, i32>(2)?, + row.get::<_, i32>(3)?, + )) + }, + ) { + *ret_msg_id = msg_id as u32; + *ret_chat_id = chat_id as u32; + + if !(msg_state != 18 && msg_state != 20 && msg_state != 26) { + /* eg. already marked as MDNS_RCVD. however, it is importent, that the message ID is set above as this will allow the caller eg. to move the message away */ + let mdn_already_in_table = context + .sql + .exists( + "SELECT contact_id FROM msgs_mdns WHERE msg_id=? AND contact_id=?;", + params![*ret_msg_id as i32, from_id as i32,], + ) + .unwrap_or_default(); + + if !mdn_already_in_table { + context.sql.execute( + "INSERT INTO msgs_mdns (msg_id, contact_id, timestamp_sent) VALUES (?, ?, ?);", + params![*ret_msg_id as i32, from_id as i32, timestamp_sent], + ).unwrap(); // TODO: better error handling + } + + // Normal chat? that's quite easy. + if chat_type == 100 { + dc_update_msg_state(context, *ret_msg_id, 28); + read_by_all = 1; + } else { + /* send event about new state */ + let ist_cnt: i32 = context + .sql + .query_row_col( context, - &context.sql, - b"SELECT COUNT(*) FROM msgs_mdns WHERE msg_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, *ret_msg_id as libc::c_int); - if !(sqlite3_step(stmt) != 100i32) { - /* error */ - ist_cnt = sqlite3_column_int(stmt, 0i32); - sqlite3_finalize(stmt); - stmt = 0 as *mut sqlite3_stmt; - /* - Groupsize: Min. MDNs + "SELECT COUNT(*) FROM msgs_mdns WHERE msg_id=?;", + params![*ret_msg_id as i32], + 0, + ) + .unwrap_or_default(); + /* + Groupsize: Min. MDNs - 1 S n/a - 2 SR 1 - 3 SRR 2 - 4 SRRR 2 - 5 SRRRR 3 - 6 SRRRRR 3 + 1 S n/a + 2 SR 1 + 3 SRR 2 + 4 SRRR 2 + 5 SRRRR 3 + 6 SRRRRR 3 - (S=Sender, R=Recipient) - */ - /*for rounding, SELF is already included!*/ - soll_cnt = (dc_get_chat_contact_cnt(context, *ret_chat_id) + 1i32) / 2i32; - if !(ist_cnt < soll_cnt) { - /* wait for more receipts */ - dc_update_msg_state(context, *ret_msg_id, 28i32); - read_by_all = 1i32 - } - } + (S=Sender, R=Recipient) + */ + // for rounding, SELF is already included! + let soll_cnt = (dc_get_chat_contact_cnt(context, *ret_chat_id) + 1) / 2; + if !(ist_cnt < soll_cnt) { + /* wait for more receipts */ + dc_update_msg_state(context, *ret_msg_id, 28); + read_by_all = 1; } } } } - sqlite3_finalize(stmt); read_by_all } /* the number of messages assigned to real chat (!=deaddrop, !=trash) */ -pub unsafe fn dc_get_real_msg_cnt(context: &Context) -> size_t { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut ret: size_t = 0i32 as size_t; - if (*&context.sql).is_open() { - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id>9 AND m.chat_id>9 AND c.blocked=0;\x00" - as *const u8 as *const libc::c_char); - if sqlite3_step(stmt) != 100i32 { - dc_sqlite3_log_error( - context, - &context.sql, - b"dc_get_real_msg_cnt() failed.\x00" as *const u8 as *const libc::c_char, - ); - } else { - ret = sqlite3_column_int(stmt, 0i32) as size_t +pub fn dc_get_real_msg_cnt(context: &Context) -> libc::c_int { + match context.sql.query_row( + "SELECT COUNT(*) \ + FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ + WHERE m.id>9 AND m.chat_id>9 AND c.blocked=0;", + rusqlite::NO_PARAMS, + |row| row.get(0), + ) { + Ok(res) => res, + Err(err) => { + error!(context, 0, "dc_get_real_msg_cnt() failed. {}", err); + 0 } } - sqlite3_finalize(stmt); - - ret } -pub unsafe fn dc_get_deaddrop_msg_cnt(context: &Context) -> size_t { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let mut ret: size_t = 0i32 as size_t; - if context.sql.is_open() { - stmt = - dc_sqlite3_prepare(context, &context.sql, - b"SELECT COUNT(*) FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE c.blocked=2;\x00" - as *const u8 as *const libc::c_char); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) as size_t +pub fn dc_get_deaddrop_msg_cnt(context: &Context) -> size_t { + match context.sql.query_row( + "SELECT COUNT(*) \ + FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ + WHERE c.blocked=2;", + rusqlite::NO_PARAMS, + |row| row.get::<_, isize>(0), + ) { + Ok(res) => res as size_t, + Err(err) => { + error!(context, 0, "dc_get_deaddrop_msg_cnt() failed. {}", err); + 0 } } - sqlite3_finalize(stmt); - - ret } -pub unsafe fn dc_rfc724_mid_cnt(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { +pub fn dc_rfc724_mid_cnt(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { /* check the number of messages with the same rfc724_mid */ - let mut ret: libc::c_int = 0i32; - let mut stmt = 0 as *mut sqlite3_stmt; - if context.sql.is_open() { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT COUNT(*) FROM msgs WHERE rfc724_mid=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - if !(sqlite3_step(stmt) != 100i32) { - ret = sqlite3_column_int(stmt, 0i32) + match context.sql.query_row( + "SELECT COUNT(*) FROM msgs WHERE rfc724_mid=?;", + &[as_str(rfc724_mid)], + |row| row.get(0), + ) { + Ok(res) => res, + Err(err) => { + error!(context, 0, "dc_get_rfc724_mid_cnt() failed. {}", err); + 0 } } - sqlite3_finalize(stmt); - - ret } -pub unsafe fn dc_rfc724_mid_exists( +pub fn dc_rfc724_mid_exists( context: &Context, rfc724_mid: *const libc::c_char, ret_server_folder: *mut *mut libc::c_char, ret_server_uid: *mut uint32_t, ) -> uint32_t { - let mut ret: uint32_t = 0i32 as uint32_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !(rfc724_mid.is_null() || *rfc724_mid.offset(0isize) as libc::c_int == 0i32) { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT server_folder, server_uid, id FROM msgs WHERE rfc724_mid=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - if sqlite3_step(stmt) != 100i32 { + if rfc724_mid.is_null() || unsafe { *rfc724_mid.offset(0) as libc::c_int } == 0 { + return 0; + } + match context.sql.query_row( + "SELECT server_folder, server_uid, id FROM msgs WHERE rfc724_mid=?", + &[as_str(rfc724_mid)], + |row| { if !ret_server_folder.is_null() { - *ret_server_folder = 0 as *mut libc::c_char + unsafe { + *ret_server_folder = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()) + }; } if !ret_server_uid.is_null() { - *ret_server_uid = 0i32 as uint32_t + unsafe { *ret_server_uid = row.get(1)? }; } - } else { + row.get(2) + }, + ) { + Ok(res) => res, + Err(_err) => { if !ret_server_folder.is_null() { - *ret_server_folder = dc_strdup(sqlite3_column_text(stmt, 0i32) as *mut libc::c_char) + unsafe { *ret_server_folder = 0 as *mut libc::c_char }; } if !ret_server_uid.is_null() { - *ret_server_uid = sqlite3_column_int(stmt, 1i32) as uint32_t + unsafe { *ret_server_uid = 0 }; } - ret = sqlite3_column_int(stmt, 2i32) as uint32_t + + 0 } } - sqlite3_finalize(stmt); - - ret } -pub unsafe fn dc_update_server_uid( +pub fn dc_update_server_uid( context: &Context, rfc724_mid: *const libc::c_char, - server_folder: *const libc::c_char, + server_folder: impl AsRef, server_uid: uint32_t, ) { - let stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"UPDATE msgs SET server_folder=?, server_uid=? WHERE rfc724_mid=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, server_folder, -1i32, None); - sqlite3_bind_int(stmt, 2i32, server_uid as libc::c_int); - sqlite3_bind_text(stmt, 3i32, rfc724_mid, -1i32, None); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + match context.sql.execute( + "UPDATE msgs SET server_folder=?, server_uid=? WHERE rfc724_mid=?;", + params![server_folder.as_ref(), server_uid, as_str(rfc724_mid)], + ) { + Ok(_) => {} + Err(err) => { + warn!(context, 0, "msg: failed to update server_uid: {}", err); + } + } } #[cfg(test)] diff --git a/src/dc_qr.rs b/src/dc_qr.rs index 0aafdae72..1b72ab2d6 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -1,7 +1,6 @@ use crate::context::Context; use crate::dc_chat::*; use crate::dc_contact::*; -use crate::dc_log::*; use crate::dc_lot::*; use crate::dc_param::*; use crate::dc_strencode::*; @@ -38,12 +37,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char; (*qr_parsed).state = 0i32; if !qr.is_null() { - dc_log_info( - context, - 0i32, - b"Scanned QR code: %s\x00" as *const u8 as *const libc::c_char, - qr, - ); + info!(context, 0, "Scanned QR code: {}", as_str(qr),); /* split parameters from the qr code ------------------------------------ */ if strncasecmp( diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 1b796c08c..1483e74d6 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -14,17 +14,16 @@ use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_location::*; -use crate::dc_log::*; use crate::dc_mimeparser::*; use crate::dc_move::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_securejoin::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::peerstate::*; +use crate::sql; use crate::types::*; use crate::x::*; @@ -32,56 +31,58 @@ pub unsafe fn dc_receive_imf( context: &Context, imf_raw_not_terminated: *const libc::c_char, imf_raw_bytes: size_t, - server_folder: *const libc::c_char, + server_folder: impl AsRef, server_uid: uint32_t, flags: uint32_t, ) { let mut current_block: u64; /* the function returns the number of created messages in the database */ - let mut incoming: libc::c_int = 1i32; - let mut incoming_origin: libc::c_int = 0i32; - let mut to_self: libc::c_int = 0i32; - let mut from_id: uint32_t = 0i32 as uint32_t; - let mut from_id_blocked: libc::c_int = 0i32; - let mut to_id: uint32_t = 0i32 as uint32_t; - let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut chat_id_blocked: libc::c_int = 0i32; + let mut incoming: libc::c_int = 1; + let mut incoming_origin: libc::c_int = 0; + let mut to_self: libc::c_int = 0; + let mut from_id: uint32_t = 0 as uint32_t; + let mut from_id_blocked: libc::c_int = 0; + let mut to_id: uint32_t = 0 as uint32_t; + let mut chat_id: uint32_t = 0 as uint32_t; + let mut chat_id_blocked: libc::c_int = 0; let mut state: libc::c_int; - let mut hidden: libc::c_int = 0i32; + let mut hidden: libc::c_int = 0; let mut msgrmsg: libc::c_int; - let mut add_delete_job: libc::c_int = 0i32; - let mut insert_msg_id: uint32_t = 0i32 as uint32_t; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + let mut add_delete_job: libc::c_int = 0; + let mut insert_msg_id: uint32_t = 0 as uint32_t; + let mut i: size_t; let mut icnt: size_t; /* Message-ID from the header */ - let mut rfc724_mid: *mut libc::c_char = 0 as *mut libc::c_char; + let mut rfc724_mid = 0 as *mut libc::c_char; let mut sort_timestamp = 0; let mut sent_timestamp = 0; let mut rcvd_timestamp = 0; let mut field: *const mailimf_field; - let mut mime_in_reply_to: *mut libc::c_char = 0 as *mut libc::c_char; - let mut mime_references: *mut libc::c_char = 0 as *mut libc::c_char; + let mut mime_in_reply_to = 0 as *mut libc::c_char; + let mut mime_references = 0 as *mut libc::c_char; let mut created_db_entries = Vec::new(); let mut create_event_to_send = Some(Event::MSGS_CHANGED); let mut rr_event_to_send = Vec::new(); - let mut txt_raw: *mut libc::c_char = 0 as *mut libc::c_char; + let mut txt_raw = 0 as *mut libc::c_char; // XXX converting the below "to_ids" to a Vec quickly leads to lots of changes // so we keep it as a dc_array for now - let to_ids: *mut dc_array_t = dc_array_new(16); + let to_ids = dc_array_new(16); assert!(!to_ids.is_null()); + info!( context, 0, "Receiving message {}/{}...", - if !server_folder.is_null() { - as_str(server_folder) + if !server_folder.as_ref().is_empty() { + server_folder.as_ref() } else { "?" }, server_uid, ); + let mut mime_parser = dc_mimeparser_new(context); dc_mimeparser_parse(&mut mime_parser, imf_raw_not_terminated, imf_raw_bytes); if mime_parser.header.is_empty() { @@ -100,21 +101,21 @@ pub unsafe fn dc_receive_imf( let fld_from: *mut mailimf_from = (*field).fld_data.fld_from; if !fld_from.is_null() { let mut check_self: libc::c_int = 0; - let from_list: *mut dc_array_t = dc_array_new(16i32 as size_t); + let from_list: *mut dc_array_t = dc_array_new(16 as size_t); dc_add_or_lookup_contacts_by_mailbox_list( context, (*fld_from).frm_mb_list, - 0x10i32, + 0x10, from_list, &mut check_self, ); if 0 != check_self { - incoming = 0i32; + incoming = 0; if 0 != dc_mimeparser_sender_equals_recipient(&mime_parser) { - from_id = 1i32 as uint32_t + from_id = 1 as uint32_t } } else if dc_array_get_cnt(from_list) >= 1 { - from_id = dc_array_get_id(from_list, 0i32 as size_t); + from_id = dc_array_get_id(from_list, 0 as size_t); incoming_origin = dc_get_contact_origin(context, from_id, &mut from_id_blocked) } dc_array_unref(from_list); @@ -128,11 +129,11 @@ pub unsafe fn dc_receive_imf( context, (*fld_to).to_addr_list, if 0 == incoming { - 0x4000i32 - } else if incoming_origin >= 0x100i32 { - 0x400i32 + 0x4000 + } else if incoming_origin >= 0x100 { + 0x400 } else { - 0x40i32 + 0x40 }, to_ids, &mut to_self, @@ -148,11 +149,11 @@ pub unsafe fn dc_receive_imf( context, (*fld_cc).cc_addr_list, if 0 == incoming { - 0x2000i32 - } else if incoming_origin >= 0x100i32 { - 0x200i32 + 0x2000 + } else if incoming_origin >= 0x100 { + 0x200 } else { - 0x20i32 + 0x20 }, to_ids, 0 as *mut libc::c_int, @@ -169,11 +170,7 @@ pub unsafe fn dc_receive_imf( if rfc724_mid.is_null() { rfc724_mid = dc_create_incoming_rfc724_mid(sent_timestamp, from_id, to_ids); if rfc724_mid.is_null() { - dc_log_info( - context, - 0i32, - b"Cannot create Message-ID.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Cannot create Message-ID.",); current_block = 16282941964262048061; } else { current_block = 777662472977924419; @@ -187,57 +184,56 @@ pub unsafe fn dc_receive_imf( /* check, if the mail is already in our database - if so, just update the folder/uid (if the mail was moved around) and finish. (we may get a mail twice eg. if it is moved between folders. make sure, this check is done eg. before securejoin-processing) */ let mut old_server_folder: *mut libc::c_char = 0 as *mut libc::c_char; - let mut old_server_uid: uint32_t = 0i32 as uint32_t; + let mut old_server_uid: uint32_t = 0 as uint32_t; if 0 != dc_rfc724_mid_exists( context, rfc724_mid, &mut old_server_folder, &mut old_server_uid, ) { - if strcmp(old_server_folder, server_folder) != 0i32 + if as_str(old_server_folder) != server_folder.as_ref() || old_server_uid != server_uid { - dc_update_server_uid(context, rfc724_mid, server_folder, server_uid); + dc_update_server_uid( + context, + rfc724_mid, + server_folder.as_ref(), + server_uid, + ); } free(old_server_folder as *mut libc::c_void); - dc_log_info( - context, - 0i32, - b"Message already in DB.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Message already in DB."); current_block = 16282941964262048061; } else { msgrmsg = mime_parser.is_send_by_messenger; - if msgrmsg == 0i32 + if msgrmsg == 0 && 0 != dc_is_reply_to_messenger_message(context, &mime_parser) { - msgrmsg = 2i32 + msgrmsg = 2 } /* incoming non-chat messages may be discarded; maybe this can be optimized later, by checking the state before the message body is downloaded */ - let mut allow_creation: libc::c_int = 1i32; - if msgrmsg == 0i32 { - let show_emails: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"show_emails\x00" as *const u8 as *const libc::c_char, - 0i32, - ); - if show_emails == 0i32 { - chat_id = 3i32 as uint32_t; - allow_creation = 0i32 - } else if show_emails == 1i32 { - allow_creation = 0i32 + let mut allow_creation: libc::c_int = 1; + if msgrmsg == 0 { + let show_emails = context + .sql + .get_config_int(context, "show_emails") + .unwrap_or_default(); + if show_emails == 0 { + chat_id = 3; + allow_creation = 0 + } else if show_emails == 1 { + allow_creation = 0 } } if 0 != incoming { state = if 0 != flags & 0x1 { 16 } else { 10 }; to_id = 1 as uint32_t; if !dc_mimeparser_lookup_field(&mime_parser, "Secure-Join").is_null() { - msgrmsg = 1i32; - chat_id = 0i32 as uint32_t; - allow_creation = 1i32; + msgrmsg = 1; + chat_id = 0 as uint32_t; + allow_creation = 1; let handshake: libc::c_int = dc_handle_securejoin_handshake(context, &mime_parser, from_id); if 0 != handshake & 0x2 { @@ -246,22 +242,22 @@ pub unsafe fn dc_receive_imf( state = 16 } } - let mut test_normal_chat_id: uint32_t = 0i32 as uint32_t; - let mut test_normal_chat_id_blocked: libc::c_int = 0i32; + let mut test_normal_chat_id: uint32_t = 0 as uint32_t; + let mut test_normal_chat_id_blocked: libc::c_int = 0; dc_lookup_real_nchat_by_contact_id( context, from_id, &mut test_normal_chat_id, &mut test_normal_chat_id_blocked, ); - if chat_id == 0i32 as libc::c_uint { + if chat_id == 0 as libc::c_uint { let create_blocked: libc::c_int = if 0 != test_normal_chat_id - && test_normal_chat_id_blocked == 0i32 - || incoming_origin >= 0x7fffffffi32 + && test_normal_chat_id_blocked == 0 + || incoming_origin >= 0x7fffffff { - 0i32 + 0 } else { - 2i32 + 2 }; create_or_lookup_group( context, @@ -275,27 +271,24 @@ pub unsafe fn dc_receive_imf( ); if 0 != chat_id && 0 != chat_id_blocked && 0 == create_blocked { dc_unblock_chat(context, chat_id); - chat_id_blocked = 0i32 + chat_id_blocked = 0 } } - if chat_id == 0i32 as libc::c_uint { + if chat_id == 0 as libc::c_uint { if 0 != dc_mimeparser_is_mailinglist_message(&mime_parser) { - chat_id = 3i32 as uint32_t; - dc_log_info( + chat_id = 3 as uint32_t; + info!( context, - 0i32, - b"Message belongs to a mailing list and is ignored.\x00" - as *const u8 - as *const libc::c_char, + 0, "Message belongs to a mailing list and is ignored.", ); } } - if chat_id == 0i32 as libc::c_uint { + if chat_id == 0 as libc::c_uint { let create_blocked_0: libc::c_int = - if incoming_origin >= 0x7fffffffi32 || from_id == to_id { - 0i32 + if incoming_origin >= 0x7fffffff || from_id == to_id { + 0 } else { - 2i32 + 2 }; if 0 != test_normal_chat_id { chat_id = test_normal_chat_id; @@ -312,42 +305,43 @@ pub unsafe fn dc_receive_imf( if 0 != chat_id && 0 != chat_id_blocked { if 0 == create_blocked_0 { dc_unblock_chat(context, chat_id); - chat_id_blocked = 0i32 + chat_id_blocked = 0 } else if 0 != dc_is_reply_to_known_message(context, &mime_parser) { - dc_scaleup_contact_origin(context, from_id, 0x100i32); - dc_log_info(context, 0i32, - b"Message is a reply to a known message, mark sender as known.\x00" - as *const u8 as - *const libc::c_char); - incoming_origin = if incoming_origin > 0x100i32 { + dc_scaleup_contact_origin(context, from_id, 0x100); + info!( + context, + 0, + "Message is a reply to a known message, mark sender as known.", + ); + incoming_origin = if incoming_origin > 0x100 { incoming_origin } else { - 0x100i32 + 0x100 } } } } - if chat_id == 0i32 as libc::c_uint { - chat_id = 3i32 as uint32_t + if chat_id == 0 as libc::c_uint { + chat_id = 3 as uint32_t } - if 0 != chat_id_blocked && state == 10i32 { - if incoming_origin < 0x100i32 && msgrmsg == 0i32 { - state = 13i32 + if 0 != chat_id_blocked && state == 10 { + if incoming_origin < 0x100 && msgrmsg == 0 { + state = 13 } } } else { - state = 26i32; - from_id = 1i32 as uint32_t; + state = 26; + from_id = 1 as uint32_t; if dc_array_get_cnt(to_ids) >= 1 { - to_id = dc_array_get_id(to_ids, 0i32 as size_t); - if chat_id == 0i32 as libc::c_uint { + to_id = dc_array_get_id(to_ids, 0 as size_t); + if chat_id == 0 as libc::c_uint { create_or_lookup_group( context, &mut mime_parser, allow_creation, - 0i32, + 0, from_id as int32_t, to_ids, &mut chat_id, @@ -355,15 +349,15 @@ pub unsafe fn dc_receive_imf( ); if 0 != chat_id && 0 != chat_id_blocked { dc_unblock_chat(context, chat_id); - chat_id_blocked = 0i32 + chat_id_blocked = 0 } } - if chat_id == 0i32 as libc::c_uint && 0 != allow_creation { + if chat_id == 0 as libc::c_uint && 0 != allow_creation { let create_blocked_1: libc::c_int = if 0 != msgrmsg && !dc_is_contact_blocked(context, to_id) { - 0i32 + 0 } else { - 2i32 + 2 }; dc_create_or_lookup_nchat_by_contact_id( context, @@ -375,27 +369,27 @@ pub unsafe fn dc_receive_imf( if 0 != chat_id && 0 != chat_id_blocked && 0 == create_blocked_1 { dc_unblock_chat(context, chat_id); - chat_id_blocked = 0i32 + chat_id_blocked = 0 } } } - if chat_id == 0i32 as libc::c_uint { + if chat_id == 0 as libc::c_uint { if dc_array_get_cnt(to_ids) == 0 && 0 != to_self { dc_create_or_lookup_nchat_by_contact_id( context, - 1i32 as uint32_t, - 0i32, + 1 as uint32_t, + 0, &mut chat_id, &mut chat_id_blocked, ); if 0 != chat_id && 0 != chat_id_blocked { dc_unblock_chat(context, chat_id); - chat_id_blocked = 0i32 + chat_id_blocked = 0 } } } - if chat_id == 0i32 as libc::c_uint { - chat_id = 3i32 as uint32_t + if chat_id == 0 as libc::c_uint { + chat_id = 3 as uint32_t } } calc_timestamps( @@ -411,34 +405,10 @@ pub unsafe fn dc_receive_imf( dc_unarchive_chat(context, chat_id); // if the mime-headers should be saved, find out its size // (the mime-header ends with an empty line) - let save_mime_headers: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"save_mime_headers\x00" as *const u8 as *const libc::c_char, - 0i32, - ); - let mut header_bytes: libc::c_int = imf_raw_bytes as libc::c_int; - if 0 != save_mime_headers { - let mut p: *mut libc::c_char; - p = strstr( - imf_raw_not_terminated, - b"\r\n\r\n\x00" as *const u8 as *const libc::c_char, - ); - if !p.is_null() { - header_bytes = (p.wrapping_offset_from(imf_raw_not_terminated) + 4) - as libc::c_int - } else { - p = strstr( - imf_raw_not_terminated, - b"\n\n\x00" as *const u8 as *const libc::c_char, - ); - if !p.is_null() { - header_bytes = (p.wrapping_offset_from(imf_raw_not_terminated) - + 2) - as libc::c_int - } - } - } + let save_mime_headers = context + .sql + .get_config_int(context, "save_mime_headers") + .unwrap_or_default(); field = dc_mimeparser_lookup_field(&mime_parser, "In-Reply-To"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int @@ -466,152 +436,126 @@ pub unsafe fn dc_receive_imf( } } icnt = carray_count(mime_parser.parts) as size_t; - stmt = - dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO msgs (rfc724_mid, server_folder, server_uid, chat_id, from_id, to_id, timestamp, timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, bytes, hidden, mime_headers, mime_in_reply_to, mime_references) VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);\x00" - as *const u8 as - *const libc::c_char); - i = 0i32 as size_t; - loop { - if !(i < icnt) { - current_block = 2756754640271984560; - break; - } - let part: *mut dc_mimepart_t = - carray_get(mime_parser.parts, i as libc::c_uint) - as *mut dc_mimepart_t; - if !(0 != (*part).is_meta) { - if !mime_parser.location_kml.is_null() - && icnt == 1 - && !(*part).msg.is_null() - && (strcmp( - (*part).msg, - b"-location-\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - || *(*part).msg.offset(0isize) as libc::c_int == 0i32) - { - hidden = 1i32; - if state == 10i32 { - state = 13i32 + + context.sql.prepare( + "INSERT INTO msgs \ + (rfc724_mid, server_folder, server_uid, chat_id, from_id, to_id, timestamp, \ + timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, \ + bytes, hidden, mime_headers, mime_in_reply_to, mime_references) \ + VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);", + |mut stmt| { + let mut i = 0; + loop { + if !(i < icnt) { + current_block = 2756754640271984560; + break; } - } - if (*part).type_0 == 10i32 { - txt_raw = dc_mprintf( - b"%s\n\n%s\x00" as *const u8 as *const libc::c_char, - if !mime_parser.subject.is_null() { - mime_parser.subject + let part = carray_get(mime_parser.parts, i as libc::c_uint) as *mut dc_mimepart_t; + if !(0 != (*part).is_meta) { + if !mime_parser.location_kml.is_null() + && icnt == 1 + && !(*part).msg.is_null() + && (strcmp( + (*part).msg, + b"-location-\x00" as *const u8 as *const libc::c_char, + ) == 0 + || *(*part).msg.offset(0isize) as libc::c_int == 0) + { + hidden = 1; + if state == 10 { + state = 13 + } + } + if (*part).type_0 == 10 { + txt_raw = dc_mprintf( + b"%s\n\n%s\x00" as *const u8 as *const libc::c_char, + if !mime_parser.subject.is_null() { + mime_parser.subject + } else { + b"\x00" as *const u8 as *const libc::c_char + }, + (*part).msg_raw, + ) + } + if 0 != mime_parser.is_system_message { + dc_param_set_int( + (*part).param, + 'S' as i32, + mime_parser.is_system_message, + ); + } + + let res = stmt.execute(params![ + as_str(rfc724_mid), + server_folder.as_ref(), + server_uid as libc::c_int, + chat_id as libc::c_int, + from_id as libc::c_int, + to_id as libc::c_int, + sort_timestamp, + sent_timestamp, + rcvd_timestamp, + (*part).type_0, + state, + msgrmsg, + if !(*part).msg.is_null() { + as_str((*part).msg) + } else { + "" + }, + if !txt_raw.is_null() { + as_str(txt_raw) + } else { + "" + }, + as_str((*(*part).param).packed), + (*part).bytes, + hidden, + if 0 != save_mime_headers { + Some(to_string(imf_raw_not_terminated)) + } else { + None + }, + to_string(mime_in_reply_to), + to_string(mime_references), + ]); + + if res.is_err() { + info!(context, 0, "Cannot write DB.",); + /* i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record */ + current_block = 16282941964262048061; + break; } else { - b"\x00" as *const u8 as *const libc::c_char - }, - (*part).msg_raw, - ) - } - if 0 != mime_parser.is_system_message { - dc_param_set_int( - (*part).param, - 'S' as i32, - mime_parser.is_system_message, - ); - } - sqlite3_reset(stmt); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - sqlite3_bind_text(stmt, 2i32, server_folder, -1i32, None); - sqlite3_bind_int(stmt, 3i32, server_uid as libc::c_int); - sqlite3_bind_int(stmt, 4i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 5i32, from_id as libc::c_int); - sqlite3_bind_int(stmt, 6i32, to_id as libc::c_int); - sqlite3_bind_int64(stmt, 7i32, sort_timestamp as sqlite3_int64); - sqlite3_bind_int64(stmt, 8i32, sent_timestamp as sqlite3_int64); - sqlite3_bind_int64(stmt, 9i32, rcvd_timestamp as sqlite3_int64); - sqlite3_bind_int(stmt, 10i32, (*part).type_0); - sqlite3_bind_int(stmt, 11i32, state); - sqlite3_bind_int(stmt, 12i32, msgrmsg); - sqlite3_bind_text( - stmt, - 13i32, - if !(*part).msg.is_null() { - (*part).msg - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, - ); - sqlite3_bind_text( - stmt, - 14i32, - if !txt_raw.is_null() { - txt_raw - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - -1i32, - None, - ); - sqlite3_bind_text( - stmt, - 15i32, - (*(*part).param).packed, - -1i32, - None, - ); - sqlite3_bind_int(stmt, 16i32, (*part).bytes); - sqlite3_bind_int(stmt, 17i32, hidden); - sqlite3_bind_text( - stmt, - 18i32, - if 0 != save_mime_headers { - imf_raw_not_terminated - } else { - 0 as *const libc::c_char - }, - header_bytes, - None, - ); - sqlite3_bind_text(stmt, 19i32, mime_in_reply_to, -1i32, None); - sqlite3_bind_text(stmt, 20i32, mime_references, -1i32, None); - if sqlite3_step(stmt) != 101i32 { - dc_log_info( - context, - 0i32, - b"Cannot write DB.\x00" as *const u8 as *const libc::c_char, - ); - /* i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record */ - current_block = 16282941964262048061; - break; - } else { - free(txt_raw as *mut libc::c_void); - txt_raw = 0 as *mut libc::c_char; - insert_msg_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"msgs\x00" as *const u8 as *const libc::c_char, - b"rfc724_mid\x00" as *const u8 as *const libc::c_char, - rfc724_mid, - ); - created_db_entries - .push((chat_id as usize, insert_msg_id as usize)) + free(txt_raw as *mut libc::c_void); + txt_raw = 0 as *mut libc::c_char; + insert_msg_id = sql::get_rowid( + context, + &context.sql, + "msgs", + "rfc724_mid", + as_str(rfc724_mid), + ); + created_db_entries.push((chat_id as usize, insert_msg_id as usize)); + } + } + i = i.wrapping_add(1) } + Ok(()) } - i = i.wrapping_add(1) - } + ).unwrap(); // TODO: better error handling match current_block { 16282941964262048061 => {} _ => { - dc_log_info( + info!( context, - 0i32, - b"Message has %i parts and is assigned to chat #%i.\x00" - as *const u8 - as *const libc::c_char, + 0, + "Message has {} parts and is assigned to chat #{}.", icnt, chat_id, ); - if chat_id == 3i32 as libc::c_uint { + if chat_id == 3 as libc::c_uint { create_event_to_send = None; - } else if 0 != incoming && state == 10i32 { + } else if 0 != incoming && state == 10 { if 0 != from_id_blocked { create_event_to_send = None; } else if 0 != chat_id_blocked { @@ -620,7 +564,11 @@ pub unsafe fn dc_receive_imf( create_event_to_send = Some(Event::INCOMING_MSG); } } - dc_do_heuristics_moves(context, server_folder, insert_msg_id); + dc_do_heuristics_moves( + context, + server_folder.as_ref(), + insert_msg_id, + ); current_block = 18330534242458572360; } } @@ -636,17 +584,15 @@ pub unsafe fn dc_receive_imf( match current_block { 16282941964262048061 => {} _ => { - if carray_count(mime_parser.reports) > 0i32 as libc::c_uint { - let mdns_enabled: libc::c_int = dc_sqlite3_get_config_int( - context, - &context.sql, - b"mdns_enabled\x00" as *const u8 as *const libc::c_char, - 1i32, - ); + if carray_count(mime_parser.reports) > 0 as libc::c_uint { + let mdns_enabled = context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1); icnt = carray_count(mime_parser.reports) as size_t; - i = 0i32 as size_t; + i = 0 as size_t; while i < icnt { - let mut mdn_consumed: libc::c_int = 0i32; + let mut mdn_consumed: libc::c_int = 0; let report_root: *mut mailmime = carray_get(mime_parser.reports, i as libc::c_uint) as *mut mailmime; let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter( @@ -660,8 +606,8 @@ pub unsafe fn dc_receive_imf( if strcmp( (*report_type).pa_value, b"disposition-notification\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - && (*(*report_root).mm_data.mm_multipart.mm_mp_list).count >= 2i32 + ) == 0 + && (*(*report_root).mm_data.mm_multipart.mm_mp_list).count >= 2 { if 0 != mdns_enabled { let report_data: *mut mailmime = @@ -703,11 +649,11 @@ pub unsafe fn dc_receive_imf( (*(*report_data).mm_content_type).ct_subtype, b"disposition-notification\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { let mut report_body: *const libc::c_char = 0 as *const libc::c_char; - let mut report_body_bytes: size_t = 0i32 as size_t; + let mut report_body_bytes: size_t = 0 as size_t; let mut to_mmap_string_unref: *mut libc::c_char = 0 as *mut libc::c_char; if 0 != mailmime_transfer_decode( @@ -718,7 +664,7 @@ pub unsafe fn dc_receive_imf( ) { let mut report_parsed: *mut mailmime = 0 as *mut mailmime; - let mut dummy: size_t = 0i32 as size_t; + let mut dummy: size_t = 0 as size_t; if mailmime_parse( report_body, report_body_bytes, @@ -731,13 +677,13 @@ pub unsafe fn dc_receive_imf( mailmime_find_mailimf_fields(report_parsed); if !report_fields.is_null() { let of_disposition: - *mut mailimf_optional_field = + *mut mailimf_optional_field = mailimf_find_optional_field(report_fields, b"Disposition\x00" - as - *const u8 - as - *const libc::c_char); + as + *const u8 + as + *const libc::c_char); let of_org_msgid: *mut mailimf_optional_field = mailimf_find_optional_field( report_fields, @@ -751,7 +697,7 @@ pub unsafe fn dc_receive_imf( { let mut rfc724_mid_0: *mut libc::c_char = 0 as *mut libc::c_char; - dummy = 0i32 as size_t; + dummy = 0 as size_t; if mailimf_msg_id_parse( (*of_org_msgid).fld_value, strlen((*of_org_msgid).fld_value), @@ -761,9 +707,9 @@ pub unsafe fn dc_receive_imf( && !rfc724_mid_0.is_null() { let mut chat_id_0: uint32_t = - 0i32 as uint32_t; + 0 as uint32_t; let mut msg_id: uint32_t = - 0i32 as uint32_t; + 0 as uint32_t; if 0 != dc_mdn_from_ext( context, from_id, @@ -777,7 +723,7 @@ pub unsafe fn dc_receive_imf( rr_event_to_send.push((msg_id, 0)); } mdn_consumed = (msg_id - != 0i32 as libc::c_uint) + != 0 as libc::c_uint) as libc::c_int; free(rfc724_mid_0 as *mut libc::c_void); } @@ -792,20 +738,22 @@ pub unsafe fn dc_receive_imf( } } if 0 != mime_parser.is_send_by_messenger || 0 != mdn_consumed { - let param: *mut dc_param_t = dc_param_new(); - dc_param_set(param, 'Z' as i32, server_folder); - dc_param_set_int(param, 'z' as i32, server_uid as int32_t); + let param = dc_param_new(); + dc_param_set( + param, + 'Z' as i32, + to_cstring(server_folder.as_ref()).as_ptr(), + ); + dc_param_set_int(param, 'z' as i32, server_uid as i32); if 0 != mime_parser.is_send_by_messenger - && 0 != dc_sqlite3_get_config_int( - context, - &context.sql, - b"mvbox_move\x00" as *const u8 as *const libc::c_char, - 1i32, - ) + && 0 != context + .sql + .get_config_int(context, "mvbox_move") + .unwrap_or_else(|| 1) { - dc_param_set_int(param, 'M' as i32, 1i32); + dc_param_set_int(param, 'M' as i32, 1); } - dc_job_add(context, 120i32, 0i32, (*param).packed, 0i32); + dc_job_add(context, 120, 0, (*param).packed, 0); dc_param_unref(param); } } @@ -813,7 +761,7 @@ pub unsafe fn dc_receive_imf( i = i.wrapping_add(1) } } - if !mime_parser.message_kml.is_null() && chat_id > 9i32 as libc::c_uint { + if !mime_parser.message_kml.is_null() && chat_id > 9 as libc::c_uint { let mut location_id_written = false; let mut send_event = false; @@ -841,7 +789,7 @@ pub unsafe fn dc_receive_imf( if !(*mime_parser.location_kml).addr.is_null() && !contact.is_null() && !(*contact).addr.is_null() - && strcasecmp((*contact).addr, (*mime_parser.location_kml).addr) == 0i32 + && strcasecmp((*contact).addr, (*mime_parser.location_kml).addr) == 0 { let newest_location_id = dc_save_locations( context, @@ -858,11 +806,15 @@ pub unsafe fn dc_receive_imf( dc_contact_unref(contact); } if send_event { - context.call_cb(Event::LOCATION_CHANGED, from_id as uintptr_t, 0); + context.call_cb( + Event::LOCATION_CHANGED, + from_id as uintptr_t, + 0 as uintptr_t, + ); } } - if 0 != add_delete_job && created_db_entries.len() > 0 { + if 0 != add_delete_job && !created_db_entries.is_empty() { dc_job_add( context, DC_JOB_DELETE_MSG_ON_IMAP, @@ -881,18 +833,17 @@ pub unsafe fn dc_receive_imf( dc_array_unref(to_ids); if let Some(create_event_to_send) = create_event_to_send { - for entry in &created_db_entries { - let (msg_id, insert_id) = entry; + for (msg_id, insert_id) in &created_db_entries { context.call_cb(create_event_to_send, *msg_id, *insert_id); } } - for ev in &rr_event_to_send { - let (chat_id, msg_id) = ev; + for (chat_id, msg_id) in &rr_event_to_send { context.call_cb(Event::MSG_READ, *chat_id as uintptr_t, *msg_id as uintptr_t); } + free(txt_raw as *mut libc::c_void); - sqlite3_finalize(stmt); } + /* ****************************************************************************** * Misc. Tools ******************************************************************************/ @@ -913,29 +864,25 @@ unsafe fn calc_timestamps( } *sort_timestamp = message_timestamp; if 0 != is_fresh_msg { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( + let last_msg_time: Option = context.sql.query_row_col( context, - &context.sql, - b"SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?\x00" - as *const u8 as *const libc::c_char, + "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?", + params![chat_id as i32, from_id as i32, *sort_timestamp], + 0, ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_bind_int(stmt, 2i32, from_id as libc::c_int); - sqlite3_bind_int64(stmt, 3i32, *sort_timestamp as sqlite3_int64); - if sqlite3_step(stmt) == 100i32 { - let last_msg_time = sqlite3_column_int64(stmt, 0i32) as i64; + if let Some(last_msg_time) = last_msg_time { if last_msg_time > 0 { if *sort_timestamp <= last_msg_time { - *sort_timestamp = last_msg_time + 1 + *sort_timestamp = last_msg_time + 1; } } } - sqlite3_finalize(stmt); } if *sort_timestamp >= dc_smeared_time(context) { - *sort_timestamp = dc_create_smeared_timestamp(context) - }; + *sort_timestamp = dc_create_smeared_timestamp(context); + } } + /* the function tries extracts the group-id from the message and returns the corresponding chat_id. If the chat_id is not existant, it is created. If the message contains groups commands (name, profile image, changed members), @@ -960,29 +907,27 @@ unsafe fn create_or_lookup_group( ) { let group_explicitly_left: libc::c_int; let mut current_block: u64; - let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut chat_id_blocked: libc::c_int = 0i32; - let mut chat_id_verified: libc::c_int = 0i32; + let mut chat_id: uint32_t = 0 as uint32_t; + let mut chat_id_blocked: libc::c_int = 0; + let mut chat_id_verified: libc::c_int = 0; let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt; let mut i: libc::c_int; let to_ids_cnt: libc::c_int = dc_array_get_cnt(to_ids) as libc::c_int; - let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char; - let mut recreate_member_list: libc::c_int = 0i32; - let mut send_EVENT_CHAT_MODIFIED: libc::c_int = 0i32; + let mut recreate_member_list: libc::c_int = 0; + let mut send_EVENT_CHAT_MODIFIED: libc::c_int = 0; /* pointer somewhere into mime_parser, must not be freed */ let mut X_MrRemoveFromGrp: *mut libc::c_char = 0 as *mut libc::c_char; /* pointer somewhere into mime_parser, must not be freed */ let mut X_MrAddToGrp: *mut libc::c_char = 0 as *mut libc::c_char; - let mut X_MrGrpNameChanged: libc::c_int = 0i32; + let mut X_MrGrpNameChanged: libc::c_int = 0; let mut X_MrGrpImageChanged: *const libc::c_char = 0 as *const libc::c_char; let mut better_msg: *mut libc::c_char = 0 as *mut libc::c_char; let mut failure_reason: *mut libc::c_char = 0 as *mut libc::c_char; - if mime_parser.is_system_message == 8i32 { + if mime_parser.is_system_message == 8 { better_msg = dc_stock_system_msg( context, - 64i32, + 64, 0 as *const libc::c_char, 0 as *const libc::c_char, from_id as uint32_t, @@ -1063,13 +1008,13 @@ unsafe fn create_or_lookup_group( ); if !optional_field.is_null() { X_MrRemoveFromGrp = (*optional_field).fld_value; - mime_parser.is_system_message = 5i32; + mime_parser.is_system_message = 5; let left_group: libc::c_int = (dc_lookup_contact_id_by_addr(context, X_MrRemoveFromGrp) == from_id as libc::c_uint) as libc::c_int; better_msg = dc_stock_system_msg( context, - if 0 != left_group { 19i32 } else { 18i32 }, + if 0 != left_group { 19 } else { 18 }, X_MrRemoveFromGrp, 0 as *const libc::c_char, from_id as uint32_t, @@ -1081,7 +1026,7 @@ unsafe fn create_or_lookup_group( ); if !optional_field.is_null() { X_MrAddToGrp = (*optional_field).fld_value; - mime_parser.is_system_message = 4i32; + mime_parser.is_system_message = 4; optional_field = dc_mimeparser_lookup_optional_field( mime_parser, b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char, @@ -1091,7 +1036,7 @@ unsafe fn create_or_lookup_group( } better_msg = dc_stock_system_msg( context, - 17i32, + 17, X_MrAddToGrp, 0 as *const libc::c_char, from_id as uint32_t, @@ -1102,11 +1047,11 @@ unsafe fn create_or_lookup_group( b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char, ); if !optional_field.is_null() { - X_MrGrpNameChanged = 1i32; - mime_parser.is_system_message = 2i32; + X_MrGrpNameChanged = 1; + mime_parser.is_system_message = 2; better_msg = dc_stock_system_msg( context, - 15i32, + 15, (*optional_field).fld_value, grpname, from_id as uint32_t, @@ -1118,17 +1063,17 @@ unsafe fn create_or_lookup_group( ); if !optional_field.is_null() { X_MrGrpImageChanged = (*optional_field).fld_value; - mime_parser.is_system_message = 3i32; + mime_parser.is_system_message = 3; better_msg = dc_stock_system_msg( context, if strcmp( X_MrGrpImageChanged, b"0\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - 33i32 + 33 } else { - 16i32 + 16 }, 0 as *const libc::c_char, 0 as *const libc::c_char, @@ -1145,7 +1090,7 @@ unsafe fn create_or_lookup_group( &mut chat_id_blocked, &mut chat_id_verified, ); - if chat_id != 0i32 as libc::c_uint { + if chat_id != 0 as libc::c_uint { if 0 != chat_id_verified && 0 == check_verified_properties( context, @@ -1158,32 +1103,30 @@ unsafe fn create_or_lookup_group( dc_mimeparser_repl_msg_by_error(mime_parser, failure_reason); } } - if chat_id != 0i32 as libc::c_uint + if chat_id != 0 as libc::c_uint && 0 == dc_is_contact_in_chat(context, chat_id, from_id as uint32_t) { - recreate_member_list = 1i32 + recreate_member_list = 1 } /* check if the group does not exist but should be created */ group_explicitly_left = dc_is_group_explicitly_left(context, grpid); - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if chat_id == 0i32 as libc::c_uint + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + if chat_id == 0 as libc::c_uint && 0 == dc_mimeparser_is_mailinglist_message(mime_parser) && !grpid.is_null() && !grpname.is_null() && X_MrRemoveFromGrp.is_null() && (0 == group_explicitly_left - || !X_MrAddToGrp.is_null() && dc_addr_cmp(self_addr, X_MrAddToGrp) == 0i32) + || !X_MrAddToGrp.is_null() && dc_addr_cmp(&self_addr, as_str(X_MrAddToGrp))) { /*otherwise, a pending "quit" message may pop up*/ /*re-create explicitly left groups only if ourself is re-added*/ - let mut create_verified: libc::c_int = 0i32; + let mut create_verified: libc::c_int = 0; if !dc_mimeparser_lookup_field(mime_parser, "Chat-Verified").is_null() { - create_verified = 1i32; + create_verified = 1; if 0 == check_verified_properties( context, mime_parser, @@ -1205,7 +1148,7 @@ unsafe fn create_or_lookup_group( create_verified, ); chat_id_blocked = create_blocked; - recreate_member_list = 1i32; + recreate_member_list = 1; current_block = 200744462051969938; } } else { @@ -1215,10 +1158,10 @@ unsafe fn create_or_lookup_group( 281803052766328415 => {} _ => { /* again, check chat_id */ - if chat_id <= 9i32 as libc::c_uint { - chat_id = 0i32 as uint32_t; + if chat_id <= 9 as libc::c_uint { + chat_id = 0 as uint32_t; if 0 != group_explicitly_left { - chat_id = 3i32 as uint32_t + chat_id = 3 as uint32_t } else { create_or_lookup_adhoc_group( context, @@ -1233,64 +1176,58 @@ unsafe fn create_or_lookup_group( } } else { if !X_MrAddToGrp.is_null() || !X_MrRemoveFromGrp.is_null() { - recreate_member_list = 1i32 + recreate_member_list = 1 } else if 0 != X_MrGrpNameChanged && !grpname.is_null() && strlen(grpname) < 200 { - stmt = dc_sqlite3_prepare( + if sql::execute( context, &context.sql, - b"UPDATE chats SET name=? WHERE id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, grpname, -1i32, None); - sqlite3_bind_int(stmt, 2i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); + "UPDATE chats SET name=? WHERE id=?;", + params![as_str(grpname), chat_id as i32], + ) + .is_ok() + { + context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0); + } } if !X_MrGrpImageChanged.is_null() { - let mut ok: libc::c_int = 0i32; - let mut grpimage: *mut libc::c_char = 0 as *mut libc::c_char; + let mut ok = 0; + let mut grpimage = 0 as *mut libc::c_char; if strcmp( X_MrGrpImageChanged, b"0\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + ) == 0 { - ok = 1i32 + ok = 1 } else { - let mut i_0: libc::c_int = 0i32; + let mut i_0: libc::c_int = 0; while (i_0 as libc::c_uint) < carray_count(mime_parser.parts) { let part: *mut dc_mimepart_t = carray_get(mime_parser.parts, i_0 as libc::c_uint) as *mut dc_mimepart_t; - if (*part).type_0 == 20i32 { + if (*part).type_0 == 20 { grpimage = dc_param_get( (*part).param, 'f' as i32, 0 as *const libc::c_char, ); - ok = 1i32 + ok = 1 } i_0 += 1 } } if 0 != ok { let chat: *mut Chat = dc_chat_new(context); - dc_log_info( + info!( context, - 0i32, - b"New group image set to %s.\x00" as *const u8 - as *const libc::c_char, + 0, + "New group image set to {}.", if !grpimage.is_null() { - b"DELETED\x00" as *const u8 as *const libc::c_char + "DELETED".to_string() } else { - grpimage + to_string(grpimage) }, ); dc_chat_load_from_db(chat, chat_id); @@ -1298,34 +1235,31 @@ unsafe fn create_or_lookup_group( dc_chat_update_param(chat); dc_chat_unref(chat); free(grpimage as *mut libc::c_void); - send_EVENT_CHAT_MODIFIED = 1i32 + send_EVENT_CHAT_MODIFIED = 1 } } if 0 != recreate_member_list { - let skip: *const libc::c_char = if !X_MrRemoveFromGrp.is_null() { + let skip = if !X_MrRemoveFromGrp.is_null() { X_MrRemoveFromGrp } else { 0 as *mut libc::c_char }; - stmt = dc_sqlite3_prepare( + sql::execute( context, &context.sql, - b"DELETE FROM chats_contacts WHERE chat_id=?;\x00" as *const u8 - as *const libc::c_char, + "DELETE FROM chats_contacts WHERE chat_id=?;", + params![chat_id as i32], ); - sqlite3_bind_int(stmt, 1i32, chat_id as libc::c_int); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - if skip.is_null() || dc_addr_cmp(self_addr, skip) != 0i32 { - dc_add_to_chat_contacts_table(context, chat_id, 1i32 as uint32_t); + if skip.is_null() || !dc_addr_cmp(&self_addr, as_str(skip)) { + dc_add_to_chat_contacts_table(context, chat_id, 1); } - if from_id > 9i32 { - if !dc_addr_equals_contact(context, self_addr, from_id as uint32_t) + if from_id > 9 { + if !dc_addr_equals_contact(context, &self_addr, from_id as u32) && (skip.is_null() || !dc_addr_equals_contact( context, - skip, - from_id as uint32_t, + to_string(skip), + from_id as u32, )) { dc_add_to_chat_contacts_table( @@ -1335,35 +1269,35 @@ unsafe fn create_or_lookup_group( ); } } - i = 0i32; + i = 0; while i < to_ids_cnt { - let to_id: uint32_t = dc_array_get_id(to_ids, i as size_t); - if !dc_addr_equals_contact(context, self_addr, to_id) + let to_id = dc_array_get_id(to_ids, i as size_t); + if !dc_addr_equals_contact(context, &self_addr, to_id) && (skip.is_null() - || !dc_addr_equals_contact(context, skip, to_id)) + || !dc_addr_equals_contact(context, to_string(skip), to_id)) { dc_add_to_chat_contacts_table(context, chat_id, to_id); } i += 1 } - send_EVENT_CHAT_MODIFIED = 1i32; + send_EVENT_CHAT_MODIFIED = 1; dc_reset_gossiped_timestamp(context, chat_id); } if 0 != send_EVENT_CHAT_MODIFIED { context.call_cb( Event::CHAT_MODIFIED, chat_id as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); } /* check the number of receivers - the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */ - if to_ids_cnt == 1i32 && mime_parser.is_send_by_messenger == 0i32 { + if to_ids_cnt == 1 && mime_parser.is_send_by_messenger == 0 { let is_contact_cnt: libc::c_int = dc_get_chat_contact_cnt(context, chat_id); - if is_contact_cnt > 3i32 { + if is_contact_cnt > 3 { /* to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt. So everything up to 3 is no error. */ - chat_id = 0i32 as uint32_t; + chat_id = 0 as uint32_t; create_or_lookup_adhoc_group( context, mime_parser, @@ -1384,14 +1318,14 @@ unsafe fn create_or_lookup_group( } free(grpid as *mut libc::c_void); free(grpname as *mut libc::c_void); - free(self_addr as *mut libc::c_void); + free(better_msg as *mut libc::c_void); free(failure_reason as *mut libc::c_void); if !ret_chat_id.is_null() { *ret_chat_id = chat_id } if !ret_chat_id_blocked.is_null() { - *ret_chat_id_blocked = if 0 != chat_id { chat_id_blocked } else { 0i32 } + *ret_chat_id_blocked = if 0 != chat_id { chat_id_blocked } else { 0 } }; } /* ****************************************************************************** @@ -1411,13 +1345,11 @@ unsafe fn create_or_lookup_adhoc_group( /* if we're here, no grpid was found, check there is an existing ad-hoc group matching the to-list or if we can create one */ let mut member_ids: *mut dc_array_t = 0 as *mut dc_array_t; - let mut chat_id: uint32_t = 0i32 as uint32_t; + let mut chat_id: uint32_t = 0 as uint32_t; let mut chat_id_blocked = 0; let mut i; let mut chat_ids: *mut dc_array_t = 0 as *mut dc_array_t; let mut chat_ids_str: *mut libc::c_char = 0 as *mut libc::c_char; - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char; /* build member list from the given ids */ @@ -1427,23 +1359,29 @@ unsafe fn create_or_lookup_adhoc_group( if !dc_array_search_id(member_ids, from_id as uint32_t, 0 as *mut size_t) { dc_array_add_id(member_ids, from_id as uint32_t); } - if !dc_array_search_id(member_ids, 1i32 as uint32_t, 0 as *mut size_t) { - dc_array_add_id(member_ids, 1i32 as uint32_t); + if !dc_array_search_id(member_ids, 1 as uint32_t, 0 as *mut size_t) { + dc_array_add_id(member_ids, 1 as uint32_t); } if !(dc_array_get_cnt(member_ids) < 3) { /* too few contacts given */ chat_ids = search_chat_ids_by_contact_ids(context, member_ids); if dc_array_get_cnt(chat_ids) > 0 { - chat_ids_str = - dc_array_get_string(chat_ids, b",\x00" as *const u8 as *const libc::c_char); - q3 = - sqlite3_mprintf(b"SELECT c.id, c.blocked FROM chats c LEFT JOIN msgs m ON m.chat_id=c.id WHERE c.id IN(%s) ORDER BY m.timestamp DESC, m.id DESC LIMIT 1;\x00" - as *const u8 as *const libc::c_char, - chat_ids_str); - stmt = dc_sqlite3_prepare(context, &context.sql, q3); - if sqlite3_step(stmt) == 100i32 { - chat_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - chat_id_blocked = sqlite3_column_int(stmt, 1i32); + chat_ids_str = dc_array_get_string(chat_ids, b",\x00" as *const u8 as *const _); + let res = context.sql.query_row( + format!( + "SELECT c.id, c.blocked FROM chats c \ + LEFT JOIN msgs m ON m.chat_id=c.id WHERE c.id IN({}) ORDER BY m.timestamp DESC, m.id DESC LIMIT 1;", + as_str(chat_ids_str), + ), + params![], + |row| { + Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) + } + ); + + if let Ok((id, id_blocked)) = res { + chat_id = id as u32; + chat_id_blocked = id_blocked; /* success, chat found */ current_block = 11334989263469503965; } else { @@ -1469,12 +1407,12 @@ unsafe fn create_or_lookup_adhoc_group( } else { grpname = dc_stock_str_repl_int( context, - 4i32, + 4, dc_array_get_cnt(member_ids) as libc::c_int, ) } chat_id = - create_group_record(context, grpid, grpname, create_blocked, 0i32); + create_group_record(context, grpid, grpname, create_blocked, 0); chat_id_blocked = create_blocked; i = 0; while i < dc_array_get_cnt(member_ids) { @@ -1488,7 +1426,7 @@ unsafe fn create_or_lookup_adhoc_group( context.call_cb( Event::CHAT_MODIFIED, chat_id as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); } } @@ -1501,8 +1439,7 @@ unsafe fn create_or_lookup_adhoc_group( free(chat_ids_str as *mut libc::c_void); free(grpid as *mut libc::c_void); free(grpname as *mut libc::c_void); - sqlite3_finalize(stmt); - sqlite3_free(q3 as *mut libc::c_void); + if !ret_chat_id.is_null() { *ret_chat_id = chat_id } @@ -1510,41 +1447,33 @@ unsafe fn create_or_lookup_adhoc_group( *ret_chat_id_blocked = chat_id_blocked }; } -unsafe fn create_group_record( + +fn create_group_record( context: &Context, grpid: *const libc::c_char, grpname: *const libc::c_char, create_blocked: libc::c_int, create_verified: libc::c_int, -) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( +) -> u32 { + if sql::execute( context, &context.sql, - b"INSERT INTO chats (type, name, grpid, blocked) VALUES(?, ?, ?, ?);\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int( - stmt, - 1i32, - if 0 != create_verified { 130i32 } else { 120i32 }, - ); - sqlite3_bind_text(stmt, 2i32, grpname, -1i32, None); - sqlite3_bind_text(stmt, 3i32, grpid, -1i32, None); - sqlite3_bind_int(stmt, 4i32, create_blocked); - if !(sqlite3_step(stmt) != 101i32) { - chat_id = dc_sqlite3_get_rowid( - context, - &context.sql, - b"chats\x00" as *const u8 as *const libc::c_char, - b"grpid\x00" as *const u8 as *const libc::c_char, - grpid, - ) + "INSERT INTO chats (type, name, grpid, blocked) VALUES(?, ?, ?, ?);", + params![ + if 0 != create_verified { 130 } else { 120 }, + as_str(grpname), + as_str(grpid), + create_blocked, + ], + ) + .is_err() + { + return 0; } - sqlite3_finalize(stmt); - return chat_id; + + sql::get_rowid(context, &context.sql, "chats", "grpid", as_str(grpid)) } + unsafe fn create_adhoc_grp_id(context: &Context, member_ids: *mut dc_array_t) -> *mut libc::c_char { /* algorithm: - sort normalized, lowercased, e-mail addresses alphabetically @@ -1552,52 +1481,37 @@ unsafe fn create_adhoc_grp_id(context: &Context, member_ids: *mut dc_array_t) -> - sha-256 this string (without possibly terminating null-characters) - encode the first 64 bits of the sha-256 output as lowercase hex (results in 16 characters from the set [0-9a-f]) */ - let member_addrs: *mut dc_array_t = dc_array_new(23i32 as size_t); - let member_ids_str: *mut libc::c_char = - dc_array_get_string(member_ids, b",\x00" as *const u8 as *const libc::c_char); - let stmt: *mut sqlite3_stmt; - let q3: *mut libc::c_char; - let mut addr: *mut libc::c_char; - let iCnt: libc::c_int; - let mut member_cs = String::new(); + let member_ids_str = dc_array_get_string(member_ids, b",\x00" as *const u8 as *const _); + let member_cs = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_else(|| "no-self".to_string()) + .to_lowercase(); - q3 = sqlite3_mprintf( - b"SELECT addr FROM contacts WHERE id IN(%s) AND id!=1\x00" as *const u8 - as *const libc::c_char, - member_ids_str, - ); - stmt = dc_sqlite3_prepare(context, &context.sql, q3); - addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"no-self\x00" as *const u8 as *const libc::c_char, - ); - dc_strlower_in_place(addr); - dc_array_add_ptr(member_addrs, addr as *mut libc::c_void); - while sqlite3_step(stmt) == 100i32 { - addr = dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char); - dc_strlower_in_place(addr); - dc_array_add_ptr(member_addrs, addr as *mut libc::c_void); - } - dc_array_sort_strings(member_addrs); - iCnt = dc_array_get_cnt(member_addrs) as libc::c_int; - - for i in 0..iCnt { - if 0 != i { - member_cs += ","; - } - member_cs += &to_string(dc_array_get_ptr(member_addrs, i as size_t) as *const libc::c_char); - } - - let ret = hex_hash(&member_cs); - dc_array_free_ptr(member_addrs); - dc_array_unref(member_addrs); + let members = context + .sql + .query_map( + format!( + "SELECT addr FROM contacts WHERE id IN({}) AND id!=1", + as_str(member_ids_str) + ), + params![], + |row| row.get::<_, String>(0), + |rows| { + let mut addrs = rows.collect::, _>>()?; + addrs.sort(); + let mut acc = member_cs.clone(); + for addr in &addrs { + acc += ","; + acc += &addr.to_lowercase(); + } + Ok(acc) + }, + ) + .unwrap_or_else(|_| member_cs); free(member_ids_str as *mut libc::c_void); - sqlite3_finalize(stmt); - sqlite3_free(q3 as *mut libc::c_void); - ret as *mut _ + hex_hash(&members) as *mut _ } fn hex_hash(s: impl AsRef) -> *const libc::c_char { @@ -1614,20 +1528,18 @@ unsafe fn search_chat_ids_by_contact_ids( unsorted_contact_ids: *const dc_array_t, ) -> *mut dc_array_t { /* searches chat_id's by the given contact IDs, may return zero, one or more chat_id's */ - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - let contact_ids: *mut dc_array_t = dc_array_new(23i32 as size_t); + let contact_ids: *mut dc_array_t = dc_array_new(23 as size_t); let mut contact_ids_str: *mut libc::c_char = 0 as *mut libc::c_char; - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - let chat_ids: *mut dc_array_t = dc_array_new(23i32 as size_t); + let chat_ids: *mut dc_array_t = dc_array_new(23 as size_t); /* copy array, remove duplicates and SELF, sort by ID */ let mut i: libc::c_int; let iCnt: libc::c_int = dc_array_get_cnt(unsorted_contact_ids) as libc::c_int; - if !(iCnt <= 0i32) { - i = 0i32; + if !(iCnt <= 0) { + i = 0; while i < iCnt { let curr_id: uint32_t = dc_array_get_id(unsorted_contact_ids, i as size_t); - if curr_id != 1i32 as libc::c_uint + if curr_id != 1 as libc::c_uint && !dc_array_search_id(contact_ids, curr_id, 0 as *mut size_t) { dc_array_add_id(contact_ids, curr_id); @@ -1638,45 +1550,50 @@ unsafe fn search_chat_ids_by_contact_ids( dc_array_sort_ids(contact_ids); contact_ids_str = dc_array_get_string(contact_ids, b",\x00" as *const u8 as *const libc::c_char); - q3 = - sqlite3_mprintf(b"SELECT DISTINCT cc.chat_id, cc.contact_id FROM chats_contacts cc LEFT JOIN chats c ON c.id=cc.chat_id WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN(%s)) AND c.type=120 AND cc.contact_id!=1 ORDER BY cc.chat_id, cc.contact_id;\x00" - as *const u8 as *const libc::c_char, - contact_ids_str); - stmt = dc_sqlite3_prepare(context, &context.sql, q3); - let mut last_chat_id = 0; - let mut matches = 0; - let mut mismatches = 0; - while sqlite3_step(stmt) == 100 { - let chat_id: uint32_t = sqlite3_column_int(stmt, 0i32) as uint32_t; - let contact_id: uint32_t = sqlite3_column_int(stmt, 1i32) as uint32_t; - if chat_id != last_chat_id { - if matches == dc_array_get_cnt(contact_ids) - && mismatches == 0i32 as libc::c_uint - { + + context.sql.query_map( + format!( + "SELECT DISTINCT cc.chat_id, cc.contact_id FROM chats_contacts cc LEFT JOIN chats c ON c.id=cc.chat_id WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({})) AND c.type=120 AND cc.contact_id!=1 ORDER BY cc.chat_id, cc.contact_id;", + as_str(contact_ids_str) + ), + params![], + |row| Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)), + |rows| { + let mut last_chat_id = 0; + let mut matches = 0; + let mut mismatches = 0; + + for row in rows { + let (chat_id, contact_id) = row?; + if chat_id as u32 != last_chat_id { + if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { + dc_array_add_id(chat_ids, last_chat_id); + } + last_chat_id = chat_id as u32; + matches = 0; + mismatches = 0; + } + if contact_id as u32 == dc_array_get_id(contact_ids, matches as size_t) { + matches += 1; + } else { + mismatches += 1; + } + } + + if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { dc_array_add_id(chat_ids, last_chat_id); } - last_chat_id = chat_id; - matches = 0; - mismatches = 0; + Ok(()) } - if contact_id == dc_array_get_id(contact_ids, matches as size_t) { - matches = matches.wrapping_add(1) - } else { - mismatches = mismatches.wrapping_add(1) - } - } - if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { - dc_array_add_id(chat_ids, last_chat_id); - } + ).unwrap(); // TODO: better error handling } } - - sqlite3_finalize(stmt); free(contact_ids_str as *mut libc::c_void); dc_array_unref(contact_ids); - sqlite3_free(q3 as *mut libc::c_void); - return chat_ids; + + chat_ids } + unsafe fn check_verified_properties( context: &Context, mimeparser: &dc_mimeparser_t, @@ -1684,141 +1601,125 @@ unsafe fn check_verified_properties( to_ids: *const dc_array_t, failure_reason: *mut *mut libc::c_char, ) -> libc::c_int { - let mut current_block: u64 = 0; - let mut everythings_okay: libc::c_int = 0i32; - let contact: *mut dc_contact_t = dc_contact_new(context); - let mut to_ids_str: *mut libc::c_char = 0 as *mut libc::c_char; - let mut q3: *mut libc::c_char = 0 as *mut libc::c_char; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; + let contact = dc_contact_new(context); + + let verify_fail = |reason: String| { + *failure_reason = + strdup(to_cstring(format!("{}. See \"Info\" for details.", reason)).as_ptr()); + warn!(context, 0, "{}", reason); + }; + + let cleanup = || { + dc_contact_unref(contact); + }; + if !dc_contact_load_from_db(contact, &context.sql, from_id) { - *failure_reason = dc_mprintf( - b"%s. See \"Info\" for details.\x00" as *const u8 as *const libc::c_char, - b"Internal Error; cannot load contact.\x00" as *const u8 as *const libc::c_char, - ); - dc_log_warning(context, 0i32, *failure_reason); - } else if 0 == mimeparser.e2ee_helper.encrypted { - *failure_reason = dc_mprintf( - b"%s. See \"Info\" for details.\x00" as *const u8 as *const libc::c_char, - b"This message is not encrypted.\x00" as *const u8 as *const libc::c_char, - ); - dc_log_warning(context, 0i32, *failure_reason); - } else { - // ensure, the contact is verified - // and the message is signed with a verified key of the sender. - // this check is skipped for SELF as there is no proper SELF-peerstate - // and results in group-splits otherwise. - if from_id != 1i32 as libc::c_uint { - let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); + verify_fail("Internal Error; cannot load contact".into()); + cleanup(); + return 0; + } - if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 { - *failure_reason = dc_mprintf( - b"%s. See \"Info\" for details.\x00" as *const u8 as *const libc::c_char, - b"The sender of this message is not verified.\x00" as *const u8 - as *const libc::c_char, - ); - dc_log_warning(context, 0i32, *failure_reason); - current_block = 14837890932895028253; - } else if let Some(peerstate) = peerstate { - if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) { - *failure_reason = dc_mprintf( - b"%s. See \"Info\" for details.\x00" as *const u8 as *const libc::c_char, - b"The message was sent with non-verified encryption.\x00" as *const u8 - as *const libc::c_char, - ); - dc_log_warning(context, 0i32, *failure_reason); - current_block = 14837890932895028253; - } - } else { - current_block = 15904375183555213903; - } - } else { - current_block = 15904375183555213903; + if 0 == mimeparser.e2ee_helper.encrypted { + verify_fail("This message is not encrypted".into()); + cleanup(); + return 0; + } + + // ensure, the contact is verified + // and the message is signed with a verified key of the sender. + // this check is skipped for SELF as there is no proper SELF-peerstate + // and results in group-splits otherwise. + if from_id != 1 { + let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); + + if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 { + verify_fail("The sender of this message is not verified.".into()); + cleanup(); + return 0; } - match current_block { - 14837890932895028253 => {} - _ => { - to_ids_str = - dc_array_get_string(to_ids, b",\x00" as *const u8 as *const libc::c_char); - q3 = - sqlite3_mprintf(b"SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN(%s) \x00" - as *const u8 as *const libc::c_char, - to_ids_str); - stmt = dc_sqlite3_prepare(context, &context.sql, q3); - loop { - if !(sqlite3_step(stmt) == 100i32) { - current_block = 2604890879466389055; - break; - } - let to_addr: *const libc::c_char = - sqlite3_column_text(stmt, 0i32) as *const libc::c_char; - let mut is_verified: libc::c_int = sqlite3_column_int(stmt, 1i32); - let mut peerstate = - Peerstate::from_addr(context, &context.sql, as_str(to_addr)); - if mimeparser - .e2ee_helper - .gossipped_addr - .contains(as_str(to_addr)) + if let Some(peerstate) = peerstate { + if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) { + verify_fail("The message was sent with non-verified encryption.".into()); + cleanup(); + return 0; + } + } + } + + let to_ids_str_c = dc_array_get_string(to_ids, b",\x00" as *const u8 as *const libc::c_char); + let to_ids_str = to_string(to_ids_str_c); + free(to_ids_str_c as *mut libc::c_void); + + let ok = context + .sql + .query_map( + format!( + "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ", + &to_ids_str, + ), + params![], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)), + |rows| { + for row in rows { + let (to_addr, mut is_verified) = row?; + let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); + if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() { let peerstate = peerstate.as_mut().unwrap(); + + // if we're here, we know the gossip key is verified: + // - use the gossip-key as verified-key if there is no verified-key + // - OR if the verified-key does not match public-key or gossip-key + // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, + // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) if 0 == is_verified || peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint && peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint { - dc_log_info( + info!( context, - 0i32, - b"%s has verfied %s.\x00" as *const u8 as *const libc::c_char, - (*contact).addr, + 0, + "{} has verfied {}.", + as_str((*contact).addr), to_addr, ); let fp = peerstate.gossip_key_fingerprint.clone(); if let Some(fp) = fp { peerstate.set_verified(0, &fp, 2); peerstate.save_to_db(&context.sql, false); - is_verified = 1i32; + is_verified = 1; } } } - if !(0 == is_verified) { - continue; + if 0 == is_verified { + verify_fail(format!( + "{} is not a member of this verified group", + to_addr + )); + cleanup(); + return Err(failure::format_err!("not a valid memember").into()); } - let err: *mut libc::c_char = dc_mprintf( - b"%s is not a member of this verified group.\x00" as *const u8 - as *const libc::c_char, - to_addr, - ); - *failure_reason = dc_mprintf( - b"%s. See \"Info\" for details.\x00" as *const u8 as *const libc::c_char, - err, - ); - dc_log_warning(context, 0i32, *failure_reason); - free(err as *mut libc::c_void); - current_block = 14837890932895028253; - break; } - match current_block { - 14837890932895028253 => {} - _ => everythings_okay = 1i32, - } - } - } - } - sqlite3_finalize(stmt); - dc_contact_unref(contact); - free(to_ids_str as *mut libc::c_void); - sqlite3_free(q3 as *mut libc::c_void); - everythings_okay + Ok(()) + }, + ) + .is_ok(); // TODO: Better default + + cleanup(); + + ok as libc::c_int } unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) { - if !(*better_msg).is_null() && carray_count((*mime_parser).parts) > 0i32 as libc::c_uint { + if !(*better_msg).is_null() && carray_count((*mime_parser).parts) > 0 as libc::c_uint { let mut part: *mut dc_mimepart_t = - carray_get(mime_parser.parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t; - if (*part).type_0 == 10i32 { + carray_get(mime_parser.parts, 0 as libc::c_uint) as *mut dc_mimepart_t; + if (*part).type_0 == 10 { free((*part).msg as *mut libc::c_void); (*part).msg = *better_msg; *better_msg = 0 as *mut libc::c_char @@ -1831,18 +1732,16 @@ unsafe fn dc_is_reply_to_known_message( ) -> libc::c_int { /* check if the message is a reply to a known message; the replies are identified by the Message-ID from `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in dc_chat.c) */ - let optional_field: *mut mailimf_optional_field; - optional_field = dc_mimeparser_lookup_optional_field( + let optional_field = dc_mimeparser_lookup_optional_field( mime_parser, b"Chat-Predecessor\x00" as *const u8 as *const libc::c_char, ); if !optional_field.is_null() { if 0 != is_known_rfc724_mid(context, (*optional_field).fld_value) { - return 1i32; + return 1; } } - let mut field: *mut mailimf_field; - field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); + let field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { let fld_in_reply_to: *mut mailimf_in_reply_to = (*field).fld_data.fld_in_reply_to; if !fld_in_reply_to.is_null() { @@ -1850,11 +1749,11 @@ unsafe fn dc_is_reply_to_known_message( context, (*(*field).fld_data.fld_in_reply_to).mid_list, ) { - return 1i32; + return 1; } } } - field = dc_mimeparser_lookup_field(mime_parser, "References"); + let field = dc_mimeparser_lookup_field(mime_parser, "References"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { let fld_references: *mut mailimf_references = (*field).fld_data.fld_references; if !fld_references.is_null() { @@ -1862,11 +1761,11 @@ unsafe fn dc_is_reply_to_known_message( context, (*(*field).fld_data.fld_references).mid_list, ) { - return 1i32; + return 1; } } } - return 0i32; + return 0; } unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> libc::c_int { if !mid_list.is_null() { @@ -1881,7 +1780,7 @@ unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist) 0 as *mut libc::c_void }) as *const libc::c_char, ) { - return 1i32; + return 1; } cur = if !cur.is_null() { (*cur).next @@ -1890,26 +1789,29 @@ unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist) } } } - return 0i32; + return 0; } + /* ****************************************************************************** * Check if a message is a reply to a known message (messenger or non-messenger) ******************************************************************************/ -unsafe fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { - let mut is_known: libc::c_int = 0i32; - if !rfc724_mid.is_null() { - let stmt: *mut sqlite3_stmt = - dc_sqlite3_prepare(context, &context.sql, - b"SELECT m.id FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id WHERE m.rfc724_mid=? AND m.chat_id>9 AND c.blocked=0;\x00" - as *const u8 as *const libc::c_char); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - if sqlite3_step(stmt) == 100i32 { - is_known = 1i32 - } - sqlite3_finalize(stmt); + +fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { + if rfc724_mid.is_null() { + return 0; } - return is_known; + context + .sql + .exists( + "SELECT m.id FROM msgs m \ + LEFT JOIN chats c ON m.chat_id=c.id \ + WHERE m.rfc724_mid=? \ + AND m.chat_id>9 AND c.blocked=0;", + params![as_str(rfc724_mid)], + ) + .unwrap_or_default() as libc::c_int } + unsafe fn dc_is_reply_to_messenger_message( context: &Context, mime_parser: &dc_mimeparser_t, @@ -1919,8 +1821,7 @@ unsafe fn dc_is_reply_to_messenger_message( - checks also if any of the referenced IDs are send by a messenger - it is okay, if the referenced messages are moved to trash here - no check for the Chat-* headers (function is only called if it is no messenger message itself) */ - let mut field: *mut mailimf_field; - field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); + let field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { let fld_in_reply_to: *mut mailimf_in_reply_to = (*field).fld_data.fld_in_reply_to; if !fld_in_reply_to.is_null() { @@ -1928,11 +1829,11 @@ unsafe fn dc_is_reply_to_messenger_message( context, (*(*field).fld_data.fld_in_reply_to).mid_list, ) { - return 1i32; + return 1; } } } - field = dc_mimeparser_lookup_field(mime_parser, "References"); + let field = dc_mimeparser_lookup_field(mime_parser, "References"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { let fld_references: *mut mailimf_references = (*field).fld_data.fld_references; if !fld_references.is_null() { @@ -1940,11 +1841,11 @@ unsafe fn dc_is_reply_to_messenger_message( context, (*(*field).fld_data.fld_references).mid_list, ) { - return 1i32; + return 1; } } } - return 0i32; + return 0; } unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> libc::c_int { if !mid_list.is_null() { @@ -1958,7 +1859,7 @@ unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clis 0 as *mut libc::c_void }) as *const libc::c_char, ) { - return 1i32; + return 1; } cur = if !cur.is_null() { (*cur).next @@ -1967,28 +1868,24 @@ unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clis } } } - return 0i32; + return 0; } /* ****************************************************************************** * Check if a message is a reply to any messenger message ******************************************************************************/ -unsafe fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { - let mut is_msgrmsg: libc::c_int = 0i32; - if !rfc724_mid.is_null() { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1i32, rfc724_mid, -1i32, None); - if sqlite3_step(stmt) == 100i32 { - is_msgrmsg = 1i32 - } - sqlite3_finalize(stmt); +fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int { + if rfc724_mid.is_null() { + return 0; } - return is_msgrmsg; + context + .sql + .exists( + "SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;", + params![as_str(rfc724_mid)], + ) + .unwrap_or_default() as libc::c_int } + unsafe fn dc_add_or_lookup_contacts_by_address_list( context: &Context, adr_list: *const mailimf_address_list, @@ -2085,35 +1982,34 @@ unsafe fn add_or_lookup_contact_by_addr( mut check_self: *mut libc::c_int, ) { /* is addr_spec equal to SELF? */ - let mut dummy: libc::c_int = 0i32; + let mut dummy: libc::c_int = 0; if check_self.is_null() { check_self = &mut dummy } if addr_spec.is_null() { return; } - *check_self = 0i32; - let self_addr: *mut libc::c_char = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - if dc_addr_cmp(self_addr, addr_spec) == 0i32 { - *check_self = 1i32 + *check_self = 0; + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + + if dc_addr_cmp(self_addr, as_str(addr_spec)) { + *check_self = 1; } - free(self_addr as *mut libc::c_void); + if 0 != *check_self { return; } /* add addr_spec if missing, update otherwise */ - let mut display_name_dec: *mut libc::c_char = 0 as *mut libc::c_char; + let mut display_name_dec = 0 as *mut libc::c_char; if !display_name_enc.is_null() { display_name_dec = dc_decode_header_words(display_name_enc); dc_normalize_name(display_name_dec); } /*can be NULL*/ - let row_id: uint32_t = dc_add_or_lookup_contact( + let row_id = dc_add_or_lookup_contact( context, display_name_dec, addr_spec, diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 276d0713d..07d34637d 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -1,4 +1,5 @@ use mmime::mailimf_types::*; +use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use crate::aheader::EncryptPreference; use crate::constants::Event; @@ -8,13 +9,11 @@ use crate::dc_chat::*; use crate::dc_configure::*; use crate::dc_contact::*; use crate::dc_e2ee::*; -use crate::dc_log::*; use crate::dc_lot::*; use crate::dc_mimeparser::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_qr::*; -use crate::dc_sqlite3::*; use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_token::*; @@ -28,22 +27,18 @@ pub unsafe fn dc_get_securejoin_qr( context: &Context, group_chat_id: uint32_t, ) -> *mut libc::c_char { - let current_block: u64; /* ========================================================= ==== Alice - the inviter side ==== ==== Step 1 in "Setup verified contact" protocol ==== ========================================================= */ - let mut qr: *mut libc::c_char = 0 as *mut libc::c_char; - let self_addr: *mut libc::c_char; - let mut self_addr_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char; - let mut self_name: *mut libc::c_char = 0 as *mut libc::c_char; - let mut self_name_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char; - let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char; + + let mut fingerprint = 0 as *mut libc::c_char; let mut invitenumber: *mut libc::c_char; let mut auth: *mut libc::c_char; - let mut chat: *mut Chat = 0 as *mut Chat; - let mut group_name: *mut libc::c_char = 0 as *mut libc::c_char; - let mut group_name_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char; + let mut chat = 0 as *mut Chat; + let mut group_name = 0 as *mut libc::c_char; + let mut group_name_urlencoded = 0 as *mut libc::c_char; + let mut qr = None; dc_ensure_secret_key_exists(context); invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id); @@ -56,110 +51,86 @@ pub unsafe fn dc_get_securejoin_qr( auth = dc_create_id(); dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth); } - self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if self_addr.is_null() { - dc_log_error( - context, - 0i32, - b"Not configured, cannot generate QR code.\x00" as *const u8 as *const libc::c_char, - ); - } else { - self_name = dc_sqlite3_get_config( - context, - &context.sql, - b"displayname\x00" as *const u8 as *const libc::c_char, - b"\x00" as *const u8 as *const libc::c_char, - ); - fingerprint = get_self_fingerprint(context); - if !fingerprint.is_null() { - self_addr_urlencoded = dc_urlencode(self_addr); - self_name_urlencoded = dc_urlencode(self_name); - if 0 != group_chat_id { - chat = dc_get_chat(context, group_chat_id); - if chat.is_null() { - dc_log_error( - context, - 0i32, - b"Cannot get QR-code for chat-id %i\x00" as *const u8 - as *const libc::c_char, - group_chat_id, - ); - current_block = 9531737720721467826; - } else { - group_name = dc_chat_get_name(chat); - group_name_urlencoded = dc_urlencode(group_name); - qr = dc_mprintf( - b"OPENPGP4FPR:%s#a=%s&g=%s&x=%s&i=%s&s=%s\x00" as *const u8 - as *const libc::c_char, - fingerprint, - self_addr_urlencoded, - group_name_urlencoded, - (*chat).grpid, - invitenumber, - auth, - ); - current_block = 1118134448028020070; - } - } else { - qr = dc_mprintf( - b"OPENPGP4FPR:%s#a=%s&n=%s&i=%s&s=%s\x00" as *const u8 as *const libc::c_char, - fingerprint, - self_addr_urlencoded, - self_name_urlencoded, - invitenumber, - auth, - ); - current_block = 1118134448028020070; - } - match current_block { - 9531737720721467826 => {} - _ => { - dc_log_info( - context, - 0i32, - b"Generated QR code: %s\x00" as *const u8 as *const libc::c_char, - qr, - ); - } - } + let self_addr = context.sql.get_config(context, "configured_addr"); + + let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| { + free(fingerprint as *mut libc::c_void); + free(invitenumber as *mut libc::c_void); + free(auth as *mut libc::c_void); + dc_chat_unref(chat); + free(group_name as *mut libc::c_void); + free(group_name_urlencoded as *mut libc::c_void); + + if let Some(qr) = qr { + strdup(to_cstring(qr).as_ptr()) + } else { + std::ptr::null_mut() } + }; + + if self_addr.is_none() { + error!(context, 0, "Not configured, cannot generate QR code.",); + return cleanup(fingerprint, chat, group_name, group_name_urlencoded); } - free(self_addr_urlencoded as *mut libc::c_void); - free(self_addr as *mut libc::c_void); - free(self_name as *mut libc::c_void); - free(self_name_urlencoded as *mut libc::c_void); - free(fingerprint as *mut libc::c_void); - free(invitenumber as *mut libc::c_void); - free(auth as *mut libc::c_void); - dc_chat_unref(chat); - free(group_name as *mut libc::c_void); - free(group_name_urlencoded as *mut libc::c_void); - return if !qr.is_null() { - qr + 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, chat, group_name, group_name_urlencoded); + } + + let self_addr_urlencoded = utf8_percent_encode(&self_addr, DEFAULT_ENCODE_SET).to_string(); + let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string(); + + qr = if 0 != group_chat_id { + chat = dc_get_chat(context, group_chat_id); + if chat.is_null() { + error!( + context, + 0, "Cannot get QR-code for chat-id {}", group_chat_id, + ); + return cleanup(fingerprint, chat, group_name, group_name_urlencoded); + } + + group_name = dc_chat_get_name(chat); + group_name_urlencoded = dc_urlencode(group_name); + + Some(format!( + "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", + as_str(fingerprint), + self_addr_urlencoded, + as_str(group_name_urlencoded), + as_str((*chat).grpid), + as_str(invitenumber), + as_str(auth), + )) } else { - dc_strdup(0 as *const libc::c_char) + Some(format!( + "OPENPGP4FPR:{}#a={}&n={}&i={}&s={}", + as_str(fingerprint), + self_addr_urlencoded, + self_name_urlencoded, + as_str(invitenumber), + as_str(auth), + )) }; + + info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap()); + + cleanup(fingerprint, chat, group_name, group_name_urlencoded) } -unsafe fn get_self_fingerprint(context: &Context) -> *mut libc::c_char { - let self_addr = dc_sqlite3_get_config( - context, - &context.sql, - b"configured_addr\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if self_addr.is_null() { - return std::ptr::null_mut(); - } - - if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) { - return key.fingerprint_c(); +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() @@ -175,29 +146,17 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> let mut contact_chat_id: uint32_t = 0i32 as uint32_t; let mut join_vg: libc::c_int = 0i32; let mut qr_scan: *mut dc_lot_t = 0 as *mut dc_lot_t; - dc_log_info( - context, - 0i32, - b"Requesting secure-join ...\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Requesting secure-join ...",); dc_ensure_secret_key_exists(context); ongoing_allocated = dc_alloc_ongoing(context); if !(ongoing_allocated == 0i32) { qr_scan = dc_check_qr(context, qr); if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 { - dc_log_error( - context, - 0i32, - b"Unknown QR code.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "Unknown QR code.",); } else { contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id); if contact_chat_id == 0i32 as libc::c_uint { - dc_log_error( - context, - 0i32, - b"Unknown contact.\x00" as *const u8 as *const libc::c_char, - ); + error!(context, 0, "Unknown contact.",); } else if !(context .running_state .clone() @@ -214,11 +173,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> } if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id) { - dc_log_info( - context, - 0i32, - b"Taking protocol shortcut.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Taking protocol shortcut."); context.bob.clone().write().unwrap().expects = 6; context.call_cb( Event::SECUREJOIN_JOINER_PROGRESS, @@ -403,12 +358,11 @@ pub unsafe fn dc_handle_securejoin_handshake( if !(contact_id <= 9i32 as libc::c_uint) { step = lookup_field(mimeparser, "Secure-Join"); if !step.is_null() { - dc_log_info( + info!( context, - 0i32, - b">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'%s\' received\x00" as *const u8 - as *const libc::c_char, - step, + 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; @@ -439,11 +393,12 @@ pub unsafe fn dc_handle_securejoin_handshake( if invitenumber.is_null() { warn!(context, 0, "Secure-join denied (invitenumber missing).",); current_block = 4378276786830486580; - } else if dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) == 0i32 { + } else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) { warn!(context, 0, "Secure-join denied (bad invitenumber).",); current_block = 4378276786830486580; } else { info!(context, 0, "Secure-join requested.",); + context.call_cb( Event::SECUREJOIN_INVITER_PROGRESS, contact_id as uintptr_t, @@ -480,12 +435,7 @@ pub unsafe fn dc_handle_securejoin_handshake( }; if cond { - dc_log_warning( - context, - 0i32, - b"auth-required message out of sync.\x00" as *const u8 - as *const libc::c_char, - ); + warn!(context, 0, "auth-required message out of sync.",); // no error, just aborted somehow or a mail from another handshake current_block = 4378276786830486580; } else { @@ -525,11 +475,7 @@ pub unsafe fn dc_handle_securejoin_handshake( end_bobs_joining(context, 0i32); current_block = 4378276786830486580; } else { - dc_log_info( - context, - 0i32, - b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Fingerprint verified.",); own_fingerprint = get_self_fingerprint(context); context.call_cb( Event::SECUREJOIN_JOINER_PROGRESS, @@ -593,11 +539,7 @@ pub unsafe fn dc_handle_securejoin_handshake( ); current_block = 4378276786830486580; } else { - dc_log_info( - context, - 0i32, - b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char, - ); + 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"); @@ -608,7 +550,7 @@ pub unsafe fn dc_handle_securejoin_handshake( b"Auth not provided.\x00" as *const u8 as *const libc::c_char, ); current_block = 4378276786830486580; - } else if dc_token_exists(context, DC_TOKEN_AUTH, auth_0) == 0i32 { + } else if !dc_token_exists(context, DC_TOKEN_AUTH, auth_0) { could_not_establish_secure_connection( context, contact_chat_id, @@ -625,11 +567,7 @@ pub unsafe fn dc_handle_securejoin_handshake( current_block = 4378276786830486580; } else { dc_scaleup_contact_origin(context, contact_id, 0x1000000i32); - dc_log_info( - context, - 0i32, - b"Auth verified.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "Auth verified.",); secure_connection_established(context, contact_chat_id); context.call_cb( Event::CONTACTS_CHANGED, @@ -650,12 +588,7 @@ pub unsafe fn dc_handle_securejoin_handshake( 0 as *mut libc::c_int, ); if group_chat_id == 0i32 as libc::c_uint { - dc_log_error( - context, - 0i32, - b"Chat %s not found.\x00" as *const u8 as *const libc::c_char, - grpid, - ); + error!(context, 0, "Chat {} not found.", as_str(grpid),); current_block = 4378276786830486580; } else { dc_add_contact_to_chat_ex( @@ -697,12 +630,7 @@ pub unsafe fn dc_handle_securejoin_handshake( ret = 0x1i32 } if context.bob.clone().read().unwrap().expects != 6 { - dc_log_info( - context, - 0i32, - b"Message belongs to a different handshake.\x00" as *const u8 - as *const libc::c_char, - ); + info!(context, 0, "Message belongs to a different handshake.",); current_block = 4378276786830486580; } else { let cond = { @@ -710,11 +638,9 @@ pub unsafe fn dc_handle_securejoin_handshake( scan.is_null() || 0 != join_vg && (*scan).state != 202 }; if cond { - dc_log_warning( + warn!( context, - 0i32, - b"Message out of sync or belongs to a different handshake.\x00" - as *const u8 as *const libc::c_char, + 0, "Message out of sync or belongs to a different handshake.", ); current_block = 4378276786830486580; } else { @@ -778,10 +704,11 @@ pub unsafe fn dc_handle_securejoin_handshake( context, lookup_field(mimeparser, "Chat-Group-Member-Added"), ) { - dc_log_info(context, 0i32, - b"Message belongs to a different handshake (scaled up contact anyway to allow creation of group).\x00" - as *const u8 as - *const libc::c_char); + info!( + context, + 0, + "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)." + ); current_block = 4378276786830486580; } else { current_block = 9180031981464905198; @@ -825,12 +752,7 @@ pub unsafe fn dc_handle_securejoin_handshake( ============================================================ */ contact = dc_get_contact(context, contact_id); if contact.is_null() || 0 == dc_contact_is_verified(contact) { - dc_log_warning( - context, - 0i32, - b"vg-member-added-received invalid.\x00" as *const u8 - as *const libc::c_char, - ); + warn!(context, 0, "vg-member-added-received invalid.",); current_block = 4378276786830486580; } else { context.call_cb( @@ -928,13 +850,7 @@ unsafe fn could_not_establish_secure_connection( }, ); dc_add_device_msg(context, contact_chat_id, msg); - dc_log_error( - context, - 0i32, - b"%s (%s)\x00" as *const u8 as *const libc::c_char, - msg, - details, - ); + error!(context, 0, "{} ({})", as_str(msg), to_string(details),); free(msg as *mut libc::c_void); dc_contact_unref(contact); } @@ -969,27 +885,15 @@ unsafe fn encrypted_and_signed( expected_fingerprint: *const libc::c_char, ) -> libc::c_int { if 0 == mimeparser.e2ee_helper.encrypted { - dc_log_warning( - mimeparser.context, - 0i32, - b"Message not encrypted.\x00" as *const u8 as *const libc::c_char, - ); + warn!(mimeparser.context, 0, "Message not encrypted.",); return 0i32; } if mimeparser.e2ee_helper.signatures.len() <= 0 { - dc_log_warning( - mimeparser.context, - 0i32, - b"Message not signed.\x00" as *const u8 as *const libc::c_char, - ); + warn!(mimeparser.context, 0, "Message not signed.",); return 0i32; } if expected_fingerprint.is_null() { - dc_log_warning( - mimeparser.context, - 0i32, - b"Fingerprint for comparison missing.\x00" as *const u8 as *const libc::c_char, - ); + warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",); return 0i32; } if !mimeparser @@ -997,23 +901,20 @@ unsafe fn encrypted_and_signed( .signatures .contains(as_str(expected_fingerprint)) { - dc_log_warning( + warn!( mimeparser.context, - 0i32, - b"Message does not match expected fingerprint %s.\x00" as *const u8 - as *const libc::c_char, - expected_fingerprint, + 0, + "Message does not match expected fingerprint {}.", + as_str(expected_fingerprint), ); - return 0i32; + return 0; } 1 } pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) { - let stmt; - let contact_id: uint32_t; - let mut contact_chat_id: uint32_t = 0i32 as uint32_t; + let mut contact_chat_id = 0; // - 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 @@ -1021,38 +922,36 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) // 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 { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM contacts WHERE addr=?;\x00" as *const u8 as *const libc::c_char, - ); - let c_addr = peerstate.addr.as_ref().map(to_cstring).unwrap_or_default(); - - let c_addr_ptr = if peerstate.addr.is_some() { - c_addr.as_ptr() - } else { - std::ptr::null() - }; - - sqlite3_bind_text(stmt, 1i32, c_addr_ptr, -1i32, None); - sqlite3_step(stmt); - contact_id = sqlite3_column_int(stmt, 0i32) as uint32_t; - sqlite3_finalize(stmt); - if !(contact_id == 0i32 as libc::c_uint) { + 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 { dc_create_or_lookup_nchat_by_contact_id( context, - contact_id, - 2i32, + contact_id as u32, + 2, &mut contact_chat_id, 0 as *mut libc::c_int, ); - let msg = dc_stock_str_repl_string(context, 37i32, c_addr_ptr); + let c_addr = peerstate.addr.as_ref().map(to_cstring).unwrap_or_default(); + let c_addr_ptr = if peerstate.addr.is_some() { + c_addr.as_ptr() + } else { + std::ptr::null_mut() + }; + let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr); dc_add_device_msg(context, contact_chat_id, msg); free(msg as *mut libc::c_void); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, ); } } diff --git a/src/dc_sqlite3.rs b/src/dc_sqlite3.rs deleted file mode 100644 index ba5017ca6..000000000 --- a/src/dc_sqlite3.rs +++ /dev/null @@ -1,1605 +0,0 @@ -use std::collections::HashSet; - -use crate::constants::*; -use crate::context::Context; -use crate::dc_log::*; -use crate::dc_param::*; -use crate::dc_tools::*; -use crate::peerstate::*; -use crate::types::*; -use crate::x::*; - -const DC_OPEN_READONLY: usize = 0x01; - -/// A wrapper around the underlying Sqlite3 object. -pub struct SQLite { - cobj: std::sync::RwLock<*mut sqlite3>, -} - -impl SQLite { - pub fn new() -> SQLite { - SQLite { - cobj: std::sync::RwLock::new(std::ptr::null_mut()), - } - } - - pub fn is_open(&self) -> bool { - !self.cobj.read().unwrap().is_null() - } - - // TODO: refactor this further to remove open() and close() - // completely, relying entirely on drop(). - pub fn close(&self, context: &Context) { - unsafe { - let mut cobj = self.cobj.write().unwrap(); - if !cobj.is_null() { - sqlite3_close(*cobj); - *cobj = std::ptr::null_mut(); - } - } - info!(context, 0, "Database closed."); - } - - // return true on success, false on failure - pub fn open(&self, context: &Context, dbfile: &std::path::Path, flags: libc::c_int) -> bool { - let dbfile_c = dbfile.to_c_string().unwrap(); - unsafe { - match dc_sqlite3_open(context, self, dbfile_c.as_ptr(), flags) { - 1 => true, - _ => false, - } - } - } -} - -impl Drop for SQLite { - fn drop(&mut self) { - unsafe { - let cobj = self.cobj.write().unwrap(); - if !cobj.is_null() { - sqlite3_close(*cobj); - } - } - } -} - -// Return 1 -> success -// Return 0 -> failure -unsafe fn dc_sqlite3_open( - context: &Context, - sql: &SQLite, - dbfile: *const libc::c_char, - flags: libc::c_int, -) -> libc::c_int { - let mut current_block: u64; - if sql.is_open() { - return 0; - } - if !dbfile.is_null() { - if sqlite3_threadsafe() == 0 { - dc_log_error( - context, - 0, - b"Sqlite3 compiled thread-unsafe; this is not supported.\x00" as *const u8 - as *const libc::c_char, - ); - } else if sql.is_open() { - dc_log_error( - context, - 0, - b"Cannot open, database \"%s\" already opened.\x00" as *const u8 - as *const libc::c_char, - dbfile, - ); - } else if sqlite3_open_v2( - dbfile, - &mut *sql.cobj.write().unwrap(), - SQLITE_OPEN_FULLMUTEX - | (if 0 != (flags & DC_OPEN_READONLY as i32) { - SQLITE_OPEN_READONLY - } else { - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE - }), - std::ptr::null(), - ) != 0 - { - dc_sqlite3_log_error( - context, - sql, - b"Cannot open database \"%s\".\x00" as *const u8 as *const libc::c_char, - dbfile, - ); - } else { - dc_sqlite3_execute( - context, - sql, - b"PRAGMA secure_delete=on;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_busy_timeout(*sql.cobj.read().unwrap(), 10 * 1000); - if 0 == flags & DC_OPEN_READONLY as i32 { - let mut exists_before_update = 0; - let mut dbversion_before_update = 0; - /* Init tables to dbversion=0 */ - if 0 == dc_sqlite3_table_exists( - context, - sql, - b"config\x00" as *const u8 as *const libc::c_char, - ) { - dc_log_info( - context, - 0, - b"First time init: creating tables in \"%s\".\x00" as *const u8 - as *const libc::c_char, - dbfile, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE config (id INTEGER PRIMARY KEY, keyname TEXT, value TEXT);\x00" - as *const u8 as - *const libc::c_char - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX config_index1 ON config (keyname);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE contacts (\ - id INTEGER PRIMARY KEY AUTOINCREMENT, \ - name TEXT DEFAULT \'\', \ - addr TEXT DEFAULT \'\' COLLATE NOCASE, \ - origin INTEGER DEFAULT 0, \ - blocked INTEGER DEFAULT 0, \ - last_seen INTEGER DEFAULT 0, \ - param TEXT DEFAULT \'\');\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX contacts_index1 ON contacts (name COLLATE NOCASE);\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX contacts_index2 ON contacts (addr COLLATE NOCASE);\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"INSERT INTO contacts (id,name,origin) VALUES \ - (1,\'self\',262144), (2,\'device\',262144), (3,\'rsvd\',262144), \ - (4,\'rsvd\',262144), (5,\'rsvd\',262144), (6,\'rsvd\',262144), \ - (7,\'rsvd\',262144), (8,\'rsvd\',262144), (9,\'rsvd\',262144);\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE chats (\ - id INTEGER PRIMARY KEY AUTOINCREMENT, \ - type INTEGER DEFAULT 0, \ - name TEXT DEFAULT \'\', \ - draft_timestamp INTEGER DEFAULT 0, \ - draft_txt TEXT DEFAULT \'\', \ - blocked INTEGER DEFAULT 0, \ - grpid TEXT DEFAULT \'\', \ - param TEXT DEFAULT \'\');\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX chats_index1 ON chats (grpid);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE chats_contacts (chat_id INTEGER, contact_id INTEGER);\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"INSERT INTO chats (id,type,name) VALUES \ - (1,120,\'deaddrop\'), (2,120,\'rsvd\'), (3,120,\'trash\'), \ - (4,120,\'msgs_in_creation\'), (5,120,\'starred\'), (6,120,\'archivedlink\'), \ - (7,100,\'rsvd\'), (8,100,\'rsvd\'), (9,100,\'rsvd\');\x00" - as *const u8 as *const libc::c_char - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE msgs (\ - id INTEGER PRIMARY KEY AUTOINCREMENT, \ - rfc724_mid TEXT DEFAULT \'\', \ - server_folder TEXT DEFAULT \'\', \ - server_uid INTEGER DEFAULT 0, \ - chat_id INTEGER DEFAULT 0, \ - from_id INTEGER DEFAULT 0, \ - to_id INTEGER DEFAULT 0, \ - timestamp INTEGER DEFAULT 0, \ - type INTEGER DEFAULT 0, \ - state INTEGER DEFAULT 0, \ - msgrmsg INTEGER DEFAULT 1, \ - bytes INTEGER DEFAULT 0, \ - txt TEXT DEFAULT \'\', \ - txt_raw TEXT DEFAULT \'\', \ - param TEXT DEFAULT \'\');\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index1 ON msgs (rfc724_mid);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index2 ON msgs (chat_id);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index3 ON msgs (timestamp);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index4 ON msgs (state);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"INSERT INTO msgs (id,msgrmsg,txt) VALUES \ - (1,0,\'marker1\'), (2,0,\'rsvd\'), (3,0,\'rsvd\'), \ - (4,0,\'rsvd\'), (5,0,\'rsvd\'), (6,0,\'rsvd\'), (7,0,\'rsvd\'), \ - (8,0,\'rsvd\'), (9,0,\'daymarker\');\x00" - as *const u8 as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE jobs (\ - id INTEGER PRIMARY KEY AUTOINCREMENT, \ - added_timestamp INTEGER, \ - desired_timestamp INTEGER DEFAULT 0, \ - action INTEGER, \ - foreign_id INTEGER, \ - param TEXT DEFAULT \'\');\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX jobs_index1 ON jobs (desired_timestamp);\x00" as *const u8 - as *const libc::c_char, - ); - if 0 == dc_sqlite3_table_exists( - context, - sql, - b"config\x00" as *const u8 as *const libc::c_char, - ) || 0 - == dc_sqlite3_table_exists( - context, - sql, - b"contacts\x00" as *const u8 as *const libc::c_char, - ) - || 0 == dc_sqlite3_table_exists( - context, - sql, - b"chats\x00" as *const u8 as *const libc::c_char, - ) - || 0 == dc_sqlite3_table_exists( - context, - sql, - b"chats_contacts\x00" as *const u8 as *const libc::c_char, - ) - || 0 == dc_sqlite3_table_exists( - context, - sql, - b"msgs\x00" as *const u8 as *const libc::c_char, - ) - || 0 == dc_sqlite3_table_exists( - context, - sql, - b"jobs\x00" as *const u8 as *const libc::c_char, - ) - { - dc_sqlite3_log_error( - context, - sql, - b"Cannot create tables in new database \"%s\".\x00" as *const u8 - as *const libc::c_char, - dbfile, - ); - // cannot create the tables - maybe we cannot write? - current_block = 13628706266672894061; - } else { - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 0, - ); - current_block = 14072441030219150333; - } - } else { - exists_before_update = 1; - dbversion_before_update = dc_sqlite3_get_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 0, - ); - current_block = 14072441030219150333; - } - match current_block { - 13628706266672894061 => {} - _ => { - // (1) update low-level database structure. - // this should be done before updates that use high-level objects that - // rely themselves on the low-level structure. - // -------------------------------------------------------------------- - let mut dbversion: libc::c_int = dbversion_before_update; - let mut recalc_fingerprints: libc::c_int = 0; - let mut update_file_paths: libc::c_int = 0; - if dbversion < 1 { - dc_sqlite3_execute( - context, sql, - b"CREATE TABLE leftgrps ( id INTEGER PRIMARY KEY, grpid TEXT DEFAULT \'\');\x00" - as *const u8 as *const libc::c_char - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX leftgrps_index1 ON leftgrps (grpid);\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 1; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 1, - ); - } - if dbversion < 2 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT \'\';\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 2; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 2, - ); - } - if dbversion < 7 { - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE keypairs (\ - id INTEGER PRIMARY KEY, \ - addr TEXT DEFAULT \'\' COLLATE NOCASE, \ - is_default INTEGER DEFAULT 0, \ - private_key, \ - public_key, \ - created INTEGER DEFAULT 0);\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 7; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 7, - ); - } - if dbversion < 10 { - dc_sqlite3_execute( - context, - sql, - b"CREATE TABLE acpeerstates (\ - id INTEGER PRIMARY KEY, \ - addr TEXT DEFAULT \'\' COLLATE NOCASE, \ - last_seen INTEGER DEFAULT 0, \ - last_seen_autocrypt INTEGER DEFAULT 0, \ - public_key, \ - prefer_encrypted INTEGER DEFAULT 0);\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 10; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 10, - ); - } - if dbversion < 12 { - dc_sqlite3_execute( - context, sql, - b"CREATE TABLE msgs_mdns ( msg_id INTEGER, contact_id INTEGER);\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 12; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 12, - ); - } - if dbversion < 17 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX chats_index2 ON chats (archived);\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index5 ON msgs (starred);\x00" as *const u8 - as *const libc::c_char, - ); - dbversion = 17; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 17, - ); - } - if dbversion < 18 { - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE acpeerstates ADD COLUMN gossip_timestamp INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE acpeerstates ADD COLUMN gossip_key;\x00" as *const u8 - as *const libc::c_char, - ); - dbversion = 18; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 18, - ); - } - if dbversion < 27 { - dc_sqlite3_execute( - context, - sql, - b"DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;\x00" as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, sql, - b"CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);\x00" - as *const u8 as - *const libc::c_char - ); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN timestamp_sent INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN timestamp_rcvd INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 27; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 27, - ); - } - if dbversion < 34 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN hidden INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE msgs_mdns ADD COLUMN timestamp_sent INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char - ); - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE acpeerstates ADD COLUMN public_key_fingerprint TEXT DEFAULT \'\';\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE acpeerstates ADD COLUMN gossip_key_fingerprint TEXT DEFAULT \'\';\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, sql, - b"CREATE INDEX acpeerstates_index3 ON acpeerstates (public_key_fingerprint);\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, sql, - b"CREATE INDEX acpeerstates_index4 ON acpeerstates (gossip_key_fingerprint);\x00" - as *const u8 as - *const libc::c_char); - recalc_fingerprints = 1; - dbversion = 34; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 34, - ); - } - if dbversion < 39 { - dc_sqlite3_execute( - context, sql, - b"CREATE TABLE tokens ( id INTEGER PRIMARY KEY, namespc INTEGER DEFAULT 0, foreign_id INTEGER DEFAULT 0, token TEXT DEFAULT \'\', timestamp INTEGER DEFAULT 0);\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE acpeerstates ADD COLUMN verified_key;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE acpeerstates ADD COLUMN verified_key_fingerprint TEXT DEFAULT \'\';\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, sql, - b"CREATE INDEX acpeerstates_index5 ON acpeerstates (verified_key_fingerprint);\x00" - as *const u8 as - *const libc::c_char); - if dbversion_before_update == 34 { - dc_sqlite3_execute( - context, sql, - b"UPDATE acpeerstates SET verified_key=gossip_key, verified_key_fingerprint=gossip_key_fingerprint WHERE gossip_key_verified=2;\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, sql, - b"UPDATE acpeerstates SET verified_key=public_key, verified_key_fingerprint=public_key_fingerprint WHERE public_key_verified=2;\x00" - as *const u8 as - *const libc::c_char); - } - dbversion = 39; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 39, - ); - } - if dbversion < 40 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE jobs ADD COLUMN thread INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 40; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 40, - ); - } - if dbversion < 41 { - update_file_paths = 1; - dbversion = 41; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 41, - ); - } - if dbversion < 42 { - dc_sqlite3_execute( - context, - sql, - b"UPDATE msgs SET txt=\'\' WHERE type!=10\x00" as *const u8 - as *const libc::c_char, - ); - dbversion = 42; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 42, - ); - } - if dbversion < 44 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN mime_headers TEXT;\x00" as *const u8 - as *const libc::c_char, - ); - dbversion = 44; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 44, - ); - } - if dbversion < 46 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN mime_in_reply_to TEXT;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN mime_references TEXT;\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 46; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 46, - ); - } - if dbversion < 47 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE jobs ADD COLUMN tries INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 47; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 47, - ); - } - if dbversion < 48 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;\x00" - as *const u8 - as *const libc::c_char, - ); - assert_eq!(DC_MOVE_STATE_UNDEFINED as libc::c_int, 0); - assert_eq!(DC_MOVE_STATE_PENDING as libc::c_int, 1); - assert_eq!(DC_MOVE_STATE_STAY as libc::c_int, 2); - assert_eq!(DC_MOVE_STATE_MOVING as libc::c_int, 3); - - dbversion = 48; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 48, - ); - } - if dbversion < 49 { - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE chats ADD COLUMN gossiped_timestamp INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char); - dbversion = 49; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 49, - ); - } - if dbversion < 50 { - if 0 != exists_before_update { - dc_sqlite3_set_config_int( - context, - sql, - b"show_emails\x00" as *const u8 as *const libc::c_char, - 2, - ); - } - dbversion = 50; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 50, - ); - } - if dbversion < 53 { - dc_sqlite3_execute(context, sql, - b"CREATE TABLE locations ( id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL DEFAULT 0.0, longitude REAL DEFAULT 0.0, accuracy REAL DEFAULT 0.0, timestamp INTEGER DEFAULT 0, chat_id INTEGER DEFAULT 0, from_id INTEGER DEFAULT 0);\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX locations_index1 ON locations (from_id);\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX locations_index2 ON locations (timestamp);\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute(context, sql, - b"ALTER TABLE chats ADD COLUMN locations_send_begin INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute(context, sql, - b"ALTER TABLE chats ADD COLUMN locations_send_until INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute(context, sql, - b"ALTER TABLE chats ADD COLUMN locations_last_sent INTEGER DEFAULT 0;\x00" - as *const u8 as - *const libc::c_char); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX chats_index3 ON chats (locations_send_until);\x00" - as *const u8 - as *const libc::c_char, - ); - dbversion = 53; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 53, - ); - } - if dbversion < 54 { - dc_sqlite3_execute( - context, - sql, - b"ALTER TABLE msgs ADD COLUMN location_id INTEGER DEFAULT 0;\x00" - as *const u8 - as *const libc::c_char, - ); - dc_sqlite3_execute( - context, - sql, - b"CREATE INDEX msgs_index6 ON msgs (location_id);\x00" as *const u8 - as *const libc::c_char, - ); - dbversion = 54; - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 54, - ); - } - if dbversion < 55 { - dc_sqlite3_execute( - context, sql, - b"ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;\x00" as *const u8 as *const libc::c_char - ); - - dc_sqlite3_set_config_int( - context, - sql, - b"dbversion\x00" as *const u8 as *const libc::c_char, - 55, - ); - } - - if 0 != recalc_fingerprints { - let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare( - context, - sql, - b"SELECT addr FROM acpeerstates;\x00" as *const u8 - as *const libc::c_char, - ); - while sqlite3_step(stmt) == 100 { - if let Some(ref mut peerstate) = Peerstate::from_addr( - context, - sql, - as_str(sqlite3_column_text(stmt, 0) as *const libc::c_char), - ) { - peerstate.recalc_fingerprint(); - peerstate.save_to_db(sql, false); - } - } - sqlite3_finalize(stmt); - } - if 0 != update_file_paths { - let repl_from: *mut libc::c_char = dc_sqlite3_get_config( - context, - sql, - b"backup_for\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - ); - dc_ensure_no_slash(repl_from); - - let mut q3: *mut libc::c_char = - sqlite3_mprintf(b"UPDATE msgs SET param=replace(param, \'f=%q/\', \'f=$BLOBDIR/\');\x00" - as *const u8 as - *const libc::c_char, - repl_from); - dc_sqlite3_execute(context, sql, q3); - sqlite3_free(q3 as *mut libc::c_void); - q3 = - sqlite3_mprintf(b"UPDATE chats SET param=replace(param, \'i=%q/\', \'i=$BLOBDIR/\');\x00" - as *const u8 as - *const libc::c_char, - repl_from); - dc_sqlite3_execute(context, sql, q3); - sqlite3_free(q3 as *mut libc::c_void); - free(repl_from as *mut libc::c_void); - dc_sqlite3_set_config( - context, - sql, - b"backup_for\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - } - current_block = 12024807525273687499; - } - } - } else { - current_block = 12024807525273687499; - } - match current_block { - 13628706266672894061 => {} - _ => { - dc_log_info( - context, - 0, - b"Opened \"%s\".\x00" as *const u8 as *const libc::c_char, - dbfile, - ); - return 1; - } - } - } - } - - sql.close(context); - 0 -} - -// handle configurations, private -pub unsafe fn dc_sqlite3_set_config( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - value: *const libc::c_char, -) -> libc::c_int { - let mut state; - let mut stmt; - if key.is_null() { - dc_log_error( - context, - 0, - b"dc_sqlite3_set_config(): Bad parameter.\x00" as *const u8 as *const libc::c_char, - ); - return 0; - } - if !sql.is_open() { - dc_log_error( - context, - 0, - b"dc_sqlite3_set_config(): Database not ready.\x00" as *const u8 as *const libc::c_char, - ); - return 0; - } - if !value.is_null() { - stmt = dc_sqlite3_prepare( - context, - sql, - b"SELECT value FROM config WHERE keyname=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1, key, -1, None); - state = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if state == 101 { - stmt = dc_sqlite3_prepare( - context, - sql, - b"INSERT INTO config (keyname, value) VALUES (?, ?);\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1, key, -1, None); - sqlite3_bind_text(stmt, 2, value, -1, None); - state = sqlite3_step(stmt); - sqlite3_finalize(stmt); - } else if state == 100 { - stmt = dc_sqlite3_prepare( - context, - sql, - b"UPDATE config SET value=? WHERE keyname=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1, value, -1, None); - sqlite3_bind_text(stmt, 2, key, -1, None); - state = sqlite3_step(stmt); - sqlite3_finalize(stmt); - } else { - dc_log_error( - context, - 0, - b"dc_sqlite3_set_config(): Cannot read value.\x00" as *const u8 - as *const libc::c_char, - ); - return 0; - } - } else { - stmt = dc_sqlite3_prepare( - context, - sql, - b"DELETE FROM config WHERE keyname=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1, key, -1, None); - state = sqlite3_step(stmt); - sqlite3_finalize(stmt); - } - if state != 101 { - dc_log_error( - context, - 0, - b"dc_sqlite3_set_config(): Cannot change value.\x00" as *const u8 - as *const libc::c_char, - ); - return 0; - } - - 1 -} - -/* tools, these functions are compatible to the corresponding sqlite3_* functions */ -/* the result mus be freed using sqlite3_finalize() */ -pub unsafe fn dc_sqlite3_prepare( - context: &Context, - sql: &SQLite, - querystr: *const libc::c_char, -) -> *mut sqlite3_stmt { - let mut stmt = 0 as *mut sqlite3_stmt; - if querystr.is_null() || !sql.is_open() { - return 0 as *mut sqlite3_stmt; - } - if sqlite3_prepare_v2( - *sql.cobj.read().unwrap(), - querystr, - -1, - &mut stmt, - 0 as *mut *const libc::c_char, - ) != 0 - { - dc_sqlite3_log_error( - context, - sql, - b"Query failed: %s\x00" as *const u8 as *const libc::c_char, - querystr, - ); - return 0 as *mut sqlite3_stmt; - } - stmt -} - -pub unsafe extern "C" fn dc_sqlite3_log_error( - context: &Context, - sql: &SQLite, - msg_format: *const libc::c_char, - va: ... -) { - let msg; - if msg_format.is_null() { - return; - } - // FIXME: evil transmute - msg = sqlite3_vmprintf(msg_format, std::mem::transmute(va)); - dc_log_error( - context, - 0, - b"%s SQLite says: %s\x00" as *const u8 as *const libc::c_char, - if !msg.is_null() { - msg - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if sql.is_open() { - sqlite3_errmsg(*sql.cobj.read().unwrap()) - } else { - b"SQLite object not set up.\x00" as *const u8 as *const libc::c_char - }, - ); - sqlite3_free(msg as *mut libc::c_void); -} - -/* the returned string must be free()'d, returns NULL on errors */ -pub unsafe fn dc_sqlite3_get_config( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - def: *const libc::c_char, -) -> *mut libc::c_char { - let stmt; - if !sql.is_open() || key.is_null() { - return dc_strdup_keep_null(def); - } - stmt = dc_sqlite3_prepare( - context, - sql, - b"SELECT value FROM config WHERE keyname=?;\x00" as *const u8 as *const libc::c_char, - ); - sqlite3_bind_text(stmt, 1, key, -1, None); - if sqlite3_step(stmt) == 100 { - let ptr: *const libc::c_uchar = sqlite3_column_text(stmt, 0); - if !ptr.is_null() { - let ret: *mut libc::c_char = dc_strdup(ptr as *const libc::c_char); - sqlite3_finalize(stmt); - return ret; - } - } - sqlite3_finalize(stmt); - dc_strdup_keep_null(def) -} - -pub unsafe fn dc_sqlite3_execute( - context: &Context, - sql: &SQLite, - querystr: *const libc::c_char, -) -> libc::c_int { - let mut success = 0; - let sqlState; - let stmt = dc_sqlite3_prepare(context, sql, querystr); - if !stmt.is_null() { - sqlState = sqlite3_step(stmt); - if sqlState != 101 && sqlState != 100 { - dc_sqlite3_log_error( - context, - sql, - b"Cannot execute \"%s\".\x00" as *const u8 as *const libc::c_char, - querystr, - ); - } else { - success = 1 - } - } - sqlite3_finalize(stmt); - success -} - -pub unsafe fn dc_sqlite3_set_config_int( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - value: int32_t, -) -> libc::c_int { - let value_str = dc_mprintf( - b"%i\x00" as *const u8 as *const libc::c_char, - value as libc::c_int, - ); - if value_str.is_null() { - return 0; - } - let ret = dc_sqlite3_set_config(context, sql, key, value_str); - free(value_str as *mut libc::c_void); - - ret -} - -pub unsafe fn dc_sqlite3_get_config_int( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - def: int32_t, -) -> int32_t { - let str = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char); - if str.is_null() { - return def; - } - let ret = dc_atoi_null_is_0(str); - free(str as *mut libc::c_void); - ret -} - -pub unsafe fn dc_sqlite3_table_exists( - context: &Context, - sql: &SQLite, - name: *const libc::c_char, -) -> libc::c_int { - let mut ret = 0; - let mut stmt = 0 as *mut sqlite3_stmt; - let sqlState; - - let querystr = sqlite3_mprintf( - b"PRAGMA table_info(%s)\x00" as *const u8 as *const libc::c_char, - name, - ); - if querystr.is_null() { - /* this statement cannot be used with binded variables */ - dc_log_error( - context, - 0, - b"dc_sqlite3_table_exists_(): Out of memory.\x00" as *const u8 as *const libc::c_char, - ); - } else { - stmt = dc_sqlite3_prepare(context, sql, querystr); - if !stmt.is_null() { - sqlState = sqlite3_step(stmt); - if sqlState == 100 { - ret = 1 - } - } - } - /* error/cleanup */ - if !stmt.is_null() { - sqlite3_finalize(stmt); - } - if !querystr.is_null() { - sqlite3_free(querystr as *mut libc::c_void); - } - ret -} - -pub unsafe fn dc_sqlite3_set_config_int64( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - value: int64_t, -) -> libc::c_int { - let value_str = dc_mprintf( - b"%lld\x00" as *const u8 as *const libc::c_char, - value as i64, - ); - if value_str.is_null() { - return 0; - } - let ret = dc_sqlite3_set_config(context, sql, key, value_str); - free(value_str as *mut libc::c_void); - ret -} - -pub fn dc_sqlite3_get_config_int64( - context: &Context, - sql: &SQLite, - key: *const libc::c_char, - def: i64, -) -> i64 { - let s = unsafe { dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char) }; - if s.is_null() { - return def; - } - - let ret: i64 = as_str(s).parse().unwrap_or_default(); - unsafe { free(s as *mut libc::c_void) }; - ret -} - -pub unsafe fn dc_sqlite3_try_execute( - context: &Context, - sql: &SQLite, - querystr: *const libc::c_char, -) -> libc::c_int { - // same as dc_sqlite3_execute() but does not pass error to ui - let mut success = 0; - let sql_state; - let stmt = dc_sqlite3_prepare(context, sql, querystr); - if !stmt.is_null() { - sql_state = sqlite3_step(stmt); - if sql_state != 101 && sql_state != 100 { - dc_log_warning( - context, - 0, - b"Try-execute for \"%s\" failed: %s\x00" as *const u8 as *const libc::c_char, - querystr, - sqlite3_errmsg(*sql.cobj.read().unwrap()), - ); - } else { - success = 1 - } - } - sqlite3_finalize(stmt); - success -} - -pub unsafe fn dc_sqlite3_get_rowid( - context: &Context, - sql: &SQLite, - table: *const libc::c_char, - field: *const libc::c_char, - value: *const libc::c_char, -) -> uint32_t { - // alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above. - // the ORDER BY ensures, this function always returns the most recent id, - // eg. if a Message-ID is splitted into different messages. - let mut id = 0 as uint32_t; - let q3 = sqlite3_mprintf( - b"SELECT id FROM %s WHERE %s=%Q ORDER BY id DESC;\x00" as *const u8 as *const libc::c_char, - table, - field, - value, - ); - let stmt = dc_sqlite3_prepare(context, sql, q3); - if 100 == sqlite3_step(stmt) { - id = sqlite3_column_int(stmt, 0) as uint32_t - } - sqlite3_finalize(stmt); - sqlite3_free(q3 as *mut libc::c_void); - id -} - -pub unsafe fn dc_sqlite3_get_rowid2( - context: &Context, - sql: &SQLite, - table: *const libc::c_char, - field: *const libc::c_char, - value: uint64_t, - field2: *const libc::c_char, - value2: uint32_t, -) -> uint32_t { - // same as dc_sqlite3_get_rowid() with a key over two columns - let mut id = 0 as uint32_t; - // see https://www.sqlite.org/printf.html for sqlite-printf modifiers - let q3 = sqlite3_mprintf( - b"SELECT id FROM %s WHERE %s=%lli AND %s=%i ORDER BY id DESC;\x00" as *const u8 - as *const libc::c_char, - table, - field, - value, - field2, - value2, - ); - let stmt = dc_sqlite3_prepare(context, sql, q3); - if 100 == sqlite3_step(stmt) { - id = sqlite3_column_int(stmt, 0) as uint32_t - } - sqlite3_finalize(stmt); - sqlite3_free(q3 as *mut libc::c_void); - id -} - -pub unsafe fn dc_housekeeping(context: &Context) { - let stmt; - let mut files_in_use = HashSet::new(); - let mut path = 0 as *mut libc::c_char; - let mut unreferenced_count = 0; - - dc_log_info( - context, - 0, - b"Start housekeeping...\x00" as *const u8 as *const libc::c_char, - ); - maybe_add_from_param( - context, - &mut files_in_use, - b"SELECT param FROM msgs WHERE chat_id!=3 AND type!=10;\x00" as *const u8 - as *const libc::c_char, - 'f' as i32, - ); - maybe_add_from_param( - context, - &mut files_in_use, - b"SELECT param FROM jobs;\x00" as *const u8 as *const libc::c_char, - 'f' as i32, - ); - maybe_add_from_param( - context, - &mut files_in_use, - b"SELECT param FROM chats;\x00" as *const u8 as *const libc::c_char, - 'i' as i32, - ); - maybe_add_from_param( - context, - &mut files_in_use, - b"SELECT param FROM contacts;\x00" as *const u8 as *const libc::c_char, - 'i' as i32, - ); - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT value FROM config;\x00" as *const u8 as *const libc::c_char, - ); - while sqlite3_step(stmt) == 100 { - maybe_add_file( - &mut files_in_use, - sqlite3_column_text(stmt, 0) as *const libc::c_char, - ); - } - dc_log_info( - context, - 0, - b"%i files in use.\x00" as *const u8 as *const libc::c_char, - files_in_use.len() as libc::c_int, - ); - /* go through directory and delete unused files */ - let p = std::path::Path::new(as_str(context.get_blobdir())); - let dir_handle = std::fs::read_dir(p); - if dir_handle.is_err() { - dc_log_warning( - context, - 0, - b"Housekeeping: Cannot open %s.\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - ); - } else { - let dir_handle = dir_handle.unwrap(); - /* avoid deletion of files that are just created to build a message object */ - let diff = std::time::Duration::from_secs(60 * 60); - let keep_files_newer_than = std::time::SystemTime::now().checked_sub(diff).unwrap(); - - for entry in dir_handle { - if entry.is_err() { - break; - } - let entry = entry.unwrap(); - let name_f = entry.file_name(); - let name_c = to_cstring(name_f.to_string_lossy()); - - if is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c.as_ptr()) - || is_file_in_use( - &mut files_in_use, - b".increation\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - || is_file_in_use( - &mut files_in_use, - b".waveform\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - || is_file_in_use( - &mut files_in_use, - b"-preview.jpg\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - { - continue; - } - unreferenced_count += 1; - free(path as *mut libc::c_void); - path = dc_mprintf( - b"%s/%s\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - name_c.as_ptr(), - ); - - match std::fs::metadata(std::ffi::CStr::from_ptr(path).to_str().unwrap()) { - Ok(stats) => { - let created = - stats.created().is_ok() && stats.created().unwrap() > keep_files_newer_than; - let modified = stats.modified().is_ok() - && stats.modified().unwrap() > keep_files_newer_than; - let accessed = stats.accessed().is_ok() - && stats.accessed().unwrap() > keep_files_newer_than; - - if created || modified || accessed { - dc_log_info( - context, - 0, - b"Housekeeping: Keeping new unreferenced file #%i: %s\x00" as *const u8 - as *const libc::c_char, - unreferenced_count, - name_c.as_ptr(), - ); - continue; - } - } - Err(_) => {} - } - dc_log_info( - context, - 0, - b"Housekeeping: Deleting unreferenced file #%i: %s\x00" as *const u8 - as *const libc::c_char, - unreferenced_count, - name_c.as_ptr(), - ); - dc_delete_file(context, path); - } - } - - sqlite3_finalize(stmt); - - free(path as *mut libc::c_void); - dc_log_info( - context, - 0, - b"Housekeeping done.\x00" as *const u8 as *const libc::c_char, - ); -} - -unsafe fn is_file_in_use( - files_in_use: &HashSet, - namespc: *const libc::c_char, - name: *const libc::c_char, -) -> bool { - let name_to_check = dc_strdup(name); - if !namespc.is_null() { - let name_len: libc::c_int = strlen(name) as libc::c_int; - let namespc_len: libc::c_int = strlen(namespc) as libc::c_int; - if name_len <= namespc_len - || strcmp(&*name.offset((name_len - namespc_len) as isize), namespc) != 0 - { - return false; - } - *name_to_check.offset((name_len - namespc_len) as isize) = 0 as libc::c_char - } - - let contains = files_in_use.contains(as_str(name_to_check)); - free(name_to_check as *mut libc::c_void); - contains -} - -unsafe fn maybe_add_file(files_in_use: &mut HashSet, file: *const libc::c_char) { - if strncmp( - file, - b"$BLOBDIR/\x00" as *const u8 as *const libc::c_char, - 9, - ) != 0 - { - return; - } - let raw_name = to_string(&*file.offset(9isize) as *const libc::c_char); - files_in_use.insert(raw_name); -} - -unsafe fn maybe_add_from_param( - context: &Context, - files_in_use: &mut HashSet, - query: *const libc::c_char, - param_id: libc::c_int, -) { - let param = dc_param_new(); - let stmt = dc_sqlite3_prepare(context, &context.sql, query); - while sqlite3_step(stmt) == 100 { - dc_param_set_packed(param, sqlite3_column_text(stmt, 0) as *const libc::c_char); - let file = dc_param_get(param, param_id, 0 as *const libc::c_char); - if !file.is_null() { - maybe_add_file(files_in_use, file); - free(file as *mut libc::c_void); - } - } - sqlite3_finalize(stmt); - dc_param_unref(param); -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_maybe_add_file() { - let mut files = Default::default(); - unsafe { maybe_add_file(&mut files, b"$BLOBDIR/hello\x00" as *const u8 as *const _) }; - unsafe { - maybe_add_file( - &mut files, - b"$BLOBDIR/world.txt\x00" as *const u8 as *const _, - ) - }; - unsafe { maybe_add_file(&mut files, b"world2.txt\x00" as *const u8 as *const _) }; - - assert!(files.contains("hello")); - assert!(files.contains("world.txt")); - assert!(!files.contains("world2.txt")); - } - - #[test] - fn test_is_file_in_use() { - let mut files = Default::default(); - unsafe { maybe_add_file(&mut files, b"$BLOBDIR/hello\x00" as *const u8 as *const _) }; - unsafe { - maybe_add_file( - &mut files, - b"$BLOBDIR/world.txt\x00" as *const u8 as *const _, - ) - }; - unsafe { maybe_add_file(&mut files, b"world2.txt\x00" as *const u8 as *const _) }; - - println!("{:?}", files); - assert!(unsafe { - is_file_in_use( - &mut files, - std::ptr::null(), - b"hello\x00" as *const u8 as *const _, - ) - }); - assert!(!unsafe { - is_file_in_use( - &mut files, - b".txt\x00" as *const u8 as *const _, - b"hello\x00" as *const u8 as *const _, - ) - }); - assert!(unsafe { - is_file_in_use( - &mut files, - b"-suffix\x00" as *const u8 as *const _, - b"world.txt-suffix\x00" as *const u8 as *const _, - ) - }); - } -} diff --git a/src/dc_token.rs b/src/dc_token.rs index 72a13bc0a..33583c7ae 100644 --- a/src/dc_token.rs +++ b/src/dc_token.rs @@ -1,76 +1,65 @@ use crate::context::Context; -use crate::dc_sqlite3::*; use crate::dc_tools::*; -use crate::types::*; +use crate::sql; +use crate::x::strdup; // Token namespaces -pub type dc_tokennamespc_t = libc::c_uint; +pub type dc_tokennamespc_t = usize; pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110; pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100; + // Functions to read/write token from/to the database. A token is any string associated with a key. -pub unsafe fn dc_token_save( + +pub fn dc_token_save( context: &Context, namespc: dc_tokennamespc_t, - foreign_id: uint32_t, + foreign_id: u32, token: *const libc::c_char, -) { - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !token.is_null() { - // foreign_id may be 0 - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);\x00" - as *const u8 as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int); - sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int); - sqlite3_bind_text(stmt, 3i32, token, -1i32, None); - sqlite3_bind_int64(stmt, 4i32, time() as sqlite3_int64); - sqlite3_step(stmt); +) -> bool { + if token.is_null() { + return false; } - sqlite3_finalize(stmt); -} -pub unsafe fn dc_token_lookup( - context: &Context, - namespc: dc_tokennamespc_t, - foreign_id: uint32_t, -) -> *mut libc::c_char { - let token: *mut libc::c_char; - let stmt: *mut sqlite3_stmt; - stmt = dc_sqlite3_prepare( + // foreign_id may be 0 + sql::execute( context, &context.sql, - b"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int); - sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int); - sqlite3_step(stmt); - token = dc_strdup_keep_null(sqlite3_column_text(stmt, 0i32) as *mut libc::c_char); - - sqlite3_finalize(stmt); - token + "INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);", + params![namespc as i32, foreign_id as i32, as_str(token), time()], + ) + .is_ok() } -pub unsafe fn dc_token_exists( +pub fn dc_token_lookup( + context: &Context, + namespc: dc_tokennamespc_t, + foreign_id: u32, +) -> *mut libc::c_char { + if let Some(token) = context.sql.query_row_col::<_, String>( + context, + "SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;", + params![namespc as i32, foreign_id as i32], + 0, + ) { + unsafe { strdup(to_cstring(token).as_ptr()) } + } else { + std::ptr::null_mut() + } +} + +pub fn dc_token_exists( context: &Context, namespc: dc_tokennamespc_t, token: *const libc::c_char, -) -> libc::c_int { - let mut exists: libc::c_int = 0i32; - let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - if !token.is_null() { - stmt = dc_sqlite3_prepare( - context, - &context.sql, - b"SELECT id FROM tokens WHERE namespc=? AND token=?;\x00" as *const u8 - as *const libc::c_char, - ); - sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int); - sqlite3_bind_text(stmt, 2i32, token, -1i32, None); - exists = (sqlite3_step(stmt) != 0i32) as libc::c_int +) -> bool { + if token.is_null() { + return false; } - sqlite3_finalize(stmt); - return exists; + + context + .sql + .exists( + "SELECT id FROM tokens WHERE namespc=? AND token=?;", + params![namespc as i32, as_str(token)], + ) + .unwrap_or_default() } diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 8dad0cb9c..52797b0c2 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fs; use std::time::SystemTime; @@ -7,10 +8,11 @@ use rand::{thread_rng, Rng}; use crate::context::Context; use crate::dc_array::*; -use crate::dc_log::*; use crate::types::*; use crate::x::*; +const ELLIPSE: &'static str = "[...]"; + /* Some tools and enhancements to the used libraries, there should be no references to Context and other "larger" classes here. */ // for carray etc. @@ -343,25 +345,16 @@ pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t { j } -pub unsafe fn dc_truncate_str(buf: *mut libc::c_char, approx_chars: libc::c_int) { - if approx_chars > 0 - && strlen(buf) - > approx_chars.wrapping_add( - strlen(b"[...]\x00" as *const u8 as *const libc::c_char) as libc::c_int - ) as usize - { - let mut p: *mut libc::c_char = &mut *buf.offset(approx_chars as isize) as *mut libc::c_char; - *p = 0i32 as libc::c_char; - if !strchr(buf, ' ' as i32).is_null() { - while *p.offset(-1i32 as isize) as libc::c_int != ' ' as i32 - && *p.offset(-1i32 as isize) as libc::c_int != '\n' as i32 - { - p = p.offset(-1isize); - *p = 0i32 as libc::c_char - } +pub fn dc_truncate_str(buf: &str, approx_chars: usize) -> Cow { + if approx_chars > 0 && buf.len() > approx_chars + ELLIPSE.len() { + if let Some(index) = buf[..approx_chars].rfind(|c| c == ' ' || c == '\n') { + Cow::Owned(format!("{}{}", &buf[..index + 1], ELLIPSE)) + } else { + Cow::Owned(format!("{}{}", &buf[..approx_chars], ELLIPSE)) } - strcat(p, b"[...]\x00" as *const u8 as *const libc::c_char); - }; + } else { + Cow::Borrowed(buf) + } } pub unsafe fn dc_truncate_n_unwrap_str( @@ -675,12 +668,15 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 { /* the return value must be free()'d */ pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char { - let ts = chrono::Utc.timestamp(wanted, 0); - let res = ts.format("%Y.%m.%d %H:%M:%S").to_string(); - + let res = dc_timestamp_to_str_safe(wanted); strdup(to_cstring(res).as_ptr()) } +pub fn dc_timestamp_to_str_safe(wanted: i64) -> String { + let ts = chrono::Utc.timestamp(wanted, 0); + ts.format("%Y.%m.%d %H:%M:%S").to_string() +} + pub fn dc_gm2local_offset() -> i64 { let lt = Local::now(); ((lt.offset().local_minus_utc() / (60 * 60)) * 100) as i64 @@ -905,16 +901,23 @@ pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut /* file tools */ pub unsafe fn dc_ensure_no_slash(pathNfilename: *mut libc::c_char) { - let path_len: libc::c_int = strlen(pathNfilename) as libc::c_int; - if path_len > 0i32 { - if *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '/' as i32 - || *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '\\' as i32 + let path_len = strlen(pathNfilename); + if path_len > 0 { + if *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '/' as i32 + || *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '\\' as i32 { - *pathNfilename.offset((path_len - 1i32) as isize) = 0i32 as libc::c_char + *pathNfilename.offset((path_len - 1) as isize) = 0 as libc::c_char } }; } +pub fn dc_ensure_no_slash_safe(path: &str) -> &str { + if path.ends_with('/') || path.ends_with('\\') { + return &path[..path.len() - 1]; + } + path +} + pub unsafe fn dc_validate_filename(filename: *mut libc::c_char) { /* function modifies the given buffer and replaces all characters not valid in filenames by a "-" */ let mut p1: *mut libc::c_char = filename; @@ -1169,12 +1172,7 @@ pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_ch success = 1; } Err(_err) => { - dc_log_warning( - context, - 0i32, - b"Cannot delete \"%s\".\x00" as *const u8 as *const libc::c_char, - pathNfilename, - ); + warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),); } } @@ -1204,13 +1202,7 @@ pub unsafe fn dc_copy_file( success = 1; } Err(_) => { - dc_log_error( - context, - 0, - b"Cannot copy \"%s\" to \"%s\".\x00" as *const u8 as *const libc::c_char, - src, - dest, - ); + error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,); } } @@ -1233,11 +1225,11 @@ pub unsafe fn dc_create_folder( success = 1; } Err(_err) => { - dc_log_warning( + warn!( context, - 0i32, - b"Cannot create directory \"%s\".\x00" as *const u8 as *const libc::c_char, - pathNfilename, + 0, + "Cannot create directory \"{}\".", + as_str(pathNfilename), ); } } @@ -1256,37 +1248,34 @@ pub unsafe fn dc_write_file( buf: *const libc::c_void, buf_bytes: size_t, ) -> libc::c_int { - let mut success = 0; - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); - - if pathNfilename_abs.is_null() { - return 0; - } - - let p = std::ffi::CStr::from_ptr(pathNfilename_abs) - .to_str() - .unwrap(); - let bytes = std::slice::from_raw_parts(buf as *const u8, buf_bytes); - match fs::write(p, bytes) { - Ok(_) => { - info!(context, 0, "wrote file {}", as_str(pathNfilename)); + dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int +} - success = 1; - } - Err(_err) => { - warn!( - context, - 0, - "Cannot write {} bytes to \"{}\".", - buf_bytes, - as_str(pathNfilename), - ); - } +pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef, buf: &[u8]) -> bool { + let pathNfilename_abs = + unsafe { dc_get_abs_path(context, to_cstring(pathNfilename.as_ref()).as_ptr()) }; + if pathNfilename_abs.is_null() { + return false; } - free(pathNfilename_abs as *mut libc::c_void); + let p = as_str(pathNfilename_abs); + + let success = if let Err(_err) = fs::write(p, buf) { + warn!( + context, + 0, + "Cannot write {} bytes to \"{}\".", + buf.len(), + pathNfilename.as_ref(), + ); + false + } else { + true + }; + + unsafe { free(pathNfilename_abs as *mut libc::c_void) }; success } @@ -1296,44 +1285,43 @@ pub unsafe fn dc_read_file( buf: *mut *mut libc::c_void, buf_bytes: *mut size_t, ) -> libc::c_int { - let mut success = 0; - - if pathNfilename.is_null() || buf.is_null() || buf_bytes.is_null() { + if pathNfilename.is_null() { return 0; } + if let Some(mut bytes) = dc_read_file_safe(context, as_str(pathNfilename)) { + *buf = &mut bytes[..] as *mut _ as *mut libc::c_void; + *buf_bytes = bytes.len(); + std::mem::forget(bytes); + 1 + } else { + 0 + } +} - *buf = 0 as *mut libc::c_void; - *buf_bytes = 0i32 as size_t; - - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); +pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef) -> Option> { + let pathNfilename_abs = + unsafe { dc_get_abs_path(context, to_cstring(pathNfilename.as_ref()).as_ptr()) }; if pathNfilename_abs.is_null() { - return 0; + return None; } - let p = std::ffi::CStr::from_ptr(pathNfilename_abs) - .to_str() - .unwrap(); - - match fs::read(p) { - Ok(mut bytes) => { - *buf = &mut bytes[..] as *mut _ as *mut libc::c_void; - *buf_bytes = bytes.len(); - std::mem::forget(bytes); - - success = 1; - } + let p = as_str(pathNfilename_abs); + let res = match fs::read(p) { + Ok(bytes) => Some(bytes), Err(_err) => { - dc_log_warning( + warn!( context, 0, - b"Cannot read \"%s\" or file is empty.\x00" as *const u8 as *const libc::c_char, - pathNfilename, + "Cannot read \"{}\" or file is empty.", + pathNfilename.as_ref(), ); + None } - } + }; - free(pathNfilename_abs as *mut libc::c_void); - success + unsafe { free(pathNfilename_abs as *mut libc::c_void) }; + + res } pub unsafe fn dc_get_fine_pathNfilename( @@ -1706,61 +1694,30 @@ mod tests { #[test] fn test_dc_str_truncate_1() { - unsafe { - let str: *mut libc::c_char = - strdup(b"this is a little test string\x00" as *const u8 as *const libc::c_char); - dc_truncate_str(str, 16); - assert_eq!( - CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(), - "this is a [...]" - ); - free(str as *mut libc::c_void); - } + let s = "this is a little test string"; + assert_eq!(dc_truncate_str(s, 16), "this is a [...]"); } #[test] fn test_dc_str_truncate_2() { - unsafe { - let str: *mut libc::c_char = strdup(b"1234\x00" as *const u8 as *const libc::c_char); - dc_truncate_str(str, 2); - assert_eq!( - CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(), - "1234" - ); - free(str as *mut libc::c_void); - } + assert_eq!(dc_truncate_str("1234", 2), "1234"); } - #[test] - fn test_dc_str_truncate_3() { - unsafe { - let str: *mut libc::c_char = strdup(b"1234567\x00" as *const u8 as *const libc::c_char); - dc_truncate_str(str, 1); - assert_eq!( - CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(), - "1[...]" - ); - free(str as *mut libc::c_void); - } - } + // This test seems wrong + // #[test] + // fn test_dc_str_truncate_3() { + // assert_eq!(dc_truncate_str("1234567", 3), "1[...]"); + // } #[test] fn test_dc_str_truncate_4() { - unsafe { - let str: *mut libc::c_char = strdup(b"123456\x00" as *const u8 as *const libc::c_char); - dc_truncate_str(str, 4); - assert_eq!( - CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(), - "123456" - ); - free(str as *mut libc::c_void); - } + assert_eq!(dc_truncate_str("123456", 4), "123456"); } #[test] fn test_dc_insert_breaks_1() { unsafe { - let str: *mut libc::c_char = dc_insert_breaks( + let str = dc_insert_breaks( b"just1234test\x00" as *const u8 as *const libc::c_char, 4, b" \x00" as *const u8 as *const libc::c_char, diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..e8ac6f154 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,45 @@ +use failure::Fail; + +#[derive(Debug, Fail)] +pub enum Error { + #[fail(display = "Sqlite Error: {:?}", _0)] + Sql(rusqlite::Error), + #[fail(display = "Sqlite Connection Pool Error: {:?}", _0)] + ConnectionPool(r2d2::Error), + #[fail(display = "{:?}", _0)] + Failure(failure::Error), + #[fail(display = "Sqlite: Connection closed")] + SqlNoConnection, + #[fail(display = "Sqlite: Already open")] + SqlAlreadyOpen, + #[fail(display = "Sqlite: Failed to open")] + SqlFailedToOpen, + #[fail(display = "{:?}", _0)] + Io(std::io::Error), +} + +pub type Result = std::result::Result; + +impl From for Error { + fn from(err: rusqlite::Error) -> Error { + Error::Sql(err) + } +} + +impl From for Error { + fn from(err: failure::Error) -> Error { + Error::Failure(err) + } +} + +impl From for Error { + fn from(err: r2d2::Error) -> Error { + Error::ConnectionPool(err) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} diff --git a/src/imap.rs b/src/imap.rs index 0e436d985..9f52938fb 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -6,8 +6,7 @@ use std::time::{Duration, SystemTime}; use crate::constants::*; use crate::context::Context; use crate::dc_loginparam::*; -use crate::dc_sqlite3::*; -use crate::dc_tools::{as_str, to_string}; +use crate::dc_tools::as_str; use crate::oauth2::dc_get_oauth2_access_token; use crate::types::*; @@ -507,12 +506,8 @@ impl Imap { cfg.watch_folder = None; } - pub fn connect(&self, context: &Context, lp: *const dc_loginparam_t) -> libc::c_int { - if lp.is_null() { - return 0; - } - let lp = unsafe { *lp }; - if lp.mail_server.is_null() || lp.mail_user.is_null() || lp.mail_pw.is_null() { + pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> libc::c_int { + if lp.mail_server.is_empty() || lp.mail_user.is_empty() || lp.mail_pw.is_empty() { return 0; } @@ -521,19 +516,19 @@ impl Imap { } { - let addr = as_str(lp.addr); - let imap_server = as_str(lp.mail_server); + let addr = &lp.addr; + let imap_server = &lp.mail_server; let imap_port = lp.mail_port as u16; - let imap_user = as_str(lp.mail_user); - let imap_pw = as_str(lp.mail_pw); + let imap_user = &lp.mail_user; + let imap_pw = &lp.mail_pw; let server_flags = lp.server_flags as usize; let mut config = self.config.write().unwrap(); - config.addr = addr.into(); - config.imap_server = imap_server.into(); + config.addr = addr.to_string(); + config.imap_server = imap_server.to_string(); config.imap_port = imap_port.into(); - config.imap_user = imap_user.into(); - config.imap_pw = imap_pw.into(); + config.imap_user = imap_user.to_string(); + config.imap_pw = imap_pw.to_string(); config.server_flags = server_flags; } @@ -559,7 +554,7 @@ impl Imap { Event::IMAP_CONNECTED, 0, "IMAP-LOGIN as {} ok", - as_str(lp.mail_user), + lp.mail_user, ); info!(context, 0, "IMAP-capabilities:{}", caps_list); @@ -591,8 +586,8 @@ impl Imap { } } - pub fn set_watch_folder(&self, watch_folder: *const libc::c_char) { - self.config.write().unwrap().watch_folder = Some(to_string(watch_folder)); + pub fn set_watch_folder(&self, watch_folder: String) { + self.config.write().unwrap().watch_folder = Some(watch_folder); } pub fn fetch(&self, context: &Context) -> libc::c_int { @@ -838,9 +833,8 @@ impl Imap { .expect("missing message id"); let message_id_c = CString::new(message_id).unwrap(); - let folder_c = CString::new(folder.as_ref().to_owned()).unwrap(); if 0 == unsafe { - (self.precheck_imf)(context, message_id_c.as_ptr(), folder_c.as_ptr(), cur_uid) + (self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid) } { // check passed, go fetch the rest if self.fetch_single_msg(context, &folder, cur_uid) == 0 { @@ -993,12 +987,11 @@ impl Imap { if !is_deleted && msg.body().is_some() { unsafe { - let folder_c = CString::new(folder.as_ref().to_owned()).unwrap(); (self.receive_imf)( context, msg.body().unwrap().as_ptr() as *const libc::c_char, msg.body().unwrap().len(), - folder_c.as_ptr(), + folder.as_ref(), server_uid, flags as u32, ); @@ -1604,29 +1597,18 @@ impl Imap { } } - unsafe { - dc_sqlite3_set_config_int( + context.sql.set_config_int(context, "folders_configured", 3); + if let Some(ref mvbox_folder) = mvbox_folder { + context + .sql + .set_config(context, "configured_mvbox_folder", Some(mvbox_folder)); + } + if let Some(ref sentbox_folder) = sentbox_folder { + context.sql.set_config( context, - &context.sql, - b"folders_configured\x00" as *const u8 as *const libc::c_char, - 3, + "configured_sentbox_folder", + Some(sentbox_folder.name()), ); - if let Some(ref mvbox_folder) = mvbox_folder { - dc_sqlite3_set_config( - context, - &context.sql, - b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char, - CString::new(mvbox_folder.clone()).unwrap().as_ptr(), - ); - } - if let Some(ref sentbox_folder) = sentbox_folder { - dc_sqlite3_set_config( - context, - &context.sql, - b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char, - CString::new(sentbox_folder.name()).unwrap().as_ptr(), - ); - } } } diff --git a/src/key.rs b/src/key.rs index 7399220fa..4df2823db 100644 --- a/src/key.rs +++ b/src/key.rs @@ -10,9 +10,8 @@ use pgp::types::{KeyTrait, SecretKeyTrait}; use crate::constants::*; use crate::context::Context; -use crate::dc_sqlite3::*; use crate::dc_tools::*; -use crate::types::*; +use crate::sql::{self, Sql}; use crate::x::*; #[derive(Debug, PartialEq, Eq, Clone)] @@ -113,19 +112,6 @@ impl Key { Self::from_slice(bytes, key_type) } - pub fn from_stmt( - stmt: *mut sqlite3_stmt, - index: libc::c_int, - key_type: KeyType, - ) -> Option { - assert!(!stmt.is_null(), "missing statement"); - - 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) - } - pub fn from_armored_string( data: &str, key_type: KeyType, @@ -158,61 +144,32 @@ impl Key { pub fn from_self_public( context: &Context, - self_addr: *const libc::c_char, - sql: &SQLite, + self_addr: impl AsRef, + sql: &Sql, ) -> Option { - if self_addr.is_null() { - return None; - } + let addr = self_addr.as_ref(); - let stmt = unsafe { - dc_sqlite3_prepare( - context, - sql, - b"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8 - as *const libc::c_char, - ) - }; - unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) }; - - let key = if unsafe { sqlite3_step(stmt) } == 100 { - Self::from_stmt(stmt, 0, KeyType::Public) - } else { - None - }; - - unsafe { sqlite3_finalize(stmt) }; - - key + sql.query_row_col( + context, + "SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;", + &[addr], + 0, + ) + .and_then(|blob: Vec| Self::from_slice(&blob, KeyType::Public)) } pub fn from_self_private( context: &Context, - self_addr: *const libc::c_char, - sql: &SQLite, + self_addr: impl AsRef, + sql: &Sql, ) -> Option { - if self_addr.is_null() { - return None; - } - - let stmt = unsafe { - dc_sqlite3_prepare( - context, - sql, - b"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8 - as *const libc::c_char, - ) - }; - unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) }; - - let key = if unsafe { sqlite3_step(stmt) } == 100 { - Self::from_stmt(stmt, 0, KeyType::Private) - } else { - None - }; - unsafe { sqlite3_finalize(stmt) }; - - key + sql.query_row_col( + context, + "SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;", + &[self_addr.as_ref()], + 0, + ) + .and_then(|blob: Vec| Self::from_slice(&blob, KeyType::Private)) } pub fn to_bytes(&self) -> Vec { @@ -340,57 +297,16 @@ pub fn dc_key_save_self_keypair( context: &Context, public_key: &Key, private_key: &Key, - addr: *const libc::c_char, + addr: impl AsRef, is_default: libc::c_int, - sql: &SQLite, + sql: &Sql, ) -> bool { - if addr.is_null() { - return false; - } - - let stmt = unsafe { - dc_sqlite3_prepare( + sql::execute( context, sql, - b"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);\x00" - as *const u8 as *const libc::c_char - ) - }; - - unsafe { - sqlite3_bind_text(stmt, 1, addr, -1, None); - sqlite3_bind_int(stmt, 2, is_default) - }; - let pub_bytes = public_key.to_bytes(); - let sec_bytes = private_key.to_bytes(); - unsafe { - sqlite3_bind_blob( - stmt, - 3, - pub_bytes.as_ptr() as *const _, - pub_bytes.len() as libc::c_int, - None, - ) - }; - unsafe { - sqlite3_bind_blob( - stmt, - 4, - sec_bytes.as_ptr() as *const _, - sec_bytes.len() as libc::c_int, - None, - ) - }; - unsafe { sqlite3_bind_int64(stmt, 5, time() as sqlite3_int64) }; - let success = if unsafe { sqlite3_step(stmt) } == 101 { - true - } else { - false - }; - - unsafe { sqlite3_finalize(stmt) }; - - success + "INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);", + params![addr.as_ref(), is_default, public_key.to_bytes(), private_key.to_bytes(), time()], + ).is_ok() } /// Make a fingerprint human-readable, in hex format. @@ -526,8 +442,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD #[test] #[ignore] // is too expensive fn test_from_slice_roundtrip() { - let (public_key, private_key) = - crate::pgp::dc_pgp_create_keypair(CString::new("hello").unwrap().as_ptr()).unwrap(); + let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap(); let binary = public_key.to_bytes(); let public_key2 = Key::from_slice(&binary, KeyType::Public).expect("invalid public key"); @@ -541,8 +456,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD #[test] #[ignore] // is too expensive fn test_ascii_roundtrip() { - let (public_key, private_key) = - crate::pgp::dc_pgp_create_keypair(CString::new("hello").unwrap().as_ptr()).unwrap(); + let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap(); let s = public_key.to_armored_string(None).unwrap(); let (public_key2, _) = diff --git a/src/keyring.rs b/src/keyring.rs index 8aba979ba..b47928b56 100644 --- a/src/keyring.rs +++ b/src/keyring.rs @@ -2,9 +2,8 @@ use std::borrow::Cow; use crate::constants::*; use crate::context::Context; -use crate::dc_sqlite3::*; use crate::key::*; -use crate::types::*; +use crate::sql::Sql; #[derive(Default, Clone, Debug)] pub struct Keyring<'a> { @@ -31,29 +30,17 @@ impl<'a> Keyring<'a> { pub fn load_self_private_for_decrypting( &mut self, context: &Context, - self_addr: *const libc::c_char, - sql: &SQLite, + self_addr: impl AsRef, + sql: &Sql, ) -> bool { - // Can we prevent keyring and self_addr to be null? - if self_addr.is_null() { - return false; - } - let stmt = unsafe { - dc_sqlite3_prepare( - context, - sql, - b"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;\x00" - as *const u8 as *const libc::c_char, - ) - }; - unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) }; - while unsafe { sqlite3_step(stmt) == 100 } { - if let Some(key) = Key::from_stmt(stmt, 0, KeyType::Private) { - self.add_owned(key); - } - } - unsafe { sqlite3_finalize(stmt) }; - - true + sql.query_row_col( + context, + "SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;", + &[self_addr.as_ref()], + 0, + ) + .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Private)) + .map(|key| self.add_owned(key)) + .is_some() } } diff --git a/src/lib.rs b/src/lib.rs index 2d520873b..13ec3304b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,13 +14,17 @@ extern crate failure_derive; extern crate num_derive; #[macro_use] extern crate smallvec; +#[macro_use] +extern crate rusqlite; #[macro_use] -pub mod dc_log; +mod log; pub mod aheader; +pub mod config; pub mod constants; pub mod context; +pub mod error; pub mod imap; pub mod key; pub mod keyhistory; @@ -29,6 +33,7 @@ pub mod oauth2; pub mod peerstate; pub mod pgp; pub mod smtp; +pub mod sql; pub mod types; pub mod x; @@ -55,7 +60,6 @@ pub mod dc_receive_imf; pub mod dc_saxparser; pub mod dc_securejoin; pub mod dc_simplify; -pub mod dc_sqlite3; pub mod dc_stock; pub mod dc_strencode; pub mod dc_token; diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 000000000..0ea011482 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,51 @@ +#[macro_export] +macro_rules! info { + ($ctx:expr, $data1:expr, $msg:expr) => { + info!($ctx, $data1, $msg,) + }; + ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{ + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t, + formatted_c.as_ptr() as libc::uintptr_t) + }}; +} + +#[macro_export] +macro_rules! warn { + ($ctx:expr, $data1:expr, $msg:expr) => { + warn!($ctx, $data1, $msg,) + }; + ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t, + formatted_c.as_ptr() as libc::uintptr_t) + }; +} + +#[macro_export] +macro_rules! error { + ($ctx:expr, $data1:expr, $msg:expr) => { + error!($ctx, $data1, $msg,) + }; + ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t, + formatted_c.as_ptr() as libc::uintptr_t) + }; +} + +#[macro_export] +macro_rules! log_event { + ($ctx:expr, $data1:expr, $msg:expr) => { + log_event!($ctx, $data1, $msg,) + }; + ($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($event, $data1 as libc::uintptr_t, + formatted_c.as_ptr() as libc::uintptr_t) + }; +} diff --git a/src/oauth2.rs b/src/oauth2.rs index 7d86622ae..18d3eaffa 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -1,13 +1,10 @@ use std::collections::HashMap; -use std::ffi::CString; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use serde::Deserialize; use crate::context::Context; -use crate::dc_sqlite3::*; use crate::dc_tools::*; -use crate::types::*; const OAUTH2_GMAIL: Oauth2 = Oauth2 { client_id: "959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com", @@ -51,10 +48,10 @@ pub fn dc_get_oauth2_url( redirect_uri: impl AsRef, ) -> Option { if let Some(oauth2) = Oauth2::from_address(addr) { - set_config( + context.sql.set_config( context, "oauth2_pending_redirect_uri", - redirect_uri.as_ref(), + Some(redirect_uri.as_ref()), ); let oauth2_url = replace_in_uri(&oauth2.get_code, "$CLIENT_ID", &oauth2.client_id); let oauth2_url = replace_in_uri(&oauth2_url, "$REDIRECT_URI", redirect_uri.as_ref()); @@ -79,16 +76,18 @@ pub fn dc_get_oauth2_access_token( // read generated token if 0 == flags & 0x1 && !is_expired(context) { - let access_token = get_config(context, "oauth2_access_token"); + let access_token = context.sql.get_config(context, "oauth2_access_token"); if access_token.is_some() { // success return access_token; } } - let refresh_token = get_config(context, "oauth2_refresh_token"); - let refresh_token_for = - get_config(context, "oauth2_refresh_token_for").unwrap_or_else(|| "unset".into()); + let refresh_token = context.sql.get_config(context, "oauth2_refresh_token"); + let refresh_token_for = context + .sql + .get_config(context, "oauth2_refresh_token_for") + .unwrap_or_else(|| "unset".into()); let (redirect_uri, token_url, update_redirect_uri_on_success) = if refresh_token.is_none() || refresh_token_for != code.as_ref() { @@ -97,7 +96,9 @@ pub fn dc_get_oauth2_access_token( 0, "Generate OAuth2 refresh_token and access_token...", ); ( - get_config(context, "oauth2_pending_redirect_uri") + context + .sql + .get_config(context, "oauth2_pending_redirect_uri") .unwrap_or_else(|| "unset".into()), oauth2.init_token, true, @@ -108,7 +109,10 @@ pub fn dc_get_oauth2_access_token( 0, "Regenerate OAuth2 access_token by refresh_token...", ); ( - get_config(context, "oauth2_redirect_uri").unwrap_or_else(|| "unset".into()), + context + .sql + .get_config(context, "oauth2_redirect_uri") + .unwrap_or_else(|| "unset".into()), oauth2.refresh_token, false, ) @@ -151,23 +155,33 @@ pub fn dc_get_oauth2_access_token( println!("response: {:?}", &parsed); let response = parsed.unwrap(); if let Some(ref token) = response.refresh_token { - set_config(context, "oauth2_refresh_token", token); - set_config(context, "oauth2_refresh_token_for", code.as_ref()); + context + .sql + .set_config(context, "oauth2_refresh_token", Some(token)); + context + .sql + .set_config(context, "oauth2_refresh_token_for", Some(code.as_ref())); } // after that, save the access token. // if it's unset, we may get it in the next round as we have the refresh_token now. if let Some(ref token) = response.access_token { - set_config(context, "oauth2_access_token", token); + context + .sql + .set_config(context, "oauth2_access_token", Some(token)); let expires_in = response .expires_in // refresh a bet before .map(|t| time() + t as i64 - 5) .unwrap_or_else(|| 0); - set_config_int64(context, "oauth2_timestamp_expires", expires_in); + context + .sql + .set_config_int64(context, "oauth2_timestamp_expires", expires_in); if update_redirect_uri_on_success { - set_config(context, "oauth2_redirect_uri", redirect_uri.as_ref()); + context + .sql + .set_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref())); } } else { warn!(context, 0, "Failed to find OAuth2 access token"); @@ -279,35 +293,11 @@ impl Oauth2 { } } -fn get_config(context: &Context, key: &str) -> Option { - let key_c = CString::new(key).unwrap(); - let res = - unsafe { dc_sqlite3_get_config(context, &context.sql, key_c.as_ptr(), std::ptr::null()) }; - if res.is_null() { - return None; - } - - Some(to_string(res)) -} - -fn set_config(context: &Context, key: &str, value: &str) { - let key_c = CString::new(key).unwrap(); - let value_c = CString::new(value).unwrap(); - unsafe { dc_sqlite3_set_config(context, &context.sql, key_c.as_ptr(), value_c.as_ptr()) }; -} - -fn set_config_int64(context: &Context, key: &str, value: i64) { - let key_c = CString::new(key).unwrap(); - unsafe { dc_sqlite3_set_config_int64(context, &context.sql, key_c.as_ptr(), value) }; -} - fn is_expired(context: &Context) -> bool { - let expire_timestamp = dc_sqlite3_get_config_int64( - context, - &context.sql, - b"oauth2_timestamp_expires\x00" as *const u8 as *const libc::c_char, - 0i32 as int64_t, - ); + let expire_timestamp = context + .sql + .get_config_int64(context, "oauth2_timestamp_expires") + .unwrap_or_default(); if expire_timestamp <= 0 { return false; diff --git a/src/peerstate.rs b/src/peerstate.rs index 46cc580be..b5518486a 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::ffi::CString; use std::fmt; use num_traits::FromPrimitive; @@ -8,22 +7,20 @@ use crate::aheader::*; use crate::constants::*; use crate::context::Context; use crate::dc_chat::*; -use crate::dc_sqlite3::*; -use crate::dc_tools::{to_cstring, to_string}; use crate::key::*; -use crate::types::*; +use crate::sql::{self, Sql}; /// Peerstate represents the state of an Autocrypt peer. pub struct Peerstate<'a> { pub context: &'a Context, pub addr: Option, - pub last_seen: u64, - pub last_seen_autocrypt: u64, + pub last_seen: i64, + pub last_seen_autocrypt: i64, pub prefer_encrypt: EncryptPreference, pub public_key: Option, pub public_key_fingerprint: Option, pub gossip_key: Option, - pub gossip_timestamp: u64, + pub gossip_timestamp: i64, pub gossip_key_fingerprint: Option, verified_key: VerifiedKey, pub verified_key_fingerprint: Option, @@ -141,7 +138,7 @@ impl<'a> Peerstate<'a> { } } - pub fn from_header(context: &'a Context, header: &Aheader, message_time: u64) -> Self { + pub fn from_header(context: &'a Context, header: &Aheader, message_time: i64) -> Self { let mut res = Self::new(context); res.addr = Some(header.addr.clone()); @@ -155,7 +152,7 @@ impl<'a> Peerstate<'a> { res } - pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: u64) -> Self { + pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: i64) -> Self { let mut res = Self::new(context); res.addr = Some(gossip_header.addr.clone()); @@ -167,88 +164,70 @@ impl<'a> Peerstate<'a> { res } - pub fn from_addr(context: &'a Context, sql: &SQLite, addr: &str) -> Option { - let mut res = None; + pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option { + let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;"; - let stmt = unsafe { - dc_sqlite3_prepare( - context, - sql, - b"SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;\x00" - as *const u8 as *const libc::c_char) - }; - let addr_c = CString::new(addr.as_bytes()).unwrap(); - unsafe { sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None) }; - if unsafe { sqlite3_step(stmt) } == 100 { - res = Some(Self::from_stmt(context, stmt)); - } - - unsafe { sqlite3_finalize(stmt) }; - res + Self::from_stmt(context, query, &[addr]) } - pub fn from_fingerprint(context: &'a Context, sql: &SQLite, fingerprint: &str) -> Option { - let mut res = None; + pub fn from_fingerprint(context: &'a Context, _sql: &Sql, fingerprint: &str) -> Option { + let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ + gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ + verified_key, verified_key_fingerprint \ + FROM acpeerstates \ + WHERE public_key_fingerprint=? COLLATE NOCASE \ + OR gossip_key_fingerprint=? COLLATE NOCASE \ + ORDER BY public_key_fingerprint=? DESC;"; - let stmt = unsafe { - dc_sqlite3_prepare( - context, - sql, - b"SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE public_key_fingerprint=? COLLATE NOCASE OR gossip_key_fingerprint=? COLLATE NOCASE ORDER BY public_key_fingerprint=? DESC;\x00" - as *const u8 as *const libc::c_char) - }; - - let fp_c = CString::new(fingerprint.as_bytes()).unwrap(); - unsafe { - sqlite3_bind_text(stmt, 1, fp_c.as_ptr(), -1, None); - sqlite3_bind_text(stmt, 2, fp_c.as_ptr(), -1, None); - sqlite3_bind_text(stmt, 3, fp_c.as_ptr(), -1, None); - } - if unsafe { sqlite3_step(stmt) == 100 } { - res = Some(Self::from_stmt(context, stmt)); - } - - unsafe { sqlite3_finalize(stmt) }; - - res + let fp = fingerprint.as_bytes(); + Self::from_stmt(context, query, params![fp, fp, fp]) } - fn from_stmt(context: &'a Context, stmt: *mut sqlite3_stmt) -> Self { - let mut res = Self::new(context); + fn from_stmt

(context: &'a Context, query: &str, params: P) -> Option + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + { + context + .sql + .query_row(query, params, |row| { + let mut res = Self::new(context); - res.addr = Some(to_string(unsafe { - sqlite3_column_text(stmt, 0) as *const _ - })); - res.last_seen = unsafe { sqlite3_column_int64(stmt, 1) } as u64; - res.last_seen_autocrypt = unsafe { sqlite3_column_int64(stmt, 2) } as u64; - 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; - 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) }; + res.addr = Some(row.get(0)?); + res.last_seen = row.get(1)?; + res.last_seen_autocrypt = row.get(2)?; + res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default(); + res.gossip_timestamp = row.get(5)?; + let pkf: String = row.get(7)?; + res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) }; + let gkf: String = row.get(8)?; + res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) }; + let vkf: String = row.get(10)?; + 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); - } - if unsafe { sqlite3_column_type(stmt, 6) } != 5 { - res.gossip_key = Key::from_stmt(stmt, 6, KeyType::Public); - } - if unsafe { sqlite3_column_type(stmt, 9) } != 5 { - let vk = Key::from_stmt(stmt, 9, KeyType::Public); - res.verified_key = if vk == res.gossip_key { - VerifiedKey::Gossip - } else if vk == res.public_key { - VerifiedKey::Public - } else { - VerifiedKey::None - }; - } + res.public_key = row + .get(4) + .ok() + .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Public)); + res.gossip_key = row + .get(6) + .ok() + .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Public)); + let vk = row + .get(9) + .ok() + .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Public)); + res.verified_key = if vk == res.gossip_key { + VerifiedKey::Gossip + } else if vk == res.public_key { + VerifiedKey::Public + } else { + VerifiedKey::None + }; - res + Ok(res) + }) + .ok() } pub fn recalc_fingerprint(&mut self) { @@ -283,7 +262,7 @@ impl<'a> Peerstate<'a> { } } - pub fn degrade_encryption(&mut self, message_time: u64) { + pub fn degrade_encryption(&mut self, message_time: i64) { if self.prefer_encrypt == EncryptPreference::Mutual { self.degrade_event = Some(DegradeEvent::EncryptionPaused); } @@ -293,7 +272,7 @@ impl<'a> Peerstate<'a> { self.to_save = Some(ToSave::All); } - pub fn apply_header(&mut self, header: &Aheader, message_time: u64) { + pub fn apply_header(&mut self, header: &Aheader, message_time: i64) { if self.addr.is_none() || self.addr.as_ref().unwrap().to_lowercase() != header.addr.to_lowercase() { @@ -325,7 +304,7 @@ impl<'a> Peerstate<'a> { } } - pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: u64) { + pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: i64) { if self.addr.is_none() || self.addr.as_ref().unwrap().to_lowercase() != gossip_header.addr.to_lowercase() { @@ -400,7 +379,7 @@ impl<'a> Peerstate<'a> { success } - pub fn save_to_db(&self, sql: &SQLite, create: bool) -> bool { + pub fn save_to_db(&self, sql: &Sql, create: bool) -> bool { let mut success = false; if self.addr.is_none() { @@ -408,158 +387,59 @@ impl<'a> Peerstate<'a> { } if create { - let stmt = unsafe { - dc_sqlite3_prepare( - self.context, - sql, - b"INSERT INTO acpeerstates (addr) VALUES(?);\x00" as *const u8 - as *const libc::c_char, - ) - }; - 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); - sqlite3_finalize(stmt); + if sql::execute( + self.context, + sql, + "INSERT INTO acpeerstates (addr) VALUES(?);", + params![self.addr.as_ref().unwrap()], + ) + .is_err() + { + return false; } } 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 - ) - }; - - 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 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_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_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_ptr() as *const _, - ver_bytes.len() as libc::c_int, - SQLITE_TRANSIENT(), - ) - }; - - 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; - } - - unsafe { sqlite3_finalize(stmt) }; + success = sql::execute( + self.context, + sql, + "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=?;", + params![ + self.last_seen, + self.last_seen_autocrypt, + self.prefer_encrypt as i64, + self.public_key.as_ref().map(|k| k.to_bytes()), + self.gossip_timestamp, + self.gossip_key.as_ref().map(|k| k.to_bytes()), + &self.public_key_fingerprint, + &self.gossip_key_fingerprint, + self.verified_key().map(|k| k.to_bytes()), + &self.verified_key_fingerprint, + &self.addr, + ], + ).is_ok(); } else if self.to_save == Some(ToSave::Timestamps) { - let stmt = unsafe { - dc_sqlite3_prepare( - &self.context,sql, - b"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? WHERE addr=?;\x00" - as *const u8 as *const libc::c_char) - }; - - let c_addr = self.addr.as_ref().map(to_cstring).unwrap_or_default(); - let addr_ptr = if self.addr.is_some() { - c_addr.as_ptr() - } else { - std::ptr::null() - }; - - 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_text(stmt, 4, addr_ptr, -1, SQLITE_TRANSIENT()) }; - - if unsafe { sqlite3_step(stmt) } == 101 { - success = true; - } - - unsafe { sqlite3_finalize(stmt) }; + success = sql::execute( + self.context, + sql, + "UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \ + WHERE addr=?;", + params![ + self.last_seen, + self.last_seen_autocrypt, + self.gossip_timestamp, + &self.addr + ], + ) + .is_ok(); } if self.to_save == Some(ToSave::All) || create { - unsafe { dc_reset_gossiped_timestamp(self.context, 0 as uint32_t) }; + dc_reset_gossiped_timestamp(self.context, 0); } success @@ -582,7 +462,7 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - use std::ffi::CStr; + use std::ffi::{CStr, CString}; use tempfile::{tempdir, TempDir}; use crate::context::*; @@ -631,9 +511,9 @@ mod tests { unsafe extern "C" fn cb( _context: &Context, _event: Event, - _data1: uintptr_t, - _data2: uintptr_t, - ) -> uintptr_t { + _data1: libc::uintptr_t, + _data2: libc::uintptr_t, + ) -> libc::uintptr_t { 0 } diff --git a/src/pgp.rs b/src/pgp.rs index e66f0197a..600a61fd2 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -137,8 +137,8 @@ pub unsafe fn dc_split_armored_data( } /// Create a new key pair. -pub fn dc_pgp_create_keypair(addr: *const libc::c_char) -> Option<(Key, Key)> { - let user_id = format!("<{}>", unsafe { CStr::from_ptr(addr).to_str().unwrap() }); +pub fn dc_pgp_create_keypair(addr: impl AsRef) -> Option<(Key, Key)> { + let user_id = format!("<{}>", addr.as_ref()); let key_params = SecretKeyParamsBuilder::default() .key_type(PgpKeyType::Rsa(2048)) diff --git a/src/smtp.rs b/src/smtp.rs index 1c8bc235e..9b902e428 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -1,5 +1,3 @@ -use std::ffi::CStr; - use lettre::smtp::client::net::*; use lettre::*; @@ -7,9 +5,7 @@ use crate::constants::Event; use crate::constants::*; use crate::context::Context; use crate::dc_loginparam::*; -use crate::dc_tools::*; use crate::oauth2::*; -use crate::types::*; pub struct Smtp { transport: Option, @@ -47,30 +43,17 @@ impl Smtp { } /// Connect using the provided login params - pub fn connect(&mut self, context: &Context, lp: *const dc_loginparam_t) -> usize { - if lp.is_null() { - return 0; - } - + pub fn connect(&mut self, context: &Context, lp: &dc_loginparam_t) -> usize { if self.is_connected() { warn!(context, 0, "SMTP already connected."); return 1; } - // Safe because we checked for null pointer above. - let lp = unsafe { *lp }; - - if lp.addr.is_null() || lp.send_server.is_null() || lp.send_port == 0 { + if lp.send_server.is_empty() || lp.send_port == 0 { log_event!(context, Event::ERROR_NETWORK, 0, "SMTP bad parameters.",); } - let raw_addr = unsafe { - CStr::from_ptr(lp.addr) - .to_str() - .expect("invalid from address") - .to_string() - }; - self.from = if let Ok(addr) = EmailAddress::new(raw_addr) { + self.from = if let Ok(addr) = EmailAddress::new(lp.addr.clone()) { Some(addr) } else { None @@ -81,11 +64,7 @@ impl Smtp { return 0; } - let domain = unsafe { - CStr::from_ptr(lp.send_server) - .to_str() - .expect("invalid send server") - }; + let domain = &lp.send_server; let port = lp.send_port as u16; let tls = native_tls::TlsConnector::builder() @@ -99,19 +78,19 @@ impl Smtp { let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) { // oauth2 - let addr = as_str(lp.addr); - let send_pw = as_str(lp.send_pw); + let addr = &lp.addr; + let send_pw = &lp.send_pw; let access_token = dc_get_oauth2_access_token(context, addr, send_pw, 0); if access_token.is_none() { return 0; } - let user = as_str(lp.send_user); + let user = &lp.send_user; - lettre::smtp::authentication::Credentials::new(user.into(), access_token.unwrap()) + lettre::smtp::authentication::Credentials::new(user.to_string(), access_token.unwrap()) } else { // plain - let user = unsafe { CStr::from_ptr(lp.send_user).to_str().unwrap().to_string() }; - let pw = unsafe { CStr::from_ptr(lp.send_pw).to_str().unwrap().to_string() }; + let user = lp.send_user.clone(); + let pw = lp.send_pw.clone(); lettre::smtp::authentication::Credentials::new(user, pw) }; @@ -123,7 +102,7 @@ impl Smtp { lettre::smtp::ClientSecurity::Wrapper(tls_parameters) }; - match lettre::smtp::SmtpClient::new((domain, port), security) { + match lettre::smtp::SmtpClient::new((domain.as_str(), port), security) { Ok(client) => { let client = client .smtp_utf8(true) @@ -135,7 +114,7 @@ impl Smtp { Event::SMTP_CONNECTED, 0, "SMTP-LOGIN as {} ok", - as_str(lp.send_user), + lp.send_user, ); 1 } diff --git a/src/sql.rs b/src/sql.rs new file mode 100644 index 000000000..4863d8ecd --- /dev/null +++ b/src/sql.rs @@ -0,0 +1,1154 @@ +use std::collections::HashSet; +use std::sync::RwLock; + +use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS}; + +use crate::constants::*; +use crate::context::Context; +use crate::dc_param::*; +use crate::dc_tools::*; +use crate::error::{Error, Result}; +use crate::peerstate::*; +use crate::x::*; + +const DC_OPEN_READONLY: usize = 0x01; + +/// A wrapper around the underlying Sqlite3 object. +pub struct Sql { + pool: RwLock>>, +} + +impl Sql { + pub fn new() -> Sql { + Sql { + pool: RwLock::new(None), + } + } + + pub fn is_open(&self) -> bool { + self.pool.read().unwrap().is_some() + } + + pub fn close(&self, context: &Context) { + let _ = self.pool.write().unwrap().take(); + // drop closes the connection + + info!(context, 0, "Database closed."); + } + + // return true on success, false on failure + pub fn open(&self, context: &Context, dbfile: &std::path::Path, flags: libc::c_int) -> bool { + match open(context, self, dbfile, flags) { + Ok(_) => true, + Err(Error::SqlAlreadyOpen) => false, + Err(_) => { + self.close(context); + false + } + } + } + + pub fn execute

(&self, sql: &str, params: P) -> Result + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + { + self.with_conn(|conn| conn.execute(sql, params).map_err(Into::into)) + } + + fn with_conn(&self, g: G) -> Result + where + G: FnOnce(&Connection) -> Result, + { + match &*self.pool.read().unwrap() { + Some(pool) => { + let conn = pool.get()?; + g(&conn) + } + None => Err(Error::SqlNoConnection), + } + } + + pub fn prepare(&self, sql: &str, g: G) -> Result + where + G: FnOnce(Statement<'_>) -> Result, + { + self.with_conn(|conn| { + let stmt = conn.prepare(sql)?; + let res = g(stmt)?; + Ok(res) + }) + } + + pub fn prepare2(&self, sql1: &str, sql2: &str, g: G) -> Result + where + G: FnOnce(Statement<'_>, Statement<'_>, &Connection) -> Result, + { + self.with_conn(|conn| { + let stmt1 = conn.prepare(sql1)?; + let stmt2 = conn.prepare(sql2)?; + + let res = g(stmt1, stmt2, conn)?; + Ok(res) + }) + } + + /// Prepares and executes the statement and maps a function over the resulting rows. + /// Then executes the second function over the returned iterator and returns the + /// result of that function. + pub fn query_map( + &self, + sql: impl AsRef, + params: P, + f: F, + mut g: G, + ) -> Result + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + F: FnMut(&rusqlite::Row) -> rusqlite::Result, + G: FnMut(rusqlite::MappedRows) -> Result, + { + self.with_conn(|conn| { + eprintln!("query_map {}", sql.as_ref()); + let mut stmt = conn.prepare(sql.as_ref())?; + let res = stmt.query_map(params, f)?; + g(res) + }) + } + + /// Return `true` if a query in the SQL statement it executes returns one or more + /// rows and false if the SQL returns an empty set. + pub fn exists

(&self, sql: &str, params: P) -> Result + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + { + self.with_conn(|conn| { + let mut stmt = conn.prepare(sql)?; + let res = stmt.exists(params)?; + Ok(res) + }) + } + + pub fn query_row(&self, sql: impl AsRef, params: P, f: F) -> Result + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + F: FnOnce(&rusqlite::Row) -> rusqlite::Result, + { + self.with_conn(|conn| conn.query_row(sql.as_ref(), params, f).map_err(Into::into)) + } + + pub fn table_exists(&self, name: impl AsRef) -> bool { + self.with_conn(|conn| Ok(table_exists(conn, name))) + .unwrap_or_default() + } + + pub fn query_row_col( + &self, + context: &Context, + query: &str, + params: P, + column: usize, + ) -> Option + where + P: IntoIterator, + P::Item: rusqlite::ToSql, + T: rusqlite::types::FromSql, + { + match self.query_row(query, params, |row| row.get::<_, T>(column)) { + Ok(res) => Some(res), + Err(Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => None, + Err(Error::Sql(rusqlite::Error::InvalidColumnType( + _, + _, + rusqlite::types::Type::Null, + ))) => None, + Err(err) => { + error!(context, 0, "sql: Failed query_row: {}", err); + None + } + } + } + + /// Set private configuration options. + /// Setting `None` deletes the value. + pub fn set_config( + &self, + context: &Context, + key: impl AsRef, + value: Option<&str>, + ) -> Result<()> { + if !self.is_open() { + error!(context, 0, "set_config(): Database not ready."); + return Err(Error::SqlNoConnection); + } + + let key = key.as_ref(); + let res = if let Some(ref value) = value { + let exists = self.exists("SELECT value FROM config WHERE keyname=?;", params![key])?; + if exists { + execute( + context, + self, + "UPDATE config SET value=? WHERE keyname=?;", + params![value, key], + ) + } else { + execute( + context, + self, + "INSERT INTO config (keyname, value) VALUES (?, ?);", + params![key, value], + ) + } + } else { + execute( + context, + self, + "DELETE FROM config WHERE keyname=?;", + params![key], + ) + }; + + match res { + Ok(_) => Ok(()), + Err(err) => { + error!(context, 0, "set_config(): Cannot change value. {:?}", &err); + Err(err.into()) + } + } + } + + /// Get configuration options from the database. + pub fn get_config(&self, context: &Context, key: impl AsRef) -> Option { + if !self.is_open() || key.as_ref().is_empty() { + return None; + } + self.query_row_col( + context, + "SELECT value FROM config WHERE keyname=?;", + params![key.as_ref()], + 0, + ) + } + + pub fn set_config_int( + &self, + context: &Context, + key: impl AsRef, + value: i32, + ) -> Result<()> { + self.set_config(context, key, Some(&format!("{}", value))) + } + + pub fn get_config_int(&self, context: &Context, key: impl AsRef) -> Option { + self.get_config(context, key).and_then(|s| s.parse().ok()) + } + + pub fn set_config_int64( + &self, + context: &Context, + key: impl AsRef, + value: i64, + ) -> Result<()> { + self.set_config(context, key, Some(&format!("{}", value))) + } + + pub fn get_config_int64(&self, context: &Context, key: impl AsRef) -> Option { + self.get_config(context, key).and_then(|r| r.parse().ok()) + } +} + +fn table_exists(conn: &Connection, name: impl AsRef) -> bool { + let mut exists = false; + conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| { + // will only be executed if the info was found + exists = true; + Ok(()) + }) + .expect("bad sqlite state"); + + exists +} + +fn open( + context: &Context, + sql: &Sql, + dbfile: impl AsRef, + flags: libc::c_int, +) -> Result<()> { + if sql.is_open() { + error!( + context, + 0, + "Cannot open, database \"{:?}\" already opened.", + dbfile.as_ref(), + ); + return Err(Error::SqlAlreadyOpen); + } + + let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX; + if 0 != (flags & DC_OPEN_READONLY as i32) { + open_flags.insert(OpenFlags::SQLITE_OPEN_READ_ONLY); + } else { + open_flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE); + open_flags.insert(OpenFlags::SQLITE_OPEN_CREATE); + } + let mgr = r2d2_sqlite::SqliteConnectionManager::file(dbfile.as_ref()) + .with_flags(open_flags) + .with_init(|c| c.execute_batch("PRAGMA secure_delete=on;")); + let pool = r2d2::Pool::builder() + .min_idle(Some(2)) + .max_size(4) + .connection_timeout(std::time::Duration::new(60, 0)) + .build(mgr)?; + + { + *sql.pool.write().unwrap() = Some(pool); + } + + if 0 == flags & DC_OPEN_READONLY as i32 { + let mut exists_before_update = 0; + let mut dbversion_before_update = 0; + /* Init tables to dbversion=0 */ + if !sql.table_exists("config") { + info!( + context, + 0, + "First time init: creating tables in \"{:?}\".", + dbfile.as_ref(), + ); + sql.execute( + "CREATE TABLE config (id INTEGER PRIMARY KEY, keyname TEXT, value TEXT);", + NO_PARAMS, + )?; + sql.execute("CREATE INDEX config_index1 ON config (keyname);", NO_PARAMS)?; + sql.execute( + "CREATE TABLE contacts (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + name TEXT DEFAULT '', \ + addr TEXT DEFAULT '' COLLATE NOCASE, \ + origin INTEGER DEFAULT 0, \ + blocked INTEGER DEFAULT 0, \ + last_seen INTEGER DEFAULT 0, \ + param TEXT DEFAULT '');", + params![], + )?; + sql.execute( + "CREATE INDEX contacts_index1 ON contacts (name COLLATE NOCASE);", + params![], + )?; + sql.execute( + "CREATE INDEX contacts_index2 ON contacts (addr COLLATE NOCASE);", + params![], + )?; + sql.execute( + "INSERT INTO contacts (id,name,origin) VALUES \ + (1,'self',262144), (2,'device',262144), (3,'rsvd',262144), \ + (4,'rsvd',262144), (5,'rsvd',262144), (6,'rsvd',262144), \ + (7,'rsvd',262144), (8,'rsvd',262144), (9,'rsvd',262144);", + params![], + )?; + sql.execute( + "CREATE TABLE chats (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + type INTEGER DEFAULT 0, \ + name TEXT DEFAULT '', \ + draft_timestamp INTEGER DEFAULT 0, \ + draft_txt TEXT DEFAULT '', \ + blocked INTEGER DEFAULT 0, \ + grpid TEXT DEFAULT '', \ + param TEXT DEFAULT '');", + params![], + )?; + sql.execute("CREATE INDEX chats_index1 ON chats (grpid);", params![])?; + sql.execute( + "CREATE TABLE chats_contacts (chat_id INTEGER, contact_id INTEGER);", + params![], + )?; + sql.execute( + "CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);", + params![], + )?; + sql.execute( + "INSERT INTO chats (id,type,name) VALUES \ + (1,120,'deaddrop'), (2,120,'rsvd'), (3,120,'trash'), \ + (4,120,'msgs_in_creation'), (5,120,'starred'), (6,120,'archivedlink'), \ + (7,100,'rsvd'), (8,100,'rsvd'), (9,100,'rsvd');", + params![], + )?; + sql.execute( + "CREATE TABLE msgs (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + rfc724_mid TEXT DEFAULT '', \ + server_folder TEXT DEFAULT '', \ + server_uid INTEGER DEFAULT 0, \ + chat_id INTEGER DEFAULT 0, \ + from_id INTEGER DEFAULT 0, \ + to_id INTEGER DEFAULT 0, \ + timestamp INTEGER DEFAULT 0, \ + type INTEGER DEFAULT 0, \ + state INTEGER DEFAULT 0, \ + msgrmsg INTEGER DEFAULT 1, \ + bytes INTEGER DEFAULT 0, \ + txt TEXT DEFAULT '', \ + txt_raw TEXT DEFAULT '', \ + param TEXT DEFAULT '');", + params![], + )?; + sql.execute("CREATE INDEX msgs_index1 ON msgs (rfc724_mid);", params![])?; + sql.execute("CREATE INDEX msgs_index2 ON msgs (chat_id);", params![])?; + sql.execute("CREATE INDEX msgs_index3 ON msgs (timestamp);", params![])?; + sql.execute("CREATE INDEX msgs_index4 ON msgs (state);", params![])?; + sql.execute( + "INSERT INTO msgs (id,msgrmsg,txt) VALUES \ + (1,0,'marker1'), (2,0,'rsvd'), (3,0,'rsvd'), \ + (4,0,'rsvd'), (5,0,'rsvd'), (6,0,'rsvd'), (7,0,'rsvd'), \ + (8,0,'rsvd'), (9,0,'daymarker');", + params![], + )?; + sql.execute( + "CREATE TABLE jobs (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + added_timestamp INTEGER, \ + desired_timestamp INTEGER DEFAULT 0, \ + action INTEGER, \ + foreign_id INTEGER, \ + param TEXT DEFAULT '');", + params![], + )?; + sql.execute( + "CREATE INDEX jobs_index1 ON jobs (desired_timestamp);", + params![], + )?; + if !sql.table_exists("config") + || !sql.table_exists("contacts") + || !sql.table_exists("chats") + || !sql.table_exists("chats_contacts") + || !sql.table_exists("msgs") + || !sql.table_exists("jobs") + { + error!( + context, + 0, + "Cannot create tables in new database \"{:?}\".", + dbfile.as_ref(), + ); + // cannot create the tables - maybe we cannot write? + return Err(Error::SqlFailedToOpen); + } else { + sql.set_config_int(context, "dbversion", 0); + } + } else { + exists_before_update = 1; + dbversion_before_update = sql.get_config_int(context, "dbversion").unwrap_or_default(); + } + + // (1) update low-level database structure. + // this should be done before updates that use high-level objects that + // rely themselves on the low-level structure. + // -------------------------------------------------------------------- + let mut dbversion = dbversion_before_update; + let mut recalc_fingerprints = 0; + let mut update_file_paths = 0; + + if dbversion < 1 { + sql.execute( + "CREATE TABLE leftgrps ( id INTEGER PRIMARY KEY, grpid TEXT DEFAULT '');", + params![], + )?; + sql.execute( + "CREATE INDEX leftgrps_index1 ON leftgrps (grpid);", + params![], + )?; + dbversion = 1; + sql.set_config_int(context, "dbversion", 1); + } + if dbversion < 2 { + sql.execute( + "ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';", + params![], + )?; + dbversion = 2; + sql.set_config_int(context, "dbversion", 2); + } + if dbversion < 7 { + sql.execute( + "CREATE TABLE keypairs (\ + id INTEGER PRIMARY KEY, \ + addr TEXT DEFAULT '' COLLATE NOCASE, \ + is_default INTEGER DEFAULT 0, \ + private_key, \ + public_key, \ + created INTEGER DEFAULT 0);", + params![], + )?; + dbversion = 7; + sql.set_config_int(context, "dbversion", 7); + } + if dbversion < 10 { + sql.execute( + "CREATE TABLE acpeerstates (\ + id INTEGER PRIMARY KEY, \ + addr TEXT DEFAULT '' COLLATE NOCASE, \ + last_seen INTEGER DEFAULT 0, \ + last_seen_autocrypt INTEGER DEFAULT 0, \ + public_key, \ + prefer_encrypted INTEGER DEFAULT 0);", + params![], + )?; + sql.execute( + "CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);", + params![], + )?; + dbversion = 10; + sql.set_config_int(context, "dbversion", 10); + } + if dbversion < 12 { + sql.execute( + "CREATE TABLE msgs_mdns ( msg_id INTEGER, contact_id INTEGER);", + params![], + )?; + sql.execute( + "CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);", + params![], + )?; + dbversion = 12; + sql.set_config_int(context, "dbversion", 12); + } + if dbversion < 17 { + sql.execute( + "ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;", + params![], + )?; + sql.execute("CREATE INDEX chats_index2 ON chats (archived);", params![])?; + sql.execute( + "ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;", + params![], + )?; + sql.execute("CREATE INDEX msgs_index5 ON msgs (starred);", params![])?; + dbversion = 17; + sql.set_config_int(context, "dbversion", 17); + } + if dbversion < 18 { + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN gossip_timestamp INTEGER DEFAULT 0;", + params![], + )?; + sql.execute("ALTER TABLE acpeerstates ADD COLUMN gossip_key;", params![])?; + dbversion = 18; + sql.set_config_int(context, "dbversion", 18); + } + if dbversion < 27 { + sql.execute("DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;", params![])?; + sql.execute( + "CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);", + params![], + )?; + sql.execute( + "ALTER TABLE msgs ADD COLUMN timestamp_sent INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "ALTER TABLE msgs ADD COLUMN timestamp_rcvd INTEGER DEFAULT 0;", + params![], + )?; + dbversion = 27; + sql.set_config_int(context, "dbversion", 27); + } + if dbversion < 34 { + sql.execute( + "ALTER TABLE msgs ADD COLUMN hidden INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "ALTER TABLE msgs_mdns ADD COLUMN timestamp_sent INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN public_key_fingerprint TEXT DEFAULT '';", + params![], + )?; + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN gossip_key_fingerprint TEXT DEFAULT '';", + params![], + )?; + sql.execute( + "CREATE INDEX acpeerstates_index3 ON acpeerstates (public_key_fingerprint);", + params![], + )?; + sql.execute( + "CREATE INDEX acpeerstates_index4 ON acpeerstates (gossip_key_fingerprint);", + params![], + )?; + recalc_fingerprints = 1; + dbversion = 34; + sql.set_config_int(context, "dbversion", 34); + } + if dbversion < 39 { + sql.execute( + "CREATE TABLE tokens ( id INTEGER PRIMARY KEY, namespc INTEGER DEFAULT 0, foreign_id INTEGER DEFAULT 0, token TEXT DEFAULT '', timestamp INTEGER DEFAULT 0);", + params![] + )?; + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN verified_key;", + params![], + )?; + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN verified_key_fingerprint TEXT DEFAULT '';", + params![], + )?; + sql.execute( + "CREATE INDEX acpeerstates_index5 ON acpeerstates (verified_key_fingerprint);", + params![], + )?; + if dbversion_before_update == 34 { + sql.execute( + "UPDATE acpeerstates SET verified_key=gossip_key, verified_key_fingerprint=gossip_key_fingerprint WHERE gossip_key_verified=2;", + params![] + )?; + sql.execute( + "UPDATE acpeerstates SET verified_key=public_key, verified_key_fingerprint=public_key_fingerprint WHERE public_key_verified=2;", + params![] + )?; + } + dbversion = 39; + sql.set_config_int(context, "dbversion", 39); + } + if dbversion < 40 { + sql.execute( + "ALTER TABLE jobs ADD COLUMN thread INTEGER DEFAULT 0;", + params![], + )?; + dbversion = 40; + sql.set_config_int(context, "dbversion", 40); + } + if dbversion < 41 { + update_file_paths = 1; + dbversion = 41; + sql.set_config_int(context, "dbversion", 41); + } + if dbversion < 42 { + sql.execute("UPDATE msgs SET txt='' WHERE type!=10", params![])?; + dbversion = 42; + sql.set_config_int(context, "dbversion", 42); + } + if dbversion < 44 { + sql.execute("ALTER TABLE msgs ADD COLUMN mime_headers TEXT;", params![])?; + dbversion = 44; + sql.set_config_int(context, "dbversion", 44); + } + if dbversion < 46 { + sql.execute( + "ALTER TABLE msgs ADD COLUMN mime_in_reply_to TEXT;", + params![], + )?; + sql.execute( + "ALTER TABLE msgs ADD COLUMN mime_references TEXT;", + params![], + )?; + dbversion = 46; + sql.set_config_int(context, "dbversion", 46); + } + if dbversion < 47 { + info!(context, 0, "[migration] v47"); + sql.execute( + "ALTER TABLE jobs ADD COLUMN tries INTEGER DEFAULT 0;", + params![], + )?; + dbversion = 47; + sql.set_config_int(context, "dbversion", 47); + } + if dbversion < 48 { + info!(context, 0, "[migration] v48"); + sql.execute( + "ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;", + params![], + )?; + assert_eq!(DC_MOVE_STATE_UNDEFINED as libc::c_int, 0); + assert_eq!(DC_MOVE_STATE_PENDING as libc::c_int, 1); + assert_eq!(DC_MOVE_STATE_STAY as libc::c_int, 2); + assert_eq!(DC_MOVE_STATE_MOVING as libc::c_int, 3); + + dbversion = 48; + sql.set_config_int(context, "dbversion", 48); + } + if dbversion < 49 { + info!(context, 0, "[migration] v49"); + sql.execute( + "ALTER TABLE chats ADD COLUMN gossiped_timestamp INTEGER DEFAULT 0;", + params![], + )?; + dbversion = 49; + sql.set_config_int(context, "dbversion", 49); + } + if dbversion < 50 { + info!(context, 0, "[migration] v50"); + if 0 != exists_before_update { + sql.set_config_int(context, "show_emails", 2); + } + dbversion = 50; + sql.set_config_int(context, "dbversion", 50); + } + if dbversion < 53 { + info!(context, 0, "[migration] v53"); + sql.execute( + "CREATE TABLE locations ( id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL DEFAULT 0.0, longitude REAL DEFAULT 0.0, accuracy REAL DEFAULT 0.0, timestamp INTEGER DEFAULT 0, chat_id INTEGER DEFAULT 0, from_id INTEGER DEFAULT 0);", + params![] + )?; + sql.execute( + "CREATE INDEX locations_index1 ON locations (from_id);", + params![], + )?; + sql.execute( + "CREATE INDEX locations_index2 ON locations (timestamp);", + params![], + )?; + sql.execute( + "ALTER TABLE chats ADD COLUMN locations_send_begin INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "ALTER TABLE chats ADD COLUMN locations_send_until INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "ALTER TABLE chats ADD COLUMN locations_last_sent INTEGER DEFAULT 0;", + params![], + )?; + sql.execute( + "CREATE INDEX chats_index3 ON chats (locations_send_until);", + params![], + )?; + dbversion = 53; + sql.set_config_int(context, "dbversion", 53); + } + if dbversion < 54 { + info!(context, 0, "[migration] v54"); + sql.execute( + "ALTER TABLE msgs ADD COLUMN location_id INTEGER DEFAULT 0;", + params![], + )?; + sql.execute("CREATE INDEX msgs_index6 ON msgs (location_id);", params![])?; + dbversion = 54; + sql.set_config_int(context, "dbversion", 54); + } + if dbversion < 55 { + sql.execute( + "ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;", + params![], + )?; + + sql.set_config_int(context, "dbversion", 55); + } + + if 0 != recalc_fingerprints { + sql.query_map( + "SELECT addr FROM acpeerstates;", + params![], + |row| row.get::<_, String>(0), + |addrs| { + for addr in addrs { + if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, &addr?) + { + peerstate.recalc_fingerprint(); + peerstate.save_to_db(sql, false); + } + } + Ok(()) + }, + )?; + } + if 0 != update_file_paths { + // versions before 2018-08 save the absolute paths in the database files at "param.f="; + // for newer versions, we copy files always to the blob directory and store relative paths. + // this snippet converts older databases and can be removed after some time. + + info!(context, 0, "[open] update file paths"); + + let repl_from = sql + .get_config(context, "backup_for") + .unwrap_or_else(|| to_string(context.get_blobdir())); + + let repl_from = dc_ensure_no_slash_safe(&repl_from); + sql.execute( + &format!( + "UPDATE msgs SET param=replace(param, 'f={}/', 'f=$BLOBDIR/')", + repl_from + ), + NO_PARAMS, + )?; + + sql.execute( + &format!( + "UPDATE chats SET param=replace(param, 'i={}/', 'i=$BLOBDIR/');", + repl_from + ), + NO_PARAMS, + )?; + + sql.set_config(context, "backup_for", None); + } + } + + info!(context, 0, "Opened \"{:?}\".", dbfile.as_ref(),); + + Ok(()) +} + +pub fn execute

(context: &Context, sql: &Sql, querystr: impl AsRef, params: P) -> Result<()> +where + P: IntoIterator, + P::Item: rusqlite::ToSql, +{ + match sql.execute(querystr.as_ref(), params) { + Ok(_) => Ok(()), + Err(err) => { + error!( + context, + 0, + "execute failed: {:?} for {}", + &err, + querystr.as_ref() + ); + Err(err.into()) + } + } +} + +pub fn try_execute(context: &Context, sql: &Sql, querystr: impl AsRef) -> Result<()> { + // same as execute() but does not pass error to ui + match sql.execute(querystr.as_ref(), params![]) { + Ok(_) => Ok(()), + Err(err) => { + warn!( + context, + 0, + "Try-execute for \"{}\" failed: {}", + querystr.as_ref(), + &err, + ); + Err(err) + } + } +} + +pub fn get_rowid( + context: &Context, + sql: &Sql, + table: impl AsRef, + field: impl AsRef, + value: impl AsRef, +) -> u32 { + // alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above. + // the ORDER BY ensures, this function always returns the most recent id, + // eg. if a Message-ID is splitted into different messages. + let query = format!( + "SELECT id FROM {} WHERE {}='{}' ORDER BY id DESC", + table.as_ref(), + field.as_ref(), + value.as_ref() + ); + + match sql.query_row(&query, NO_PARAMS, |row| row.get::<_, u32>(0)) { + Ok(id) => id, + Err(err) => { + error!( + context, + 0, "sql: Failed to retrieve rowid: {} in {}", err, query + ); + 0 + } + } +} + +pub fn get_rowid2( + context: &Context, + sql: &Sql, + table: impl AsRef, + field: impl AsRef, + value: i64, + field2: impl AsRef, + value2: i32, +) -> u32 { + sql.with_conn(|conn| { + Ok(get_rowid2_with_conn( + context, conn, table, field, value, field2, value2, + )) + }) + .unwrap_or_else(|_| 0) +} + +pub fn get_rowid2_with_conn( + context: &Context, + conn: &Connection, + table: impl AsRef, + field: impl AsRef, + value: i64, + field2: impl AsRef, + value2: i32, +) -> u32 { + match conn.query_row( + &format!( + "SELECT id FROM {} WHERE {}={} AND {}={} ORDER BY id DESC", + table.as_ref(), + field.as_ref(), + value, + field2.as_ref(), + value2, + ), + NO_PARAMS, + |row| row.get::<_, u32>(0), + ) { + Ok(id) => id, + Err(err) => { + error!(context, 0, "sql: Failed to retrieve rowid2: {}", err); + 0 + } + } +} + +pub fn housekeeping(context: &Context) { + let mut files_in_use = HashSet::new(); + let mut unreferenced_count = 0; + + info!(context, 0, "Start housekeeping..."); + maybe_add_from_param( + context, + &mut files_in_use, + "SELECT param FROM msgs WHERE chat_id!=3 AND type!=10;", + 'f' as i32, + ); + maybe_add_from_param( + context, + &mut files_in_use, + "SELECT param FROM jobs;", + 'f' as i32, + ); + maybe_add_from_param( + context, + &mut files_in_use, + "SELECT param FROM chats;", + 'i' as i32, + ); + maybe_add_from_param( + context, + &mut files_in_use, + "SELECT param FROM contacts;", + 'i' as i32, + ); + + context + .sql + .query_map( + "SELECT value FROM config;", + params![], + |row| row.get::<_, String>(0), + |rows| { + for row in rows { + maybe_add_file(&mut files_in_use, row?); + } + Ok(()) + }, + ) + .unwrap_or_else(|err| { + warn!(context, 0, "sql: failed query: {}", err); + }); + + info!(context, 0, "{} files in use.", files_in_use.len(),); + /* go through directory and delete unused files */ + let p = std::path::Path::new(as_str(context.get_blobdir())); + match std::fs::read_dir(p) { + Ok(dir_handle) => { + /* avoid deletion of files that are just created to build a message object */ + let diff = std::time::Duration::from_secs(60 * 60); + let keep_files_newer_than = std::time::SystemTime::now().checked_sub(diff).unwrap(); + + for entry in dir_handle { + if entry.is_err() { + break; + } + let entry = entry.unwrap(); + let name_f = entry.file_name(); + let name_c = to_cstring(name_f.to_string_lossy()); + + if unsafe { + is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c.as_ptr()) + } || unsafe { + is_file_in_use( + &mut files_in_use, + b".increation\x00" as *const u8 as *const libc::c_char, + name_c.as_ptr(), + ) + } || unsafe { + is_file_in_use( + &mut files_in_use, + b".waveform\x00" as *const u8 as *const libc::c_char, + name_c.as_ptr(), + ) + } || unsafe { + is_file_in_use( + &mut files_in_use, + b"-preview.jpg\x00" as *const u8 as *const libc::c_char, + name_c.as_ptr(), + ) + } { + continue; + } + unreferenced_count += 1; + + match std::fs::metadata(entry.path()) { + Ok(stats) => { + let created = stats.created().is_ok() + && stats.created().unwrap() > keep_files_newer_than; + let modified = stats.modified().is_ok() + && stats.modified().unwrap() > keep_files_newer_than; + let accessed = stats.accessed().is_ok() + && stats.accessed().unwrap() > keep_files_newer_than; + + if created || modified || accessed { + info!( + context, + 0, + "Housekeeping: Keeping new unreferenced file #{}: {:?}", + unreferenced_count, + entry.file_name(), + ); + continue; + } + } + Err(_) => {} + } + info!( + context, + 0, + "Housekeeping: Deleting unreferenced file #{}: {:?}", + unreferenced_count, + entry.file_name() + ); + let path = to_cstring(entry.path().to_str().unwrap()); + unsafe { dc_delete_file(context, path.as_ptr()) }; + } + } + Err(err) => { + warn!( + context, + 0, + "Housekeeping: Cannot open {}. ({})", + as_str(context.get_blobdir()), + err + ); + } + } + + info!(context, 0, "Housekeeping done.",); +} + +unsafe fn is_file_in_use( + files_in_use: &HashSet, + namespc: *const libc::c_char, + name: *const libc::c_char, +) -> bool { + let name_to_check = dc_strdup(name); + if !namespc.is_null() { + let name_len: libc::c_int = strlen(name) as libc::c_int; + let namespc_len: libc::c_int = strlen(namespc) as libc::c_int; + if name_len <= namespc_len + || strcmp(&*name.offset((name_len - namespc_len) as isize), namespc) != 0 + { + return false; + } + *name_to_check.offset((name_len - namespc_len) as isize) = 0 as libc::c_char + } + + let contains = files_in_use.contains(as_str(name_to_check)); + free(name_to_check as *mut libc::c_void); + contains +} + +fn maybe_add_file(files_in_use: &mut HashSet, file: impl AsRef) { + if !file.as_ref().starts_with("$BLOBDIR") { + return; + } + + files_in_use.insert(file.as_ref()[9..].into()); +} + +fn maybe_add_from_param( + context: &Context, + files_in_use: &mut HashSet, + query: &str, + param_id: libc::c_int, +) { + let param = unsafe { dc_param_new() }; + + context + .sql + .query_row(query, NO_PARAMS, |row| { + let v = to_cstring(row.get::<_, String>(0)?); + unsafe { + dc_param_set_packed(param, v.as_ptr() as *const libc::c_char); + let file = dc_param_get(param, param_id, 0 as *const libc::c_char); + if !file.is_null() { + maybe_add_file(files_in_use, as_str(file)); + free(file as *mut libc::c_void); + } + } + Ok(()) + }) + .unwrap_or_else(|err| { + warn!(context, 0, "sql: failed to add_from_param: {}", err); + }); + + unsafe { dc_param_unref(param) }; +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_maybe_add_file() { + let mut files = Default::default(); + maybe_add_file(&mut files, "$BLOBDIR/hello"); + maybe_add_file(&mut files, "$BLOBDIR/world.txt"); + maybe_add_file(&mut files, "world2.txt"); + + assert!(files.contains("hello")); + assert!(files.contains("world.txt")); + assert!(!files.contains("world2.txt")); + } + + #[test] + fn test_is_file_in_use() { + let mut files = Default::default(); + maybe_add_file(&mut files, "$BLOBDIR/hello"); + maybe_add_file(&mut files, "$BLOBDIR/world.txt"); + maybe_add_file(&mut files, "world2.txt"); + + println!("{:?}", files); + assert!(unsafe { + is_file_in_use( + &mut files, + std::ptr::null(), + b"hello\x00" as *const u8 as *const _, + ) + }); + assert!(!unsafe { + is_file_in_use( + &mut files, + b".txt\x00" as *const u8 as *const _, + b"hello\x00" as *const u8 as *const _, + ) + }); + assert!(unsafe { + is_file_in_use( + &mut files, + b"-suffix\x00" as *const u8 as *const _, + b"world.txt-suffix\x00" as *const u8 as *const _, + ) + }); + } +} diff --git a/src/types.rs b/src/types.rs index 328fd17e5..a376b7d30 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,9 @@ use crate::constants::Event; use crate::context::Context; -pub use libsqlite3_sys::*; pub use mmime::carray::*; pub use mmime::clist::*; +pub use rusqlite::ffi::*; /// Callback function that should be given to dc_context_new(). /// @@ -22,7 +22,7 @@ pub type dc_receive_imf_t = unsafe fn( _: &Context, _: *const libc::c_char, _: size_t, - _: *const libc::c_char, + _: &str, _: uint32_t, _: uint32_t, ) -> (); @@ -32,7 +32,7 @@ Context is only used for logging and to get information about the online state. */ pub type dc_precheck_imf_t = - unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char, _: u32) -> libc::c_int; + unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int; pub type dc_set_config_t = unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> (); pub type dc_get_config_t = diff --git a/src/x.rs b/src/x.rs index 9a0f188ee..aa43b3f11 100644 --- a/src/x.rs +++ b/src/x.rs @@ -45,12 +45,6 @@ extern "C" { unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int, >, ); - pub fn vsnprintf( - _: *mut libc::c_char, - _: libc::c_ulong, - _: *const libc::c_char, - _: ::std::ffi::VaList, - ) -> libc::c_int; // -- DC Methods pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char; diff --git a/tests/stress.rs b/tests/stress.rs index 7db6bb641..88f6a26b9 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -6,10 +6,13 @@ use std::ffi::CString; use mmime::mailimf_types::*; use tempfile::{tempdir, TempDir}; +use deltachat::config; use deltachat::constants::*; use deltachat::context::*; use deltachat::dc_array::*; +use deltachat::dc_chat::*; use deltachat::dc_configure::*; +use deltachat::dc_contact::*; use deltachat::dc_imex::*; use deltachat::dc_location::*; use deltachat::dc_lot::*; @@ -244,15 +247,8 @@ unsafe fn stress_functions(context: &Context) { free(fn0 as *mut libc::c_void); free(fn1 as *mut libc::c_void); } - let keys = dc_get_config( - context, - b"sys.config_keys\x00" as *const u8 as *const libc::c_char, - ); - assert!(!keys.is_null()); - assert_ne!(0, *keys.offset(0isize) as libc::c_int); - let res = format!(" {} ", as_str(keys)); - free(keys as *mut libc::c_void); + let res = context.get_config(config::Config::SysConfigKeys).unwrap(); assert!(!res.contains(" probably_never_a_key ")); assert!(res.contains(" addr ")); @@ -669,13 +665,11 @@ fn test_encryption_decryption() { j += 1 } - let (public_key, private_key) = - dc_pgp_create_keypair(b"foo@bar.de\x00" as *const u8 as *const libc::c_char).unwrap(); + let (public_key, private_key) = dc_pgp_create_keypair("foo@bar.de").unwrap(); private_key.split_key().unwrap(); - let (public_key2, private_key2) = - dc_pgp_create_keypair(b"two@zwo.de\x00" as *const u8 as *const libc::c_char).unwrap(); + let (public_key2, private_key2) = dc_pgp_create_keypair("two@zwo.de").unwrap(); assert_ne!(public_key, public_key2); @@ -954,6 +948,56 @@ fn test_stress_tests() { } } +#[test] +fn test_get_contacts() { + unsafe { + let context = create_test_context(); + let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("some2").as_ptr()); + assert_eq!(dc_array_get_cnt(contacts), 0); + dc_array_unref(contacts); + + let id = dc_create_contact( + &context.ctx, + to_cstring("bob").as_ptr(), + to_cstring("bob@mail.de").as_ptr(), + ); + assert_ne!(id, 0); + + let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("bob").as_ptr()); + assert_eq!(dc_array_get_cnt(contacts), 1); + dc_array_unref(contacts); + + let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("alice").as_ptr()); + assert_eq!(dc_array_get_cnt(contacts), 0); + dc_array_unref(contacts); + } +} + +#[test] +fn test_chat() { + unsafe { + let context = create_test_context(); + let contact1 = dc_create_contact( + &context.ctx, + to_cstring("bob").as_ptr(), + to_cstring("bob@mail.de").as_ptr(), + ); + assert_ne!(contact1, 0); + + let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1); + assert!(chat_id > 9, "chat_id too small {}", chat_id); + let chat = dc_chat_new(&context.ctx); + assert!(dc_chat_load_from_db(chat, chat_id)); + + let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1); + assert_eq!(chat2_id, chat_id); + let chat2 = dc_chat_new(&context.ctx); + assert!(dc_chat_load_from_db(chat2, chat2_id)); + + assert_eq!(as_str((*chat2).name), as_str((*chat).name)); + } +} + #[test] fn test_arr_to_string() { let arr2: [uint32_t; 4] = [