#![recursion_limit = "256"] #![warn(unused, clippy::all)] #![allow( non_camel_case_types, non_snake_case, non_upper_case_globals, clippy::missing_safety_doc, clippy::expect_fun_call )] #[macro_use] extern crate human_panic; use std::collections::BTreeMap; use std::convert::TryFrom; use std::fmt::Write; use std::future::Future; use std::mem::ManuallyDrop; use std::ptr; use std::str::FromStr; use std::sync::{Arc, LazyLock, Mutex}; use std::time::{Duration, SystemTime}; use anyhow::Context as _; use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration}; use deltachat::contact::{Contact, ContactId, Origin}; use deltachat::context::{Context, ContextBuilder}; use deltachat::ephemeral::Timer as EphemeralTimer; use deltachat::imex::BackupProvider; use deltachat::key::preconfigure_keypair; use deltachat::message::MsgId; use deltachat::qr_code_generator::{create_qr_svg, generate_backup_qr, get_securejoin_qr_svg}; use deltachat::stock_str::StockMessage; use deltachat::webxdc::StatusUpdateSerial; use deltachat::*; use deltachat::{accounts::Accounts, log::LogExt}; use deltachat_jsonrpc::api::CommandApi; use deltachat_jsonrpc::yerpc::{OutReceiver, RpcClient, RpcSession}; use message::Viewtype; use num_traits::{FromPrimitive, ToPrimitive}; use tokio::runtime::Runtime; use tokio::sync::RwLock; use tokio::task::JoinHandle; mod dc_array; mod lot; mod string; use deltachat::chatlist::Chatlist; use self::string::*; // as C lacks a good and portable error handling, // in general, the C Interface is forgiving wrt to bad parameters. // - objects returned by some functions // should be passable to the functions handling that object. // - if in doubt, the empty string is returned on failures; // this avoids panics if the ui just forgets to handle a case // - finally, this behaviour matches the old core-c API and UIs already depend on it const DC_GCM_ADDDAYMARKER: u32 = 0x01; // dc_context_t /// Struct representing the deltachat context. pub type dc_context_t = Context; static RT: LazyLock = LazyLock::new(|| Runtime::new().expect("unable to create tokio runtime")); fn block_on(fut: T) -> T::Output where T: Future, { RT.block_on(fut) } fn spawn(fut: T) -> JoinHandle where T: Future + Send + 'static, T::Output: Send + 'static, { RT.spawn(fut) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_new( _os_name: *const libc::c_char, dbfile: *const libc::c_char, blobdir: *const libc::c_char, ) -> *mut dc_context_t { setup_panic!(); if dbfile.is_null() { eprintln!("ignoring careless call to dc_context_new()"); return ptr::null_mut(); } let ctx = if blobdir.is_null() || unsafe { *blobdir == 0 } { // generate random ID as this functionality is not yet available on the C-api. let id = rand::random(); block_on( ContextBuilder::new(unsafe { as_path(dbfile) }.to_path_buf()) .with_id(id) .open(), ) } else { eprintln!("blobdir can not be defined explicitly anymore"); return ptr::null_mut(); }; match ctx { Ok(ctx) => Box::into_raw(Box::new(ctx)), Err(err) => { eprintln!("failed to create context: {err:#}"); ptr::null_mut() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *mut dc_context_t { setup_panic!(); if dbfile.is_null() { eprintln!("ignoring careless call to dc_context_new_closed()"); return ptr::null_mut(); } let id = rand::random(); match block_on( ContextBuilder::new(unsafe { as_path(dbfile) }.to_path_buf()) .with_id(id) .build(), ) { Ok(context) => Box::into_raw(Box::new(context)), Err(err) => { eprintln!("failed to create context: {err:#}"); ptr::null_mut() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_open( context: *mut dc_context_t, passphrase: *const libc::c_char, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_context_open()"); return 0; } let ctx = unsafe { &*context }; let passphrase = to_string_lossy(passphrase); block_on(ctx.open(passphrase)) .context("dc_context_open() failed") .log_err(ctx) .map(|b| b as libc::c_int) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_change_passphrase( context: *mut dc_context_t, passphrase: *const libc::c_char, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_context_change_passphrase()"); return 0; } let ctx = unsafe { &*context }; let passphrase = to_string_lossy(passphrase); block_on(ctx.change_passphrase(passphrase)) .context("dc_context_change_passphrase() failed") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_context_is_open()"); return 0; } let ctx = unsafe { &*context }; block_on(ctx.is_open()) as libc::c_int } /// Release the context structure. /// /// This function releases the memory of the `dc_context_t` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_context_unref()"); return; } drop(unsafe { Box::from_raw(context) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_blobdir(context: *mut dc_context_t) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_blobdir()"); return "".strdup(); } let ctx = unsafe { &*context }; ctx.get_blobdir().to_string_lossy().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_config( context: *mut dc_context_t, key: *const libc::c_char, value: *const libc::c_char, ) -> libc::c_int { if context.is_null() || key.is_null() { eprintln!("ignoring careless call to dc_set_config()"); return 0; } let ctx = unsafe { &*context }; let key = to_string_lossy(key); let value = to_opt_string_lossy(value); block_on(async move { if key.starts_with("ui.") { ctx.set_ui_config(&key, value.as_deref()) .await .with_context(|| format!("dc_set_config failed: Can't set {key} to {value:?}")) .log_err(ctx) .is_ok() as libc::c_int } else { match config::Config::from_str(&key) .context("Invalid config key") .log_err(ctx) { Ok(key) => ctx .set_config(key, value.as_deref()) .await .with_context(|| { format!("dc_set_config() failed: Can't set {key} to {value:?}") }) .log_err(ctx) .is_ok() as libc::c_int, Err(_) => 0, } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_config( context: *mut dc_context_t, key: *const libc::c_char, ) -> *mut libc::c_char { if context.is_null() || key.is_null() { eprintln!("ignoring careless call to dc_get_config()"); return "".strdup(); } let ctx = unsafe { &*context }; let key = to_string_lossy(key); block_on(async move { if key.starts_with("ui.") { ctx.get_ui_config(&key) .await .context("Can't get ui-config") .log_err(ctx) .unwrap_or_default() .unwrap_or_default() .strdup() } else { match config::Config::from_str(&key) .with_context(|| format!("Invalid key {key:?}")) .log_err(ctx) { Ok(key) => ctx .get_config(key) .await .context("Can't get config") .log_err(ctx) .unwrap_or_default() .unwrap_or_default() .strdup(), Err(_) => "".strdup(), } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_stock_translation( context: *mut dc_context_t, stock_id: u32, stock_msg: *mut libc::c_char, ) -> libc::c_int { if context.is_null() || stock_msg.is_null() { eprintln!("ignoring careless call to dc_set_stock_string"); return 0; } let msg = to_string_lossy(stock_msg); let ctx = unsafe { &*context }; match StockMessage::from_u32(stock_id) .with_context(|| format!("Invalid stock message ID {stock_id}")) .log_err(ctx) { Ok(id) => ctx .set_stock_translation(id, msg) .context("set_stock_translation failed") .log_err(ctx) .is_ok() as libc::c_int, Err(_) => 0, } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_config_from_qr( context: *mut dc_context_t, qr: *mut libc::c_char, ) -> libc::c_int { if context.is_null() || qr.is_null() { eprintln!("ignoring careless call to dc_set_config_from_qr"); return 0; } let qr = to_string_lossy(qr); let ctx = unsafe { &*context }; block_on(qr::set_config_from_qr(ctx, &qr)) .context("Failed to create account from QR code") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_info(context: *const dc_context_t) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_info()"); return "".strdup(); } let ctx = unsafe { &*context }; match block_on(ctx.get_info()) .context("Failed to get info") .log_err(ctx) { Ok(info) => { let info = render_info(info).unwrap_or_default(); info.strdup() } Err(_) => "".strdup(), } } fn render_info( info: BTreeMap<&'static str, String>, ) -> std::result::Result { let mut res = String::new(); for (key, value) in &info { writeln!(&mut res, "{key}={value}")?; } Ok(res) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_get_connectivity()"); return 0; } let ctx = unsafe { &*context }; ctx.get_connectivity() as u32 as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_connectivity_html( context: *const dc_context_t, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_connectivity_html()"); return "".strdup(); } let ctx = unsafe { &*context }; match block_on(ctx.get_connectivity_html()) .context("Failed to get connectivity html") .log_err(ctx) { Ok(html) => html.strdup(), Err(_) => "".strdup(), } } fn spawn_configure(ctx: Context) { spawn(async move { ctx.configure() .await .context("Configure failed") .log_err(&ctx) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_configure(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_configure()"); return; } let ctx = unsafe { &*context }; spawn_configure(ctx.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_is_configured()"); return 0; } let ctx = unsafe { &*context }; block_on(async move { ctx.is_configured() .await .context("failed to get configured state") .log_err(ctx) .unwrap_or_default() as libc::c_int }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_start_io(context: *mut dc_context_t) { if context.is_null() { return; } let ctx = unsafe { &mut *context }; block_on(ctx.start_io()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_id(context: *mut dc_context_t) -> libc::c_int { if context.is_null() { return 0; } let ctx = unsafe { &*context }; ctx.get_id() as libc::c_int } pub type dc_event_t = Event; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_unref(a: *mut dc_event_t) { if a.is_null() { eprintln!("ignoring careless call to dc_event_unref()"); return; } drop(unsafe { Box::from_raw(a) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_id()"); return 0; } let event = unsafe { &*event }; match event.typ { EventType::Info(_) => 100, EventType::SmtpConnected(_) => 101, EventType::ImapConnected(_) => 102, EventType::SmtpMessageSent(_) => 103, EventType::ImapMessageDeleted(_) => 104, EventType::ImapMessageMoved(_) => 105, EventType::ImapInboxIdle => 106, EventType::NewBlobFile(_) => 150, EventType::DeletedBlobFile(_) => 151, EventType::Warning(_) => 300, EventType::Error(_) => 400, EventType::ErrorSelfNotInGroup(_) => 410, EventType::MsgsChanged { .. } => 2000, EventType::ReactionsChanged { .. } => 2001, EventType::IncomingReaction { .. } => 2002, EventType::IncomingWebxdcNotify { .. } => 2003, EventType::IncomingMsg { .. } => 2005, EventType::IncomingMsgBunch => 2006, EventType::MsgsNoticed { .. } => 2008, EventType::MsgDelivered { .. } => 2010, EventType::MsgFailed { .. } => 2012, EventType::MsgRead { .. } => 2015, EventType::MsgDeleted { .. } => 2016, EventType::MsgReadCountChanged { .. } => 2018, EventType::ChatModified(_) => 2020, EventType::ChatEphemeralTimerModified { .. } => 2021, EventType::ChatDeleted { .. } => 2023, EventType::ContactsChanged(_) => 2030, EventType::LocationChanged(_) => 2035, EventType::ConfigureProgress { .. } => 2041, EventType::ImexProgress(_) => 2051, EventType::ImexFileWritten(_) => 2052, EventType::SecurejoinInviterProgress { .. } => 2060, EventType::SecurejoinJoinerProgress { .. } => 2061, EventType::ConnectivityChanged => 2100, EventType::SelfavatarChanged => 2110, EventType::ConfigSynced { .. } => 2111, EventType::WebxdcStatusUpdate { .. } => 2120, EventType::WebxdcInstanceDeleted { .. } => 2121, EventType::WebxdcRealtimeData { .. } => 2150, EventType::WebxdcRealtimeAdvertisementReceived { .. } => 2151, EventType::AccountsBackgroundFetchDone => 2200, EventType::ChatlistChanged => 2300, EventType::ChatlistItemChanged { .. } => 2301, EventType::AccountsChanged => 2302, EventType::AccountsItemChanged => 2303, EventType::EventChannelOverflow { .. } => 2400, EventType::IncomingCall { .. } => 2550, EventType::IncomingCallAccepted { .. } => 2560, EventType::OutgoingCallAccepted { .. } => 2570, EventType::CallEnded { .. } => 2580, EventType::TransportsModified => 2600, #[allow(unreachable_patterns)] #[cfg(test)] _ => unreachable!("This is just to silence a rust_analyzer false-positive"), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc::c_int { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_data1_int()"); return 0; } let event = unsafe { &(*event).typ }; match event { EventType::Info(_) | EventType::SmtpConnected(_) | EventType::ImapConnected(_) | EventType::SmtpMessageSent(_) | EventType::ImapMessageDeleted(_) | EventType::ImapMessageMoved(_) | EventType::ImapInboxIdle | EventType::NewBlobFile(_) | EventType::DeletedBlobFile(_) | EventType::Warning(_) | EventType::Error(_) | EventType::ConnectivityChanged | EventType::SelfavatarChanged | EventType::ConfigSynced { .. } | EventType::IncomingMsgBunch | EventType::ErrorSelfNotInGroup(_) | EventType::AccountsBackgroundFetchDone | EventType::ChatlistChanged | EventType::AccountsChanged | EventType::AccountsItemChanged | EventType::TransportsModified => 0, EventType::IncomingReaction { contact_id, .. } | EventType::IncomingWebxdcNotify { contact_id, .. } => contact_id.to_u32() as libc::c_int, EventType::MsgsChanged { chat_id, .. } | EventType::ReactionsChanged { chat_id, .. } | EventType::IncomingMsg { chat_id, .. } | EventType::MsgsNoticed(chat_id) | EventType::MsgDelivered { chat_id, .. } | EventType::MsgFailed { chat_id, .. } | EventType::MsgRead { chat_id, .. } | EventType::MsgDeleted { chat_id, .. } | EventType::MsgReadCountChanged { chat_id, .. } | EventType::ChatModified(chat_id) | EventType::ChatEphemeralTimerModified { chat_id, .. } | EventType::ChatDeleted { chat_id } => chat_id.to_u32() as libc::c_int, EventType::ContactsChanged(id) | EventType::LocationChanged(id) => { let id = id.unwrap_or_default(); id.to_u32() as libc::c_int } EventType::ConfigureProgress { progress, .. } | EventType::ImexProgress(progress) => { *progress as libc::c_int } EventType::ImexFileWritten(_) => 0, EventType::SecurejoinInviterProgress { contact_id, .. } | EventType::SecurejoinJoinerProgress { contact_id, .. } => { contact_id.to_u32() as libc::c_int } EventType::WebxdcRealtimeData { msg_id, .. } | EventType::WebxdcStatusUpdate { msg_id, .. } | EventType::WebxdcRealtimeAdvertisementReceived { msg_id } | EventType::WebxdcInstanceDeleted { msg_id, .. } | EventType::IncomingCall { msg_id, .. } | EventType::IncomingCallAccepted { msg_id, .. } | EventType::OutgoingCallAccepted { msg_id, .. } | EventType::CallEnded { msg_id, .. } => msg_id.to_u32() as libc::c_int, EventType::ChatlistItemChanged { chat_id } => { chat_id.unwrap_or_default().to_u32() as libc::c_int } EventType::EventChannelOverflow { n } => *n as libc::c_int, #[allow(unreachable_patterns)] #[cfg(test)] _ => unreachable!("This is just to silence a rust_analyzer false-positive"), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc::c_int { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_data2_int()"); return 0; } let event = unsafe { &(*event).typ }; match event { EventType::Info(_) | EventType::SmtpConnected(_) | EventType::ImapConnected(_) | EventType::SmtpMessageSent(_) | EventType::ImapMessageDeleted(_) | EventType::ImapMessageMoved(_) | EventType::ImapInboxIdle | EventType::NewBlobFile(_) | EventType::DeletedBlobFile(_) | EventType::Warning(_) | EventType::Error(_) | EventType::ErrorSelfNotInGroup(_) | EventType::ContactsChanged(_) | EventType::LocationChanged(_) | EventType::ConfigureProgress { .. } | EventType::ImexProgress(_) | EventType::ImexFileWritten(_) | EventType::MsgsNoticed(_) | EventType::ConnectivityChanged | EventType::WebxdcInstanceDeleted { .. } | EventType::IncomingMsgBunch | EventType::SelfavatarChanged | EventType::AccountsBackgroundFetchDone | EventType::ChatlistChanged | EventType::ChatlistItemChanged { .. } | EventType::AccountsChanged | EventType::AccountsItemChanged | EventType::ConfigSynced { .. } | EventType::ChatModified(_) | EventType::ChatDeleted { .. } | EventType::WebxdcRealtimeAdvertisementReceived { .. } | EventType::OutgoingCallAccepted { .. } | EventType::CallEnded { .. } | EventType::EventChannelOverflow { .. } | EventType::TransportsModified => 0, EventType::MsgsChanged { msg_id, .. } | EventType::ReactionsChanged { msg_id, .. } | EventType::IncomingReaction { msg_id, .. } | EventType::IncomingWebxdcNotify { msg_id, .. } | EventType::IncomingMsg { msg_id, .. } | EventType::MsgDelivered { msg_id, .. } | EventType::MsgFailed { msg_id, .. } | EventType::MsgRead { msg_id, .. } | EventType::MsgDeleted { msg_id, .. } | EventType::MsgReadCountChanged { msg_id, .. } => msg_id.to_u32() as libc::c_int, EventType::SecurejoinInviterProgress { progress, .. } | EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int, EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int, EventType::WebxdcStatusUpdate { status_update_serial, .. } => status_update_serial.to_u32() as libc::c_int, EventType::WebxdcRealtimeData { data, .. } => data.len() as libc::c_int, EventType::IncomingCall { has_video, .. } => *has_video as libc::c_int, EventType::IncomingCallAccepted { from_this_device, .. } => *from_this_device as libc::c_int, #[allow(unreachable_patterns)] #[cfg(test)] _ => unreachable!("This is just to silence a rust_analyzer false-positive"), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_data1_str(event: *mut dc_event_t) -> *mut libc::c_char { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_data1_str()"); return ptr::null_mut(); } let event = unsafe { &(*event).typ }; match event { EventType::IncomingWebxdcNotify { href, .. } => { if let Some(href) = href { href.to_c_string().unwrap_or_default().into_raw() } else { ptr::null_mut() } } _ => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut libc::c_char { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_data2_str()"); return ptr::null_mut(); } let event = unsafe { &(*event).typ }; match event { EventType::Info(msg) | EventType::SmtpConnected(msg) | EventType::ImapConnected(msg) | EventType::SmtpMessageSent(msg) | EventType::ImapMessageDeleted(msg) | EventType::ImapMessageMoved(msg) | EventType::NewBlobFile(msg) | EventType::DeletedBlobFile(msg) | EventType::Warning(msg) | EventType::Error(msg) | EventType::ErrorSelfNotInGroup(msg) => { let data2 = msg.to_c_string().unwrap_or_default(); data2.into_raw() } EventType::MsgsChanged { .. } | EventType::ReactionsChanged { .. } | EventType::IncomingMsg { .. } | EventType::ImapInboxIdle | EventType::MsgsNoticed(_) | EventType::MsgDelivered { .. } | EventType::MsgFailed { .. } | EventType::MsgRead { .. } | EventType::MsgDeleted { .. } | EventType::MsgReadCountChanged { .. } | EventType::ChatModified(_) | EventType::ContactsChanged(_) | EventType::LocationChanged(_) | EventType::ImexProgress(_) | EventType::SecurejoinInviterProgress { .. } | EventType::SecurejoinJoinerProgress { .. } | EventType::ConnectivityChanged | EventType::SelfavatarChanged | EventType::WebxdcStatusUpdate { .. } | EventType::WebxdcInstanceDeleted { .. } | EventType::AccountsBackgroundFetchDone | EventType::ChatEphemeralTimerModified { .. } | EventType::ChatDeleted { .. } | EventType::IncomingMsgBunch | EventType::ChatlistItemChanged { .. } | EventType::ChatlistChanged | EventType::AccountsChanged | EventType::AccountsItemChanged | EventType::IncomingCallAccepted { .. } | EventType::WebxdcRealtimeAdvertisementReceived { .. } | EventType::TransportsModified => ptr::null_mut(), EventType::IncomingCall { place_call_info, .. } => { let data2 = place_call_info.to_c_string().unwrap_or_default(); data2.into_raw() } EventType::OutgoingCallAccepted { accept_call_info, .. } => { let data2 = accept_call_info.to_c_string().unwrap_or_default(); data2.into_raw() } EventType::CallEnded { .. } | EventType::EventChannelOverflow { .. } => ptr::null_mut(), EventType::ConfigureProgress { comment, .. } => { if let Some(comment) = comment { comment.to_c_string().unwrap_or_default().into_raw() } else { ptr::null_mut() } } EventType::ImexFileWritten(file) => { let data2 = file.to_c_string().unwrap_or_default(); data2.into_raw() } EventType::ConfigSynced { key } => { let data2 = key.to_string().to_c_string().unwrap_or_default(); data2.into_raw() } EventType::WebxdcRealtimeData { data, .. } => { let ptr = unsafe { libc::malloc(data.len()) }; unsafe { libc::memcpy(ptr, data.as_ptr() as *mut libc::c_void, data.len()) }; ptr as *mut libc::c_char } EventType::IncomingReaction { reaction, .. } => reaction .as_str() .to_c_string() .unwrap_or_default() .into_raw(), EventType::IncomingWebxdcNotify { text, .. } => { text.to_c_string().unwrap_or_default().into_raw() } #[allow(unreachable_patterns)] #[cfg(test)] _ => unreachable!("This is just to silence a rust_analyzer false-positive"), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_get_account_id(event: *mut dc_event_t) -> u32 { if event.is_null() { eprintln!("ignoring careless call to dc_event_get_account_id()"); return 0; } unsafe { (*event).id } } pub type dc_event_emitter_t = EventEmitter; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_event_emitter( context: *mut dc_context_t, ) -> *mut dc_event_emitter_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_event_emitter()"); return ptr::null_mut(); } unsafe { let ctx = &*context; Box::into_raw(Box::new(ctx.get_event_emitter())) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_emitter_unref(emitter: *mut dc_event_emitter_t) { if emitter.is_null() { eprintln!("ignoring careless call to dc_event_emitter_unref()"); return; } drop(unsafe { Box::from_raw(emitter) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_next_event(events: *mut dc_event_emitter_t) -> *mut dc_event_t { if events.is_null() { eprintln!("ignoring careless call to dc_get_next_event()"); return ptr::null_mut(); } let events = unsafe { &*events }; block_on(async move { events .recv() .await .map(|ev| Box::into_raw(Box::new(ev))) .unwrap_or_else(ptr::null_mut) }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_stop_io(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_stop_io()"); return; } let ctx = unsafe { &*context }; block_on(async move { ctx.stop_io().await; }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_maybe_network()"); return; } let ctx = unsafe { &*context }; block_on(async move { ctx.maybe_network().await }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_preconfigure_keypair( context: *mut dc_context_t, secret_data: *const libc::c_char, ) -> i32 { if context.is_null() { eprintln!("ignoring careless call to dc_preconfigure_keypair()"); return 0; } let ctx = unsafe { &*context }; let secret_data = to_string_lossy(secret_data); block_on(preconfigure_keypair(ctx, &secret_data)) .context("Failed to save keypair") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chatlist( context: *mut dc_context_t, flags: libc::c_int, query_str: *const libc::c_char, query_id: u32, ) -> *mut dc_chatlist_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_chatlist()"); return ptr::null_mut(); } let context = unsafe { &*context }; let qs = to_opt_string_lossy(query_str); let qi = if query_id == 0 { None } else { Some(ContactId::new(query_id)) }; match block_on(chatlist::Chatlist::try_load( context, flags as usize, qs.as_deref(), qi, )) .context("Failed to get chatlist") .log_err(context) { Ok(list) => { let ffi_list = ChatlistWrapper { context: context.clone(), list, }; Box::into_raw(Box::new(ffi_list)) } Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_create_chat_by_contact_id( context: *mut dc_context_t, contact_id: u32, ) -> u32 { if context.is_null() { eprintln!("ignoring careless call to dc_create_chat_by_contact_id()"); return 0; } let ctx = unsafe { &*context }; block_on(ChatId::create_for_contact(ctx, ContactId::new(contact_id))) .context("Failed to create chat from contact_id") .log_err(ctx) .map(|id| id.to_u32()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_id_by_contact_id( context: *mut dc_context_t, contact_id: u32, ) -> u32 { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_id_by_contact_id()"); return 0; } let ctx = unsafe { &*context }; block_on(ChatId::lookup_by_contact(ctx, ContactId::new(contact_id))) .context("Failed to get chat for contact_id") .log_err(ctx) .unwrap_or_default() // unwraps the Result .map(|id| id.to_u32()) .unwrap_or(0) // unwraps the Option } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_msg( context: *mut dc_context_t, chat_id: u32, msg: *mut dc_msg_t, ) -> u32 { if context.is_null() || msg.is_null() { eprintln!("ignoring careless call to dc_send_msg()"); return 0; } let ctx = unsafe { &mut *context }; let ffi_msg = unsafe { &mut *msg }; block_on(chat::send_msg( ctx, ChatId::new(chat_id), &mut ffi_msg.message, )) .unwrap_or_log_default(ctx, "Failed to send message") .to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_msg_sync( context: *mut dc_context_t, chat_id: u32, msg: *mut dc_msg_t, ) -> u32 { if context.is_null() || msg.is_null() { eprintln!("ignoring careless call to dc_send_msg_sync()"); return 0; } let ctx = unsafe { &mut *context }; let ffi_msg = unsafe { &mut *msg }; block_on(chat::send_msg_sync( ctx, ChatId::new(chat_id), &mut ffi_msg.message, )) .unwrap_or_log_default(ctx, "Failed to send message") .to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_text_msg( context: *mut dc_context_t, chat_id: u32, text_to_send: *const libc::c_char, ) -> u32 { if context.is_null() || text_to_send.is_null() { eprintln!("ignoring careless call to dc_send_text_msg()"); return 0; } let ctx = unsafe { &*context }; let text_to_send = to_string_lossy(text_to_send); block_on(chat::send_text_msg(ctx, ChatId::new(chat_id), text_to_send)) .map(|msg_id| msg_id.to_u32()) .unwrap_or_log_default(ctx, "Failed to send text message") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_edit_request( context: *mut dc_context_t, msg_id: u32, new_text: *const libc::c_char, ) { if context.is_null() || new_text.is_null() { eprintln!("ignoring careless call to dc_send_edit_request()"); return; } let ctx = unsafe { &*context }; let new_text = to_string_lossy(new_text); block_on(chat::send_edit_request(ctx, MsgId::new(msg_id), new_text)) .unwrap_or_log_default(ctx, "Failed to send text edit") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_delete_request( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, ) { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 { eprintln!("ignoring careless call to dc_send_delete_request()"); return; } let ctx = unsafe { &*context }; let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); block_on(message::delete_msgs_ext(ctx, &msg_ids, true)) .context("failed dc_send_delete_request() call") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_webxdc_status_update( context: *mut dc_context_t, msg_id: u32, json: *const libc::c_char, _descr: *const libc::c_char, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_send_webxdc_status_update()"); return 0; } let ctx = unsafe { &*context }; block_on(ctx.send_webxdc_status_update(MsgId::new(msg_id), &to_string_lossy(json))) .context("Failed to send webxdc update") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_webxdc_status_updates( context: *mut dc_context_t, msg_id: u32, last_known_serial: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_webxdc_status_updates()"); return "".strdup(); } let ctx = unsafe { &*context }; block_on(ctx.get_webxdc_status_updates( MsgId::new(msg_id), StatusUpdateSerial::new(last_known_serial), )) .unwrap_or_log_default(ctx, "Failed to get webxdc status updates") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_webxdc_integration( context: *mut dc_context_t, file: *const libc::c_char, ) { if context.is_null() || file.is_null() { eprintln!("ignoring careless call to dc_set_webxdc_integration()"); return; } let ctx = unsafe { &*context }; block_on(ctx.set_webxdc_integration(&to_string_lossy(file))) .log_err(ctx) .unwrap_or_default(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_init_webxdc_integration( context: *mut dc_context_t, chat_id: u32, ) -> u32 { if context.is_null() { eprintln!("ignoring careless call to dc_init_webxdc_integration()"); return 0; } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; block_on(ctx.init_webxdc_integration(chat_id)) .log_err(ctx) .map(|msg_id| msg_id.map(|id| id.to_u32()).unwrap_or_default()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_place_outgoing_call( context: *mut dc_context_t, chat_id: u32, place_call_info: *const libc::c_char, has_video: bool, ) -> u32 { if context.is_null() || chat_id == 0 { eprintln!("ignoring careless call to dc_place_outgoing_call()"); return 0; } let ctx = unsafe { &*context }; let chat_id = ChatId::new(chat_id); let place_call_info = to_string_lossy(place_call_info); block_on(ctx.place_outgoing_call(chat_id, place_call_info, has_video)) .context("Failed to place call") .log_err(ctx) .map(|msg_id| msg_id.to_u32()) .unwrap_or_log_default(ctx, "Failed to place call") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accept_incoming_call( context: *mut dc_context_t, msg_id: u32, accept_call_info: *const libc::c_char, ) -> libc::c_int { if context.is_null() || msg_id == 0 { eprintln!("ignoring careless call to dc_accept_incoming_call()"); return 0; } let ctx = unsafe { &*context }; let msg_id = MsgId::new(msg_id); let accept_call_info = to_string_lossy(accept_call_info); block_on(ctx.accept_incoming_call(msg_id, accept_call_info)) .context("Failed to accept call") .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_end_call(context: *mut dc_context_t, msg_id: u32) -> libc::c_int { if context.is_null() || msg_id == 0 { eprintln!("ignoring careless call to dc_end_call()"); return 0; } let ctx = unsafe { &*context }; let msg_id = MsgId::new(msg_id); block_on(ctx.end_call(msg_id)) .context("Failed to end call") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_draft( context: *mut dc_context_t, chat_id: u32, msg: *mut dc_msg_t, ) { if context.is_null() { eprintln!("ignoring careless call to dc_set_draft()"); return; } let ctx = unsafe { &*context }; let msg = if msg.is_null() { None } else { let ffi_msg = unsafe { &mut *msg }; Some(&mut ffi_msg.message) }; block_on(ChatId::new(chat_id).set_draft(ctx, msg)) .unwrap_or_log_default(ctx, "failed to set draft"); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_add_device_msg( context: *mut dc_context_t, label: *const libc::c_char, msg: *mut dc_msg_t, ) -> u32 { if context.is_null() || (label.is_null() && msg.is_null()) { eprintln!("ignoring careless call to dc_add_device_msg()"); return 0; } let ctx = unsafe { &mut *context }; let msg = if msg.is_null() { None } else { let ffi_msg = unsafe { &mut *msg }; Some(&mut ffi_msg.message) }; block_on(chat::add_device_msg( ctx, to_opt_string_lossy(label).as_deref(), msg, )) .unwrap_or_log_default(ctx, "Failed to add device message") .to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_was_device_msg_ever_added( context: *mut dc_context_t, label: *const libc::c_char, ) -> libc::c_int { if context.is_null() || label.is_null() { eprintln!("ignoring careless call to dc_was_device_msg_ever_added()"); return 0; } let ctx = unsafe { &mut *context }; block_on(chat::was_device_msg_ever_added( ctx, &to_string_lossy(label), )) .unwrap_or(false) as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) -> *mut dc_msg_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_draft()"); return ptr::null_mut(); // NULL explicitly defined as "no draft" } let context = unsafe { &*context }; match block_on(ChatId::new(chat_id).get_draft(context)) .with_context(|| format!("Failed to get draft for chat #{chat_id}")) .unwrap_or_default() { Some(draft) => { let ffi_msg = MessageWrapper { context: context.clone(), message: draft, }; Box::into_raw(Box::new(ffi_msg)) } None => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_msgs( context: *mut dc_context_t, chat_id: u32, flags: u32, _marker1before: u32, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_msgs()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let add_daymarker = (flags & DC_GCM_ADDDAYMARKER) != 0; Box::into_raw(Box::new( block_on(chat::get_chat_msgs_ext( ctx, ChatId::new(chat_id), MessageListOptions { add_daymarker }, )) .unwrap_or_log_default(ctx, "failed to get chat msgs") .into(), )) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_get_msg_cnt()"); return 0; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).get_msg_cnt(ctx)) .unwrap_or_log_default(ctx, "failed to get msg count") as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_fresh_msg_cnt( context: *mut dc_context_t, chat_id: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_get_fresh_msg_cnt()"); return 0; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).get_fresh_msg_cnt(ctx)) .unwrap_or_log_default(ctx, "failed to get fresh msg cnt") as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_similar_chatlist( context: *mut dc_context_t, chat_id: u32, ) -> *mut dc_chatlist_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_similar_chatlist()"); return ptr::null_mut(); } let context = unsafe { &*context }; let chat_id = ChatId::new(chat_id); match block_on(chat_id.get_similar_chatlist(context)) .context("failed to get similar chatlist") .log_err(context) { Ok(list) => { let ffi_list = ChatlistWrapper { context: context.clone(), list, }; Box::into_raw(Box::new(ffi_list)) } Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_estimate_deletion_cnt( context: *mut dc_context_t, from_server: libc::c_int, seconds: i64, ) -> libc::c_int { if context.is_null() || seconds < 0 { eprintln!("ignoring careless call to dc_estimate_deletion_cnt()"); return 0; } let ctx = unsafe { &*context }; block_on(message::estimate_deletion_cnt( ctx, from_server != 0, seconds, )) .unwrap_or(0) as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_fresh_msgs( context: *mut dc_context_t, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_fresh_msgs()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let arr = dc_array_t::from( block_on(ctx.get_fresh_msgs()) .context("Failed to get fresh messages") .log_err(ctx) .unwrap_or_default() .iter() .map(|msg_id| msg_id.to_u32()) .collect::>(), ); Box::into_raw(Box::new(arr)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_next_msgs(context: *mut dc_context_t) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_next_msgs()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let msg_ids = block_on(ctx.get_next_msgs()) .context("failed to get next messages") .log_err(ctx) .unwrap_or_default(); let arr = dc_array_t::from( msg_ids .iter() .map(|msg_id| msg_id.to_u32()) .collect::>(), ); Box::into_raw(Box::new(arr)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_wait_next_msgs( context: *mut dc_context_t, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_wait_next_msgs()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let msg_ids = block_on(ctx.wait_next_msgs()) .context("failed to wait for next messages") .log_err(ctx) .unwrap_or_default(); let arr = dc_array_t::from( msg_ids .iter() .map(|msg_id| msg_id.to_u32()) .collect::>(), ); Box::into_raw(Box::new(arr)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_marknoticed_chat()"); return; } let ctx = unsafe { &*context }; block_on(chat::marknoticed_chat(ctx, ChatId::new(chat_id))) .context("Failed marknoticed chat") .log_err(ctx) .unwrap_or(()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_markfresh_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_markfresh_chat()"); return; } let ctx = unsafe { &*context }; block_on(chat::markfresh_chat(ctx, ChatId::new(chat_id))) .context("Failed markfresh chat") .log_err(ctx) .unwrap_or(()) } fn from_prim(s: S) -> Option where T: FromPrimitive, S: Into, { FromPrimitive::from_i64(s.into()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_media( context: *mut dc_context_t, chat_id: u32, msg_type: libc::c_int, or_msg_type2: libc::c_int, or_msg_type3: libc::c_int, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_media()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {msg_type}")); let or_msg_type2 = from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {or_msg_type2}")); let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {or_msg_type3}")); Box::into_raw(Box::new( block_on(chat::get_chat_media( ctx, chat_id, msg_type, or_msg_type2, or_msg_type3, )) .unwrap_or_log_default(ctx, "Failed get_chat_media") .into(), )) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_chat_visibility( context: *mut dc_context_t, chat_id: u32, archive: libc::c_int, ) { if context.is_null() { eprintln!("ignoring careless call to dc_set_chat_visibility()"); return; } let ctx = unsafe { &*context }; let visibility = match archive { 0 => ChatVisibility::Normal, 1 => ChatVisibility::Archived, 2 => ChatVisibility::Pinned, _ => { eprintln!("ignoring careless call to dc_set_chat_visibility(): unknown archived state"); return; } }; block_on(ChatId::new(chat_id).set_visibility(ctx, visibility)) .context("Failed setting chat visibility") .log_err(ctx) .unwrap_or(()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_delete_chat()"); return; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).delete(ctx)) .context("Failed chat delete") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_block_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_block_chat()"); return; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).block(ctx)) .context("Failed chat block") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accept_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_accept_chat()"); return; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).accept(ctx)) .context("Failed chat accept") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_contacts( context: *mut dc_context_t, chat_id: u32, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_contacts()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let arr = dc_array_t::from( block_on(chat::get_chat_contacts(ctx, ChatId::new(chat_id))) .unwrap_or_log_default(ctx, "Failed get_chat_contacts") .iter() .map(|id| id.to_u32()) .collect::>(), ); Box::into_raw(Box::new(arr)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_search_msgs( context: *mut dc_context_t, chat_id: u32, query: *const libc::c_char, ) -> *mut dc_array::dc_array_t { if context.is_null() || query.is_null() { eprintln!("ignoring careless call to dc_search_msgs()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; let arr = dc_array_t::from( block_on(ctx.search_msgs(chat_id, &to_string_lossy(query))) .unwrap_or_log_default(ctx, "Failed search_msgs") .iter() .map(|msg_id| msg_id.to_u32()) .collect::>(), ); Box::into_raw(Box::new(arr)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) -> *mut dc_chat_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat()"); return ptr::null_mut(); } let context = unsafe { &*context }; match block_on(chat::Chat::load_from_db(context, ChatId::new(chat_id))) { Ok(chat) => { let ffi_chat = ChatWrapper { context: context.clone(), chat, }; Box::into_raw(Box::new(ffi_chat)) } Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_create_group_chat( context: *mut dc_context_t, _protect: libc::c_int, name: *const libc::c_char, ) -> u32 { if context.is_null() || name.is_null() { eprintln!("ignoring careless call to dc_create_group_chat()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::create_group(ctx, &to_string_lossy(name))) .context("Failed to create group chat") .log_err(ctx) .map(|id| id.to_u32()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_create_broadcast_list(context: *mut dc_context_t) -> u32 { unsafe { if context.is_null() { eprintln!("ignoring careless call to dc_create_broadcast_list()"); return 0; } let ctx = &*context; block_on(chat::create_broadcast(ctx, "Channel".to_string())) .context("Failed to create broadcast channel") .log_err(ctx) .map(|id| id.to_u32()) .unwrap_or(0) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_is_contact_in_chat( context: *mut dc_context_t, chat_id: u32, contact_id: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_is_contact_in_chat()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::is_contact_in_chat( ctx, ChatId::new(chat_id), ContactId::new(contact_id), )) .context("is_contact_in_chat failed") .log_err(ctx) .unwrap_or_default() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_add_contact_to_chat( context: *mut dc_context_t, chat_id: u32, contact_id: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_add_contact_to_chat()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::add_contact_to_chat( ctx, ChatId::new(chat_id), ContactId::new(contact_id), )) .context("Failed to add contact") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_remove_contact_from_chat( context: *mut dc_context_t, chat_id: u32, contact_id: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_remove_contact_from_chat()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::remove_contact_from_chat( ctx, ChatId::new(chat_id), ContactId::new(contact_id), )) .context("Failed to remove contact") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_chat_name( context: *mut dc_context_t, chat_id: u32, name: *const libc::c_char, ) -> libc::c_int { if context.is_null() || chat_id <= ChatId::LAST_SPECIAL.to_u32() || name.is_null() { eprintln!("ignoring careless call to dc_set_chat_name()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::set_chat_name( ctx, ChatId::new(chat_id), &to_string_lossy(name), )) .map(|_| 1) .unwrap_or_log_default(ctx, "Failed to set chat name") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_chat_profile_image( context: *mut dc_context_t, chat_id: u32, image: *const libc::c_char, ) -> libc::c_int { if context.is_null() || chat_id <= ChatId::LAST_SPECIAL.to_u32() { eprintln!("ignoring careless call to dc_set_chat_profile_image()"); return 0; } let ctx = unsafe { &*context }; block_on(chat::set_chat_profile_image( ctx, ChatId::new(chat_id), &to_string_lossy(image), )) .map(|_| 1) .unwrap_or_log_default(ctx, "Failed to set profile image") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_chat_mute_duration( context: *mut dc_context_t, chat_id: u32, duration: i64, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_set_chat_mute_duration()"); return 0; } let ctx = unsafe { &*context }; let mute_duration = match duration { 0 => MuteDuration::NotMuted, -1 => MuteDuration::Forever, n if n > 0 => SystemTime::now() .checked_add(Duration::from_secs(duration as u64)) .map_or(MuteDuration::Forever, MuteDuration::Until), _ => { eprintln!("dc_chat_set_mute_duration(): Can not use negative duration other than -1"); return 0; } }; block_on(chat::set_muted(ctx, ChatId::new(chat_id), mute_duration)) .map(|_| 1) .unwrap_or_log_default(ctx, "Failed to set mute duration") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_encrinfo( context: *mut dc_context_t, chat_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_encrinfo()"); return "".strdup(); } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).get_encryption_info(ctx)) .map(|s| s.strdup()) .log_err(ctx) .unwrap_or(ptr::null_mut()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_chat_ephemeral_timer( context: *mut dc_context_t, chat_id: u32, ) -> u32 { if context.is_null() { eprintln!("ignoring careless call to dc_get_chat_ephemeral_timer()"); return 0; } let ctx = unsafe { &*context }; // Timer value 0 is returned in the rare case of a database error, // but it is not dangerous since it is only meant to be used as a // default when changing the value. Such errors should not be // ignored when ephemeral timer value is used to construct // message headers. block_on(ChatId::new(chat_id).get_ephemeral_timer(ctx)) .context("Failed to get ephemeral timer") .log_err(ctx) .unwrap_or_default() .to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_chat_ephemeral_timer( context: *mut dc_context_t, chat_id: u32, timer: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_set_chat_ephemeral_timer()"); return 0; } let ctx = unsafe { &*context }; block_on(ChatId::new(chat_id).set_ephemeral_timer(ctx, EphemeralTimer::from_u32(timer))) .context("Failed to set ephemeral timer") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_msg_info( context: *mut dc_context_t, msg_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_msg_info()"); return "".strdup(); } let ctx = unsafe { &*context }; let msg_id = MsgId::new(msg_id); block_on(msg_id.get_info(ctx)) .unwrap_or_log_default(ctx, "failed to get msg id") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_msg_html( context: *mut dc_context_t, msg_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_msg_html()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; block_on(MsgId::new(msg_id).get_html(ctx)) .unwrap_or_log_default(ctx, "Failed get_msg_html") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_delete_msgs( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, ) { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 { eprintln!("ignoring careless call to dc_delete_msgs()"); return; } let ctx = unsafe { &*context }; let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); block_on(message::delete_msgs(ctx, &msg_ids)) .context("failed dc_delete_msgs() call") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_forward_msgs( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, chat_id: u32, ) { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 || chat_id <= ChatId::LAST_SPECIAL.to_u32() { eprintln!("ignoring careless call to dc_forward_msgs()"); return; } let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); let ctx = unsafe { &*context }; block_on(chat::forward_msgs(ctx, &msg_ids[..], ChatId::new(chat_id))) .unwrap_or_log_default(ctx, "Failed to forward message") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_save_msgs( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, ) { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 { eprintln!("ignoring careless call to dc_save_msgs()"); return; } let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); let ctx = unsafe { &*context }; block_on(chat::save_msgs(ctx, &msg_ids[..])) .unwrap_or_log_default(ctx, "Failed to save message") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_resend_msgs( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, ) -> libc::c_int { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 { eprintln!("ignoring careless call to dc_resend_msgs()"); return 0; } let ctx = unsafe { &*context }; let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); block_on(chat::resend_msgs(ctx, &msg_ids)) .context("Resending failed") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_markseen_msgs( context: *mut dc_context_t, msg_ids: *const u32, msg_cnt: libc::c_int, ) { if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 { eprintln!("ignoring careless call to dc_markseen_msgs()"); return; } let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); let ctx = unsafe { &*context }; block_on(message::markseen_msgs(ctx, msg_ids)) .context("failed dc_markseen_msgs() call") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) -> *mut dc_msg_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_msg()"); return ptr::null_mut(); } let context = unsafe { &*context }; let message = match block_on(message::Message::load_from_db(context, MsgId::new(msg_id))) .with_context(|| format!("dc_get_msg could not rectieve msg_id {msg_id}")) .log_err(context) { Ok(msg) => msg, Err(_) => { if MsgId::new(msg_id).is_special() { // C-core API returns empty messages, do the same message::Message::new(Viewtype::default()) } else { return ptr::null_mut(); } } }; let ffi_msg = MessageWrapper { context: context.clone(), message, }; Box::into_raw(Box::new(ffi_msg)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_download_full_msg(context: *mut dc_context_t, msg_id: u32) { if context.is_null() { eprintln!("ignoring careless call to dc_download_full_msg()"); return; } let ctx = unsafe { &*context }; block_on(MsgId::new(msg_id).download_full(ctx)) .context("Failed to download message fully.") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *const libc::c_char) -> libc::c_int { if addr.is_null() { eprintln!("ignoring careless call to dc_may_be_valid_addr()"); return 0; } contact::may_be_valid_addr(&to_string_lossy(addr)) as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lookup_contact_id_by_addr( context: *mut dc_context_t, addr: *const libc::c_char, ) -> u32 { if context.is_null() || addr.is_null() { eprintln!("ignoring careless call to dc_lookup_contact_id_by_addr()"); return 0; } let ctx = unsafe { &*context }; block_on(Contact::lookup_id_by_addr( ctx, &to_string_lossy(addr), Origin::IncomingReplyTo, )) .unwrap_or_log_default(ctx, "failed to lookup id") .map(|id| id.to_u32()) .unwrap_or_default() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_create_contact( context: *mut dc_context_t, name: *const libc::c_char, addr: *const libc::c_char, ) -> u32 { if context.is_null() || addr.is_null() { eprintln!("ignoring careless call to dc_create_contact()"); return 0; } let ctx = unsafe { &*context }; let name = to_string_lossy(name); block_on(Contact::create(ctx, &name, &to_string_lossy(addr))) .context("Cannot create contact") .log_err(ctx) .map(|id| id.to_u32()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_add_address_book( context: *mut dc_context_t, addr_book: *const libc::c_char, ) -> libc::c_int { if context.is_null() || addr_book.is_null() { eprintln!("ignoring careless call to dc_add_address_book()"); return 0; } let ctx = unsafe { &*context }; match block_on(Contact::add_address_book(ctx, &to_string_lossy(addr_book))) { Ok(cnt) => cnt as libc::c_int, Err(_) => 0, } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_make_vcard( context: *mut dc_context_t, contact_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_make_vcard()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let contact_id = ContactId::new(contact_id); block_on(contact::make_vcard(ctx, &[contact_id])) .unwrap_or_log_default(ctx, "dc_make_vcard failed") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_import_vcard( context: *mut dc_context_t, vcard: *const libc::c_char, ) -> *mut dc_array::dc_array_t { if context.is_null() || vcard.is_null() { eprintln!("ignoring careless call to dc_import_vcard()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; match block_on(contact::import_vcard(ctx, &to_string_lossy(vcard))) .context("dc_import_vcard failed") .log_err(ctx) { Ok(contact_ids) => Box::into_raw(Box::new(dc_array_t::from( contact_ids .iter() .map(|id| id.to_u32()) .collect::>(), ))), Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_contacts( context: *mut dc_context_t, flags: u32, query: *const libc::c_char, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_contacts()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let query = to_opt_string_lossy(query); match block_on(Contact::get_all(ctx, flags, query.as_deref())) { Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from( contacts.iter().map(|id| id.to_u32()).collect::>(), ))), Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_blocked_contacts( context: *mut dc_context_t, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_blocked_contacts()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; Box::into_raw(Box::new(dc_array_t::from( block_on(Contact::get_all_blocked(ctx)) .context("Can't get blocked contacts") .log_err(ctx) .unwrap_or_default() .iter() .map(|id| id.to_u32()) .collect::>(), ))) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_block_contact( context: *mut dc_context_t, contact_id: u32, block: libc::c_int, ) { let contact_id = ContactId::new(contact_id); if context.is_null() || contact_id.is_special() { eprintln!("ignoring careless call to dc_block_contact()"); return; } let ctx = unsafe { &*context }; block_on(async move { if block == 0 { Contact::unblock(ctx, contact_id) .await .context("Can't unblock contact") .log_err(ctx) .ok(); } else { Contact::block(ctx, contact_id) .await .context("Can't block contact") .log_err(ctx) .ok(); } }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_contact_encrinfo( context: *mut dc_context_t, contact_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_contact_encrinfo()"); return "".strdup(); } let ctx = unsafe { &*context }; block_on(Contact::get_encrinfo(ctx, ContactId::new(contact_id))) .map(|s| s.strdup()) .log_err(ctx) .unwrap_or(ptr::null_mut()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_delete_contact( context: *mut dc_context_t, contact_id: u32, ) -> libc::c_int { let contact_id = ContactId::new(contact_id); if context.is_null() || contact_id.is_special() { eprintln!("ignoring careless call to dc_delete_contact()"); return 0; } let ctx = unsafe { &*context }; block_on(Contact::delete(ctx, contact_id)) .context("Cannot delete contact") .log_err(ctx) .is_ok() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_contact( context: *mut dc_context_t, contact_id: u32, ) -> *mut dc_contact_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_contact()"); return ptr::null_mut(); } let context = unsafe { &*context }; block_on(async move { Contact::get_by_id(context, ContactId::new(contact_id)) .await .map(|contact| { Box::into_raw(Box::new(ContactWrapper { context: context.clone(), contact, })) }) .unwrap_or_else(|_| ptr::null_mut()) }) } fn spawn_imex(ctx: Context, what: imex::ImexMode, param1: String, passphrase: Option) { spawn(async move { imex::imex(&ctx, what, param1.as_ref(), passphrase) .await .context("IMEX failed") .log_err(&ctx) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_imex( context: *mut dc_context_t, what_raw: libc::c_int, param1: *const libc::c_char, param2: *const libc::c_char, ) { if context.is_null() { eprintln!("ignoring careless call to dc_imex()"); return; } let what = match imex::ImexMode::from_i32(what_raw) { Some(what) => what, None => { eprintln!("ignoring invalid argument {what_raw} to dc_imex"); return; } }; let passphrase = to_opt_string_lossy(param2); let ctx = unsafe { &*context }; if let Some(param1) = to_opt_string_lossy(param1) { spawn_imex(ctx.clone(), what, param1, passphrase); } else { eprintln!("dc_imex called without a valid directory"); } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_imex_has_backup( context: *mut dc_context_t, dir: *const libc::c_char, ) -> *mut libc::c_char { if context.is_null() || dir.is_null() { eprintln!("ignoring careless call to dc_imex_has_backup()"); return ptr::null_mut(); // NULL explicitly defined as "has no backup" } let ctx = unsafe { &*context }; match block_on(imex::has_backup(ctx, to_string_lossy(dir).as_ref())) .context("dc_imex_has_backup") .log_err(ctx) { Ok(res) => res.strdup(), Err(_) => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_stop_ongoing_process()"); return; } let ctx = unsafe { &*context }; block_on(ctx.stop_ongoing()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_check_qr( context: *mut dc_context_t, qr: *const libc::c_char, ) -> *mut dc_lot_t { if context.is_null() || qr.is_null() { eprintln!("ignoring careless call to dc_check_qr()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let lot = match block_on(qr::check_qr(ctx, &to_string_lossy(qr))) { Ok(qr) => qr.into(), Err(err) => err.into(), }; Box::into_raw(Box::new(lot)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_securejoin_qr( context: *mut dc_context_t, chat_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_securejoin_qr()"); return "".strdup(); } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; block_on(securejoin::get_securejoin_qr(ctx, chat_id)) .unwrap_or_log_default(ctx, "Failed to generate securejoin QR code") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_securejoin_qr_svg( context: *mut dc_context_t, chat_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to generate_verification_qr()"); return "".strdup(); } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; block_on(get_securejoin_qr_svg(ctx, chat_id)) .unwrap_or_log_default(ctx, "Failed to generate securejoin QR code SVG") .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_join_securejoin( context: *mut dc_context_t, qr: *const libc::c_char, ) -> u32 { if context.is_null() || qr.is_null() { eprintln!("ignoring careless call to dc_join_securejoin()"); return 0; } let ctx = unsafe { &*context }; block_on(async move { securejoin::join_securejoin(ctx, &to_string_lossy(qr)) .await .map(|chatid| chatid.to_u32()) .context("failed dc_join_securejoin() call") .log_err(ctx) .unwrap_or_default() }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_send_locations_to_chat( context: *mut dc_context_t, chat_id: u32, seconds: libc::c_int, ) { if context.is_null() || chat_id <= ChatId::LAST_SPECIAL.to_u32() || seconds < 0 { eprintln!("ignoring careless call to dc_send_locations_to_chat()"); return; } let ctx = unsafe { &*context }; block_on(location::send_to_chat( ctx, ChatId::new(chat_id), seconds as i64, )) .context("Failed dc_send_locations_to_chat()") .log_err(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_is_sending_locations_to_chat( context: *mut dc_context_t, chat_id: u32, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_is_sending_locations_to_chat()"); return 0; } let ctx = unsafe { &*context }; if chat_id == 0 { block_on(location::is_sending(ctx)) .unwrap_or_log_default(ctx, "Failed is_sending_locations()") as libc::c_int } else { block_on(location::is_sending_to_chat(ctx, ChatId::new(chat_id))) .unwrap_or_log_default(ctx, "Failed is_sending_locations_to_chat()") as libc::c_int } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_set_location( context: *mut dc_context_t, latitude: libc::c_double, longitude: libc::c_double, accuracy: libc::c_double, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_set_location()"); return 0; } let ctx = unsafe { &*context }; block_on(location::set(ctx, latitude, longitude, accuracy)) .log_err(ctx) .unwrap_or_default() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_locations( context: *mut dc_context_t, chat_id: u32, contact_id: u32, timestamp_begin: i64, timestamp_end: i64, ) -> *mut dc_array::dc_array_t { if context.is_null() { eprintln!("ignoring careless call to dc_get_locations()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let chat_id = if chat_id == 0 { None } else { Some(ChatId::new(chat_id)) }; let contact_id = if contact_id == 0 { None } else { Some(contact_id) }; let res = block_on(location::get_range( ctx, chat_id, contact_id, timestamp_begin, timestamp_end, )) .unwrap_or_log_default(ctx, "Failed get_locations"); Box::into_raw(Box::new(dc_array_t::from(res))) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_create_qr_svg(payload: *const libc::c_char) -> *mut libc::c_char { if payload.is_null() { eprintln!("ignoring careless call to dc_create_qr_svg()"); return "".strdup(); } create_qr_svg(&to_string_lossy(payload)) .unwrap_or_else(|_| "".to_string()) .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_get_last_error(context: *mut dc_context_t) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_get_last_error()"); return "".strdup(); } let ctx = unsafe { &*context }; ctx.get_last_error().strdup() } // dc_array_t pub type dc_array_t = dc_array::dc_array_t; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) { if a.is_null() { eprintln!("ignoring careless call to dc_array_unref()"); return; } drop(unsafe { Box::from_raw(a) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_cnt()"); return 0; } unsafe { (*array).len() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_id(array: *const dc_array_t, index: libc::size_t) -> u32 { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_id()"); return 0; } unsafe { (*array).get_id(index) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_latitude( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_latitude()"); return 0.0; } unsafe { (*array).get_location(index).latitude } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_longitude( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_longitude()"); return 0.0; } unsafe { (*array).get_location(index).longitude } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_accuracy( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_accuracy()"); return 0.0; } unsafe { (*array).get_location(index).accuracy } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_timestamp( array: *const dc_array_t, index: libc::size_t, ) -> i64 { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_timestamp()"); return 0; } unsafe { (*array).get_timestamp(index).unwrap_or_default() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_chat_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_chat_id()"); return 0; } unsafe { (*array).get_location(index).chat_id.to_u32() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_contact_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_contact_id()"); return 0; } unsafe { (*array).get_location(index).contact_id.to_u32() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_msg_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_msg_id()"); return 0; } unsafe { (*array).get_location(index).msg_id } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_get_marker( array: *const dc_array_t, index: libc::size_t, ) -> *mut libc::c_char { if array.is_null() { eprintln!("ignoring careless call to dc_array_get_marker()"); return std::ptr::null_mut(); // NULL explicitly defined as "no markers" } if let Some(s) = unsafe { (*array).get_marker(index) } { s.strdup() } else { std::ptr::null_mut() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_search_id( array: *const dc_array_t, needle: libc::c_uint, ret_index: *mut libc::size_t, ) -> libc::c_int { if array.is_null() { eprintln!("ignoring careless call to dc_array_search_id()"); return 0; } if let Some(i) = unsafe { (*array).search_id(needle) } { if !ret_index.is_null() { unsafe { *ret_index = i } } 1 } else { 0 } } // Return the independent-state of the location at the given index. // Independent locations do not belong to the track of the user. // Returns 1 if location belongs to the track of the user, // 0 if location was reported independently. #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_array_is_independent( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_int { if array.is_null() { eprintln!("ignoring careless call to dc_array_is_independent()"); return 0; } unsafe { (*array).get_location(index).independent as libc::c_int } } // dc_chatlist_t /// FFI struct for [dc_chatlist_t] /// /// This is the structure behind [dc_chatlist_t] which is the opaque /// structure representing a chatlist in the FFI API. It exists /// because the FFI API has a reference from the message to the /// context, but the Rust API does not, so the FFI layer needs to glue /// these together. pub struct ChatlistWrapper { context: Context, list: chatlist::Chatlist, } pub type dc_chatlist_t = ChatlistWrapper; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_unref(chatlist: *mut dc_chatlist_t) { if chatlist.is_null() { eprintln!("ignoring careless call to dc_chatlist_unref()"); return; } drop(unsafe { Box::from_raw(chatlist) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_get_cnt(chatlist: *mut dc_chatlist_t) -> libc::size_t { if chatlist.is_null() { eprintln!("ignoring careless call to dc_chatlist_get_cnt()"); return 0; } let ffi_list = unsafe { &*chatlist }; ffi_list.list.len() as libc::size_t } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_get_chat_id( chatlist: *mut dc_chatlist_t, index: libc::size_t, ) -> u32 { if chatlist.is_null() { eprintln!("ignoring careless call to dc_chatlist_get_chat_id()"); return 0; } let ffi_list = unsafe { &*chatlist }; match ffi_list .list .get_chat_id(index) .context("get_chat_id failed") .log_err(&ffi_list.context) { Ok(chat_id) => chat_id.to_u32(), Err(_) => 0, } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_get_msg_id( chatlist: *mut dc_chatlist_t, index: libc::size_t, ) -> u32 { if chatlist.is_null() { eprintln!("ignoring careless call to dc_chatlist_get_msg_id()"); return 0; } let ffi_list = unsafe { &*chatlist }; match ffi_list .list .get_msg_id(index) .context("get_msg_id failed") .log_err(&ffi_list.context) { Ok(msg_id) => msg_id.map_or(0, |msg_id| msg_id.to_u32()), Err(_) => 0, } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_get_summary( chatlist: *mut dc_chatlist_t, index: libc::size_t, chat: *mut dc_chat_t, ) -> *mut dc_lot_t { if chatlist.is_null() { eprintln!("ignoring careless call to dc_chatlist_get_summary()"); return ptr::null_mut(); } let maybe_chat = if chat.is_null() { None } else { let ffi_chat = unsafe { &*chat }; Some(&ffi_chat.chat) }; let ffi_list = unsafe { &*chatlist }; let summary = block_on( ffi_list .list .get_summary(&ffi_list.context, index, maybe_chat), ) .context("get_summary failed") .log_err(&ffi_list.context) .unwrap_or_default(); Box::into_raw(Box::new(summary.into())) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chatlist_get_summary2( context: *mut dc_context_t, chat_id: u32, msg_id: u32, ) -> *mut dc_lot_t { if context.is_null() { eprintln!("ignoring careless call to dc_chatlist_get_summary2()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; let msg_id = if msg_id == 0 { None } else { Some(MsgId::new(msg_id)) }; let summary = block_on(Chatlist::get_summary2( ctx, ChatId::new(chat_id), msg_id, None, )) .context("get_summary2 failed") .log_err(ctx) .unwrap_or_default(); Box::into_raw(Box::new(summary.into())) } // dc_chat_t /// FFI struct for [dc_chat_t] /// /// This is the structure behind [dc_chat_t] which is the opaque /// structure representing a chat in the FFI API. It exists /// because the FFI API has a reference from the message to the /// context, but the Rust API does not, so the FFI layer needs to glue /// these together. pub struct ChatWrapper { context: Context, chat: chat::Chat, } pub type dc_chat_t = ChatWrapper; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_unref()"); return; } drop(unsafe { Box::from_raw(chat) }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_id()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.get_id().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_type()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.get_type() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_name()"); return "".strdup(); } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.get_name().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_mailinglist_addr(chat: *mut dc_chat_t) -> *mut libc::c_char { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_mailinglist_addr()"); return "".strdup(); } let ffi_chat = unsafe { &*chat }; ffi_chat .chat .get_mailinglist_addr() .unwrap_or_default() .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_profile_image()"); return ptr::null_mut(); // NULL explicitly defined as "no image" } let ffi_chat = unsafe { &*chat }; match block_on(ffi_chat.chat.get_profile_image(&ffi_chat.context)) .context("Failed to get profile image") .log_err(&ffi_chat.context) .unwrap_or_default() { Some(p) => p.to_string_lossy().strdup(), None => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_color()"); return 0; } let ffi_chat = unsafe { &*chat }; block_on(ffi_chat.chat.get_color(&ffi_chat.context)) .unwrap_or_log_default(&ffi_chat.context, "Failed get_color") } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_visibility(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_visibility()"); return 0; } let ffi_chat = unsafe { &*chat }; match ffi_chat.chat.visibility { ChatVisibility::Normal => 0, ChatVisibility::Archived => 1, ChatVisibility::Pinned => 2, } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_contact_request(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_contact_request()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_contact_request() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_unpromoted()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_unpromoted() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_self_talk()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_self_talk() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_device_talk(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_device_talk()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_device_talk() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_can_send()"); return 0; } let ffi_chat = unsafe { &*chat }; block_on(ffi_chat.chat.can_send(&ffi_chat.context)) .context("can_send failed") .log_err(&ffi_chat.context) .unwrap_or_default() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_encrypted(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_encrypted()"); return 0; } let ffi_chat = unsafe { &*chat }; block_on(ffi_chat.chat.is_encrypted(&ffi_chat.context)) .unwrap_or_log_default(&ffi_chat.context, "Failed dc_chat_is_encrypted") as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_sending_locations()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_sending_locations() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_is_muted(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_is_muted()"); return 0; } let ffi_chat = unsafe { &*chat }; ffi_chat.chat.is_muted() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_remaining_mute_duration(chat: *mut dc_chat_t) -> i64 { if chat.is_null() { eprintln!("ignoring careless call to dc_chat_get_remaining_mute_duration()"); return 0; } let ffi_chat = unsafe { &*chat }; if !ffi_chat.chat.is_muted() { return 0; } // If the chat was muted to before the epoch, it is not muted. match ffi_chat.chat.mute_duration { MuteDuration::NotMuted => 0, MuteDuration::Forever => -1, MuteDuration::Until(when) => when .duration_since(SystemTime::now()) .map(|d| d.as_secs() as i64) .unwrap_or(0), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_chat_get_info_json( context: *mut dc_context_t, chat_id: u32, ) -> *mut libc::c_char { if context.is_null() { eprintln!("ignoring careless call to dc_chat_get_info_json()"); return "".strdup(); } let ctx = unsafe { &*context }; let Ok(chat) = block_on(chat::Chat::load_from_db(ctx, ChatId::new(chat_id))) .context("dc_get_chat_info_json() failed to load chat") .log_err(ctx) else { return "".strdup(); }; let Ok(info) = block_on(chat.get_info(ctx)) .context("dc_get_chat_info_json() failed to get chat info") .log_err(ctx) else { return "".strdup(); }; serde_json::to_string(&info) .unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json") .strdup() } // dc_msg_t /// FFI struct for [dc_msg_t] /// /// This is the structure behind [dc_msg_t] which is the opaque /// structure representing a message in the FFI API. It exists /// because the FFI API has a reference from the message to the /// context, but the Rust API does not, so the FFI layer needs to glue /// these together. pub struct MessageWrapper { context: Context, message: message::Message, } pub type dc_msg_t = MessageWrapper; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_new( context: *mut dc_context_t, viewtype: libc::c_int, ) -> *mut dc_msg_t { if context.is_null() { eprintln!("ignoring careless call to dc_msg_new()"); return ptr::null_mut(); } let context = unsafe { &*context }; let viewtype = from_prim(viewtype).expect(&format!("invalid viewtype = {viewtype}")); let msg = MessageWrapper { context: context.clone(), message: message::Message::new(viewtype), }; Box::into_raw(Box::new(msg)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_unref(msg: *mut dc_msg_t) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_unref()"); return; } drop(unsafe { Box::from_raw(msg) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_id()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_id().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_from_id(msg: *mut dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_from_id()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_from_id().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_chat_id()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_chat_id().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_viewtype()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg .message .get_viewtype() .to_i64() .expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_state()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_state() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_download_state(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_download_state()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.download_state() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_timestamp(msg: *mut dc_msg_t) -> i64 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_received_timestamp()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_timestamp() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_received_timestamp(msg: *mut dc_msg_t) -> i64 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_received_timestamp()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_received_timestamp() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_sort_timestamp(msg: *mut dc_msg_t) -> i64 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_sort_timestamp()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_sort_timestamp() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_text()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_text().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_subject(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_subject()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_subject().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_file()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; ffi_msg .message .get_file(&ffi_msg.context) .map(|p| p.to_string_lossy().strdup()) .unwrap_or_else(|| "".strdup()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_save_file( msg: *mut dc_msg_t, path: *const libc::c_char, ) -> libc::c_int { if msg.is_null() || path.is_null() { eprintln!("ignoring careless call to dc_msg_save_file()"); return 0; } let ffi_msg = unsafe { &*msg }; let path = to_string_lossy(path); let r = block_on( ffi_msg .message .save_file(&ffi_msg.context, &std::path::PathBuf::from(path)), ); match r { Ok(()) => 1, Err(_) => { r.context("Failed to save file from message") .log_err(&ffi_msg.context) .unwrap_or_default(); 0 } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_filename()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_filename().unwrap_or_default().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_webxdc_blob( msg: *mut dc_msg_t, filename: *const libc::c_char, ret_bytes: *mut libc::size_t, ) -> *mut libc::c_char { if msg.is_null() || filename.is_null() || ret_bytes.is_null() { eprintln!("ignoring careless call to dc_msg_get_webxdc_blob()"); return ptr::null_mut(); } let ffi_msg = unsafe { &*msg }; let blob = block_on( ffi_msg .message .get_webxdc_blob(&ffi_msg.context, &to_string_lossy(filename)), ); match blob { Ok(blob) => unsafe { *ret_bytes = blob.len(); let ptr = libc::malloc(*ret_bytes); libc::memcpy(ptr, blob.as_ptr() as *mut libc::c_void, *ret_bytes); ptr as *mut libc::c_char }, Err(err) => { eprintln!("failed read blob from archive: {err}"); ptr::null_mut() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_webxdc_info(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_webxdc_info()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; let Ok(info) = block_on(ffi_msg.message.get_webxdc_info(&ffi_msg.context)) .context("dc_msg_get_webxdc_info() failed to get info") .log_err(&ffi_msg.context) else { return "".strdup(); }; serde_json::to_string(&info) .unwrap_or_log_default( &ffi_msg.context, "dc_msg_get_webxdc_info() failed to serialise to json", ) .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_filemime(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_filemime()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; if let Some(x) = ffi_msg.message.get_filemime() { x.strdup() } else { "".strdup() } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg_t) -> u64 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_filebytes()"); return 0; } let ffi_msg = unsafe { &*msg }; block_on(ffi_msg.message.get_filebytes(&ffi_msg.context)) .unwrap_or_log_default(&ffi_msg.context, "Cannot get file size") .unwrap_or_default() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_width(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_width()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_width() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_height(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_height()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_height() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_duration(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_duration()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_duration() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_showpadlock()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_showpadlock() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_bot(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_bot()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_bot() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_ephemeral_timer(msg: *mut dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_ephemeral_timer().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_ephemeral_timestamp(msg: *mut dc_msg_t) -> i64 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_ephemeral_timestamp() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_summary( msg: *mut dc_msg_t, chat: *mut dc_chat_t, ) -> *mut dc_lot_t { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_summary()"); return ptr::null_mut(); } let maybe_chat = if chat.is_null() { None } else { let ffi_chat = unsafe { &*chat }; Some(&ffi_chat.chat) }; let ffi_msg = unsafe { &mut *msg }; let summary = block_on(ffi_msg.message.get_summary(&ffi_msg.context, maybe_chat)) .context("dc_msg_get_summary failed") .log_err(&ffi_msg.context) .unwrap_or_default(); Box::into_raw(Box::new(summary.into())) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_summarytext( msg: *mut dc_msg_t, approx_characters: libc::c_int, ) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_summarytext()"); return "".strdup(); } let ffi_msg = unsafe { &mut *msg }; let summary = block_on(ffi_msg.message.get_summary(&ffi_msg.context, None)) .context("dc_msg_get_summarytext failed") .log_err(&ffi_msg.context) .unwrap_or_default(); match usize::try_from(approx_characters) { Ok(chars) => summary.truncated_text(chars).strdup(), Err(_) => summary.text.strdup(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_override_sender_name(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_override_sender_name()"); return "".strdup(); } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.get_override_sender_name().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_has_deviating_timestamp(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_has_deviating_timestamp()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.has_deviating_timestamp().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_has_location(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_has_location()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.has_location() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_sent()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_sent().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_forwarded(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_forwarded()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_forwarded().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_edited(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_edited()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_edited().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_info()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_info().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_info_type()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_info_type() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_info_contact_id(msg: *mut dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_info_contact_id()"); return 0; } let ffi_msg = unsafe { &*msg }; block_on(ffi_msg.message.get_info_contact_id(&ffi_msg.context)) .unwrap_or_default() .map(|id| id.to_u32()) .unwrap_or_default() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_webxdc_href(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_webxdc_href()"); return "".strdup(); } let ffi_msg = unsafe { &*msg }; ffi_msg.message.get_webxdc_href().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_has_html(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_has_html()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.has_html().into() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg_t, text: *const libc::c_char) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_text()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_text(to_string_lossy(text)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_html(msg: *mut dc_msg_t, html: *const libc::c_char) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_html()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_html(to_opt_string_lossy(html)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_subject(msg: *mut dc_msg_t, subject: *const libc::c_char) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_subject()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_subject(to_string_lossy(subject)); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_override_sender_name( msg: *mut dc_msg_t, name: *const libc::c_char, ) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_override_sender_name()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg .message .set_override_sender_name(to_opt_string_lossy(name)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_file_and_deduplicate( msg: *mut dc_msg_t, file: *const libc::c_char, name: *const libc::c_char, filemime: *const libc::c_char, ) { if msg.is_null() || file.is_null() { eprintln!("ignoring careless call to dc_msg_set_file_and_deduplicate()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg .message .set_file_and_deduplicate( &ffi_msg.context, unsafe { as_path(file) }, to_opt_string_lossy(name).as_deref(), to_opt_string_lossy(filemime).as_deref(), ) .context("Failed to set file") .log_err(&ffi_msg.context) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_dimension( msg: *mut dc_msg_t, width: libc::c_int, height: libc::c_int, ) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_dimension()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_dimension(width, height) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_duration(msg: *mut dc_msg_t, duration: libc::c_int) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_duration()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_duration(duration) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_location( msg: *mut dc_msg_t, latitude: libc::c_double, longitude: libc::c_double, ) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_location()"); return; } let ffi_msg = unsafe { &mut *msg }; ffi_msg.message.set_location(latitude, longitude) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_latefiling_mediasize( msg: *mut dc_msg_t, width: libc::c_int, height: libc::c_int, duration: libc::c_int, ) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_latefiling_mediasize()"); return; } let ffi_msg = unsafe { &mut *msg }; block_on({ ffi_msg .message .latefiling_mediasize(&ffi_msg.context, width, height, duration) }) .context("Cannot set media size") .log_err(&ffi_msg.context) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_error(msg: *mut dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_error()"); return ptr::null_mut(); } let ffi_msg = unsafe { &*msg }; match ffi_msg.message.error() { Some(error) => error.strdup(), None => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_set_quote(msg: *mut dc_msg_t, quote: *const dc_msg_t) { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_set_quote()"); return; } let ffi_msg = unsafe { &mut *msg }; let quote_msg = if quote.is_null() { None } else { let ffi_quote = unsafe { &*quote }; if ffi_msg.context.get_id() != ffi_quote.context.get_id() { eprintln!("ignoring attempt to quote message from a different context"); return; } Some(&ffi_quote.message) }; block_on(ffi_msg.message.set_quote(&ffi_msg.context, quote_msg)) .context("failed to set quote") .log_err(&ffi_msg.context) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_quoted_text(msg: *const dc_msg_t) -> *mut libc::c_char { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_quoted_text()"); return ptr::null_mut(); } let ffi_msg = unsafe { &*msg }; ffi_msg .message .quoted_text() .map_or_else(ptr::null_mut, |s| s.strdup()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_quoted_msg(msg: *const dc_msg_t) -> *mut dc_msg_t { if msg.is_null() { eprintln!("ignoring careless call to dc_get_quoted_msg()"); return ptr::null_mut(); } let ffi_msg = unsafe { &*msg }; let res = block_on(ffi_msg.message.quoted_message(&ffi_msg.context)) .context("failed to get quoted message") .log_err(&ffi_msg.context) .unwrap_or(None); match res { Some(message) => Box::into_raw(Box::new(MessageWrapper { context: ffi_msg.context.clone(), message, })), None => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_parent(msg: *const dc_msg_t) -> *mut dc_msg_t { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_parent()"); return ptr::null_mut(); } let ffi_msg = unsafe { &*msg }; let res = block_on(ffi_msg.message.parent(&ffi_msg.context)) .context("failed to get parent message") .log_err(&ffi_msg.context) .unwrap_or(None); match res { Some(message) => Box::into_raw(Box::new(MessageWrapper { context: ffi_msg.context.clone(), message, })), None => ptr::null_mut(), } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_original_msg_id(msg: *const dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_original_msg_id()"); return 0; } let ffi_msg = unsafe { &*msg }; block_on(ffi_msg.message.get_original_msg_id(&ffi_msg.context)) .context("failed to get original message") .log_err(&ffi_msg.context) .unwrap_or_default() .map(|id| id.to_u32()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_get_saved_msg_id(msg: *const dc_msg_t) -> u32 { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_get_saved_msg_id()"); return 0; } let ffi_msg = unsafe { &*msg }; block_on(ffi_msg.message.get_saved_msg_id(&ffi_msg.context)) .context("failed to get original message") .log_err(&ffi_msg.context) .unwrap_or_default() .map(|id| id.to_u32()) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_msg_is_pinned(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { eprintln!("ignoring careless call to dc_msg_is_pinned()"); return 0; } let ffi_msg = unsafe { &*msg }; ffi_msg.message.is_pinned().into() } // dc_contact_t /// FFI struct for [dc_contact_t] /// /// This is the structure behind [dc_contact_t] which is the opaque /// structure representing a contact in the FFI API. It exists /// because the FFI API has a reference from the message to the /// context, but the Rust API does not, so the FFI layer needs to glue /// these together. pub struct ContactWrapper { context: Context, contact: contact::Contact, } pub type dc_contact_t = ContactWrapper; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_unref(contact: *mut dc_contact_t) { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_unref()"); return; } drop(unsafe { Box::from_raw(contact) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_id(contact: *mut dc_contact_t) -> u32 { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_id()"); return 0; } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_id().to_u32() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_addr(contact: *mut dc_contact_t) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_addr()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_addr().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_name(contact: *mut dc_contact_t) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_name()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_name().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_auth_name(contact: *mut dc_contact_t) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_auth_name()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_authname().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_display_name( contact: *mut dc_contact_t, ) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_display_name()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_display_name().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_name_n_addr( contact: *mut dc_contact_t, ) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_name_n_addr()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_name_n_addr().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_profile_image( contact: *mut dc_contact_t, ) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_profile_image()"); return ptr::null_mut(); // NULL explicitly defined as "no profile image" } let ffi_contact = unsafe { &*contact }; block_on(ffi_contact.contact.get_profile_image(&ffi_contact.context)) .unwrap_or_log_default(&ffi_contact.context, "failed to get profile image") .map(|p| p.to_string_lossy().strdup()) .unwrap_or_else(std::ptr::null_mut) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32 { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_color()"); return 0; } let ffi_contact = unsafe { &*contact }; block_on( ffi_contact .contact // We don't want any UIs displaying gray self-color. .get_or_gen_color(&ffi_contact.context), ) .context("Contact::get_color()") .log_err(&ffi_contact.context) .unwrap_or(0) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_status(contact: *mut dc_contact_t) -> *mut libc::c_char { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_status()"); return "".strdup(); } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.get_status().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_last_seen(contact: *mut dc_contact_t) -> i64 { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_last_seen()"); return 0; } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.last_seen() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_was_seen_recently()"); return 0; } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.was_seen_recently() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_is_blocked()"); return 0; } let ffi_contact = unsafe { &*contact }; ffi_contact.contact.is_blocked() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_is_verified()"); return 0; } let ffi_contact = unsafe { &*contact }; if block_on(ffi_contact.contact.is_verified(&ffi_contact.context)) .context("is_verified failed") .log_err(&ffi_contact.context) .unwrap_or_default() { // Return value is essentially a boolean, // but we return 2 for true for backwards compatibility. 2 } else { 0 } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_is_bot(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_is_bot()"); return 0; } unsafe { (*contact).contact.is_bot() as libc::c_int } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_is_key_contact(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_is_key_contact()"); return 0; } unsafe { (*contact).contact.is_key_contact() as libc::c_int } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_contact_get_verifier_id(contact: *mut dc_contact_t) -> u32 { if contact.is_null() { eprintln!("ignoring careless call to dc_contact_get_verifier_id()"); return 0; } let ffi_contact = unsafe { &*contact }; let verifier_contact_id = block_on(ffi_contact.contact.get_verifier_id(&ffi_contact.context)) .context("failed to get verifier") .log_err(&ffi_contact.context) .unwrap_or_default() .unwrap_or_default() .unwrap_or_default(); verifier_contact_id.to_u32() } // dc_lot_t pub type dc_lot_t = lot::Lot; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot_t) { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_unref()"); return; } drop(unsafe { Box::from_raw(lot) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot_t) -> *mut libc::c_char { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_text1()"); return ptr::null_mut(); // NULL explicitly defined as "there is no such text" } let lot = unsafe { &*lot }; lot.get_text1().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot_t) -> *mut libc::c_char { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_text2()"); return ptr::null_mut(); // NULL explicitly defined as "there is no such text" } let lot = unsafe { &*lot }; lot.get_text2().strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot_t) -> libc::c_int { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_text1_meaning()"); return 0; } let lot = unsafe { &*lot }; lot.get_text1_meaning() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_state()"); return 0; } let lot = unsafe { &*lot }; lot.get_state() as libc::c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot_t) -> u32 { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_id()"); return 0; } let lot = unsafe { &*lot }; lot.get_id() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot_t) -> i64 { if lot.is_null() { eprintln!("ignoring careless call to dc_lot_get_timestamp()"); return 0; } let lot = unsafe { &*lot }; lot.get_timestamp() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) { unsafe { libc::free(s as *mut _) } } pub struct BackupProviderWrapper { context: *const dc_context_t, provider: BackupProvider, } pub type dc_backup_provider_t = BackupProviderWrapper; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_backup_provider_new( context: *mut dc_context_t, ) -> *mut dc_backup_provider_t { if context.is_null() { eprintln!("ignoring careless call to dc_backup_provider_new()"); return ptr::null_mut(); } let ctx = unsafe { &*context }; block_on(BackupProvider::prepare(ctx)) .map(|provider| BackupProviderWrapper { context: ctx, provider, }) .map(|ffi_provider| Box::into_raw(Box::new(ffi_provider))) .context("BackupProvider failed") .log_err(ctx) .set_last_error(ctx) .unwrap_or(ptr::null_mut()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_backup_provider_get_qr( provider: *const dc_backup_provider_t, ) -> *mut libc::c_char { if provider.is_null() { eprintln!("ignoring careless call to dc_backup_provider_qr"); return "".strdup(); } let ffi_provider = unsafe { &*provider }; let ctx = unsafe { &*ffi_provider.context }; deltachat::qr::format_backup(&ffi_provider.provider.qr()) .context("BackupProvider get_qr failed") .log_err(ctx) .set_last_error(ctx) .unwrap_or_default() .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_backup_provider_get_qr_svg( provider: *const dc_backup_provider_t, ) -> *mut libc::c_char { if provider.is_null() { eprintln!("ignoring careless call to dc_backup_provider_qr_svg()"); return "".strdup(); } let ffi_provider = unsafe { &*provider }; let ctx = unsafe { &*ffi_provider.context }; let provider = &ffi_provider.provider; block_on(generate_backup_qr(ctx, &provider.qr())) .context("BackupProvider get_qr_svg failed") .log_err(ctx) .set_last_error(ctx) .unwrap_or_default() .strdup() } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_backup_provider_wait(provider: *mut dc_backup_provider_t) { if provider.is_null() { eprintln!("ignoring careless call to dc_backup_provider_wait()"); return; } let ffi_provider = unsafe { &mut *provider }; let ctx = unsafe { &*ffi_provider.context }; let provider = &mut ffi_provider.provider; block_on(provider) .context("Failed to await backup provider") .log_err(ctx) .set_last_error(ctx) .ok(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_backup_provider_unref(provider: *mut dc_backup_provider_t) { if provider.is_null() { eprintln!("ignoring careless call to dc_backup_provider_unref()"); return; } drop(unsafe { Box::from_raw(provider) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_receive_backup( context: *mut dc_context_t, qr: *const libc::c_char, ) -> libc::c_int { if context.is_null() { eprintln!("ignoring careless call to dc_receive_backup()"); return 0; } let ctx = unsafe { &*context }; let qr_text = to_string_lossy(qr); receive_backup(ctx.clone(), qr_text) } // Because this is a long-running operation make sure we own the Context. This stops a FFI // user from deallocating it by calling unref on the object while we are using it. fn receive_backup(ctx: Context, qr_text: String) -> libc::c_int { let qr = match block_on(qr::check_qr(&ctx, &qr_text)) .context("Invalid QR code") .log_err(&ctx) .set_last_error(&ctx) { Ok(qr) => qr, Err(_) => return 0, }; match block_on(imex::get_backup(&ctx, qr)) .context("Get backup failed") .log_err(&ctx) .set_last_error(&ctx) { Ok(_) => 1, Err(_) => 0, } } trait ResultExt { /// Like `log_err()`, but: /// - returns the default value instead of an Err value. /// - emits an error instead of a warning for an [Err] result. This means /// that the error will be shown to the user in a small pop-up. fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T; } impl ResultExt for Result { fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T { self.map_err(|err| anyhow::anyhow!("{err:#}")) .with_context(|| message.to_string()) .log_err(context) .unwrap_or_default() } } trait ResultLastError where E: std::fmt::Display, { /// Sets this `Err` value using [`Context::set_last_error`]. /// /// Normally each FFI-API *should* call this if it handles an error from the Rust API: /// errors which need to be reported to users in response to an API call need to be /// propagated up the Rust API and at the FFI boundary need to be stored into the "last /// error" so the FFI users can retrieve an appropriate error message on failure. Often /// you will want to combine this with a call to [`LogExt::log_err`]. /// /// Since historically calls to the `deltachat::log::error!()` macro were (and sometimes /// still are) shown as error toasts to the user, this macro also calls /// [`Context::set_last_error`]. It is preferable however to rely on normal error /// propagation in Rust code however and only use this `ResultExt::set_last_error` call /// in the FFI layer. /// /// # Example /// /// Fully handling an error in the FFI code looks like this currently: /// /// ```no_compile /// some_dc_rust_api_call_returning_result() /// .context("My API call failed") /// .log_err(&context) /// .set_last_error(&context) /// .unwrap_or_default() /// ``` /// /// As shows it is a shame the `.log_err()` call currently needs a message instead of /// relying on an implicit call to the [`anyhow::Context`] call if needed. This stems /// from a time before we fully embraced anyhow. Some day we'll also fix that. /// /// [`Context::set_last_error`]: context::Context::set_last_error fn set_last_error(self, context: &context::Context) -> Result; } impl ResultLastError for Result where E: std::fmt::Display, { fn set_last_error(self, context: &context::Context) -> Result { if let Err(ref err) = self { context.set_last_error(&format!("{err:#}")); } self } } fn convert_and_prune_message_ids(msg_ids: *const u32, msg_cnt: libc::c_int) -> Vec { let ids = unsafe { std::slice::from_raw_parts(msg_ids, msg_cnt as usize) }; let msg_ids: Vec = ids .iter() .filter(|id| **id > MsgId::LAST_SPECIAL.to_u32()) .map(|id| MsgId::new(*id)) .collect(); msg_ids } // -- Accounts /// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using /// `dc_accounts_t` in multiple threads at once. pub type dc_accounts_t = RwLock; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_new( dir: *const libc::c_char, writable: libc::c_int, ) -> *const dc_accounts_t { setup_panic!(); if dir.is_null() { eprintln!("ignoring careless call to dc_accounts_new()"); return ptr::null_mut(); } let accs = block_on(Accounts::new(unsafe { as_path(dir) }.into(), writable != 0)); match accs { Ok(accs) => Arc::into_raw(Arc::new(RwLock::new(accs))), Err(err) => { // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: eprintln!("failed to create accounts: {err:#}"); ptr::null_mut() } } } pub type dc_event_channel_t = Mutex>; #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_channel_new() -> *mut dc_event_channel_t { Box::into_raw(Box::new(Mutex::new(Some(Events::new())))) } /// Release the events channel structure. /// /// This function releases the memory of the `dc_event_channel_t` structure. /// /// you can call it after calling dc_accounts_new_with_event_channel, /// which took the events channel out of it already, so this just frees the underlying option. #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_channel_unref(event_channel: *mut dc_event_channel_t) { if event_channel.is_null() { eprintln!("ignoring careless call to dc_event_channel_unref()"); return; } drop(unsafe { Box::from_raw(event_channel) }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_event_channel_get_event_emitter( event_channel: *mut dc_event_channel_t, ) -> *mut dc_event_emitter_t { if event_channel.is_null() { eprintln!("ignoring careless call to dc_event_channel_get_event_emitter()"); return ptr::null_mut(); } unsafe { let Some(event_channel) = &*(*event_channel) .lock() .expect("call to dc_event_channel_get_event_emitter() failed: mutex is poisoned") else { eprintln!( "ignoring careless call to dc_event_channel_get_event_emitter() -> channel was already consumed, make sure you call this before dc_accounts_new_with_event_channel" ); return ptr::null_mut(); }; let emitter = event_channel.get_emitter(); Box::into_raw(Box::new(emitter)) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_new_with_event_channel( dir: *const libc::c_char, writable: libc::c_int, event_channel: *mut dc_event_channel_t, ) -> *const dc_accounts_t { setup_panic!(); if dir.is_null() || event_channel.is_null() { eprintln!("ignoring careless call to dc_accounts_new_with_event_channel()"); return ptr::null_mut(); } // consuming channel enforce that you need to get the event emitter // before initializing the account manager, // so that you don't miss events/errors during initialisation. // It also prevents you from using the same channel on multiple account managers. let event_channel = unsafe { let Some(event_channel) = (*event_channel) .lock() .expect("call to dc_event_channel_get_event_emitter() failed: mutex is poisoned") .take() else { eprintln!( "ignoring careless call to dc_accounts_new_with_event_channel() -> channel was already consumed" ); return ptr::null_mut(); }; event_channel }; let accs = block_on(Accounts::new_with_events( unsafe { as_path(dir) }.into(), writable != 0, event_channel, )); match accs { Ok(accs) => Arc::into_raw(Arc::new(RwLock::new(accs))), Err(err) => { // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: eprintln!("failed to create accounts: {err:#}"); ptr::null_mut() } } } /// Release the accounts structure. /// /// This function releases the memory of the `dc_accounts_t` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_unref(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_unref()"); return; } drop(unsafe { Arc::from_raw(accounts) }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_get_account( accounts: *const dc_accounts_t, id: u32, ) -> *mut dc_context_t { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_get_account()"); return ptr::null_mut(); } let accounts = unsafe { &*accounts }; block_on(accounts.read()) .get_account(id) .map(|ctx| Box::into_raw(Box::new(ctx))) .unwrap_or_else(std::ptr::null_mut) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_get_selected_account( accounts: *const dc_accounts_t, ) -> *mut dc_context_t { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_get_selected_account()"); return ptr::null_mut(); } let accounts = unsafe { &*accounts }; block_on(accounts.read()) .get_selected_account() .map(|ctx| Box::into_raw(Box::new(ctx))) .unwrap_or_else(std::ptr::null_mut) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_select_account( accounts: *const dc_accounts_t, id: u32, ) -> libc::c_int { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_select_account()"); return 0; } let accounts = unsafe { &*accounts }; block_on(async move { let mut accounts = accounts.write().await; match accounts.select_account(id).await { Ok(()) => 1, Err(err) => { accounts.emit_event(EventType::Error(format!( "Failed to select account: {err:#}" ))); 0 } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_add_account(accounts: *const dc_accounts_t) -> u32 { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_add_account()"); return 0; } let accounts = unsafe { &*accounts }; block_on(async move { let mut accounts = accounts.write().await; match accounts.add_account().await { Ok(id) => id, Err(err) => { accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}"))); 0 } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_add_closed_account(accounts: *const dc_accounts_t) -> u32 { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_add_closed_account()"); return 0; } let accounts = unsafe { &*accounts }; block_on(async move { let mut accounts = accounts.write().await; match accounts.add_closed_account().await { Ok(id) => id, Err(err) => { accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}"))); 0 } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_remove_account( accounts: *const dc_accounts_t, id: u32, ) -> libc::c_int { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_remove_account()"); return 0; } let accounts = unsafe { &*accounts }; block_on(async move { let mut accounts = accounts.write().await; match accounts.remove_account(id).await { Ok(()) => 1, Err(err) => { accounts.emit_event(EventType::Error(format!( "Failed to remove account: {err:#}" ))); 0 } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_migrate_account( accounts: *const dc_accounts_t, dbfile: *const libc::c_char, ) -> u32 { if accounts.is_null() || dbfile.is_null() { eprintln!("ignoring careless call to dc_accounts_migrate_account()"); return 0; } let accounts = unsafe { &*accounts }; let dbfile = to_string_lossy(dbfile); block_on(async move { let mut accounts = accounts.write().await; match accounts .migrate_account(std::path::PathBuf::from(dbfile)) .await { Ok(id) => id, Err(err) => { accounts.emit_event(EventType::Error(format!( "Failed to migrate account: {err:#}" ))); 0 } } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_get_all(accounts: *const dc_accounts_t) -> *mut dc_array_t { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_get_all()"); return ptr::null_mut(); } let accounts = unsafe { &*accounts }; let list = block_on(accounts.read()).get_all(); let array: dc_array_t = list.into(); Box::into_raw(Box::new(array)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_start_io(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_start_io()"); return; } let accounts = unsafe { &*accounts }; block_on(async move { accounts.write().await.start_io().await }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_stop_io(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_stop_io()"); return; } let accounts = unsafe { &*accounts }; block_on(async move { accounts.read().await.stop_io().await }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_maybe_network(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_maybe_network()"); return; } let accounts = unsafe { &*accounts }; block_on(async move { accounts.read().await.maybe_network().await }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_maybe_network_lost()"); return; } let accounts = unsafe { &*accounts }; block_on(async move { accounts.read().await.maybe_network_lost().await }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_background_fetch( accounts: *const dc_accounts_t, timeout_in_seconds: u64, ) -> libc::c_int { if accounts.is_null() || timeout_in_seconds <= 2 { eprintln!("ignoring careless call to dc_accounts_background_fetch()"); return 0; } let accounts = unsafe { &*accounts }; let background_fetch_future = { let lock = block_on(accounts.read()); lock.background_fetch(Duration::from_secs(timeout_in_seconds)) }; // At this point account manager is not locked anymore. block_on(background_fetch_future); 1 } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_stop_background_fetch(accounts: *const dc_accounts_t) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_stop_background_fetch()"); return; } let accounts = unsafe { &*accounts }; block_on(accounts.read()).stop_background_fetch(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_set_push_device_token( accounts: *const dc_accounts_t, token: *const libc::c_char, ) { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_set_push_device_token()"); return; } let accounts = unsafe { &*accounts }; let token = to_string_lossy(token); block_on(async move { let accounts = accounts.read().await; if let Err(err) = accounts.set_push_device_token(&token) { accounts.emit_event(EventType::Error(format!( "Failed to set notify token: {err:#}." ))); } }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_accounts_get_event_emitter( accounts: *const dc_accounts_t, ) -> *mut dc_event_emitter_t { if accounts.is_null() { eprintln!("ignoring careless call to dc_accounts_get_event_emitter()"); return ptr::null_mut(); } let accounts = unsafe { &*accounts }; let emitter = block_on(accounts.read()).get_event_emitter(); Box::into_raw(Box::new(emitter)) } pub struct dc_jsonrpc_instance_t { receiver: OutReceiver, handle: RpcSession, } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_jsonrpc_init( account_manager: *const dc_accounts_t, ) -> *mut dc_jsonrpc_instance_t { if account_manager.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_init()"); return ptr::null_mut(); } let account_manager = ManuallyDrop::new(unsafe { Arc::from_raw(account_manager) }); let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc(Arc::clone( &account_manager, ))); let (request_handle, receiver) = RpcClient::new(); let handle = RpcSession::new(request_handle, cmd_api); let instance = dc_jsonrpc_instance_t { receiver, handle }; Box::into_raw(Box::new(instance)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_jsonrpc_unref(jsonrpc_instance: *mut dc_jsonrpc_instance_t) { if jsonrpc_instance.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_unref()"); return; } drop(unsafe { Box::from_raw(jsonrpc_instance) }); } fn spawn_handle_jsonrpc_request(handle: RpcSession, request: String) { spawn(async move { handle.handle_incoming(&request).await; }); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_jsonrpc_request( jsonrpc_instance: *mut dc_jsonrpc_instance_t, request: *const libc::c_char, ) { if jsonrpc_instance.is_null() || request.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_request()"); return; } let handle = unsafe { &(*jsonrpc_instance).handle }; let request = to_string_lossy(request); spawn_handle_jsonrpc_request(handle.clone(), request); } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_jsonrpc_next_response( jsonrpc_instance: *mut dc_jsonrpc_instance_t, ) -> *mut libc::c_char { if jsonrpc_instance.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_next_response()"); return ptr::null_mut(); } let api = unsafe { &*jsonrpc_instance }; block_on(api.receiver.recv()) .map(|result| serde_json::to_string(&result).unwrap_or_default().strdup()) .unwrap_or(ptr::null_mut()) } #[unsafe(no_mangle)] pub unsafe extern "C" fn dc_jsonrpc_blocking_call( jsonrpc_instance: *mut dc_jsonrpc_instance_t, input: *const libc::c_char, ) -> *mut libc::c_char { if jsonrpc_instance.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_blocking_call()"); return ptr::null_mut(); } let api = unsafe { &*jsonrpc_instance }; let input = to_string_lossy(input); let res = block_on(api.handle.process_incoming(&input)); match res { Some(message) => { if let Ok(message) = serde_json::to_string(&message) { message.strdup() } else { ptr::null_mut() } } None => ptr::null_mut(), } }