deltachat/
lib.rs

1#![recursion_limit = "256"]
2#![warn(unused, clippy::all)]
3#![allow(
4    non_camel_case_types,
5    non_snake_case,
6    non_upper_case_globals,
7    clippy::missing_safety_doc,
8    clippy::expect_fun_call
9)]
10
11#[macro_use]
12extern crate human_panic;
13
14use std::collections::BTreeMap;
15use std::convert::TryFrom;
16use std::fmt::Write;
17use std::future::Future;
18use std::mem::ManuallyDrop;
19use std::ptr;
20use std::str::FromStr;
21use std::sync::{Arc, LazyLock, Mutex};
22use std::time::{Duration, SystemTime};
23
24use anyhow::Context as _;
25use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration};
26use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
27use deltachat::contact::{Contact, ContactId, Origin};
28use deltachat::context::{Context, ContextBuilder};
29use deltachat::ephemeral::Timer as EphemeralTimer;
30use deltachat::imex::BackupProvider;
31use deltachat::key::preconfigure_keypair;
32use deltachat::message::MsgId;
33use deltachat::qr_code_generator::{create_qr_svg, generate_backup_qr, get_securejoin_qr_svg};
34use deltachat::stock_str::StockMessage;
35use deltachat::webxdc::StatusUpdateSerial;
36use deltachat::*;
37use deltachat::{accounts::Accounts, log::LogExt};
38use deltachat_jsonrpc::api::CommandApi;
39use deltachat_jsonrpc::yerpc::{OutReceiver, RpcClient, RpcSession};
40use message::Viewtype;
41use num_traits::{FromPrimitive, ToPrimitive};
42use tokio::runtime::Runtime;
43use tokio::sync::RwLock;
44use tokio::task::JoinHandle;
45
46mod dc_array;
47mod lot;
48
49mod string;
50use deltachat::chatlist::Chatlist;
51
52use self::string::*;
53
54// as C lacks a good and portable error handling,
55// in general, the C Interface is forgiving wrt to bad parameters.
56// - objects returned by some functions
57//   should be passable to the functions handling that object.
58// - if in doubt, the empty string is returned on failures;
59//   this avoids panics if the ui just forgets to handle a case
60// - finally, this behaviour matches the old core-c API and UIs already depend on it
61
62const DC_GCM_ADDDAYMARKER: u32 = 0x01;
63
64// dc_context_t
65
66/// Struct representing the deltachat context.
67pub type dc_context_t = Context;
68
69static RT: LazyLock<Runtime> =
70    LazyLock::new(|| Runtime::new().expect("unable to create tokio runtime"));
71
72fn block_on<T>(fut: T) -> T::Output
73where
74    T: Future,
75{
76    RT.block_on(fut)
77}
78
79fn spawn<T>(fut: T) -> JoinHandle<T::Output>
80where
81    T: Future + Send + 'static,
82    T::Output: Send + 'static,
83{
84    RT.spawn(fut)
85}
86
87#[no_mangle]
88pub unsafe extern "C" fn dc_context_new(
89    _os_name: *const libc::c_char,
90    dbfile: *const libc::c_char,
91    blobdir: *const libc::c_char,
92) -> *mut dc_context_t {
93    setup_panic!();
94
95    if dbfile.is_null() {
96        eprintln!("ignoring careless call to dc_context_new()");
97        return ptr::null_mut();
98    }
99
100    let ctx = if blobdir.is_null() || *blobdir == 0 {
101        // generate random ID as this functionality is not yet available on the C-api.
102        let id = rand::random();
103        block_on(
104            ContextBuilder::new(as_path(dbfile).to_path_buf())
105                .with_id(id)
106                .open(),
107        )
108    } else {
109        eprintln!("blobdir can not be defined explicitly anymore");
110        return ptr::null_mut();
111    };
112    match ctx {
113        Ok(ctx) => Box::into_raw(Box::new(ctx)),
114        Err(err) => {
115            eprintln!("failed to create context: {err:#}");
116            ptr::null_mut()
117        }
118    }
119}
120
121#[no_mangle]
122pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *mut dc_context_t {
123    setup_panic!();
124
125    if dbfile.is_null() {
126        eprintln!("ignoring careless call to dc_context_new_closed()");
127        return ptr::null_mut();
128    }
129
130    let id = rand::random();
131    match block_on(
132        ContextBuilder::new(as_path(dbfile).to_path_buf())
133            .with_id(id)
134            .build(),
135    ) {
136        Ok(context) => Box::into_raw(Box::new(context)),
137        Err(err) => {
138            eprintln!("failed to create context: {err:#}");
139            ptr::null_mut()
140        }
141    }
142}
143
144#[no_mangle]
145pub unsafe extern "C" fn dc_context_open(
146    context: *mut dc_context_t,
147    passphrase: *const libc::c_char,
148) -> libc::c_int {
149    if context.is_null() {
150        eprintln!("ignoring careless call to dc_context_open()");
151        return 0;
152    }
153
154    let ctx = &*context;
155    let passphrase = to_string_lossy(passphrase);
156    block_on(ctx.open(passphrase))
157        .context("dc_context_open() failed")
158        .log_err(ctx)
159        .map(|b| b as libc::c_int)
160        .unwrap_or(0)
161}
162
163#[no_mangle]
164pub unsafe extern "C" fn dc_context_change_passphrase(
165    context: *mut dc_context_t,
166    passphrase: *const libc::c_char,
167) -> libc::c_int {
168    if context.is_null() {
169        eprintln!("ignoring careless call to dc_context_change_passphrase()");
170        return 0;
171    }
172
173    let ctx = &*context;
174    let passphrase = to_string_lossy(passphrase);
175    block_on(ctx.change_passphrase(passphrase))
176        .context("dc_context_change_passphrase() failed")
177        .log_err(ctx)
178        .is_ok() as libc::c_int
179}
180
181#[no_mangle]
182pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc::c_int {
183    if context.is_null() {
184        eprintln!("ignoring careless call to dc_context_is_open()");
185        return 0;
186    }
187
188    let ctx = &*context;
189    block_on(ctx.is_open()) as libc::c_int
190}
191
192/// Release the context structure.
193///
194/// This function releases the memory of the `dc_context_t` structure.
195#[no_mangle]
196pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) {
197    if context.is_null() {
198        eprintln!("ignoring careless call to dc_context_unref()");
199        return;
200    }
201    drop(Box::from_raw(context));
202}
203
204#[no_mangle]
205pub unsafe extern "C" fn dc_get_blobdir(context: *mut dc_context_t) -> *mut libc::c_char {
206    if context.is_null() {
207        eprintln!("ignoring careless call to dc_get_blobdir()");
208        return "".strdup();
209    }
210    let ctx = &*context;
211    ctx.get_blobdir().to_string_lossy().strdup()
212}
213
214#[no_mangle]
215pub unsafe extern "C" fn dc_set_config(
216    context: *mut dc_context_t,
217    key: *const libc::c_char,
218    value: *const libc::c_char,
219) -> libc::c_int {
220    if context.is_null() || key.is_null() {
221        eprintln!("ignoring careless call to dc_set_config()");
222        return 0;
223    }
224    let ctx = &*context;
225    let key = to_string_lossy(key);
226    let value = to_opt_string_lossy(value);
227
228    block_on(async move {
229        if key.starts_with("ui.") {
230            ctx.set_ui_config(&key, value.as_deref())
231                .await
232                .with_context(|| format!("dc_set_config failed: Can't set {key} to {value:?}"))
233                .log_err(ctx)
234                .is_ok() as libc::c_int
235        } else {
236            match config::Config::from_str(&key)
237                .context("Invalid config key")
238                .log_err(ctx)
239            {
240                Ok(key) => ctx
241                    .set_config(key, value.as_deref())
242                    .await
243                    .with_context(|| {
244                        format!("dc_set_config() failed: Can't set {key} to {value:?}")
245                    })
246                    .log_err(ctx)
247                    .is_ok() as libc::c_int,
248                Err(_) => 0,
249            }
250        }
251    })
252}
253
254#[no_mangle]
255pub unsafe extern "C" fn dc_get_config(
256    context: *mut dc_context_t,
257    key: *const libc::c_char,
258) -> *mut libc::c_char {
259    if context.is_null() || key.is_null() {
260        eprintln!("ignoring careless call to dc_get_config()");
261        return "".strdup();
262    }
263    let ctx = &*context;
264
265    let key = to_string_lossy(key);
266
267    block_on(async move {
268        if key.starts_with("ui.") {
269            ctx.get_ui_config(&key)
270                .await
271                .context("Can't get ui-config")
272                .log_err(ctx)
273                .unwrap_or_default()
274                .unwrap_or_default()
275                .strdup()
276        } else {
277            match config::Config::from_str(&key)
278                .with_context(|| format!("Invalid key {:?}", &key))
279                .log_err(ctx)
280            {
281                Ok(key) => ctx
282                    .get_config(key)
283                    .await
284                    .context("Can't get config")
285                    .log_err(ctx)
286                    .unwrap_or_default()
287                    .unwrap_or_default()
288                    .strdup(),
289                Err(_) => "".strdup(),
290            }
291        }
292    })
293}
294
295#[no_mangle]
296pub unsafe extern "C" fn dc_set_stock_translation(
297    context: *mut dc_context_t,
298    stock_id: u32,
299    stock_msg: *mut libc::c_char,
300) -> libc::c_int {
301    if context.is_null() || stock_msg.is_null() {
302        eprintln!("ignoring careless call to dc_set_stock_string");
303        return 0;
304    }
305    let msg = to_string_lossy(stock_msg);
306    let ctx = &*context;
307
308    match StockMessage::from_u32(stock_id)
309        .with_context(|| format!("Invalid stock message ID {stock_id}"))
310        .log_err(ctx)
311    {
312        Ok(id) => ctx
313            .set_stock_translation(id, msg)
314            .context("set_stock_translation failed")
315            .log_err(ctx)
316            .is_ok() as libc::c_int,
317        Err(_) => 0,
318    }
319}
320
321#[no_mangle]
322pub unsafe extern "C" fn dc_set_config_from_qr(
323    context: *mut dc_context_t,
324    qr: *mut libc::c_char,
325) -> libc::c_int {
326    if context.is_null() || qr.is_null() {
327        eprintln!("ignoring careless call to dc_set_config_from_qr");
328        return 0;
329    }
330    let qr = to_string_lossy(qr);
331    let ctx = &*context;
332
333    block_on(qr::set_config_from_qr(ctx, &qr))
334        .context("Failed to create account from QR code")
335        .log_err(ctx)
336        .is_ok() as libc::c_int
337}
338
339#[no_mangle]
340pub unsafe extern "C" fn dc_get_info(context: *const dc_context_t) -> *mut libc::c_char {
341    if context.is_null() {
342        eprintln!("ignoring careless call to dc_get_info()");
343        return "".strdup();
344    }
345    let ctx = &*context;
346    match block_on(ctx.get_info())
347        .context("Failed to get info")
348        .log_err(ctx)
349    {
350        Ok(info) => render_info(info).unwrap_or_default().strdup(),
351        Err(_) => "".strdup(),
352    }
353}
354
355fn render_info(
356    info: BTreeMap<&'static str, String>,
357) -> std::result::Result<String, std::fmt::Error> {
358    let mut res = String::new();
359    for (key, value) in &info {
360        writeln!(&mut res, "{key}={value}")?;
361    }
362
363    Ok(res)
364}
365
366#[no_mangle]
367pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> libc::c_int {
368    if context.is_null() {
369        eprintln!("ignoring careless call to dc_get_connectivity()");
370        return 0;
371    }
372    let ctx = &*context;
373    ctx.get_connectivity() as u32 as libc::c_int
374}
375
376#[no_mangle]
377pub unsafe extern "C" fn dc_get_connectivity_html(
378    context: *const dc_context_t,
379) -> *mut libc::c_char {
380    if context.is_null() {
381        eprintln!("ignoring careless call to dc_get_connectivity_html()");
382        return "".strdup();
383    }
384    let ctx = &*context;
385    match block_on(ctx.get_connectivity_html())
386        .context("Failed to get connectivity html")
387        .log_err(ctx)
388    {
389        Ok(html) => html.strdup(),
390        Err(_) => "".strdup(),
391    }
392}
393
394#[no_mangle]
395pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
396    if context.is_null() {
397        eprintln!("ignoring careless call to dc_get_push_state()");
398        return 0;
399    }
400    let ctx = &*context;
401    block_on(ctx.push_state()) as libc::c_int
402}
403
404#[no_mangle]
405pub unsafe extern "C" fn dc_get_oauth2_url(
406    context: *mut dc_context_t,
407    addr: *const libc::c_char,
408    redirect: *const libc::c_char,
409) -> *mut libc::c_char {
410    if context.is_null() {
411        eprintln!("ignoring careless call to dc_get_oauth2_url()");
412        return ptr::null_mut(); // NULL explicitly defined as "unknown"
413    }
414    let ctx = &*context;
415    let addr = to_string_lossy(addr);
416    let redirect = to_string_lossy(redirect);
417
418    block_on(async move {
419        match oauth2::get_oauth2_url(ctx, &addr, &redirect)
420            .await
421            .context("dc_get_oauth2_url failed")
422            .log_err(ctx)
423        {
424            Ok(Some(res)) => res.strdup(),
425            Ok(None) | Err(_) => ptr::null_mut(),
426        }
427    })
428}
429
430fn spawn_configure(ctx: Context) {
431    spawn(async move {
432        ctx.configure()
433            .await
434            .context("Configure failed")
435            .log_err(&ctx)
436    });
437}
438
439#[no_mangle]
440pub unsafe extern "C" fn dc_configure(context: *mut dc_context_t) {
441    if context.is_null() {
442        eprintln!("ignoring careless call to dc_configure()");
443        return;
444    }
445
446    let ctx = &*context;
447    spawn_configure(ctx.clone());
448}
449
450#[no_mangle]
451pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c_int {
452    if context.is_null() {
453        eprintln!("ignoring careless call to dc_is_configured()");
454        return 0;
455    }
456    let ctx = &*context;
457
458    block_on(async move {
459        ctx.is_configured()
460            .await
461            .context("failed to get configured state")
462            .log_err(ctx)
463            .unwrap_or_default() as libc::c_int
464    })
465}
466
467#[no_mangle]
468pub unsafe extern "C" fn dc_start_io(context: *mut dc_context_t) {
469    if context.is_null() {
470        return;
471    }
472    let ctx = &mut *context;
473
474    block_on(ctx.start_io())
475}
476
477#[no_mangle]
478pub unsafe extern "C" fn dc_get_id(context: *mut dc_context_t) -> libc::c_int {
479    if context.is_null() {
480        return 0;
481    }
482    let ctx = &*context;
483
484    ctx.get_id() as libc::c_int
485}
486
487pub type dc_event_t = Event;
488
489#[no_mangle]
490pub unsafe extern "C" fn dc_event_unref(a: *mut dc_event_t) {
491    if a.is_null() {
492        eprintln!("ignoring careless call to dc_event_unref()");
493        return;
494    }
495
496    drop(Box::from_raw(a));
497}
498
499#[no_mangle]
500pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int {
501    if event.is_null() {
502        eprintln!("ignoring careless call to dc_event_get_id()");
503        return 0;
504    }
505
506    let event = &*event;
507    match event.typ {
508        EventType::Info(_) => 100,
509        EventType::SmtpConnected(_) => 101,
510        EventType::ImapConnected(_) => 102,
511        EventType::SmtpMessageSent(_) => 103,
512        EventType::ImapMessageDeleted(_) => 104,
513        EventType::ImapMessageMoved(_) => 105,
514        EventType::ImapInboxIdle => 106,
515        EventType::NewBlobFile(_) => 150,
516        EventType::DeletedBlobFile(_) => 151,
517        EventType::Warning(_) => 300,
518        EventType::Error(_) => 400,
519        EventType::ErrorSelfNotInGroup(_) => 410,
520        EventType::MsgsChanged { .. } => 2000,
521        EventType::ReactionsChanged { .. } => 2001,
522        EventType::IncomingReaction { .. } => 2002,
523        EventType::IncomingWebxdcNotify { .. } => 2003,
524        EventType::IncomingMsg { .. } => 2005,
525        EventType::IncomingMsgBunch => 2006,
526        EventType::MsgsNoticed { .. } => 2008,
527        EventType::MsgDelivered { .. } => 2010,
528        EventType::MsgFailed { .. } => 2012,
529        EventType::MsgRead { .. } => 2015,
530        EventType::MsgDeleted { .. } => 2016,
531        EventType::ChatModified(_) => 2020,
532        EventType::ChatEphemeralTimerModified { .. } => 2021,
533        EventType::ChatDeleted { .. } => 2023,
534        EventType::ContactsChanged(_) => 2030,
535        EventType::LocationChanged(_) => 2035,
536        EventType::ConfigureProgress { .. } => 2041,
537        EventType::ImexProgress(_) => 2051,
538        EventType::ImexFileWritten(_) => 2052,
539        EventType::SecurejoinInviterProgress { .. } => 2060,
540        EventType::SecurejoinJoinerProgress { .. } => 2061,
541        EventType::ConnectivityChanged => 2100,
542        EventType::SelfavatarChanged => 2110,
543        EventType::ConfigSynced { .. } => 2111,
544        EventType::WebxdcStatusUpdate { .. } => 2120,
545        EventType::WebxdcInstanceDeleted { .. } => 2121,
546        EventType::WebxdcRealtimeData { .. } => 2150,
547        EventType::WebxdcRealtimeAdvertisementReceived { .. } => 2151,
548        EventType::AccountsBackgroundFetchDone => 2200,
549        EventType::ChatlistChanged => 2300,
550        EventType::ChatlistItemChanged { .. } => 2301,
551        EventType::AccountsChanged => 2302,
552        EventType::AccountsItemChanged => 2303,
553        EventType::EventChannelOverflow { .. } => 2400,
554        EventType::IncomingCall { .. } => 2550,
555        EventType::IncomingCallAccepted { .. } => 2560,
556        EventType::OutgoingCallAccepted { .. } => 2570,
557        EventType::CallEnded { .. } => 2580,
558        EventType::TransportsModified => 2600,
559        #[allow(unreachable_patterns)]
560        #[cfg(test)]
561        _ => unreachable!("This is just to silence a rust_analyzer false-positive"),
562    }
563}
564
565#[no_mangle]
566pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc::c_int {
567    if event.is_null() {
568        eprintln!("ignoring careless call to dc_event_get_data1_int()");
569        return 0;
570    }
571
572    let event = &(*event).typ;
573    match event {
574        EventType::Info(_)
575        | EventType::SmtpConnected(_)
576        | EventType::ImapConnected(_)
577        | EventType::SmtpMessageSent(_)
578        | EventType::ImapMessageDeleted(_)
579        | EventType::ImapMessageMoved(_)
580        | EventType::ImapInboxIdle
581        | EventType::NewBlobFile(_)
582        | EventType::DeletedBlobFile(_)
583        | EventType::Warning(_)
584        | EventType::Error(_)
585        | EventType::ConnectivityChanged
586        | EventType::SelfavatarChanged
587        | EventType::ConfigSynced { .. }
588        | EventType::IncomingMsgBunch
589        | EventType::ErrorSelfNotInGroup(_)
590        | EventType::AccountsBackgroundFetchDone
591        | EventType::ChatlistChanged
592        | EventType::AccountsChanged
593        | EventType::AccountsItemChanged
594        | EventType::TransportsModified => 0,
595        EventType::IncomingReaction { contact_id, .. }
596        | EventType::IncomingWebxdcNotify { contact_id, .. } => contact_id.to_u32() as libc::c_int,
597        EventType::MsgsChanged { chat_id, .. }
598        | EventType::ReactionsChanged { chat_id, .. }
599        | EventType::IncomingMsg { chat_id, .. }
600        | EventType::MsgsNoticed(chat_id)
601        | EventType::MsgDelivered { chat_id, .. }
602        | EventType::MsgFailed { chat_id, .. }
603        | EventType::MsgRead { chat_id, .. }
604        | EventType::MsgDeleted { chat_id, .. }
605        | EventType::ChatModified(chat_id)
606        | EventType::ChatEphemeralTimerModified { chat_id, .. }
607        | EventType::ChatDeleted { chat_id } => chat_id.to_u32() as libc::c_int,
608        EventType::ContactsChanged(id) | EventType::LocationChanged(id) => {
609            let id = id.unwrap_or_default();
610            id.to_u32() as libc::c_int
611        }
612        EventType::ConfigureProgress { progress, .. } | EventType::ImexProgress(progress) => {
613            *progress as libc::c_int
614        }
615        EventType::ImexFileWritten(_) => 0,
616        EventType::SecurejoinInviterProgress { contact_id, .. }
617        | EventType::SecurejoinJoinerProgress { contact_id, .. } => {
618            contact_id.to_u32() as libc::c_int
619        }
620        EventType::WebxdcRealtimeData { msg_id, .. }
621        | EventType::WebxdcStatusUpdate { msg_id, .. }
622        | EventType::WebxdcRealtimeAdvertisementReceived { msg_id }
623        | EventType::WebxdcInstanceDeleted { msg_id, .. }
624        | EventType::IncomingCall { msg_id, .. }
625        | EventType::IncomingCallAccepted { msg_id, .. }
626        | EventType::OutgoingCallAccepted { msg_id, .. }
627        | EventType::CallEnded { msg_id, .. } => msg_id.to_u32() as libc::c_int,
628        EventType::ChatlistItemChanged { chat_id } => {
629            chat_id.unwrap_or_default().to_u32() as libc::c_int
630        }
631        EventType::EventChannelOverflow { n } => *n as libc::c_int,
632        #[allow(unreachable_patterns)]
633        #[cfg(test)]
634        _ => unreachable!("This is just to silence a rust_analyzer false-positive"),
635    }
636}
637
638#[no_mangle]
639pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc::c_int {
640    if event.is_null() {
641        eprintln!("ignoring careless call to dc_event_get_data2_int()");
642        return 0;
643    }
644
645    let event = &(*event).typ;
646
647    match event {
648        EventType::Info(_)
649        | EventType::SmtpConnected(_)
650        | EventType::ImapConnected(_)
651        | EventType::SmtpMessageSent(_)
652        | EventType::ImapMessageDeleted(_)
653        | EventType::ImapMessageMoved(_)
654        | EventType::ImapInboxIdle
655        | EventType::NewBlobFile(_)
656        | EventType::DeletedBlobFile(_)
657        | EventType::Warning(_)
658        | EventType::Error(_)
659        | EventType::ErrorSelfNotInGroup(_)
660        | EventType::ContactsChanged(_)
661        | EventType::LocationChanged(_)
662        | EventType::ConfigureProgress { .. }
663        | EventType::ImexProgress(_)
664        | EventType::ImexFileWritten(_)
665        | EventType::MsgsNoticed(_)
666        | EventType::ConnectivityChanged
667        | EventType::WebxdcInstanceDeleted { .. }
668        | EventType::IncomingMsgBunch
669        | EventType::SelfavatarChanged
670        | EventType::AccountsBackgroundFetchDone
671        | EventType::ChatlistChanged
672        | EventType::ChatlistItemChanged { .. }
673        | EventType::AccountsChanged
674        | EventType::AccountsItemChanged
675        | EventType::ConfigSynced { .. }
676        | EventType::ChatModified(_)
677        | EventType::ChatDeleted { .. }
678        | EventType::WebxdcRealtimeAdvertisementReceived { .. }
679        | EventType::OutgoingCallAccepted { .. }
680        | EventType::CallEnded { .. }
681        | EventType::EventChannelOverflow { .. }
682        | EventType::TransportsModified => 0,
683        EventType::MsgsChanged { msg_id, .. }
684        | EventType::ReactionsChanged { msg_id, .. }
685        | EventType::IncomingReaction { msg_id, .. }
686        | EventType::IncomingWebxdcNotify { msg_id, .. }
687        | EventType::IncomingMsg { msg_id, .. }
688        | EventType::MsgDelivered { msg_id, .. }
689        | EventType::MsgFailed { msg_id, .. }
690        | EventType::MsgRead { msg_id, .. }
691        | EventType::MsgDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
692        EventType::SecurejoinInviterProgress { progress, .. }
693        | EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int,
694        EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int,
695        EventType::WebxdcStatusUpdate {
696            status_update_serial,
697            ..
698        } => status_update_serial.to_u32() as libc::c_int,
699        EventType::WebxdcRealtimeData { data, .. } => data.len() as libc::c_int,
700        EventType::IncomingCall { has_video, .. } => *has_video as libc::c_int,
701        EventType::IncomingCallAccepted {
702            from_this_device, ..
703        } => *from_this_device as libc::c_int,
704
705        #[allow(unreachable_patterns)]
706        #[cfg(test)]
707        _ => unreachable!("This is just to silence a rust_analyzer false-positive"),
708    }
709}
710
711#[no_mangle]
712pub unsafe extern "C" fn dc_event_get_data1_str(event: *mut dc_event_t) -> *mut libc::c_char {
713    if event.is_null() {
714        eprintln!("ignoring careless call to dc_event_get_data1_str()");
715        return ptr::null_mut();
716    }
717
718    let event = &(*event).typ;
719
720    match event {
721        EventType::IncomingWebxdcNotify { href, .. } => {
722            if let Some(href) = href {
723                href.to_c_string().unwrap_or_default().into_raw()
724            } else {
725                ptr::null_mut()
726            }
727        }
728        _ => ptr::null_mut(),
729    }
730}
731
732#[no_mangle]
733pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut libc::c_char {
734    if event.is_null() {
735        eprintln!("ignoring careless call to dc_event_get_data2_str()");
736        return ptr::null_mut();
737    }
738
739    let event = &(*event).typ;
740
741    match event {
742        EventType::Info(msg)
743        | EventType::SmtpConnected(msg)
744        | EventType::ImapConnected(msg)
745        | EventType::SmtpMessageSent(msg)
746        | EventType::ImapMessageDeleted(msg)
747        | EventType::ImapMessageMoved(msg)
748        | EventType::NewBlobFile(msg)
749        | EventType::DeletedBlobFile(msg)
750        | EventType::Warning(msg)
751        | EventType::Error(msg)
752        | EventType::ErrorSelfNotInGroup(msg) => {
753            let data2 = msg.to_c_string().unwrap_or_default();
754            data2.into_raw()
755        }
756        EventType::MsgsChanged { .. }
757        | EventType::ReactionsChanged { .. }
758        | EventType::IncomingMsg { .. }
759        | EventType::ImapInboxIdle
760        | EventType::MsgsNoticed(_)
761        | EventType::MsgDelivered { .. }
762        | EventType::MsgFailed { .. }
763        | EventType::MsgRead { .. }
764        | EventType::MsgDeleted { .. }
765        | EventType::ChatModified(_)
766        | EventType::ContactsChanged(_)
767        | EventType::LocationChanged(_)
768        | EventType::ImexProgress(_)
769        | EventType::SecurejoinInviterProgress { .. }
770        | EventType::SecurejoinJoinerProgress { .. }
771        | EventType::ConnectivityChanged
772        | EventType::SelfavatarChanged
773        | EventType::WebxdcStatusUpdate { .. }
774        | EventType::WebxdcInstanceDeleted { .. }
775        | EventType::AccountsBackgroundFetchDone
776        | EventType::ChatEphemeralTimerModified { .. }
777        | EventType::ChatDeleted { .. }
778        | EventType::IncomingMsgBunch
779        | EventType::ChatlistItemChanged { .. }
780        | EventType::ChatlistChanged
781        | EventType::AccountsChanged
782        | EventType::AccountsItemChanged
783        | EventType::IncomingCallAccepted { .. }
784        | EventType::WebxdcRealtimeAdvertisementReceived { .. }
785        | EventType::TransportsModified => ptr::null_mut(),
786        EventType::IncomingCall {
787            place_call_info, ..
788        } => {
789            let data2 = place_call_info.to_c_string().unwrap_or_default();
790            data2.into_raw()
791        }
792        EventType::OutgoingCallAccepted {
793            accept_call_info, ..
794        } => {
795            let data2 = accept_call_info.to_c_string().unwrap_or_default();
796            data2.into_raw()
797        }
798        EventType::CallEnded { .. } | EventType::EventChannelOverflow { .. } => ptr::null_mut(),
799        EventType::ConfigureProgress { comment, .. } => {
800            if let Some(comment) = comment {
801                comment.to_c_string().unwrap_or_default().into_raw()
802            } else {
803                ptr::null_mut()
804            }
805        }
806        EventType::ImexFileWritten(file) => {
807            let data2 = file.to_c_string().unwrap_or_default();
808            data2.into_raw()
809        }
810        EventType::ConfigSynced { key } => {
811            let data2 = key.to_string().to_c_string().unwrap_or_default();
812            data2.into_raw()
813        }
814        EventType::WebxdcRealtimeData { data, .. } => {
815            let ptr = libc::malloc(data.len());
816            libc::memcpy(ptr, data.as_ptr() as *mut libc::c_void, data.len());
817            ptr as *mut libc::c_char
818        }
819        EventType::IncomingReaction { reaction, .. } => reaction
820            .as_str()
821            .to_c_string()
822            .unwrap_or_default()
823            .into_raw(),
824        EventType::IncomingWebxdcNotify { text, .. } => {
825            text.to_c_string().unwrap_or_default().into_raw()
826        }
827        #[allow(unreachable_patterns)]
828        #[cfg(test)]
829        _ => unreachable!("This is just to silence a rust_analyzer false-positive"),
830    }
831}
832
833#[no_mangle]
834pub unsafe extern "C" fn dc_event_get_account_id(event: *mut dc_event_t) -> u32 {
835    if event.is_null() {
836        eprintln!("ignoring careless call to dc_event_get_account_id()");
837        return 0;
838    }
839
840    (*event).id
841}
842
843pub type dc_event_emitter_t = EventEmitter;
844
845#[no_mangle]
846pub unsafe extern "C" fn dc_get_event_emitter(
847    context: *mut dc_context_t,
848) -> *mut dc_event_emitter_t {
849    if context.is_null() {
850        eprintln!("ignoring careless call to dc_get_event_emitter()");
851        return ptr::null_mut();
852    }
853    let ctx = &*context;
854    Box::into_raw(Box::new(ctx.get_event_emitter()))
855}
856
857#[no_mangle]
858pub unsafe extern "C" fn dc_event_emitter_unref(emitter: *mut dc_event_emitter_t) {
859    if emitter.is_null() {
860        eprintln!("ignoring careless call to dc_event_emitter_unref()");
861        return;
862    }
863
864    drop(Box::from_raw(emitter));
865}
866
867#[no_mangle]
868pub unsafe extern "C" fn dc_get_next_event(events: *mut dc_event_emitter_t) -> *mut dc_event_t {
869    if events.is_null() {
870        eprintln!("ignoring careless call to dc_get_next_event()");
871        return ptr::null_mut();
872    }
873    let events = &*events;
874
875    block_on(async move {
876        events
877            .recv()
878            .await
879            .map(|ev| Box::into_raw(Box::new(ev)))
880            .unwrap_or_else(ptr::null_mut)
881    })
882}
883
884#[no_mangle]
885pub unsafe extern "C" fn dc_stop_io(context: *mut dc_context_t) {
886    if context.is_null() {
887        eprintln!("ignoring careless call to dc_stop_io()");
888        return;
889    }
890    let ctx = &*context;
891
892    block_on(async move {
893        ctx.stop_io().await;
894    })
895}
896
897#[no_mangle]
898pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) {
899    if context.is_null() {
900        eprintln!("ignoring careless call to dc_maybe_network()");
901        return;
902    }
903    let ctx = &*context;
904
905    block_on(async move { ctx.maybe_network().await })
906}
907
908#[no_mangle]
909pub unsafe extern "C" fn dc_preconfigure_keypair(
910    context: *mut dc_context_t,
911    secret_data: *const libc::c_char,
912) -> i32 {
913    if context.is_null() {
914        eprintln!("ignoring careless call to dc_preconfigure_keypair()");
915        return 0;
916    }
917    let ctx = &*context;
918    let secret_data = to_string_lossy(secret_data);
919    block_on(preconfigure_keypair(ctx, &secret_data))
920        .context("Failed to save keypair")
921        .log_err(ctx)
922        .is_ok() as libc::c_int
923}
924
925#[no_mangle]
926pub unsafe extern "C" fn dc_get_chatlist(
927    context: *mut dc_context_t,
928    flags: libc::c_int,
929    query_str: *const libc::c_char,
930    query_id: u32,
931) -> *mut dc_chatlist_t {
932    if context.is_null() {
933        eprintln!("ignoring careless call to dc_get_chatlist()");
934        return ptr::null_mut();
935    }
936    let ctx = &*context;
937    let qs = to_opt_string_lossy(query_str);
938
939    let qi = if query_id == 0 {
940        None
941    } else {
942        Some(ContactId::new(query_id))
943    };
944
945    block_on(async move {
946        match chatlist::Chatlist::try_load(ctx, flags as usize, qs.as_deref(), qi)
947            .await
948            .context("Failed to get chatlist")
949            .log_err(ctx)
950        {
951            Ok(list) => {
952                let ffi_list = ChatlistWrapper { context, list };
953                Box::into_raw(Box::new(ffi_list))
954            }
955            Err(_) => ptr::null_mut(),
956        }
957    })
958}
959
960#[no_mangle]
961pub unsafe extern "C" fn dc_create_chat_by_contact_id(
962    context: *mut dc_context_t,
963    contact_id: u32,
964) -> u32 {
965    if context.is_null() {
966        eprintln!("ignoring careless call to dc_create_chat_by_contact_id()");
967        return 0;
968    }
969    let ctx = &*context;
970
971    block_on(async move {
972        ChatId::create_for_contact(ctx, ContactId::new(contact_id))
973            .await
974            .context("Failed to create chat from contact_id")
975            .log_err(ctx)
976            .map(|id| id.to_u32())
977            .unwrap_or(0)
978    })
979}
980
981#[no_mangle]
982pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
983    context: *mut dc_context_t,
984    contact_id: u32,
985) -> u32 {
986    if context.is_null() {
987        eprintln!("ignoring careless call to dc_get_chat_id_by_contact_id()");
988        return 0;
989    }
990    let ctx = &*context;
991
992    block_on(async move {
993        ChatId::lookup_by_contact(ctx, ContactId::new(contact_id))
994            .await
995            .context("Failed to get chat for contact_id")
996            .log_err(ctx)
997            .unwrap_or_default() // unwraps the Result
998            .map(|id| id.to_u32())
999            .unwrap_or(0) // unwraps the Option
1000    })
1001}
1002
1003#[no_mangle]
1004pub unsafe extern "C" fn dc_send_msg(
1005    context: *mut dc_context_t,
1006    chat_id: u32,
1007    msg: *mut dc_msg_t,
1008) -> u32 {
1009    if context.is_null() || msg.is_null() {
1010        eprintln!("ignoring careless call to dc_send_msg()");
1011        return 0;
1012    }
1013    let ctx = &mut *context;
1014    let ffi_msg = &mut *msg;
1015
1016    block_on(async move {
1017        chat::send_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
1018            .await
1019            .unwrap_or_log_default(ctx, "Failed to send message")
1020    })
1021    .to_u32()
1022}
1023
1024#[no_mangle]
1025pub unsafe extern "C" fn dc_send_msg_sync(
1026    context: *mut dc_context_t,
1027    chat_id: u32,
1028    msg: *mut dc_msg_t,
1029) -> u32 {
1030    if context.is_null() || msg.is_null() {
1031        eprintln!("ignoring careless call to dc_send_msg_sync()");
1032        return 0;
1033    }
1034    let ctx = &mut *context;
1035    let ffi_msg = &mut *msg;
1036
1037    block_on(async move {
1038        chat::send_msg_sync(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
1039            .await
1040            .unwrap_or_log_default(ctx, "Failed to send message")
1041    })
1042    .to_u32()
1043}
1044
1045#[no_mangle]
1046pub unsafe extern "C" fn dc_send_text_msg(
1047    context: *mut dc_context_t,
1048    chat_id: u32,
1049    text_to_send: *const libc::c_char,
1050) -> u32 {
1051    if context.is_null() || text_to_send.is_null() {
1052        eprintln!("ignoring careless call to dc_send_text_msg()");
1053        return 0;
1054    }
1055    let ctx = &*context;
1056    let text_to_send = to_string_lossy(text_to_send);
1057
1058    block_on(async move {
1059        chat::send_text_msg(ctx, ChatId::new(chat_id), text_to_send)
1060            .await
1061            .map(|msg_id| msg_id.to_u32())
1062            .unwrap_or_log_default(ctx, "Failed to send text message")
1063    })
1064}
1065
1066#[no_mangle]
1067pub unsafe extern "C" fn dc_send_edit_request(
1068    context: *mut dc_context_t,
1069    msg_id: u32,
1070    new_text: *const libc::c_char,
1071) {
1072    if context.is_null() || new_text.is_null() {
1073        eprintln!("ignoring careless call to dc_send_edit_request()");
1074        return;
1075    }
1076    let ctx = &*context;
1077    let new_text = to_string_lossy(new_text);
1078
1079    block_on(chat::send_edit_request(ctx, MsgId::new(msg_id), new_text))
1080        .unwrap_or_log_default(ctx, "Failed to send text edit")
1081}
1082
1083#[no_mangle]
1084pub unsafe extern "C" fn dc_send_delete_request(
1085    context: *mut dc_context_t,
1086    msg_ids: *const u32,
1087    msg_cnt: libc::c_int,
1088) {
1089    if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
1090        eprintln!("ignoring careless call to dc_send_delete_request()");
1091        return;
1092    }
1093    let ctx = &*context;
1094    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
1095
1096    block_on(message::delete_msgs_ex(ctx, &msg_ids, true))
1097        .context("failed dc_send_delete_request() call")
1098        .log_err(ctx)
1099        .ok();
1100}
1101
1102#[no_mangle]
1103pub unsafe extern "C" fn dc_send_webxdc_status_update(
1104    context: *mut dc_context_t,
1105    msg_id: u32,
1106    json: *const libc::c_char,
1107    _descr: *const libc::c_char,
1108) -> libc::c_int {
1109    if context.is_null() {
1110        eprintln!("ignoring careless call to dc_send_webxdc_status_update()");
1111        return 0;
1112    }
1113    let ctx = &*context;
1114
1115    block_on(ctx.send_webxdc_status_update(MsgId::new(msg_id), &to_string_lossy(json)))
1116        .context("Failed to send webxdc update")
1117        .log_err(ctx)
1118        .is_ok() as libc::c_int
1119}
1120
1121#[no_mangle]
1122pub unsafe extern "C" fn dc_get_webxdc_status_updates(
1123    context: *mut dc_context_t,
1124    msg_id: u32,
1125    last_known_serial: u32,
1126) -> *mut libc::c_char {
1127    if context.is_null() {
1128        eprintln!("ignoring careless call to dc_get_webxdc_status_updates()");
1129        return "".strdup();
1130    }
1131    let ctx = &*context;
1132
1133    block_on(ctx.get_webxdc_status_updates(
1134        MsgId::new(msg_id),
1135        StatusUpdateSerial::new(last_known_serial),
1136    ))
1137    .unwrap_or_else(|_| "".to_string())
1138    .strdup()
1139}
1140
1141#[no_mangle]
1142pub unsafe extern "C" fn dc_set_webxdc_integration(
1143    context: *mut dc_context_t,
1144    file: *const libc::c_char,
1145) {
1146    if context.is_null() || file.is_null() {
1147        eprintln!("ignoring careless call to dc_set_webxdc_integration()");
1148        return;
1149    }
1150    let ctx = &*context;
1151    block_on(ctx.set_webxdc_integration(&to_string_lossy(file)))
1152        .log_err(ctx)
1153        .unwrap_or_default();
1154}
1155
1156#[no_mangle]
1157pub unsafe extern "C" fn dc_init_webxdc_integration(
1158    context: *mut dc_context_t,
1159    chat_id: u32,
1160) -> u32 {
1161    if context.is_null() {
1162        eprintln!("ignoring careless call to dc_init_webxdc_integration()");
1163        return 0;
1164    }
1165    let ctx = &*context;
1166    let chat_id = if chat_id == 0 {
1167        None
1168    } else {
1169        Some(ChatId::new(chat_id))
1170    };
1171
1172    block_on(ctx.init_webxdc_integration(chat_id))
1173        .log_err(ctx)
1174        .map(|msg_id| msg_id.map(|id| id.to_u32()).unwrap_or_default())
1175        .unwrap_or(0)
1176}
1177
1178#[no_mangle]
1179pub unsafe extern "C" fn dc_place_outgoing_call(
1180    context: *mut dc_context_t,
1181    chat_id: u32,
1182    place_call_info: *const libc::c_char,
1183    has_video: bool,
1184) -> u32 {
1185    if context.is_null() || chat_id == 0 {
1186        eprintln!("ignoring careless call to dc_place_outgoing_call()");
1187        return 0;
1188    }
1189    let ctx = &*context;
1190    let chat_id = ChatId::new(chat_id);
1191    let place_call_info = to_string_lossy(place_call_info);
1192
1193    block_on(ctx.place_outgoing_call(chat_id, place_call_info, has_video))
1194        .context("Failed to place call")
1195        .log_err(ctx)
1196        .map(|msg_id| msg_id.to_u32())
1197        .unwrap_or_log_default(ctx, "Failed to place call")
1198}
1199
1200#[no_mangle]
1201pub unsafe extern "C" fn dc_accept_incoming_call(
1202    context: *mut dc_context_t,
1203    msg_id: u32,
1204    accept_call_info: *const libc::c_char,
1205) -> libc::c_int {
1206    if context.is_null() || msg_id == 0 {
1207        eprintln!("ignoring careless call to dc_accept_incoming_call()");
1208        return 0;
1209    }
1210    let ctx = &*context;
1211    let msg_id = MsgId::new(msg_id);
1212    let accept_call_info = to_string_lossy(accept_call_info);
1213
1214    block_on(ctx.accept_incoming_call(msg_id, accept_call_info))
1215        .context("Failed to accept call")
1216        .is_ok() as libc::c_int
1217}
1218
1219#[no_mangle]
1220pub unsafe extern "C" fn dc_end_call(context: *mut dc_context_t, msg_id: u32) -> libc::c_int {
1221    if context.is_null() || msg_id == 0 {
1222        eprintln!("ignoring careless call to dc_end_call()");
1223        return 0;
1224    }
1225    let ctx = &*context;
1226    let msg_id = MsgId::new(msg_id);
1227
1228    block_on(ctx.end_call(msg_id))
1229        .context("Failed to end call")
1230        .log_err(ctx)
1231        .is_ok() as libc::c_int
1232}
1233
1234#[no_mangle]
1235pub unsafe extern "C" fn dc_set_draft(
1236    context: *mut dc_context_t,
1237    chat_id: u32,
1238    msg: *mut dc_msg_t,
1239) {
1240    if context.is_null() {
1241        eprintln!("ignoring careless call to dc_set_draft()");
1242        return;
1243    }
1244    let ctx = &*context;
1245    let msg = if msg.is_null() {
1246        None
1247    } else {
1248        let ffi_msg: &mut MessageWrapper = &mut *msg;
1249        Some(&mut ffi_msg.message)
1250    };
1251
1252    block_on(async move {
1253        ChatId::new(chat_id)
1254            .set_draft(ctx, msg)
1255            .await
1256            .unwrap_or_log_default(ctx, "failed to set draft");
1257    });
1258}
1259
1260#[no_mangle]
1261pub unsafe extern "C" fn dc_add_device_msg(
1262    context: *mut dc_context_t,
1263    label: *const libc::c_char,
1264    msg: *mut dc_msg_t,
1265) -> u32 {
1266    if context.is_null() || (label.is_null() && msg.is_null()) {
1267        eprintln!("ignoring careless call to dc_add_device_msg()");
1268        return 0;
1269    }
1270    let ctx = &mut *context;
1271    let msg = if msg.is_null() {
1272        None
1273    } else {
1274        let ffi_msg: &mut MessageWrapper = &mut *msg;
1275        Some(&mut ffi_msg.message)
1276    };
1277
1278    block_on(async move {
1279        chat::add_device_msg(ctx, to_opt_string_lossy(label).as_deref(), msg)
1280            .await
1281            .unwrap_or_log_default(ctx, "Failed to add device message")
1282    })
1283    .to_u32()
1284}
1285
1286#[no_mangle]
1287pub unsafe extern "C" fn dc_was_device_msg_ever_added(
1288    context: *mut dc_context_t,
1289    label: *const libc::c_char,
1290) -> libc::c_int {
1291    if context.is_null() || label.is_null() {
1292        eprintln!("ignoring careless call to dc_was_device_msg_ever_added()");
1293        return 0;
1294    }
1295    let ctx = &mut *context;
1296
1297    block_on(async move {
1298        chat::was_device_msg_ever_added(ctx, &to_string_lossy(label))
1299            .await
1300            .unwrap_or(false) as libc::c_int
1301    })
1302}
1303
1304#[no_mangle]
1305pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) -> *mut dc_msg_t {
1306    if context.is_null() {
1307        eprintln!("ignoring careless call to dc_get_draft()");
1308        return ptr::null_mut(); // NULL explicitly defined as "no draft"
1309    }
1310    let ctx = &*context;
1311
1312    match block_on(ChatId::new(chat_id).get_draft(ctx))
1313        .with_context(|| format!("Failed to get draft for chat #{chat_id}"))
1314        .unwrap_or_default()
1315    {
1316        Some(draft) => {
1317            let ffi_msg = MessageWrapper {
1318                context,
1319                message: draft,
1320            };
1321            Box::into_raw(Box::new(ffi_msg))
1322        }
1323        None => ptr::null_mut(),
1324    }
1325}
1326
1327#[no_mangle]
1328pub unsafe extern "C" fn dc_get_chat_msgs(
1329    context: *mut dc_context_t,
1330    chat_id: u32,
1331    flags: u32,
1332    _marker1before: u32,
1333) -> *mut dc_array::dc_array_t {
1334    if context.is_null() {
1335        eprintln!("ignoring careless call to dc_get_chat_msgs()");
1336        return ptr::null_mut();
1337    }
1338    let ctx = &*context;
1339
1340    let add_daymarker = (flags & DC_GCM_ADDDAYMARKER) != 0;
1341    block_on(async move {
1342        Box::into_raw(Box::new(
1343            chat::get_chat_msgs_ex(
1344                ctx,
1345                ChatId::new(chat_id),
1346                MessageListOptions { add_daymarker },
1347            )
1348            .await
1349            .unwrap_or_log_default(ctx, "failed to get chat msgs")
1350            .into(),
1351        ))
1352    })
1353}
1354
1355#[no_mangle]
1356pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32) -> libc::c_int {
1357    if context.is_null() {
1358        eprintln!("ignoring careless call to dc_get_msg_cnt()");
1359        return 0;
1360    }
1361    let ctx = &*context;
1362
1363    block_on(async move {
1364        ChatId::new(chat_id)
1365            .get_msg_cnt(ctx)
1366            .await
1367            .unwrap_or_log_default(ctx, "failed to get msg count") as libc::c_int
1368    })
1369}
1370
1371#[no_mangle]
1372pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
1373    context: *mut dc_context_t,
1374    chat_id: u32,
1375) -> libc::c_int {
1376    if context.is_null() {
1377        eprintln!("ignoring careless call to dc_get_fresh_msg_cnt()");
1378        return 0;
1379    }
1380    let ctx = &*context;
1381
1382    block_on(async move {
1383        ChatId::new(chat_id)
1384            .get_fresh_msg_cnt(ctx)
1385            .await
1386            .unwrap_or_log_default(ctx, "failed to get fresh msg cnt") as libc::c_int
1387    })
1388}
1389
1390#[no_mangle]
1391pub unsafe extern "C" fn dc_get_similar_chatlist(
1392    context: *mut dc_context_t,
1393    chat_id: u32,
1394) -> *mut dc_chatlist_t {
1395    if context.is_null() {
1396        eprintln!("ignoring careless call to dc_get_similar_chatlist()");
1397        return ptr::null_mut();
1398    }
1399    let ctx = &*context;
1400
1401    let chat_id = ChatId::new(chat_id);
1402    match block_on(chat_id.get_similar_chatlist(ctx))
1403        .context("failed to get similar chatlist")
1404        .log_err(ctx)
1405    {
1406        Ok(list) => {
1407            let ffi_list = ChatlistWrapper { context, list };
1408            Box::into_raw(Box::new(ffi_list))
1409        }
1410        Err(_) => ptr::null_mut(),
1411    }
1412}
1413
1414#[no_mangle]
1415pub unsafe extern "C" fn dc_estimate_deletion_cnt(
1416    context: *mut dc_context_t,
1417    from_server: libc::c_int,
1418    seconds: i64,
1419) -> libc::c_int {
1420    if context.is_null() || seconds < 0 {
1421        eprintln!("ignoring careless call to dc_estimate_deletion_cnt()");
1422        return 0;
1423    }
1424    let ctx = &*context;
1425    block_on(async move {
1426        message::estimate_deletion_cnt(ctx, from_server != 0, seconds)
1427            .await
1428            .unwrap_or(0) as libc::c_int
1429    })
1430}
1431
1432#[no_mangle]
1433pub unsafe extern "C" fn dc_get_fresh_msgs(
1434    context: *mut dc_context_t,
1435) -> *mut dc_array::dc_array_t {
1436    if context.is_null() {
1437        eprintln!("ignoring careless call to dc_get_fresh_msgs()");
1438        return ptr::null_mut();
1439    }
1440    let ctx = &*context;
1441
1442    block_on(async move {
1443        let arr = dc_array_t::from(
1444            ctx.get_fresh_msgs()
1445                .await
1446                .context("Failed to get fresh messages")
1447                .log_err(ctx)
1448                .unwrap_or_default()
1449                .iter()
1450                .map(|msg_id| msg_id.to_u32())
1451                .collect::<Vec<u32>>(),
1452        );
1453        Box::into_raw(Box::new(arr))
1454    })
1455}
1456
1457#[no_mangle]
1458pub unsafe extern "C" fn dc_get_next_msgs(context: *mut dc_context_t) -> *mut dc_array::dc_array_t {
1459    if context.is_null() {
1460        eprintln!("ignoring careless call to dc_get_next_msgs()");
1461        return ptr::null_mut();
1462    }
1463    let ctx = &*context;
1464
1465    let msg_ids = block_on(ctx.get_next_msgs())
1466        .context("failed to get next messages")
1467        .log_err(ctx)
1468        .unwrap_or_default();
1469    let arr = dc_array_t::from(
1470        msg_ids
1471            .iter()
1472            .map(|msg_id| msg_id.to_u32())
1473            .collect::<Vec<u32>>(),
1474    );
1475    Box::into_raw(Box::new(arr))
1476}
1477
1478#[no_mangle]
1479pub unsafe extern "C" fn dc_wait_next_msgs(
1480    context: *mut dc_context_t,
1481) -> *mut dc_array::dc_array_t {
1482    if context.is_null() {
1483        eprintln!("ignoring careless call to dc_wait_next_msgs()");
1484        return ptr::null_mut();
1485    }
1486    let ctx = &*context;
1487
1488    let msg_ids = block_on(ctx.wait_next_msgs())
1489        .context("failed to wait for next messages")
1490        .log_err(ctx)
1491        .unwrap_or_default();
1492    let arr = dc_array_t::from(
1493        msg_ids
1494            .iter()
1495            .map(|msg_id| msg_id.to_u32())
1496            .collect::<Vec<u32>>(),
1497    );
1498    Box::into_raw(Box::new(arr))
1499}
1500
1501#[no_mangle]
1502pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id: u32) {
1503    if context.is_null() {
1504        eprintln!("ignoring careless call to dc_marknoticed_chat()");
1505        return;
1506    }
1507    let ctx = &*context;
1508
1509    block_on(async move {
1510        chat::marknoticed_chat(ctx, ChatId::new(chat_id))
1511            .await
1512            .context("Failed marknoticed chat")
1513            .log_err(ctx)
1514            .unwrap_or(())
1515    })
1516}
1517
1518#[no_mangle]
1519pub unsafe extern "C" fn dc_markfresh_chat(context: *mut dc_context_t, chat_id: u32) {
1520    if context.is_null() {
1521        eprintln!("ignoring careless call to dc_markfresh_chat()");
1522        return;
1523    }
1524    let ctx = &*context;
1525
1526    block_on(async move {
1527        chat::markfresh_chat(ctx, ChatId::new(chat_id))
1528            .await
1529            .context("Failed markfresh chat")
1530            .log_err(ctx)
1531            .unwrap_or(())
1532    })
1533}
1534
1535fn from_prim<S, T>(s: S) -> Option<T>
1536where
1537    T: FromPrimitive,
1538    S: Into<i64>,
1539{
1540    FromPrimitive::from_i64(s.into())
1541}
1542
1543#[no_mangle]
1544pub unsafe extern "C" fn dc_get_chat_media(
1545    context: *mut dc_context_t,
1546    chat_id: u32,
1547    msg_type: libc::c_int,
1548    or_msg_type2: libc::c_int,
1549    or_msg_type3: libc::c_int,
1550) -> *mut dc_array::dc_array_t {
1551    if context.is_null() {
1552        eprintln!("ignoring careless call to dc_get_chat_media()");
1553        return ptr::null_mut();
1554    }
1555    let ctx = &*context;
1556    let chat_id = if chat_id == 0 {
1557        None
1558    } else {
1559        Some(ChatId::new(chat_id))
1560    };
1561    let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {msg_type}"));
1562    let or_msg_type2 =
1563        from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {or_msg_type2}"));
1564    let or_msg_type3 =
1565        from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {or_msg_type3}"));
1566
1567    block_on(async move {
1568        Box::into_raw(Box::new(
1569            chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3)
1570                .await
1571                .unwrap_or_log_default(ctx, "Failed get_chat_media")
1572                .into(),
1573        ))
1574    })
1575}
1576
1577#[no_mangle]
1578pub unsafe extern "C" fn dc_set_chat_visibility(
1579    context: *mut dc_context_t,
1580    chat_id: u32,
1581    archive: libc::c_int,
1582) {
1583    if context.is_null() {
1584        eprintln!("ignoring careless call to dc_set_chat_visibility()");
1585        return;
1586    }
1587    let ctx = &*context;
1588    let visibility = match archive {
1589        0 => ChatVisibility::Normal,
1590        1 => ChatVisibility::Archived,
1591        2 => ChatVisibility::Pinned,
1592        _ => {
1593            eprintln!("ignoring careless call to dc_set_chat_visibility(): unknown archived state");
1594            return;
1595        }
1596    };
1597
1598    block_on(async move {
1599        ChatId::new(chat_id)
1600            .set_visibility(ctx, visibility)
1601            .await
1602            .context("Failed setting chat visibility")
1603            .log_err(ctx)
1604            .unwrap_or(())
1605    })
1606}
1607
1608#[no_mangle]
1609pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32) {
1610    if context.is_null() {
1611        eprintln!("ignoring careless call to dc_delete_chat()");
1612        return;
1613    }
1614    let ctx = &*context;
1615
1616    block_on(async move {
1617        ChatId::new(chat_id)
1618            .delete(ctx)
1619            .await
1620            .context("Failed chat delete")
1621            .log_err(ctx)
1622            .ok();
1623    })
1624}
1625
1626#[no_mangle]
1627pub unsafe extern "C" fn dc_block_chat(context: *mut dc_context_t, chat_id: u32) {
1628    if context.is_null() {
1629        eprintln!("ignoring careless call to dc_block_chat()");
1630        return;
1631    }
1632    let ctx = &*context;
1633
1634    block_on(async move {
1635        ChatId::new(chat_id)
1636            .block(ctx)
1637            .await
1638            .context("Failed chat block")
1639            .log_err(ctx)
1640            .ok();
1641    })
1642}
1643
1644#[no_mangle]
1645pub unsafe extern "C" fn dc_accept_chat(context: *mut dc_context_t, chat_id: u32) {
1646    if context.is_null() {
1647        eprintln!("ignoring careless call to dc_accept_chat()");
1648        return;
1649    }
1650    let ctx = &*context;
1651
1652    block_on(async move {
1653        ChatId::new(chat_id)
1654            .accept(ctx)
1655            .await
1656            .context("Failed chat accept")
1657            .log_err(ctx)
1658            .ok();
1659    })
1660}
1661
1662#[no_mangle]
1663pub unsafe extern "C" fn dc_get_chat_contacts(
1664    context: *mut dc_context_t,
1665    chat_id: u32,
1666) -> *mut dc_array::dc_array_t {
1667    if context.is_null() {
1668        eprintln!("ignoring careless call to dc_get_chat_contacts()");
1669        return ptr::null_mut();
1670    }
1671    let ctx = &*context;
1672
1673    block_on(async move {
1674        let arr = dc_array_t::from(
1675            chat::get_chat_contacts(ctx, ChatId::new(chat_id))
1676                .await
1677                .unwrap_or_log_default(ctx, "Failed get_chat_contacts")
1678                .iter()
1679                .map(|id| id.to_u32())
1680                .collect::<Vec<u32>>(),
1681        );
1682        Box::into_raw(Box::new(arr))
1683    })
1684}
1685
1686#[no_mangle]
1687pub unsafe extern "C" fn dc_search_msgs(
1688    context: *mut dc_context_t,
1689    chat_id: u32,
1690    query: *const libc::c_char,
1691) -> *mut dc_array::dc_array_t {
1692    if context.is_null() || query.is_null() {
1693        eprintln!("ignoring careless call to dc_search_msgs()");
1694        return ptr::null_mut();
1695    }
1696    let ctx = &*context;
1697    let chat_id = if chat_id == 0 {
1698        None
1699    } else {
1700        Some(ChatId::new(chat_id))
1701    };
1702
1703    block_on(async move {
1704        let arr = dc_array_t::from(
1705            ctx.search_msgs(chat_id, &to_string_lossy(query))
1706                .await
1707                .unwrap_or_log_default(ctx, "Failed search_msgs")
1708                .iter()
1709                .map(|msg_id| msg_id.to_u32())
1710                .collect::<Vec<u32>>(),
1711        );
1712        Box::into_raw(Box::new(arr))
1713    })
1714}
1715
1716#[no_mangle]
1717pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) -> *mut dc_chat_t {
1718    if context.is_null() {
1719        eprintln!("ignoring careless call to dc_get_chat()");
1720        return ptr::null_mut();
1721    }
1722    let ctx = &*context;
1723    let context: Context = ctx.clone();
1724
1725    block_on(async move {
1726        match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)).await {
1727            Ok(chat) => {
1728                let ffi_chat = ChatWrapper { context, chat };
1729                Box::into_raw(Box::new(ffi_chat))
1730            }
1731            Err(_) => ptr::null_mut(),
1732        }
1733    })
1734}
1735
1736#[no_mangle]
1737pub unsafe extern "C" fn dc_create_group_chat(
1738    context: *mut dc_context_t,
1739    _protect: libc::c_int,
1740    name: *const libc::c_char,
1741) -> u32 {
1742    if context.is_null() || name.is_null() {
1743        eprintln!("ignoring careless call to dc_create_group_chat()");
1744        return 0;
1745    }
1746    let ctx = &*context;
1747
1748    block_on(chat::create_group(ctx, &to_string_lossy(name)))
1749        .context("Failed to create group chat")
1750        .log_err(ctx)
1751        .map(|id| id.to_u32())
1752        .unwrap_or(0)
1753}
1754
1755#[no_mangle]
1756pub unsafe extern "C" fn dc_create_broadcast_list(context: *mut dc_context_t) -> u32 {
1757    if context.is_null() {
1758        eprintln!("ignoring careless call to dc_create_broadcast_list()");
1759        return 0;
1760    }
1761    let ctx = &*context;
1762    block_on(chat::create_broadcast(ctx, "Channel".to_string()))
1763        .context("Failed to create broadcast channel")
1764        .log_err(ctx)
1765        .map(|id| id.to_u32())
1766        .unwrap_or(0)
1767}
1768
1769#[no_mangle]
1770pub unsafe extern "C" fn dc_is_contact_in_chat(
1771    context: *mut dc_context_t,
1772    chat_id: u32,
1773    contact_id: u32,
1774) -> libc::c_int {
1775    if context.is_null() {
1776        eprintln!("ignoring careless call to dc_is_contact_in_chat()");
1777        return 0;
1778    }
1779    let ctx = &*context;
1780
1781    block_on(chat::is_contact_in_chat(
1782        ctx,
1783        ChatId::new(chat_id),
1784        ContactId::new(contact_id),
1785    ))
1786    .context("is_contact_in_chat failed")
1787    .log_err(ctx)
1788    .unwrap_or_default() as libc::c_int
1789}
1790
1791#[no_mangle]
1792pub unsafe extern "C" fn dc_add_contact_to_chat(
1793    context: *mut dc_context_t,
1794    chat_id: u32,
1795    contact_id: u32,
1796) -> libc::c_int {
1797    if context.is_null() {
1798        eprintln!("ignoring careless call to dc_add_contact_to_chat()");
1799        return 0;
1800    }
1801    let ctx = &*context;
1802
1803    block_on(chat::add_contact_to_chat(
1804        ctx,
1805        ChatId::new(chat_id),
1806        ContactId::new(contact_id),
1807    ))
1808    .context("Failed to add contact")
1809    .log_err(ctx)
1810    .is_ok() as libc::c_int
1811}
1812
1813#[no_mangle]
1814pub unsafe extern "C" fn dc_remove_contact_from_chat(
1815    context: *mut dc_context_t,
1816    chat_id: u32,
1817    contact_id: u32,
1818) -> libc::c_int {
1819    if context.is_null() {
1820        eprintln!("ignoring careless call to dc_remove_contact_from_chat()");
1821        return 0;
1822    }
1823    let ctx = &*context;
1824
1825    block_on(chat::remove_contact_from_chat(
1826        ctx,
1827        ChatId::new(chat_id),
1828        ContactId::new(contact_id),
1829    ))
1830    .context("Failed to remove contact")
1831    .log_err(ctx)
1832    .is_ok() as libc::c_int
1833}
1834
1835#[no_mangle]
1836pub unsafe extern "C" fn dc_set_chat_name(
1837    context: *mut dc_context_t,
1838    chat_id: u32,
1839    name: *const libc::c_char,
1840) -> libc::c_int {
1841    if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() || name.is_null()
1842    {
1843        eprintln!("ignoring careless call to dc_set_chat_name()");
1844        return 0;
1845    }
1846    let ctx = &*context;
1847
1848    block_on(async move {
1849        chat::set_chat_name(ctx, ChatId::new(chat_id), &to_string_lossy(name))
1850            .await
1851            .map(|_| 1)
1852            .unwrap_or_log_default(ctx, "Failed to set chat name")
1853    })
1854}
1855
1856#[no_mangle]
1857pub unsafe extern "C" fn dc_set_chat_profile_image(
1858    context: *mut dc_context_t,
1859    chat_id: u32,
1860    image: *const libc::c_char,
1861) -> libc::c_int {
1862    if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() {
1863        eprintln!("ignoring careless call to dc_set_chat_profile_image()");
1864        return 0;
1865    }
1866    let ctx = &*context;
1867
1868    block_on(async move {
1869        chat::set_chat_profile_image(ctx, ChatId::new(chat_id), &to_string_lossy(image))
1870            .await
1871            .map(|_| 1)
1872            .unwrap_or_log_default(ctx, "Failed to set profile image")
1873    })
1874}
1875
1876#[no_mangle]
1877pub unsafe extern "C" fn dc_set_chat_mute_duration(
1878    context: *mut dc_context_t,
1879    chat_id: u32,
1880    duration: i64,
1881) -> libc::c_int {
1882    if context.is_null() {
1883        eprintln!("ignoring careless call to dc_set_chat_mute_duration()");
1884        return 0;
1885    }
1886    let ctx = &*context;
1887    let mute_duration = match duration {
1888        0 => MuteDuration::NotMuted,
1889        -1 => MuteDuration::Forever,
1890        n if n > 0 => SystemTime::now()
1891            .checked_add(Duration::from_secs(duration as u64))
1892            .map_or(MuteDuration::Forever, MuteDuration::Until),
1893        _ => {
1894            eprintln!("dc_chat_set_mute_duration(): Can not use negative duration other than -1");
1895            return 0;
1896        }
1897    };
1898
1899    block_on(async move {
1900        chat::set_muted(ctx, ChatId::new(chat_id), mute_duration)
1901            .await
1902            .map(|_| 1)
1903            .unwrap_or_log_default(ctx, "Failed to set mute duration")
1904    })
1905}
1906
1907#[no_mangle]
1908pub unsafe extern "C" fn dc_get_chat_encrinfo(
1909    context: *mut dc_context_t,
1910    chat_id: u32,
1911) -> *mut libc::c_char {
1912    if context.is_null() {
1913        eprintln!("ignoring careless call to dc_get_chat_encrinfo()");
1914        return "".strdup();
1915    }
1916    let ctx = &*context;
1917
1918    block_on(ChatId::new(chat_id).get_encryption_info(ctx))
1919        .map(|s| s.strdup())
1920        .log_err(ctx)
1921        .unwrap_or(ptr::null_mut())
1922}
1923
1924#[no_mangle]
1925pub unsafe extern "C" fn dc_get_chat_ephemeral_timer(
1926    context: *mut dc_context_t,
1927    chat_id: u32,
1928) -> u32 {
1929    if context.is_null() {
1930        eprintln!("ignoring careless call to dc_get_chat_ephemeral_timer()");
1931        return 0;
1932    }
1933    let ctx = &*context;
1934
1935    // Timer value 0 is returned in the rare case of a database error,
1936    // but it is not dangerous since it is only meant to be used as a
1937    // default when changing the value. Such errors should not be
1938    // ignored when ephemeral timer value is used to construct
1939    // message headers.
1940    block_on(async move { ChatId::new(chat_id).get_ephemeral_timer(ctx).await })
1941        .context("Failed to get ephemeral timer")
1942        .log_err(ctx)
1943        .unwrap_or_default()
1944        .to_u32()
1945}
1946
1947#[no_mangle]
1948pub unsafe extern "C" fn dc_set_chat_ephemeral_timer(
1949    context: *mut dc_context_t,
1950    chat_id: u32,
1951    timer: u32,
1952) -> libc::c_int {
1953    if context.is_null() {
1954        eprintln!("ignoring careless call to dc_set_chat_ephemeral_timer()");
1955        return 0;
1956    }
1957    let ctx = &*context;
1958
1959    block_on(async move {
1960        ChatId::new(chat_id)
1961            .set_ephemeral_timer(ctx, EphemeralTimer::from_u32(timer))
1962            .await
1963            .context("Failed to set ephemeral timer")
1964            .log_err(ctx)
1965            .is_ok() as libc::c_int
1966    })
1967}
1968
1969#[no_mangle]
1970pub unsafe extern "C" fn dc_get_msg_info(
1971    context: *mut dc_context_t,
1972    msg_id: u32,
1973) -> *mut libc::c_char {
1974    if context.is_null() {
1975        eprintln!("ignoring careless call to dc_get_msg_info()");
1976        return "".strdup();
1977    }
1978    let ctx = &*context;
1979    let msg_id = MsgId::new(msg_id);
1980    block_on(msg_id.get_info(ctx))
1981        .unwrap_or_log_default(ctx, "failed to get msg id")
1982        .strdup()
1983}
1984
1985#[no_mangle]
1986pub unsafe extern "C" fn dc_get_msg_html(
1987    context: *mut dc_context_t,
1988    msg_id: u32,
1989) -> *mut libc::c_char {
1990    if context.is_null() {
1991        eprintln!("ignoring careless call to dc_get_msg_html()");
1992        return ptr::null_mut();
1993    }
1994    let ctx = &*context;
1995
1996    block_on(MsgId::new(msg_id).get_html(ctx))
1997        .unwrap_or_log_default(ctx, "Failed get_msg_html")
1998        .strdup()
1999}
2000
2001#[no_mangle]
2002pub unsafe extern "C" fn dc_delete_msgs(
2003    context: *mut dc_context_t,
2004    msg_ids: *const u32,
2005    msg_cnt: libc::c_int,
2006) {
2007    if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
2008        eprintln!("ignoring careless call to dc_delete_msgs()");
2009        return;
2010    }
2011    let ctx = &*context;
2012    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
2013
2014    block_on(message::delete_msgs(ctx, &msg_ids))
2015        .context("failed dc_delete_msgs() call")
2016        .log_err(ctx)
2017        .ok();
2018}
2019
2020#[no_mangle]
2021pub unsafe extern "C" fn dc_forward_msgs(
2022    context: *mut dc_context_t,
2023    msg_ids: *const u32,
2024    msg_cnt: libc::c_int,
2025    chat_id: u32,
2026) {
2027    if context.is_null()
2028        || msg_ids.is_null()
2029        || msg_cnt <= 0
2030        || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32()
2031    {
2032        eprintln!("ignoring careless call to dc_forward_msgs()");
2033        return;
2034    }
2035    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
2036    let ctx = &*context;
2037
2038    block_on(async move {
2039        chat::forward_msgs(ctx, &msg_ids[..], ChatId::new(chat_id))
2040            .await
2041            .unwrap_or_log_default(ctx, "Failed to forward message")
2042    })
2043}
2044
2045#[no_mangle]
2046pub unsafe extern "C" fn dc_save_msgs(
2047    context: *mut dc_context_t,
2048    msg_ids: *const u32,
2049    msg_cnt: libc::c_int,
2050) {
2051    if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
2052        eprintln!("ignoring careless call to dc_save_msgs()");
2053        return;
2054    }
2055    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
2056    let ctx = &*context;
2057
2058    block_on(async move {
2059        chat::save_msgs(ctx, &msg_ids[..])
2060            .await
2061            .unwrap_or_log_default(ctx, "Failed to save message")
2062    })
2063}
2064
2065#[no_mangle]
2066pub unsafe extern "C" fn dc_resend_msgs(
2067    context: *mut dc_context_t,
2068    msg_ids: *const u32,
2069    msg_cnt: libc::c_int,
2070) -> libc::c_int {
2071    if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
2072        eprintln!("ignoring careless call to dc_resend_msgs()");
2073        return 0;
2074    }
2075    let ctx = &*context;
2076    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
2077
2078    block_on(chat::resend_msgs(ctx, &msg_ids))
2079        .context("Resending failed")
2080        .log_err(ctx)
2081        .is_ok() as libc::c_int
2082}
2083
2084#[no_mangle]
2085pub unsafe extern "C" fn dc_markseen_msgs(
2086    context: *mut dc_context_t,
2087    msg_ids: *const u32,
2088    msg_cnt: libc::c_int,
2089) {
2090    if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
2091        eprintln!("ignoring careless call to dc_markseen_msgs()");
2092        return;
2093    }
2094    let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
2095    let ctx = &*context;
2096
2097    block_on(message::markseen_msgs(ctx, msg_ids))
2098        .context("failed dc_markseen_msgs() call")
2099        .log_err(ctx)
2100        .ok();
2101}
2102
2103#[no_mangle]
2104pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) -> *mut dc_msg_t {
2105    if context.is_null() {
2106        eprintln!("ignoring careless call to dc_get_msg()");
2107        return ptr::null_mut();
2108    }
2109    let ctx = &*context;
2110
2111    let message = match block_on(message::Message::load_from_db(ctx, MsgId::new(msg_id)))
2112        .with_context(|| format!("dc_get_msg could not rectieve msg_id {msg_id}"))
2113        .log_err(ctx)
2114    {
2115        Ok(msg) => msg,
2116        Err(_) => {
2117            if msg_id <= constants::DC_MSG_ID_LAST_SPECIAL {
2118                // C-core API returns empty messages, do the same
2119                message::Message::new(Viewtype::default())
2120            } else {
2121                return ptr::null_mut();
2122            }
2123        }
2124    };
2125    let ffi_msg = MessageWrapper { context, message };
2126    Box::into_raw(Box::new(ffi_msg))
2127}
2128
2129#[no_mangle]
2130pub unsafe extern "C" fn dc_download_full_msg(context: *mut dc_context_t, msg_id: u32) {
2131    if context.is_null() {
2132        eprintln!("ignoring careless call to dc_download_full_msg()");
2133        return;
2134    }
2135    let ctx = &*context;
2136    block_on(MsgId::new(msg_id).download_full(ctx))
2137        .context("Failed to download message fully.")
2138        .log_err(ctx)
2139        .ok();
2140}
2141
2142#[no_mangle]
2143pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *const libc::c_char) -> libc::c_int {
2144    if addr.is_null() {
2145        eprintln!("ignoring careless call to dc_may_be_valid_addr()");
2146        return 0;
2147    }
2148
2149    contact::may_be_valid_addr(&to_string_lossy(addr)) as libc::c_int
2150}
2151
2152#[no_mangle]
2153pub unsafe extern "C" fn dc_lookup_contact_id_by_addr(
2154    context: *mut dc_context_t,
2155    addr: *const libc::c_char,
2156) -> u32 {
2157    if context.is_null() || addr.is_null() {
2158        eprintln!("ignoring careless call to dc_lookup_contact_id_by_addr()");
2159        return 0;
2160    }
2161    let ctx = &*context;
2162
2163    block_on(async move {
2164        Contact::lookup_id_by_addr(ctx, &to_string_lossy(addr), Origin::IncomingReplyTo)
2165            .await
2166            .unwrap_or_log_default(ctx, "failed to lookup id")
2167            .map(|id| id.to_u32())
2168            .unwrap_or_default()
2169    })
2170}
2171
2172#[no_mangle]
2173pub unsafe extern "C" fn dc_create_contact(
2174    context: *mut dc_context_t,
2175    name: *const libc::c_char,
2176    addr: *const libc::c_char,
2177) -> u32 {
2178    if context.is_null() || addr.is_null() {
2179        eprintln!("ignoring careless call to dc_create_contact()");
2180        return 0;
2181    }
2182    let ctx = &*context;
2183    let name = to_string_lossy(name);
2184
2185    block_on(Contact::create(ctx, &name, &to_string_lossy(addr)))
2186        .context("Cannot create contact")
2187        .log_err(ctx)
2188        .map(|id| id.to_u32())
2189        .unwrap_or(0)
2190}
2191
2192#[no_mangle]
2193pub unsafe extern "C" fn dc_add_address_book(
2194    context: *mut dc_context_t,
2195    addr_book: *const libc::c_char,
2196) -> libc::c_int {
2197    if context.is_null() || addr_book.is_null() {
2198        eprintln!("ignoring careless call to dc_add_address_book()");
2199        return 0;
2200    }
2201    let ctx = &*context;
2202
2203    block_on(async move {
2204        match Contact::add_address_book(ctx, &to_string_lossy(addr_book)).await {
2205            Ok(cnt) => cnt as libc::c_int,
2206            Err(_) => 0,
2207        }
2208    })
2209}
2210
2211#[no_mangle]
2212pub unsafe extern "C" fn dc_make_vcard(
2213    context: *mut dc_context_t,
2214    contact_id: u32,
2215) -> *mut libc::c_char {
2216    if context.is_null() {
2217        eprintln!("ignoring careless call to dc_make_vcard()");
2218        return ptr::null_mut();
2219    }
2220    let ctx = &*context;
2221    let contact_id = ContactId::new(contact_id);
2222
2223    block_on(contact::make_vcard(ctx, &[contact_id]))
2224        .unwrap_or_log_default(ctx, "dc_make_vcard failed")
2225        .strdup()
2226}
2227
2228#[no_mangle]
2229pub unsafe extern "C" fn dc_import_vcard(
2230    context: *mut dc_context_t,
2231    vcard: *const libc::c_char,
2232) -> *mut dc_array::dc_array_t {
2233    if context.is_null() || vcard.is_null() {
2234        eprintln!("ignoring careless call to dc_import_vcard()");
2235        return ptr::null_mut();
2236    }
2237    let ctx = &*context;
2238
2239    match block_on(contact::import_vcard(ctx, &to_string_lossy(vcard)))
2240        .context("dc_import_vcard failed")
2241        .log_err(ctx)
2242    {
2243        Ok(contact_ids) => Box::into_raw(Box::new(dc_array_t::from(
2244            contact_ids
2245                .iter()
2246                .map(|id| id.to_u32())
2247                .collect::<Vec<u32>>(),
2248        ))),
2249        Err(_) => ptr::null_mut(),
2250    }
2251}
2252
2253#[no_mangle]
2254pub unsafe extern "C" fn dc_get_contacts(
2255    context: *mut dc_context_t,
2256    flags: u32,
2257    query: *const libc::c_char,
2258) -> *mut dc_array::dc_array_t {
2259    if context.is_null() {
2260        eprintln!("ignoring careless call to dc_get_contacts()");
2261        return ptr::null_mut();
2262    }
2263    let ctx = &*context;
2264    let query = to_opt_string_lossy(query);
2265
2266    block_on(async move {
2267        match Contact::get_all(ctx, flags, query.as_deref()).await {
2268            Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from(
2269                contacts.iter().map(|id| id.to_u32()).collect::<Vec<u32>>(),
2270            ))),
2271            Err(_) => ptr::null_mut(),
2272        }
2273    })
2274}
2275
2276#[no_mangle]
2277pub unsafe extern "C" fn dc_get_blocked_contacts(
2278    context: *mut dc_context_t,
2279) -> *mut dc_array::dc_array_t {
2280    if context.is_null() {
2281        eprintln!("ignoring careless call to dc_get_blocked_contacts()");
2282        return ptr::null_mut();
2283    }
2284    let ctx = &*context;
2285
2286    block_on(async move {
2287        Box::into_raw(Box::new(dc_array_t::from(
2288            Contact::get_all_blocked(ctx)
2289                .await
2290                .context("Can't get blocked contacts")
2291                .log_err(ctx)
2292                .unwrap_or_default()
2293                .iter()
2294                .map(|id| id.to_u32())
2295                .collect::<Vec<u32>>(),
2296        )))
2297    })
2298}
2299
2300#[no_mangle]
2301pub unsafe extern "C" fn dc_block_contact(
2302    context: *mut dc_context_t,
2303    contact_id: u32,
2304    block: libc::c_int,
2305) {
2306    let contact_id = ContactId::new(contact_id);
2307    if context.is_null() || contact_id.is_special() {
2308        eprintln!("ignoring careless call to dc_block_contact()");
2309        return;
2310    }
2311    let ctx = &*context;
2312    block_on(async move {
2313        if block == 0 {
2314            Contact::unblock(ctx, contact_id)
2315                .await
2316                .context("Can't unblock contact")
2317                .log_err(ctx)
2318                .ok();
2319        } else {
2320            Contact::block(ctx, contact_id)
2321                .await
2322                .context("Can't block contact")
2323                .log_err(ctx)
2324                .ok();
2325        }
2326    });
2327}
2328
2329#[no_mangle]
2330pub unsafe extern "C" fn dc_get_contact_encrinfo(
2331    context: *mut dc_context_t,
2332    contact_id: u32,
2333) -> *mut libc::c_char {
2334    if context.is_null() {
2335        eprintln!("ignoring careless call to dc_get_contact_encrinfo()");
2336        return "".strdup();
2337    }
2338    let ctx = &*context;
2339
2340    block_on(Contact::get_encrinfo(ctx, ContactId::new(contact_id)))
2341        .map(|s| s.strdup())
2342        .log_err(ctx)
2343        .unwrap_or(ptr::null_mut())
2344}
2345
2346#[no_mangle]
2347pub unsafe extern "C" fn dc_delete_contact(
2348    context: *mut dc_context_t,
2349    contact_id: u32,
2350) -> libc::c_int {
2351    let contact_id = ContactId::new(contact_id);
2352    if context.is_null() || contact_id.is_special() {
2353        eprintln!("ignoring careless call to dc_delete_contact()");
2354        return 0;
2355    }
2356    let ctx = &*context;
2357
2358    block_on(Contact::delete(ctx, contact_id))
2359        .context("Cannot delete contact")
2360        .log_err(ctx)
2361        .is_ok() as libc::c_int
2362}
2363
2364#[no_mangle]
2365pub unsafe extern "C" fn dc_get_contact(
2366    context: *mut dc_context_t,
2367    contact_id: u32,
2368) -> *mut dc_contact_t {
2369    if context.is_null() {
2370        eprintln!("ignoring careless call to dc_get_contact()");
2371        return ptr::null_mut();
2372    }
2373    let ctx = &*context;
2374
2375    block_on(async move {
2376        Contact::get_by_id(ctx, ContactId::new(contact_id))
2377            .await
2378            .map(|contact| Box::into_raw(Box::new(ContactWrapper { context, contact })))
2379            .unwrap_or_else(|_| ptr::null_mut())
2380    })
2381}
2382
2383fn spawn_imex(ctx: Context, what: imex::ImexMode, param1: String, passphrase: Option<String>) {
2384    spawn(async move {
2385        imex::imex(&ctx, what, param1.as_ref(), passphrase)
2386            .await
2387            .context("IMEX failed")
2388            .log_err(&ctx)
2389    });
2390}
2391
2392#[no_mangle]
2393pub unsafe extern "C" fn dc_imex(
2394    context: *mut dc_context_t,
2395    what_raw: libc::c_int,
2396    param1: *const libc::c_char,
2397    param2: *const libc::c_char,
2398) {
2399    if context.is_null() {
2400        eprintln!("ignoring careless call to dc_imex()");
2401        return;
2402    }
2403    let what = match imex::ImexMode::from_i32(what_raw) {
2404        Some(what) => what,
2405        None => {
2406            eprintln!("ignoring invalid argument {what_raw} to dc_imex");
2407            return;
2408        }
2409    };
2410    let passphrase = to_opt_string_lossy(param2);
2411
2412    let ctx = &*context;
2413
2414    if let Some(param1) = to_opt_string_lossy(param1) {
2415        spawn_imex(ctx.clone(), what, param1, passphrase);
2416    } else {
2417        eprintln!("dc_imex called without a valid directory");
2418    }
2419}
2420
2421#[no_mangle]
2422pub unsafe extern "C" fn dc_imex_has_backup(
2423    context: *mut dc_context_t,
2424    dir: *const libc::c_char,
2425) -> *mut libc::c_char {
2426    if context.is_null() || dir.is_null() {
2427        eprintln!("ignoring careless call to dc_imex_has_backup()");
2428        return ptr::null_mut(); // NULL explicitly defined as "has no backup"
2429    }
2430    let ctx = &*context;
2431
2432    match block_on(imex::has_backup(ctx, to_string_lossy(dir).as_ref()))
2433        .context("dc_imex_has_backup")
2434        .log_err(ctx)
2435    {
2436        Ok(res) => res.strdup(),
2437        Err(_) => ptr::null_mut(),
2438    }
2439}
2440
2441#[no_mangle]
2442pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) {
2443    if context.is_null() {
2444        eprintln!("ignoring careless call to dc_stop_ongoing_process()");
2445        return;
2446    }
2447    let ctx = &*context;
2448    block_on(ctx.stop_ongoing());
2449}
2450
2451#[no_mangle]
2452pub unsafe extern "C" fn dc_check_qr(
2453    context: *mut dc_context_t,
2454    qr: *const libc::c_char,
2455) -> *mut dc_lot_t {
2456    if context.is_null() || qr.is_null() {
2457        eprintln!("ignoring careless call to dc_check_qr()");
2458        return ptr::null_mut();
2459    }
2460    let ctx = &*context;
2461
2462    let lot = match block_on(qr::check_qr(ctx, &to_string_lossy(qr))) {
2463        Ok(qr) => qr.into(),
2464        Err(err) => err.into(),
2465    };
2466    Box::into_raw(Box::new(lot))
2467}
2468
2469#[no_mangle]
2470pub unsafe extern "C" fn dc_get_securejoin_qr(
2471    context: *mut dc_context_t,
2472    chat_id: u32,
2473) -> *mut libc::c_char {
2474    if context.is_null() {
2475        eprintln!("ignoring careless call to dc_get_securejoin_qr()");
2476        return "".strdup();
2477    }
2478    let ctx = &*context;
2479    let chat_id = if chat_id == 0 {
2480        None
2481    } else {
2482        Some(ChatId::new(chat_id))
2483    };
2484
2485    block_on(securejoin::get_securejoin_qr(ctx, chat_id))
2486        .unwrap_or_else(|_| "".to_string())
2487        .strdup()
2488}
2489
2490#[no_mangle]
2491pub unsafe extern "C" fn dc_get_securejoin_qr_svg(
2492    context: *mut dc_context_t,
2493    chat_id: u32,
2494) -> *mut libc::c_char {
2495    if context.is_null() {
2496        eprintln!("ignoring careless call to generate_verification_qr()");
2497        return "".strdup();
2498    }
2499    let ctx = &*context;
2500    let chat_id = if chat_id == 0 {
2501        None
2502    } else {
2503        Some(ChatId::new(chat_id))
2504    };
2505
2506    block_on(get_securejoin_qr_svg(ctx, chat_id))
2507        .unwrap_or_else(|_| "".to_string())
2508        .strdup()
2509}
2510
2511#[no_mangle]
2512pub unsafe extern "C" fn dc_join_securejoin(
2513    context: *mut dc_context_t,
2514    qr: *const libc::c_char,
2515) -> u32 {
2516    if context.is_null() || qr.is_null() {
2517        eprintln!("ignoring careless call to dc_join_securejoin()");
2518        return 0;
2519    }
2520    let ctx = &*context;
2521
2522    block_on(async move {
2523        securejoin::join_securejoin(ctx, &to_string_lossy(qr))
2524            .await
2525            .map(|chatid| chatid.to_u32())
2526            .context("failed dc_join_securejoin() call")
2527            .log_err(ctx)
2528            .unwrap_or_default()
2529    })
2530}
2531
2532#[no_mangle]
2533pub unsafe extern "C" fn dc_send_locations_to_chat(
2534    context: *mut dc_context_t,
2535    chat_id: u32,
2536    seconds: libc::c_int,
2537) {
2538    if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() || seconds < 0 {
2539        eprintln!("ignoring careless call to dc_send_locations_to_chat()");
2540        return;
2541    }
2542    let ctx = &*context;
2543
2544    block_on(location::send_to_chat(
2545        ctx,
2546        ChatId::new(chat_id),
2547        seconds as i64,
2548    ))
2549    .context("Failed dc_send_locations_to_chat()")
2550    .log_err(ctx)
2551    .ok();
2552}
2553
2554#[no_mangle]
2555pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
2556    context: *mut dc_context_t,
2557    chat_id: u32,
2558) -> libc::c_int {
2559    if context.is_null() {
2560        eprintln!("ignoring careless call to dc_is_sending_locations_to_chat()");
2561        return 0;
2562    }
2563    let ctx = &*context;
2564    if chat_id == 0 {
2565        block_on(location::is_sending(ctx))
2566            .unwrap_or_log_default(ctx, "Failed is_sending_locations()") as libc::c_int
2567    } else {
2568        block_on(location::is_sending_to_chat(ctx, ChatId::new(chat_id)))
2569            .unwrap_or_log_default(ctx, "Failed is_sending_locations_to_chat()")
2570            as libc::c_int
2571    }
2572}
2573
2574#[no_mangle]
2575pub unsafe extern "C" fn dc_set_location(
2576    context: *mut dc_context_t,
2577    latitude: libc::c_double,
2578    longitude: libc::c_double,
2579    accuracy: libc::c_double,
2580) -> libc::c_int {
2581    if context.is_null() {
2582        eprintln!("ignoring careless call to dc_set_location()");
2583        return 0;
2584    }
2585    let ctx = &*context;
2586
2587    block_on(location::set(ctx, latitude, longitude, accuracy))
2588        .log_err(ctx)
2589        .unwrap_or_default() as libc::c_int
2590}
2591
2592#[no_mangle]
2593pub unsafe extern "C" fn dc_get_locations(
2594    context: *mut dc_context_t,
2595    chat_id: u32,
2596    contact_id: u32,
2597    timestamp_begin: i64,
2598    timestamp_end: i64,
2599) -> *mut dc_array::dc_array_t {
2600    if context.is_null() {
2601        eprintln!("ignoring careless call to dc_get_locations()");
2602        return ptr::null_mut();
2603    }
2604    let ctx = &*context;
2605    let chat_id = if chat_id == 0 {
2606        None
2607    } else {
2608        Some(ChatId::new(chat_id))
2609    };
2610    let contact_id = if contact_id == 0 {
2611        None
2612    } else {
2613        Some(contact_id)
2614    };
2615
2616    block_on(async move {
2617        let res = location::get_range(ctx, chat_id, contact_id, timestamp_begin, timestamp_end)
2618            .await
2619            .unwrap_or_log_default(ctx, "Failed get_locations");
2620        Box::into_raw(Box::new(dc_array_t::from(res)))
2621    })
2622}
2623
2624#[no_mangle]
2625pub unsafe extern "C" fn dc_create_qr_svg(payload: *const libc::c_char) -> *mut libc::c_char {
2626    if payload.is_null() {
2627        eprintln!("ignoring careless call to dc_create_qr_svg()");
2628        return "".strdup();
2629    }
2630
2631    create_qr_svg(&to_string_lossy(payload))
2632        .unwrap_or_else(|_| "".to_string())
2633        .strdup()
2634}
2635
2636#[no_mangle]
2637pub unsafe extern "C" fn dc_get_last_error(context: *mut dc_context_t) -> *mut libc::c_char {
2638    if context.is_null() {
2639        eprintln!("ignoring careless call to dc_get_last_error()");
2640        return "".strdup();
2641    }
2642    let ctx = &*context;
2643    ctx.get_last_error().strdup()
2644}
2645
2646// dc_array_t
2647
2648pub type dc_array_t = dc_array::dc_array_t;
2649
2650#[no_mangle]
2651pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) {
2652    if a.is_null() {
2653        eprintln!("ignoring careless call to dc_array_unref()");
2654        return;
2655    }
2656
2657    drop(Box::from_raw(a));
2658}
2659
2660#[no_mangle]
2661pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t {
2662    if array.is_null() {
2663        eprintln!("ignoring careless call to dc_array_get_cnt()");
2664        return 0;
2665    }
2666
2667    (*array).len()
2668}
2669#[no_mangle]
2670pub unsafe extern "C" fn dc_array_get_id(array: *const dc_array_t, index: libc::size_t) -> u32 {
2671    if array.is_null() {
2672        eprintln!("ignoring careless call to dc_array_get_id()");
2673        return 0;
2674    }
2675
2676    (*array).get_id(index)
2677}
2678#[no_mangle]
2679pub unsafe extern "C" fn dc_array_get_latitude(
2680    array: *const dc_array_t,
2681    index: libc::size_t,
2682) -> libc::c_double {
2683    if array.is_null() {
2684        eprintln!("ignoring careless call to dc_array_get_latitude()");
2685        return 0.0;
2686    }
2687
2688    (*array).get_location(index).latitude
2689}
2690#[no_mangle]
2691pub unsafe extern "C" fn dc_array_get_longitude(
2692    array: *const dc_array_t,
2693    index: libc::size_t,
2694) -> libc::c_double {
2695    if array.is_null() {
2696        eprintln!("ignoring careless call to dc_array_get_longitude()");
2697        return 0.0;
2698    }
2699
2700    (*array).get_location(index).longitude
2701}
2702#[no_mangle]
2703pub unsafe extern "C" fn dc_array_get_accuracy(
2704    array: *const dc_array_t,
2705    index: libc::size_t,
2706) -> libc::c_double {
2707    if array.is_null() {
2708        eprintln!("ignoring careless call to dc_array_get_accuracy()");
2709        return 0.0;
2710    }
2711
2712    (*array).get_location(index).accuracy
2713}
2714#[no_mangle]
2715pub unsafe extern "C" fn dc_array_get_timestamp(
2716    array: *const dc_array_t,
2717    index: libc::size_t,
2718) -> i64 {
2719    if array.is_null() {
2720        eprintln!("ignoring careless call to dc_array_get_timestamp()");
2721        return 0;
2722    }
2723
2724    (*array).get_timestamp(index).unwrap_or_default()
2725}
2726#[no_mangle]
2727pub unsafe extern "C" fn dc_array_get_chat_id(
2728    array: *const dc_array_t,
2729    index: libc::size_t,
2730) -> libc::c_uint {
2731    if array.is_null() {
2732        eprintln!("ignoring careless call to dc_array_get_chat_id()");
2733        return 0;
2734    }
2735    (*array).get_location(index).chat_id.to_u32()
2736}
2737#[no_mangle]
2738pub unsafe extern "C" fn dc_array_get_contact_id(
2739    array: *const dc_array_t,
2740    index: libc::size_t,
2741) -> libc::c_uint {
2742    if array.is_null() {
2743        eprintln!("ignoring careless call to dc_array_get_contact_id()");
2744        return 0;
2745    }
2746
2747    (*array).get_location(index).contact_id.to_u32()
2748}
2749#[no_mangle]
2750pub unsafe extern "C" fn dc_array_get_msg_id(
2751    array: *const dc_array_t,
2752    index: libc::size_t,
2753) -> libc::c_uint {
2754    if array.is_null() {
2755        eprintln!("ignoring careless call to dc_array_get_msg_id()");
2756        return 0;
2757    }
2758
2759    (*array).get_location(index).msg_id
2760}
2761#[no_mangle]
2762pub unsafe extern "C" fn dc_array_get_marker(
2763    array: *const dc_array_t,
2764    index: libc::size_t,
2765) -> *mut libc::c_char {
2766    if array.is_null() {
2767        eprintln!("ignoring careless call to dc_array_get_marker()");
2768        return std::ptr::null_mut(); // NULL explicitly defined as "no markers"
2769    }
2770
2771    if let Some(s) = (*array).get_marker(index) {
2772        s.strdup()
2773    } else {
2774        std::ptr::null_mut()
2775    }
2776}
2777
2778#[no_mangle]
2779pub unsafe extern "C" fn dc_array_search_id(
2780    array: *const dc_array_t,
2781    needle: libc::c_uint,
2782    ret_index: *mut libc::size_t,
2783) -> libc::c_int {
2784    if array.is_null() {
2785        eprintln!("ignoring careless call to dc_array_search_id()");
2786        return 0;
2787    }
2788
2789    if let Some(i) = (*array).search_id(needle) {
2790        if !ret_index.is_null() {
2791            *ret_index = i
2792        }
2793        1
2794    } else {
2795        0
2796    }
2797}
2798
2799// Return the independent-state of the location at the given index.
2800// Independent locations do not belong to the track of the user.
2801// Returns 1 if location belongs to the track of the user,
2802// 0 if location was reported independently.
2803#[no_mangle]
2804pub unsafe extern "C" fn dc_array_is_independent(
2805    array: *const dc_array_t,
2806    index: libc::size_t,
2807) -> libc::c_int {
2808    if array.is_null() {
2809        eprintln!("ignoring careless call to dc_array_is_independent()");
2810        return 0;
2811    }
2812
2813    (*array).get_location(index).independent as libc::c_int
2814}
2815
2816// dc_chatlist_t
2817
2818/// FFI struct for [dc_chatlist_t]
2819///
2820/// This is the structure behind [dc_chatlist_t] which is the opaque
2821/// structure representing a chatlist in the FFI API.  It exists
2822/// because the FFI API has a reference from the message to the
2823/// context, but the Rust API does not, so the FFI layer needs to glue
2824/// these together.
2825pub struct ChatlistWrapper {
2826    context: *const dc_context_t,
2827    list: chatlist::Chatlist,
2828}
2829
2830pub type dc_chatlist_t = ChatlistWrapper;
2831
2832#[no_mangle]
2833pub unsafe extern "C" fn dc_chatlist_unref(chatlist: *mut dc_chatlist_t) {
2834    if chatlist.is_null() {
2835        eprintln!("ignoring careless call to dc_chatlist_unref()");
2836        return;
2837    }
2838    drop(Box::from_raw(chatlist));
2839}
2840
2841#[no_mangle]
2842pub unsafe extern "C" fn dc_chatlist_get_cnt(chatlist: *mut dc_chatlist_t) -> libc::size_t {
2843    if chatlist.is_null() {
2844        eprintln!("ignoring careless call to dc_chatlist_get_cnt()");
2845        return 0;
2846    }
2847    let ffi_list = &*chatlist;
2848    ffi_list.list.len() as libc::size_t
2849}
2850
2851#[no_mangle]
2852pub unsafe extern "C" fn dc_chatlist_get_chat_id(
2853    chatlist: *mut dc_chatlist_t,
2854    index: libc::size_t,
2855) -> u32 {
2856    if chatlist.is_null() {
2857        eprintln!("ignoring careless call to dc_chatlist_get_chat_id()");
2858        return 0;
2859    }
2860    let ffi_list = &*chatlist;
2861    let ctx = &*ffi_list.context;
2862    match ffi_list
2863        .list
2864        .get_chat_id(index)
2865        .context("get_chat_id failed")
2866        .log_err(ctx)
2867    {
2868        Ok(chat_id) => chat_id.to_u32(),
2869        Err(_) => 0,
2870    }
2871}
2872
2873#[no_mangle]
2874pub unsafe extern "C" fn dc_chatlist_get_msg_id(
2875    chatlist: *mut dc_chatlist_t,
2876    index: libc::size_t,
2877) -> u32 {
2878    if chatlist.is_null() {
2879        eprintln!("ignoring careless call to dc_chatlist_get_msg_id()");
2880        return 0;
2881    }
2882    let ffi_list = &*chatlist;
2883    let ctx = &*ffi_list.context;
2884    match ffi_list
2885        .list
2886        .get_msg_id(index)
2887        .context("get_msg_id failed")
2888        .log_err(ctx)
2889    {
2890        Ok(msg_id) => msg_id.map_or(0, |msg_id| msg_id.to_u32()),
2891        Err(_) => 0,
2892    }
2893}
2894
2895#[no_mangle]
2896pub unsafe extern "C" fn dc_chatlist_get_summary(
2897    chatlist: *mut dc_chatlist_t,
2898    index: libc::size_t,
2899    chat: *mut dc_chat_t,
2900) -> *mut dc_lot_t {
2901    if chatlist.is_null() {
2902        eprintln!("ignoring careless call to dc_chatlist_get_summary()");
2903        return ptr::null_mut();
2904    }
2905    let maybe_chat = if chat.is_null() {
2906        None
2907    } else {
2908        let ffi_chat = &*chat;
2909        Some(&ffi_chat.chat)
2910    };
2911    let ffi_list = &*chatlist;
2912    let ctx = &*ffi_list.context;
2913
2914    block_on(async move {
2915        let summary = ffi_list
2916            .list
2917            .get_summary(ctx, index, maybe_chat)
2918            .await
2919            .context("get_summary failed")
2920            .log_err(ctx)
2921            .unwrap_or_default();
2922        Box::into_raw(Box::new(summary.into()))
2923    })
2924}
2925
2926#[no_mangle]
2927pub unsafe extern "C" fn dc_chatlist_get_summary2(
2928    context: *mut dc_context_t,
2929    chat_id: u32,
2930    msg_id: u32,
2931) -> *mut dc_lot_t {
2932    if context.is_null() {
2933        eprintln!("ignoring careless call to dc_chatlist_get_summary2()");
2934        return ptr::null_mut();
2935    }
2936    let ctx = &*context;
2937    let msg_id = if msg_id == 0 {
2938        None
2939    } else {
2940        Some(MsgId::new(msg_id))
2941    };
2942    let summary = block_on(Chatlist::get_summary2(
2943        ctx,
2944        ChatId::new(chat_id),
2945        msg_id,
2946        None,
2947    ))
2948    .context("get_summary2 failed")
2949    .log_err(ctx)
2950    .unwrap_or_default();
2951    Box::into_raw(Box::new(summary.into()))
2952}
2953
2954#[no_mangle]
2955pub unsafe extern "C" fn dc_chatlist_get_context(
2956    chatlist: *mut dc_chatlist_t,
2957) -> *const dc_context_t {
2958    if chatlist.is_null() {
2959        eprintln!("ignoring careless call to dc_chatlist_get_context()");
2960        return ptr::null_mut();
2961    }
2962    let ffi_list = &*chatlist;
2963    ffi_list.context
2964}
2965
2966// dc_chat_t
2967
2968/// FFI struct for [dc_chat_t]
2969///
2970/// This is the structure behind [dc_chat_t] which is the opaque
2971/// structure representing a chat in the FFI API.  It exists
2972/// because the FFI API has a reference from the message to the
2973/// context, but the Rust API does not, so the FFI layer needs to glue
2974/// these together.
2975pub struct ChatWrapper {
2976    context: Context,
2977    chat: chat::Chat,
2978}
2979
2980pub type dc_chat_t = ChatWrapper;
2981
2982#[no_mangle]
2983pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
2984    if chat.is_null() {
2985        eprintln!("ignoring careless call to dc_chat_unref()");
2986        return;
2987    }
2988
2989    drop(Box::from_raw(chat));
2990}
2991
2992#[no_mangle]
2993pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
2994    if chat.is_null() {
2995        eprintln!("ignoring careless call to dc_chat_get_id()");
2996        return 0;
2997    }
2998    let ffi_chat = &*chat;
2999    ffi_chat.chat.get_id().to_u32()
3000}
3001
3002#[no_mangle]
3003pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
3004    if chat.is_null() {
3005        eprintln!("ignoring careless call to dc_chat_get_type()");
3006        return 0;
3007    }
3008    let ffi_chat = &*chat;
3009    ffi_chat.chat.get_type() as libc::c_int
3010}
3011
3012#[no_mangle]
3013pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
3014    if chat.is_null() {
3015        eprintln!("ignoring careless call to dc_chat_get_name()");
3016        return "".strdup();
3017    }
3018    let ffi_chat = &*chat;
3019    ffi_chat.chat.get_name().strdup()
3020}
3021
3022#[no_mangle]
3023pub unsafe extern "C" fn dc_chat_get_mailinglist_addr(chat: *mut dc_chat_t) -> *mut libc::c_char {
3024    if chat.is_null() {
3025        eprintln!("ignoring careless call to dc_chat_get_mailinglist_addr()");
3026        return "".strdup();
3027    }
3028    let ffi_chat = &*chat;
3029    ffi_chat
3030        .chat
3031        .get_mailinglist_addr()
3032        .unwrap_or_default()
3033        .strdup()
3034}
3035
3036#[no_mangle]
3037pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
3038    if chat.is_null() {
3039        eprintln!("ignoring careless call to dc_chat_get_profile_image()");
3040        return ptr::null_mut(); // NULL explicitly defined as "no image"
3041    }
3042    let ffi_chat = &*chat;
3043
3044    block_on(async move {
3045        match ffi_chat
3046            .chat
3047            .get_profile_image(&ffi_chat.context)
3048            .await
3049            .context("Failed to get profile image")
3050            .log_err(&ffi_chat.context)
3051            .unwrap_or_default()
3052        {
3053            Some(p) => p.to_string_lossy().strdup(),
3054            None => ptr::null_mut(),
3055        }
3056    })
3057}
3058
3059#[no_mangle]
3060pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
3061    if chat.is_null() {
3062        eprintln!("ignoring careless call to dc_chat_get_color()");
3063        return 0;
3064    }
3065    let ffi_chat = &*chat;
3066
3067    block_on(ffi_chat.chat.get_color(&ffi_chat.context))
3068        .unwrap_or_log_default(&ffi_chat.context, "Failed get_color")
3069}
3070
3071#[no_mangle]
3072pub unsafe extern "C" fn dc_chat_get_visibility(chat: *mut dc_chat_t) -> libc::c_int {
3073    if chat.is_null() {
3074        eprintln!("ignoring careless call to dc_chat_get_visibility()");
3075        return 0;
3076    }
3077    let ffi_chat = &*chat;
3078    match ffi_chat.chat.visibility {
3079        ChatVisibility::Normal => 0,
3080        ChatVisibility::Archived => 1,
3081        ChatVisibility::Pinned => 2,
3082    }
3083}
3084
3085#[no_mangle]
3086pub unsafe extern "C" fn dc_chat_is_contact_request(chat: *mut dc_chat_t) -> libc::c_int {
3087    if chat.is_null() {
3088        eprintln!("ignoring careless call to dc_chat_is_contact_request()");
3089        return 0;
3090    }
3091    let ffi_chat = &*chat;
3092    ffi_chat.chat.is_contact_request() as libc::c_int
3093}
3094
3095#[no_mangle]
3096pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
3097    if chat.is_null() {
3098        eprintln!("ignoring careless call to dc_chat_is_unpromoted()");
3099        return 0;
3100    }
3101    let ffi_chat = &*chat;
3102    ffi_chat.chat.is_unpromoted() as libc::c_int
3103}
3104
3105#[no_mangle]
3106pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
3107    if chat.is_null() {
3108        eprintln!("ignoring careless call to dc_chat_is_self_talk()");
3109        return 0;
3110    }
3111    let ffi_chat = &*chat;
3112    ffi_chat.chat.is_self_talk() as libc::c_int
3113}
3114
3115#[no_mangle]
3116pub unsafe extern "C" fn dc_chat_is_device_talk(chat: *mut dc_chat_t) -> libc::c_int {
3117    if chat.is_null() {
3118        eprintln!("ignoring careless call to dc_chat_is_device_talk()");
3119        return 0;
3120    }
3121    let ffi_chat = &*chat;
3122    ffi_chat.chat.is_device_talk() as libc::c_int
3123}
3124
3125#[no_mangle]
3126pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int {
3127    if chat.is_null() {
3128        eprintln!("ignoring careless call to dc_chat_can_send()");
3129        return 0;
3130    }
3131    let ffi_chat = &*chat;
3132    block_on(ffi_chat.chat.can_send(&ffi_chat.context))
3133        .context("can_send failed")
3134        .log_err(&ffi_chat.context)
3135        .unwrap_or_default() as libc::c_int
3136}
3137
3138#[no_mangle]
3139pub extern "C" fn dc_chat_is_protected(_chat: *mut dc_chat_t) -> libc::c_int {
3140    0
3141}
3142
3143#[no_mangle]
3144pub unsafe extern "C" fn dc_chat_is_encrypted(chat: *mut dc_chat_t) -> libc::c_int {
3145    if chat.is_null() {
3146        eprintln!("ignoring careless call to dc_chat_is_encrypted()");
3147        return 0;
3148    }
3149    let ffi_chat = &*chat;
3150
3151    block_on(ffi_chat.chat.is_encrypted(&ffi_chat.context))
3152        .unwrap_or_log_default(&ffi_chat.context, "Failed dc_chat_is_encrypted") as libc::c_int
3153}
3154
3155#[no_mangle]
3156pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
3157    if chat.is_null() {
3158        eprintln!("ignoring careless call to dc_chat_is_sending_locations()");
3159        return 0;
3160    }
3161    let ffi_chat = &*chat;
3162    ffi_chat.chat.is_sending_locations() as libc::c_int
3163}
3164
3165#[no_mangle]
3166pub unsafe extern "C" fn dc_chat_is_muted(chat: *mut dc_chat_t) -> libc::c_int {
3167    if chat.is_null() {
3168        eprintln!("ignoring careless call to dc_chat_is_muted()");
3169        return 0;
3170    }
3171    let ffi_chat = &*chat;
3172    ffi_chat.chat.is_muted() as libc::c_int
3173}
3174
3175#[no_mangle]
3176pub unsafe extern "C" fn dc_chat_get_remaining_mute_duration(chat: *mut dc_chat_t) -> i64 {
3177    if chat.is_null() {
3178        eprintln!("ignoring careless call to dc_chat_get_remaining_mute_duration()");
3179        return 0;
3180    }
3181    let ffi_chat = &*chat;
3182    if !ffi_chat.chat.is_muted() {
3183        return 0;
3184    }
3185    // If the chat was muted to before the epoch, it is not muted.
3186    match ffi_chat.chat.mute_duration {
3187        MuteDuration::NotMuted => 0,
3188        MuteDuration::Forever => -1,
3189        MuteDuration::Until(when) => when
3190            .duration_since(SystemTime::now())
3191            .map(|d| d.as_secs() as i64)
3192            .unwrap_or(0),
3193    }
3194}
3195
3196#[no_mangle]
3197pub unsafe extern "C" fn dc_chat_get_info_json(
3198    context: *mut dc_context_t,
3199    chat_id: u32,
3200) -> *mut libc::c_char {
3201    if context.is_null() {
3202        eprintln!("ignoring careless call to dc_chat_get_info_json()");
3203        return "".strdup();
3204    }
3205    let ctx = &*context;
3206
3207    block_on(async move {
3208        let Ok(chat) = chat::Chat::load_from_db(ctx, ChatId::new(chat_id))
3209            .await
3210            .context("dc_get_chat_info_json() failed to load chat")
3211            .log_err(ctx)
3212        else {
3213            return "".strdup();
3214        };
3215        let Ok(info) = chat
3216            .get_info(ctx)
3217            .await
3218            .context("dc_get_chat_info_json() failed to get chat info")
3219            .log_err(ctx)
3220        else {
3221            return "".strdup();
3222        };
3223        serde_json::to_string(&info)
3224            .unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json")
3225            .strdup()
3226    })
3227}
3228
3229// dc_msg_t
3230
3231/// FFI struct for [dc_msg_t]
3232///
3233/// This is the structure behind [dc_msg_t] which is the opaque
3234/// structure representing a message in the FFI API.  It exists
3235/// because the FFI API has a reference from the message to the
3236/// context, but the Rust API does not, so the FFI layer needs to glue
3237/// these together.
3238pub struct MessageWrapper {
3239    context: *const dc_context_t,
3240    message: message::Message,
3241}
3242
3243pub type dc_msg_t = MessageWrapper;
3244
3245#[no_mangle]
3246pub unsafe extern "C" fn dc_msg_new(
3247    context: *mut dc_context_t,
3248    viewtype: libc::c_int,
3249) -> *mut dc_msg_t {
3250    if context.is_null() {
3251        eprintln!("ignoring careless call to dc_msg_new()");
3252        return ptr::null_mut();
3253    }
3254    let context = &*context;
3255    let viewtype = from_prim(viewtype).expect(&format!("invalid viewtype = {viewtype}"));
3256    let msg = MessageWrapper {
3257        context,
3258        message: message::Message::new(viewtype),
3259    };
3260    Box::into_raw(Box::new(msg))
3261}
3262
3263#[no_mangle]
3264pub unsafe extern "C" fn dc_msg_unref(msg: *mut dc_msg_t) {
3265    if msg.is_null() {
3266        eprintln!("ignoring careless call to dc_msg_unref()");
3267        return;
3268    }
3269
3270    drop(Box::from_raw(msg));
3271}
3272
3273#[no_mangle]
3274pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg_t) -> u32 {
3275    if msg.is_null() {
3276        eprintln!("ignoring careless call to dc_msg_get_id()");
3277        return 0;
3278    }
3279    let ffi_msg = &*msg;
3280    ffi_msg.message.get_id().to_u32()
3281}
3282
3283#[no_mangle]
3284pub unsafe extern "C" fn dc_msg_get_from_id(msg: *mut dc_msg_t) -> u32 {
3285    if msg.is_null() {
3286        eprintln!("ignoring careless call to dc_msg_get_from_id()");
3287        return 0;
3288    }
3289    let ffi_msg = &*msg;
3290    ffi_msg.message.get_from_id().to_u32()
3291}
3292
3293#[no_mangle]
3294pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 {
3295    if msg.is_null() {
3296        eprintln!("ignoring careless call to dc_msg_get_chat_id()");
3297        return 0;
3298    }
3299    let ffi_msg = &*msg;
3300    ffi_msg.message.get_chat_id().to_u32()
3301}
3302
3303#[no_mangle]
3304pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg_t) -> libc::c_int {
3305    if msg.is_null() {
3306        eprintln!("ignoring careless call to dc_msg_get_viewtype()");
3307        return 0;
3308    }
3309    let ffi_msg = &*msg;
3310    ffi_msg
3311        .message
3312        .get_viewtype()
3313        .to_i64()
3314        .expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int
3315}
3316
3317#[no_mangle]
3318pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg_t) -> libc::c_int {
3319    if msg.is_null() {
3320        eprintln!("ignoring careless call to dc_msg_get_state()");
3321        return 0;
3322    }
3323    let ffi_msg = &*msg;
3324    ffi_msg.message.get_state() as libc::c_int
3325}
3326
3327#[no_mangle]
3328pub unsafe extern "C" fn dc_msg_get_download_state(msg: *mut dc_msg_t) -> libc::c_int {
3329    if msg.is_null() {
3330        eprintln!("ignoring careless call to dc_msg_get_download_state()");
3331        return 0;
3332    }
3333    let ffi_msg = &*msg;
3334    ffi_msg.message.download_state() as libc::c_int
3335}
3336
3337#[no_mangle]
3338pub unsafe extern "C" fn dc_msg_get_timestamp(msg: *mut dc_msg_t) -> i64 {
3339    if msg.is_null() {
3340        eprintln!("ignoring careless call to dc_msg_get_received_timestamp()");
3341        return 0;
3342    }
3343    let ffi_msg = &*msg;
3344    ffi_msg.message.get_timestamp()
3345}
3346
3347#[no_mangle]
3348pub unsafe extern "C" fn dc_msg_get_received_timestamp(msg: *mut dc_msg_t) -> i64 {
3349    if msg.is_null() {
3350        eprintln!("ignoring careless call to dc_msg_get_received_timestamp()");
3351        return 0;
3352    }
3353    let ffi_msg = &*msg;
3354    ffi_msg.message.get_received_timestamp()
3355}
3356
3357#[no_mangle]
3358pub unsafe extern "C" fn dc_msg_get_sort_timestamp(msg: *mut dc_msg_t) -> i64 {
3359    if msg.is_null() {
3360        eprintln!("ignoring careless call to dc_msg_get_sort_timestamp()");
3361        return 0;
3362    }
3363    let ffi_msg = &*msg;
3364    ffi_msg.message.get_sort_timestamp()
3365}
3366
3367#[no_mangle]
3368pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg_t) -> *mut libc::c_char {
3369    if msg.is_null() {
3370        eprintln!("ignoring careless call to dc_msg_get_text()");
3371        return "".strdup();
3372    }
3373    let ffi_msg = &*msg;
3374    ffi_msg.message.get_text().strdup()
3375}
3376
3377#[no_mangle]
3378pub unsafe extern "C" fn dc_msg_get_subject(msg: *mut dc_msg_t) -> *mut libc::c_char {
3379    if msg.is_null() {
3380        eprintln!("ignoring careless call to dc_msg_get_subject()");
3381        return "".strdup();
3382    }
3383    let ffi_msg = &*msg;
3384    ffi_msg.message.get_subject().strdup()
3385}
3386
3387#[no_mangle]
3388pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_char {
3389    if msg.is_null() {
3390        eprintln!("ignoring careless call to dc_msg_get_file()");
3391        return "".strdup();
3392    }
3393    let ffi_msg = &*msg;
3394    let ctx = &*ffi_msg.context;
3395    ffi_msg
3396        .message
3397        .get_file(ctx)
3398        .map(|p| p.to_string_lossy().strdup())
3399        .unwrap_or_else(|| "".strdup())
3400}
3401
3402#[no_mangle]
3403pub unsafe extern "C" fn dc_msg_save_file(
3404    msg: *mut dc_msg_t,
3405    path: *const libc::c_char,
3406) -> libc::c_int {
3407    if msg.is_null() || path.is_null() {
3408        eprintln!("ignoring careless call to dc_msg_save_file()");
3409        return 0;
3410    }
3411    let ffi_msg = &*msg;
3412    let ctx = &*ffi_msg.context;
3413    let path = to_string_lossy(path);
3414    let r = block_on(
3415        ffi_msg
3416            .message
3417            .save_file(ctx, &std::path::PathBuf::from(path)),
3418    );
3419    match r {
3420        Ok(()) => 1,
3421        Err(_) => {
3422            r.context("Failed to save file from message")
3423                .log_err(ctx)
3424                .unwrap_or_default();
3425            0
3426        }
3427    }
3428}
3429
3430#[no_mangle]
3431pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c_char {
3432    if msg.is_null() {
3433        eprintln!("ignoring careless call to dc_msg_get_filename()");
3434        return "".strdup();
3435    }
3436    let ffi_msg = &*msg;
3437    ffi_msg.message.get_filename().unwrap_or_default().strdup()
3438}
3439
3440#[no_mangle]
3441pub unsafe extern "C" fn dc_msg_get_webxdc_blob(
3442    msg: *mut dc_msg_t,
3443    filename: *const libc::c_char,
3444    ret_bytes: *mut libc::size_t,
3445) -> *mut libc::c_char {
3446    if msg.is_null() || filename.is_null() || ret_bytes.is_null() {
3447        eprintln!("ignoring careless call to dc_msg_get_webxdc_blob()");
3448        return ptr::null_mut();
3449    }
3450    let ffi_msg = &*msg;
3451    let ctx = &*ffi_msg.context;
3452    let blob = block_on(async move {
3453        ffi_msg
3454            .message
3455            .get_webxdc_blob(ctx, &to_string_lossy(filename))
3456            .await
3457    });
3458    match blob {
3459        Ok(blob) => {
3460            *ret_bytes = blob.len();
3461            let ptr = libc::malloc(*ret_bytes);
3462            libc::memcpy(ptr, blob.as_ptr() as *mut libc::c_void, *ret_bytes);
3463            ptr as *mut libc::c_char
3464        }
3465        Err(err) => {
3466            eprintln!("failed read blob from archive: {err}");
3467            ptr::null_mut()
3468        }
3469    }
3470}
3471
3472#[no_mangle]
3473pub unsafe extern "C" fn dc_msg_get_webxdc_info(msg: *mut dc_msg_t) -> *mut libc::c_char {
3474    if msg.is_null() {
3475        eprintln!("ignoring careless call to dc_msg_get_webxdc_info()");
3476        return "".strdup();
3477    }
3478    let ffi_msg = &*msg;
3479    let ctx = &*ffi_msg.context;
3480
3481    let Ok(info) = block_on(ffi_msg.message.get_webxdc_info(ctx))
3482        .context("dc_msg_get_webxdc_info() failed to get info")
3483        .log_err(ctx)
3484    else {
3485        return "".strdup();
3486    };
3487    serde_json::to_string(&info)
3488        .unwrap_or_log_default(ctx, "dc_msg_get_webxdc_info() failed to serialise to json")
3489        .strdup()
3490}
3491
3492#[no_mangle]
3493pub unsafe extern "C" fn dc_msg_get_filemime(msg: *mut dc_msg_t) -> *mut libc::c_char {
3494    if msg.is_null() {
3495        eprintln!("ignoring careless call to dc_msg_get_filemime()");
3496        return "".strdup();
3497    }
3498    let ffi_msg = &*msg;
3499    if let Some(x) = ffi_msg.message.get_filemime() {
3500        x.strdup()
3501    } else {
3502        "".strdup()
3503    }
3504}
3505
3506#[no_mangle]
3507pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg_t) -> u64 {
3508    if msg.is_null() {
3509        eprintln!("ignoring careless call to dc_msg_get_filebytes()");
3510        return 0;
3511    }
3512    let ffi_msg = &*msg;
3513    let ctx = &*ffi_msg.context;
3514
3515    block_on(ffi_msg.message.get_filebytes(ctx))
3516        .unwrap_or_log_default(ctx, "Cannot get file size")
3517        .unwrap_or_default()
3518}
3519
3520#[no_mangle]
3521pub unsafe extern "C" fn dc_msg_get_width(msg: *mut dc_msg_t) -> libc::c_int {
3522    if msg.is_null() {
3523        eprintln!("ignoring careless call to dc_msg_get_width()");
3524        return 0;
3525    }
3526    let ffi_msg = &*msg;
3527    ffi_msg.message.get_width()
3528}
3529
3530#[no_mangle]
3531pub unsafe extern "C" fn dc_msg_get_height(msg: *mut dc_msg_t) -> libc::c_int {
3532    if msg.is_null() {
3533        eprintln!("ignoring careless call to dc_msg_get_height()");
3534        return 0;
3535    }
3536    let ffi_msg = &*msg;
3537    ffi_msg.message.get_height()
3538}
3539
3540#[no_mangle]
3541pub unsafe extern "C" fn dc_msg_get_duration(msg: *mut dc_msg_t) -> libc::c_int {
3542    if msg.is_null() {
3543        eprintln!("ignoring careless call to dc_msg_get_duration()");
3544        return 0;
3545    }
3546    let ffi_msg = &*msg;
3547    ffi_msg.message.get_duration()
3548}
3549
3550#[no_mangle]
3551pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg_t) -> libc::c_int {
3552    if msg.is_null() {
3553        eprintln!("ignoring careless call to dc_msg_get_showpadlock()");
3554        return 0;
3555    }
3556    let ffi_msg = &*msg;
3557    ffi_msg.message.get_showpadlock() as libc::c_int
3558}
3559
3560#[no_mangle]
3561pub unsafe extern "C" fn dc_msg_is_bot(msg: *mut dc_msg_t) -> libc::c_int {
3562    if msg.is_null() {
3563        eprintln!("ignoring careless call to dc_msg_is_bot()");
3564        return 0;
3565    }
3566    let ffi_msg = &*msg;
3567    ffi_msg.message.is_bot() as libc::c_int
3568}
3569
3570#[no_mangle]
3571pub unsafe extern "C" fn dc_msg_get_ephemeral_timer(msg: *mut dc_msg_t) -> u32 {
3572    if msg.is_null() {
3573        eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()");
3574        return 0;
3575    }
3576    let ffi_msg = &*msg;
3577    ffi_msg.message.get_ephemeral_timer().to_u32()
3578}
3579
3580#[no_mangle]
3581pub unsafe extern "C" fn dc_msg_get_ephemeral_timestamp(msg: *mut dc_msg_t) -> i64 {
3582    if msg.is_null() {
3583        eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()");
3584        return 0;
3585    }
3586    let ffi_msg = &*msg;
3587    ffi_msg.message.get_ephemeral_timestamp()
3588}
3589
3590#[no_mangle]
3591pub unsafe extern "C" fn dc_msg_get_summary(
3592    msg: *mut dc_msg_t,
3593    chat: *mut dc_chat_t,
3594) -> *mut dc_lot_t {
3595    if msg.is_null() {
3596        eprintln!("ignoring careless call to dc_msg_get_summary()");
3597        return ptr::null_mut();
3598    }
3599    let maybe_chat = if chat.is_null() {
3600        None
3601    } else {
3602        let ffi_chat = &*chat;
3603        Some(&ffi_chat.chat)
3604    };
3605    let ffi_msg = &mut *msg;
3606    let ctx = &*ffi_msg.context;
3607
3608    let summary = block_on(ffi_msg.message.get_summary(ctx, maybe_chat))
3609        .context("dc_msg_get_summary failed")
3610        .log_err(ctx)
3611        .unwrap_or_default();
3612    Box::into_raw(Box::new(summary.into()))
3613}
3614
3615#[no_mangle]
3616pub unsafe extern "C" fn dc_msg_get_summarytext(
3617    msg: *mut dc_msg_t,
3618    approx_characters: libc::c_int,
3619) -> *mut libc::c_char {
3620    if msg.is_null() {
3621        eprintln!("ignoring careless call to dc_msg_get_summarytext()");
3622        return "".strdup();
3623    }
3624    let ffi_msg = &mut *msg;
3625    let ctx = &*ffi_msg.context;
3626
3627    let summary = block_on(ffi_msg.message.get_summary(ctx, None))
3628        .context("dc_msg_get_summarytext failed")
3629        .log_err(ctx)
3630        .unwrap_or_default();
3631    match usize::try_from(approx_characters) {
3632        Ok(chars) => summary.truncated_text(chars).strdup(),
3633        Err(_) => summary.text.strdup(),
3634    }
3635}
3636
3637#[no_mangle]
3638pub unsafe extern "C" fn dc_msg_get_override_sender_name(msg: *mut dc_msg_t) -> *mut libc::c_char {
3639    if msg.is_null() {
3640        eprintln!("ignoring careless call to dc_msg_get_override_sender_name()");
3641        return "".strdup();
3642    }
3643    let ffi_msg = &mut *msg;
3644
3645    ffi_msg.message.get_override_sender_name().strdup()
3646}
3647
3648#[no_mangle]
3649pub unsafe extern "C" fn dc_msg_has_deviating_timestamp(msg: *mut dc_msg_t) -> libc::c_int {
3650    if msg.is_null() {
3651        eprintln!("ignoring careless call to dc_msg_has_deviating_timestamp()");
3652        return 0;
3653    }
3654    let ffi_msg = &*msg;
3655    ffi_msg.message.has_deviating_timestamp().into()
3656}
3657
3658#[no_mangle]
3659pub unsafe extern "C" fn dc_msg_has_location(msg: *mut dc_msg_t) -> libc::c_int {
3660    if msg.is_null() {
3661        eprintln!("ignoring careless call to dc_msg_has_location()");
3662        return 0;
3663    }
3664    let ffi_msg = &*msg;
3665    ffi_msg.message.has_location() as libc::c_int
3666}
3667
3668#[no_mangle]
3669pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg_t) -> libc::c_int {
3670    if msg.is_null() {
3671        eprintln!("ignoring careless call to dc_msg_is_sent()");
3672        return 0;
3673    }
3674    let ffi_msg = &*msg;
3675    ffi_msg.message.is_sent().into()
3676}
3677
3678#[no_mangle]
3679pub unsafe extern "C" fn dc_msg_is_forwarded(msg: *mut dc_msg_t) -> libc::c_int {
3680    if msg.is_null() {
3681        eprintln!("ignoring careless call to dc_msg_is_forwarded()");
3682        return 0;
3683    }
3684    let ffi_msg = &*msg;
3685    ffi_msg.message.is_forwarded().into()
3686}
3687
3688#[no_mangle]
3689pub unsafe extern "C" fn dc_msg_is_edited(msg: *mut dc_msg_t) -> libc::c_int {
3690    if msg.is_null() {
3691        eprintln!("ignoring careless call to dc_msg_is_edited()");
3692        return 0;
3693    }
3694    let ffi_msg = &*msg;
3695    ffi_msg.message.is_edited().into()
3696}
3697
3698#[no_mangle]
3699pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg_t) -> libc::c_int {
3700    if msg.is_null() {
3701        eprintln!("ignoring careless call to dc_msg_is_info()");
3702        return 0;
3703    }
3704    let ffi_msg = &*msg;
3705    ffi_msg.message.is_info().into()
3706}
3707
3708#[no_mangle]
3709pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int {
3710    if msg.is_null() {
3711        eprintln!("ignoring careless call to dc_msg_get_info_type()");
3712        return 0;
3713    }
3714    let ffi_msg = &*msg;
3715    ffi_msg.message.get_info_type() as libc::c_int
3716}
3717
3718#[no_mangle]
3719pub unsafe extern "C" fn dc_msg_get_info_contact_id(msg: *mut dc_msg_t) -> u32 {
3720    if msg.is_null() {
3721        eprintln!("ignoring careless call to dc_msg_get_info_contact_id()");
3722        return 0;
3723    }
3724    let ffi_msg = &*msg;
3725    let context = &*ffi_msg.context;
3726    block_on(ffi_msg.message.get_info_contact_id(context))
3727        .unwrap_or_default()
3728        .map(|id| id.to_u32())
3729        .unwrap_or_default()
3730}
3731
3732#[no_mangle]
3733pub unsafe extern "C" fn dc_msg_get_webxdc_href(msg: *mut dc_msg_t) -> *mut libc::c_char {
3734    if msg.is_null() {
3735        eprintln!("ignoring careless call to dc_msg_get_webxdc_href()");
3736        return "".strdup();
3737    }
3738
3739    let ffi_msg = &*msg;
3740    ffi_msg.message.get_webxdc_href().strdup()
3741}
3742
3743#[no_mangle]
3744pub unsafe extern "C" fn dc_msg_has_html(msg: *mut dc_msg_t) -> libc::c_int {
3745    if msg.is_null() {
3746        eprintln!("ignoring careless call to dc_msg_has_html()");
3747        return 0;
3748    }
3749    let ffi_msg = &*msg;
3750    ffi_msg.message.has_html().into()
3751}
3752
3753#[no_mangle]
3754pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg_t, text: *const libc::c_char) {
3755    if msg.is_null() {
3756        eprintln!("ignoring careless call to dc_msg_set_text()");
3757        return;
3758    }
3759    let ffi_msg = &mut *msg;
3760    ffi_msg.message.set_text(to_string_lossy(text))
3761}
3762
3763#[no_mangle]
3764pub unsafe extern "C" fn dc_msg_set_html(msg: *mut dc_msg_t, html: *const libc::c_char) {
3765    if msg.is_null() {
3766        eprintln!("ignoring careless call to dc_msg_set_html()");
3767        return;
3768    }
3769    let ffi_msg = &mut *msg;
3770    ffi_msg.message.set_html(to_opt_string_lossy(html))
3771}
3772
3773#[no_mangle]
3774pub unsafe extern "C" fn dc_msg_set_subject(msg: *mut dc_msg_t, subject: *const libc::c_char) {
3775    if msg.is_null() {
3776        eprintln!("ignoring careless call to dc_msg_get_subject()");
3777        return;
3778    }
3779    let ffi_msg = &mut *msg;
3780    ffi_msg.message.set_subject(to_string_lossy(subject));
3781}
3782
3783#[no_mangle]
3784pub unsafe extern "C" fn dc_msg_set_override_sender_name(
3785    msg: *mut dc_msg_t,
3786    name: *const libc::c_char,
3787) {
3788    if msg.is_null() {
3789        eprintln!("ignoring careless call to dc_msg_set_override_sender_name()");
3790        return;
3791    }
3792    let ffi_msg = &mut *msg;
3793    ffi_msg
3794        .message
3795        .set_override_sender_name(to_opt_string_lossy(name))
3796}
3797
3798#[no_mangle]
3799pub unsafe extern "C" fn dc_msg_set_file_and_deduplicate(
3800    msg: *mut dc_msg_t,
3801    file: *const libc::c_char,
3802    name: *const libc::c_char,
3803    filemime: *const libc::c_char,
3804) {
3805    if msg.is_null() || file.is_null() {
3806        eprintln!("ignoring careless call to dc_msg_set_file_and_deduplicate()");
3807        return;
3808    }
3809    let ffi_msg = &mut *msg;
3810    let ctx = &*ffi_msg.context;
3811
3812    ffi_msg
3813        .message
3814        .set_file_and_deduplicate(
3815            ctx,
3816            as_path(file),
3817            to_opt_string_lossy(name).as_deref(),
3818            to_opt_string_lossy(filemime).as_deref(),
3819        )
3820        .context("Failed to set file")
3821        .log_err(&*ffi_msg.context)
3822        .ok();
3823}
3824
3825#[no_mangle]
3826pub unsafe extern "C" fn dc_msg_set_dimension(
3827    msg: *mut dc_msg_t,
3828    width: libc::c_int,
3829    height: libc::c_int,
3830) {
3831    if msg.is_null() {
3832        eprintln!("ignoring careless call to dc_msg_set_dimension()");
3833        return;
3834    }
3835    let ffi_msg = &mut *msg;
3836    ffi_msg.message.set_dimension(width, height)
3837}
3838
3839#[no_mangle]
3840pub unsafe extern "C" fn dc_msg_set_duration(msg: *mut dc_msg_t, duration: libc::c_int) {
3841    if msg.is_null() {
3842        eprintln!("ignoring careless call to dc_msg_set_duration()");
3843        return;
3844    }
3845    let ffi_msg = &mut *msg;
3846    ffi_msg.message.set_duration(duration)
3847}
3848
3849#[no_mangle]
3850pub unsafe extern "C" fn dc_msg_set_location(
3851    msg: *mut dc_msg_t,
3852    latitude: libc::c_double,
3853    longitude: libc::c_double,
3854) {
3855    if msg.is_null() {
3856        eprintln!("ignoring careless call to dc_msg_set_location()");
3857        return;
3858    }
3859    let ffi_msg = &mut *msg;
3860    ffi_msg.message.set_location(latitude, longitude)
3861}
3862
3863#[no_mangle]
3864pub unsafe extern "C" fn dc_msg_latefiling_mediasize(
3865    msg: *mut dc_msg_t,
3866    width: libc::c_int,
3867    height: libc::c_int,
3868    duration: libc::c_int,
3869) {
3870    if msg.is_null() {
3871        eprintln!("ignoring careless call to dc_msg_latefiling_mediasize()");
3872        return;
3873    }
3874    let ffi_msg = &mut *msg;
3875    let ctx = &*ffi_msg.context;
3876
3877    block_on({
3878        ffi_msg
3879            .message
3880            .latefiling_mediasize(ctx, width, height, duration)
3881    })
3882    .context("Cannot set media size")
3883    .log_err(ctx)
3884    .ok();
3885}
3886
3887#[no_mangle]
3888pub unsafe extern "C" fn dc_msg_get_error(msg: *mut dc_msg_t) -> *mut libc::c_char {
3889    if msg.is_null() {
3890        eprintln!("ignoring careless call to dc_msg_get_error()");
3891        return ptr::null_mut();
3892    }
3893    let ffi_msg = &*msg;
3894    match ffi_msg.message.error() {
3895        Some(error) => error.strdup(),
3896        None => ptr::null_mut(),
3897    }
3898}
3899
3900#[no_mangle]
3901pub unsafe extern "C" fn dc_msg_set_quote(msg: *mut dc_msg_t, quote: *const dc_msg_t) {
3902    if msg.is_null() {
3903        eprintln!("ignoring careless call to dc_msg_set_quote()");
3904        return;
3905    }
3906    let ffi_msg = &mut *msg;
3907    let quote_msg = if quote.is_null() {
3908        None
3909    } else {
3910        let ffi_quote = &*quote;
3911        if ffi_msg.context != ffi_quote.context {
3912            eprintln!("ignoring attempt to quote message from a different context");
3913            return;
3914        }
3915        Some(&ffi_quote.message)
3916    };
3917
3918    block_on(async move {
3919        ffi_msg
3920            .message
3921            .set_quote(&*ffi_msg.context, quote_msg)
3922            .await
3923            .context("failed to set quote")
3924            .log_err(&*ffi_msg.context)
3925            .ok();
3926    });
3927}
3928
3929#[no_mangle]
3930pub unsafe extern "C" fn dc_msg_get_quoted_text(msg: *const dc_msg_t) -> *mut libc::c_char {
3931    if msg.is_null() {
3932        eprintln!("ignoring careless call to dc_msg_get_quoted_text()");
3933        return ptr::null_mut();
3934    }
3935    let ffi_msg: &MessageWrapper = &*msg;
3936    ffi_msg
3937        .message
3938        .quoted_text()
3939        .map_or_else(ptr::null_mut, |s| s.strdup())
3940}
3941
3942#[no_mangle]
3943pub unsafe extern "C" fn dc_msg_get_quoted_msg(msg: *const dc_msg_t) -> *mut dc_msg_t {
3944    if msg.is_null() {
3945        eprintln!("ignoring careless call to dc_get_quoted_msg()");
3946        return ptr::null_mut();
3947    }
3948    let ffi_msg: &MessageWrapper = &*msg;
3949    let context = &*ffi_msg.context;
3950    let res = block_on(async move {
3951        ffi_msg
3952            .message
3953            .quoted_message(context)
3954            .await
3955            .context("failed to get quoted message")
3956            .log_err(context)
3957            .unwrap_or(None)
3958    });
3959
3960    match res {
3961        Some(message) => Box::into_raw(Box::new(MessageWrapper { context, message })),
3962        None => ptr::null_mut(),
3963    }
3964}
3965
3966#[no_mangle]
3967pub unsafe extern "C" fn dc_msg_get_parent(msg: *const dc_msg_t) -> *mut dc_msg_t {
3968    if msg.is_null() {
3969        eprintln!("ignoring careless call to dc_msg_get_parent()");
3970        return ptr::null_mut();
3971    }
3972    let ffi_msg: &MessageWrapper = &*msg;
3973    let context = &*ffi_msg.context;
3974    let res = block_on(async move {
3975        ffi_msg
3976            .message
3977            .parent(context)
3978            .await
3979            .context("failed to get parent message")
3980            .log_err(context)
3981            .unwrap_or(None)
3982    });
3983
3984    match res {
3985        Some(message) => Box::into_raw(Box::new(MessageWrapper { context, message })),
3986        None => ptr::null_mut(),
3987    }
3988}
3989
3990#[no_mangle]
3991pub unsafe extern "C" fn dc_msg_get_original_msg_id(msg: *const dc_msg_t) -> u32 {
3992    if msg.is_null() {
3993        eprintln!("ignoring careless call to dc_msg_get_original_msg_id()");
3994        return 0;
3995    }
3996    let ffi_msg: &MessageWrapper = &*msg;
3997    let context = &*ffi_msg.context;
3998    block_on(async move {
3999        ffi_msg
4000            .message
4001            .get_original_msg_id(context)
4002            .await
4003            .context("failed to get original message")
4004            .log_err(context)
4005            .unwrap_or_default()
4006            .map(|id| id.to_u32())
4007            .unwrap_or(0)
4008    })
4009}
4010
4011#[no_mangle]
4012pub unsafe extern "C" fn dc_msg_get_saved_msg_id(msg: *const dc_msg_t) -> u32 {
4013    if msg.is_null() {
4014        eprintln!("ignoring careless call to dc_msg_get_saved_msg_id()");
4015        return 0;
4016    }
4017    let ffi_msg: &MessageWrapper = &*msg;
4018    let context = &*ffi_msg.context;
4019    block_on(async move {
4020        ffi_msg
4021            .message
4022            .get_saved_msg_id(context)
4023            .await
4024            .context("failed to get original message")
4025            .log_err(context)
4026            .unwrap_or_default()
4027            .map(|id| id.to_u32())
4028            .unwrap_or(0)
4029    })
4030}
4031
4032// dc_contact_t
4033
4034/// FFI struct for [dc_contact_t]
4035///
4036/// This is the structure behind [dc_contact_t] which is the opaque
4037/// structure representing a contact in the FFI API.  It exists
4038/// because the FFI API has a reference from the message to the
4039/// context, but the Rust API does not, so the FFI layer needs to glue
4040/// these together.
4041pub struct ContactWrapper {
4042    context: *const dc_context_t,
4043    contact: contact::Contact,
4044}
4045
4046pub type dc_contact_t = ContactWrapper;
4047
4048#[no_mangle]
4049pub unsafe extern "C" fn dc_contact_unref(contact: *mut dc_contact_t) {
4050    if contact.is_null() {
4051        eprintln!("ignoring careless call to dc_contact_unref()");
4052        return;
4053    }
4054    drop(Box::from_raw(contact));
4055}
4056
4057#[no_mangle]
4058pub unsafe extern "C" fn dc_contact_get_id(contact: *mut dc_contact_t) -> u32 {
4059    if contact.is_null() {
4060        eprintln!("ignoring careless call to dc_contact_get_id()");
4061        return 0;
4062    }
4063    let ffi_contact = &*contact;
4064    ffi_contact.contact.get_id().to_u32()
4065}
4066
4067#[no_mangle]
4068pub unsafe extern "C" fn dc_contact_get_addr(contact: *mut dc_contact_t) -> *mut libc::c_char {
4069    if contact.is_null() {
4070        eprintln!("ignoring careless call to dc_contact_get_addr()");
4071        return "".strdup();
4072    }
4073    let ffi_contact = &*contact;
4074    ffi_contact.contact.get_addr().strdup()
4075}
4076
4077#[no_mangle]
4078pub unsafe extern "C" fn dc_contact_get_name(contact: *mut dc_contact_t) -> *mut libc::c_char {
4079    if contact.is_null() {
4080        eprintln!("ignoring careless call to dc_contact_get_name()");
4081        return "".strdup();
4082    }
4083    let ffi_contact = &*contact;
4084    ffi_contact.contact.get_name().strdup()
4085}
4086
4087#[no_mangle]
4088pub unsafe extern "C" fn dc_contact_get_auth_name(contact: *mut dc_contact_t) -> *mut libc::c_char {
4089    if contact.is_null() {
4090        eprintln!("ignoring careless call to dc_contact_get_auth_name()");
4091        return "".strdup();
4092    }
4093    let ffi_contact = &*contact;
4094    ffi_contact.contact.get_authname().strdup()
4095}
4096
4097#[no_mangle]
4098pub unsafe extern "C" fn dc_contact_get_display_name(
4099    contact: *mut dc_contact_t,
4100) -> *mut libc::c_char {
4101    if contact.is_null() {
4102        eprintln!("ignoring careless call to dc_contact_get_display_name()");
4103        return "".strdup();
4104    }
4105    let ffi_contact = &*contact;
4106    ffi_contact.contact.get_display_name().strdup()
4107}
4108
4109#[no_mangle]
4110pub unsafe extern "C" fn dc_contact_get_name_n_addr(
4111    contact: *mut dc_contact_t,
4112) -> *mut libc::c_char {
4113    if contact.is_null() {
4114        eprintln!("ignoring careless call to dc_contact_get_name_n_addr()");
4115        return "".strdup();
4116    }
4117    let ffi_contact = &*contact;
4118    ffi_contact.contact.get_name_n_addr().strdup()
4119}
4120
4121#[no_mangle]
4122pub unsafe extern "C" fn dc_contact_get_profile_image(
4123    contact: *mut dc_contact_t,
4124) -> *mut libc::c_char {
4125    if contact.is_null() {
4126        eprintln!("ignoring careless call to dc_contact_get_profile_image()");
4127        return ptr::null_mut(); // NULL explicitly defined as "no profile image"
4128    }
4129    let ffi_contact = &*contact;
4130    let ctx = &*ffi_contact.context;
4131
4132    block_on(async move {
4133        ffi_contact
4134            .contact
4135            .get_profile_image(ctx)
4136            .await
4137            .unwrap_or_log_default(ctx, "failed to get profile image")
4138            .map(|p| p.to_string_lossy().strdup())
4139            .unwrap_or_else(std::ptr::null_mut)
4140    })
4141}
4142
4143#[no_mangle]
4144pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32 {
4145    if contact.is_null() {
4146        eprintln!("ignoring careless call to dc_contact_get_color()");
4147        return 0;
4148    }
4149    let ffi_contact = &*contact;
4150    let ctx = &*ffi_contact.context;
4151    block_on(async move {
4152        ffi_contact
4153            .contact
4154            // We don't want any UIs displaying gray self-color.
4155            .get_or_gen_color(ctx)
4156            .await
4157            .context("Contact::get_color()")
4158            .log_err(ctx)
4159            .unwrap_or(0)
4160    })
4161}
4162
4163#[no_mangle]
4164pub unsafe extern "C" fn dc_contact_get_status(contact: *mut dc_contact_t) -> *mut libc::c_char {
4165    if contact.is_null() {
4166        eprintln!("ignoring careless call to dc_contact_get_status()");
4167        return "".strdup();
4168    }
4169    let ffi_contact = &*contact;
4170    ffi_contact.contact.get_status().strdup()
4171}
4172
4173#[no_mangle]
4174pub unsafe extern "C" fn dc_contact_get_last_seen(contact: *mut dc_contact_t) -> i64 {
4175    if contact.is_null() {
4176        eprintln!("ignoring careless call to dc_contact_get_last_seen()");
4177        return 0;
4178    }
4179    let ffi_contact = &*contact;
4180    ffi_contact.contact.last_seen()
4181}
4182
4183#[no_mangle]
4184pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t) -> libc::c_int {
4185    if contact.is_null() {
4186        eprintln!("ignoring careless call to dc_contact_was_seen_recently()");
4187        return 0;
4188    }
4189    let ffi_contact = &*contact;
4190    ffi_contact.contact.was_seen_recently() as libc::c_int
4191}
4192
4193#[no_mangle]
4194pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int {
4195    if contact.is_null() {
4196        eprintln!("ignoring careless call to dc_contact_is_blocked()");
4197        return 0;
4198    }
4199    let ffi_contact = &*contact;
4200    ffi_contact.contact.is_blocked() as libc::c_int
4201}
4202
4203#[no_mangle]
4204pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> libc::c_int {
4205    if contact.is_null() {
4206        eprintln!("ignoring careless call to dc_contact_is_verified()");
4207        return 0;
4208    }
4209    let ffi_contact = &*contact;
4210    let ctx = &*ffi_contact.context;
4211
4212    if block_on(ffi_contact.contact.is_verified(ctx))
4213        .context("is_verified failed")
4214        .log_err(ctx)
4215        .unwrap_or_default()
4216    {
4217        // Return value is essentially a boolean,
4218        // but we return 2 for true for backwards compatibility.
4219        2
4220    } else {
4221        0
4222    }
4223}
4224
4225#[no_mangle]
4226pub unsafe extern "C" fn dc_contact_is_bot(contact: *mut dc_contact_t) -> libc::c_int {
4227    if contact.is_null() {
4228        eprintln!("ignoring careless call to dc_contact_is_bot()");
4229        return 0;
4230    }
4231    (*contact).contact.is_bot() as libc::c_int
4232}
4233
4234#[no_mangle]
4235pub unsafe extern "C" fn dc_contact_is_key_contact(contact: *mut dc_contact_t) -> libc::c_int {
4236    if contact.is_null() {
4237        eprintln!("ignoring careless call to dc_contact_is_key_contact()");
4238        return 0;
4239    }
4240    (*contact).contact.is_key_contact() as libc::c_int
4241}
4242
4243#[no_mangle]
4244pub unsafe extern "C" fn dc_contact_get_verifier_id(contact: *mut dc_contact_t) -> u32 {
4245    if contact.is_null() {
4246        eprintln!("ignoring careless call to dc_contact_get_verifier_id()");
4247        return 0;
4248    }
4249    let ffi_contact = &*contact;
4250    let ctx = &*ffi_contact.context;
4251    let verifier_contact_id = block_on(ffi_contact.contact.get_verifier_id(ctx))
4252        .context("failed to get verifier")
4253        .log_err(ctx)
4254        .unwrap_or_default()
4255        .unwrap_or_default()
4256        .unwrap_or_default();
4257
4258    verifier_contact_id.to_u32()
4259}
4260// dc_lot_t
4261
4262pub type dc_lot_t = lot::Lot;
4263
4264#[no_mangle]
4265pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot_t) {
4266    if lot.is_null() {
4267        eprintln!("ignoring careless call to dc_lot_unref()");
4268        return;
4269    }
4270
4271    drop(Box::from_raw(lot));
4272}
4273
4274#[no_mangle]
4275pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot_t) -> *mut libc::c_char {
4276    if lot.is_null() {
4277        eprintln!("ignoring careless call to dc_lot_get_text1()");
4278        return ptr::null_mut(); // NULL explicitly defined as "there is no such text"
4279    }
4280
4281    let lot = &*lot;
4282    lot.get_text1().strdup()
4283}
4284
4285#[no_mangle]
4286pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot_t) -> *mut libc::c_char {
4287    if lot.is_null() {
4288        eprintln!("ignoring careless call to dc_lot_get_text2()");
4289        return ptr::null_mut(); // NULL explicitly defined as "there is no such text"
4290    }
4291
4292    let lot = &*lot;
4293    lot.get_text2().strdup()
4294}
4295
4296#[no_mangle]
4297pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot_t) -> libc::c_int {
4298    if lot.is_null() {
4299        eprintln!("ignoring careless call to dc_lot_get_text1_meaning()");
4300        return 0;
4301    }
4302
4303    let lot = &*lot;
4304    lot.get_text1_meaning() as libc::c_int
4305}
4306
4307#[no_mangle]
4308pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int {
4309    if lot.is_null() {
4310        eprintln!("ignoring careless call to dc_lot_get_state()");
4311        return 0;
4312    }
4313
4314    let lot = &*lot;
4315    lot.get_state() as libc::c_int
4316}
4317
4318#[no_mangle]
4319pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot_t) -> u32 {
4320    if lot.is_null() {
4321        eprintln!("ignoring careless call to dc_lot_get_id()");
4322        return 0;
4323    }
4324
4325    let lot = &*lot;
4326    lot.get_id()
4327}
4328
4329#[no_mangle]
4330pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot_t) -> i64 {
4331    if lot.is_null() {
4332        eprintln!("ignoring careless call to dc_lot_get_timestamp()");
4333        return 0;
4334    }
4335
4336    let lot = &*lot;
4337    lot.get_timestamp()
4338}
4339
4340#[no_mangle]
4341pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
4342    libc::free(s as *mut _)
4343}
4344
4345pub struct BackupProviderWrapper {
4346    context: *const dc_context_t,
4347    provider: BackupProvider,
4348}
4349
4350pub type dc_backup_provider_t = BackupProviderWrapper;
4351
4352#[no_mangle]
4353pub unsafe extern "C" fn dc_backup_provider_new(
4354    context: *mut dc_context_t,
4355) -> *mut dc_backup_provider_t {
4356    if context.is_null() {
4357        eprintln!("ignoring careless call to dc_backup_provider_new()");
4358        return ptr::null_mut();
4359    }
4360    let ctx = &*context;
4361    block_on(BackupProvider::prepare(ctx))
4362        .map(|provider| BackupProviderWrapper {
4363            context: ctx,
4364            provider,
4365        })
4366        .map(|ffi_provider| Box::into_raw(Box::new(ffi_provider)))
4367        .context("BackupProvider failed")
4368        .log_err(ctx)
4369        .set_last_error(ctx)
4370        .unwrap_or(ptr::null_mut())
4371}
4372
4373#[no_mangle]
4374pub unsafe extern "C" fn dc_backup_provider_get_qr(
4375    provider: *const dc_backup_provider_t,
4376) -> *mut libc::c_char {
4377    if provider.is_null() {
4378        eprintln!("ignoring careless call to dc_backup_provider_qr");
4379        return "".strdup();
4380    }
4381    let ffi_provider = &*provider;
4382    let ctx = &*ffi_provider.context;
4383    deltachat::qr::format_backup(&ffi_provider.provider.qr())
4384        .context("BackupProvider get_qr failed")
4385        .log_err(ctx)
4386        .set_last_error(ctx)
4387        .unwrap_or_default()
4388        .strdup()
4389}
4390
4391#[no_mangle]
4392pub unsafe extern "C" fn dc_backup_provider_get_qr_svg(
4393    provider: *const dc_backup_provider_t,
4394) -> *mut libc::c_char {
4395    if provider.is_null() {
4396        eprintln!("ignoring careless call to dc_backup_provider_qr_svg()");
4397        return "".strdup();
4398    }
4399    let ffi_provider = &*provider;
4400    let ctx = &*ffi_provider.context;
4401    let provider = &ffi_provider.provider;
4402    block_on(generate_backup_qr(ctx, &provider.qr()))
4403        .context("BackupProvider get_qr_svg failed")
4404        .log_err(ctx)
4405        .set_last_error(ctx)
4406        .unwrap_or_default()
4407        .strdup()
4408}
4409
4410#[no_mangle]
4411pub unsafe extern "C" fn dc_backup_provider_wait(provider: *mut dc_backup_provider_t) {
4412    if provider.is_null() {
4413        eprintln!("ignoring careless call to dc_backup_provider_wait()");
4414        return;
4415    }
4416    let ffi_provider = &mut *provider;
4417    let ctx = &*ffi_provider.context;
4418    let provider = &mut ffi_provider.provider;
4419    block_on(provider)
4420        .context("Failed to await backup provider")
4421        .log_err(ctx)
4422        .set_last_error(ctx)
4423        .ok();
4424}
4425
4426#[no_mangle]
4427pub unsafe extern "C" fn dc_backup_provider_unref(provider: *mut dc_backup_provider_t) {
4428    if provider.is_null() {
4429        eprintln!("ignoring careless call to dc_backup_provider_unref()");
4430        return;
4431    }
4432    drop(Box::from_raw(provider));
4433}
4434
4435#[no_mangle]
4436pub unsafe extern "C" fn dc_receive_backup(
4437    context: *mut dc_context_t,
4438    qr: *const libc::c_char,
4439) -> libc::c_int {
4440    if context.is_null() {
4441        eprintln!("ignoring careless call to dc_receive_backup()");
4442        return 0;
4443    }
4444    let ctx = &*context;
4445    let qr_text = to_string_lossy(qr);
4446    receive_backup(ctx.clone(), qr_text)
4447}
4448
4449// Because this is a long-running operation make sure we own the Context.  This stops a FFI
4450// user from deallocating it by calling unref on the object while we are using it.
4451fn receive_backup(ctx: Context, qr_text: String) -> libc::c_int {
4452    let qr = match block_on(qr::check_qr(&ctx, &qr_text))
4453        .context("Invalid QR code")
4454        .log_err(&ctx)
4455        .set_last_error(&ctx)
4456    {
4457        Ok(qr) => qr,
4458        Err(_) => return 0,
4459    };
4460    match block_on(imex::get_backup(&ctx, qr))
4461        .context("Get backup failed")
4462        .log_err(&ctx)
4463        .set_last_error(&ctx)
4464    {
4465        Ok(_) => 1,
4466        Err(_) => 0,
4467    }
4468}
4469
4470trait ResultExt<T, E> {
4471    /// Like `log_err()`, but:
4472    /// - returns the default value instead of an Err value.
4473    /// - emits an error instead of a warning for an [Err] result. This means
4474    ///   that the error will be shown to the user in a small pop-up.
4475    fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
4476}
4477
4478impl<T: Default, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
4479    fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T {
4480        self.map_err(|err| anyhow::anyhow!("{err:#}"))
4481            .with_context(|| message.to_string())
4482            .log_err(context)
4483            .unwrap_or_default()
4484    }
4485}
4486
4487trait ResultLastError<T, E>
4488where
4489    E: std::fmt::Display,
4490{
4491    /// Sets this `Err` value using [`Context::set_last_error`].
4492    ///
4493    /// Normally each FFI-API *should* call this if it handles an error from the Rust API:
4494    /// errors which need to be reported to users in response to an API call need to be
4495    /// propagated up the Rust API and at the FFI boundary need to be stored into the "last
4496    /// error" so the FFI users can retrieve an appropriate error message on failure.  Often
4497    /// you will want to combine this with a call to [`LogExt::log_err`].
4498    ///
4499    /// Since historically calls to the `deltachat::log::error!()` macro were (and sometimes
4500    /// still are) shown as error toasts to the user, this macro also calls
4501    /// [`Context::set_last_error`].  It is preferable however to rely on normal error
4502    /// propagation in Rust code however and only use this `ResultExt::set_last_error` call
4503    /// in the FFI layer.
4504    ///
4505    /// # Example
4506    ///
4507    /// Fully handling an error in the FFI code looks like this currently:
4508    ///
4509    /// ```no_compile
4510    /// some_dc_rust_api_call_returning_result()
4511    ///     .context("My API call failed")
4512    ///     .log_err(&context)
4513    ///     .set_last_error(&context)
4514    ///     .unwrap_or_default()
4515    /// ```
4516    ///
4517    /// As shows it is a shame the `.log_err()` call currently needs a message instead of
4518    /// relying on an implicit call to the [`anyhow::Context`] call if needed.  This stems
4519    /// from a time before we fully embraced anyhow.  Some day we'll also fix that.
4520    ///
4521    /// [`Context::set_last_error`]: context::Context::set_last_error
4522    fn set_last_error(self, context: &context::Context) -> Result<T, E>;
4523}
4524
4525impl<T, E> ResultLastError<T, E> for Result<T, E>
4526where
4527    E: std::fmt::Display,
4528{
4529    fn set_last_error(self, context: &context::Context) -> Result<T, E> {
4530        if let Err(ref err) = self {
4531            context.set_last_error(&format!("{err:#}"));
4532        }
4533        self
4534    }
4535}
4536
4537fn convert_and_prune_message_ids(msg_ids: *const u32, msg_cnt: libc::c_int) -> Vec<MsgId> {
4538    let ids = unsafe { std::slice::from_raw_parts(msg_ids, msg_cnt as usize) };
4539    let msg_ids: Vec<MsgId> = ids
4540        .iter()
4541        .filter(|id| **id > DC_MSG_ID_LAST_SPECIAL)
4542        .map(|id| MsgId::new(*id))
4543        .collect();
4544
4545    msg_ids
4546}
4547
4548// dc_provider_t
4549
4550pub type dc_provider_t = provider::Provider;
4551
4552#[no_mangle]
4553pub unsafe extern "C" fn dc_provider_new_from_email(
4554    context: *const dc_context_t,
4555    addr: *const libc::c_char,
4556) -> *const dc_provider_t {
4557    if context.is_null() || addr.is_null() {
4558        eprintln!("ignoring careless call to dc_provider_new_from_email()");
4559        return ptr::null();
4560    }
4561    let addr = to_string_lossy(addr);
4562
4563    let ctx = &*context;
4564
4565    match provider::get_provider_info_by_addr(addr.as_str())
4566        .log_err(ctx)
4567        .unwrap_or_default()
4568    {
4569        Some(provider) => provider,
4570        None => ptr::null_mut(),
4571    }
4572}
4573
4574#[no_mangle]
4575pub unsafe extern "C" fn dc_provider_new_from_email_with_dns(
4576    context: *const dc_context_t,
4577    addr: *const libc::c_char,
4578) -> *const dc_provider_t {
4579    if context.is_null() || addr.is_null() {
4580        eprintln!("ignoring careless call to dc_provider_new_from_email_with_dns()");
4581        return ptr::null();
4582    }
4583    let addr = to_string_lossy(addr);
4584
4585    let ctx = &*context;
4586
4587    match provider::get_provider_info_by_addr(addr.as_str())
4588        .log_err(ctx)
4589        .unwrap_or_default()
4590    {
4591        Some(provider) => provider,
4592        None => ptr::null_mut(),
4593    }
4594}
4595
4596#[no_mangle]
4597pub unsafe extern "C" fn dc_provider_get_overview_page(
4598    provider: *const dc_provider_t,
4599) -> *mut libc::c_char {
4600    if provider.is_null() {
4601        eprintln!("ignoring careless call to dc_provider_get_overview_page()");
4602        return "".strdup();
4603    }
4604    let provider = &*provider;
4605    provider.overview_page.strdup()
4606}
4607
4608#[no_mangle]
4609pub unsafe extern "C" fn dc_provider_get_before_login_hint(
4610    provider: *const dc_provider_t,
4611) -> *mut libc::c_char {
4612    if provider.is_null() {
4613        eprintln!("ignoring careless call to dc_provider_get_before_login_hint()");
4614        return "".strdup();
4615    }
4616    let provider = &*provider;
4617    provider.before_login_hint.strdup()
4618}
4619
4620#[no_mangle]
4621pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> libc::c_int {
4622    if provider.is_null() {
4623        eprintln!("ignoring careless call to dc_provider_get_status()");
4624        return 0;
4625    }
4626    let provider = &*provider;
4627    provider.status as libc::c_int
4628}
4629
4630#[no_mangle]
4631#[allow(clippy::needless_return)]
4632pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) {
4633    if provider.is_null() {
4634        eprintln!("ignoring careless call to dc_provider_unref()");
4635        return;
4636    }
4637    // currently, there is nothing to free, the provider info is a static object.
4638    // this may change once we start localizing string.
4639}
4640
4641// -- Accounts
4642
4643/// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using
4644/// `dc_accounts_t` in multiple threads at once.
4645pub type dc_accounts_t = RwLock<Accounts>;
4646
4647#[no_mangle]
4648pub unsafe extern "C" fn dc_accounts_new(
4649    dir: *const libc::c_char,
4650    writable: libc::c_int,
4651) -> *const dc_accounts_t {
4652    setup_panic!();
4653
4654    if dir.is_null() {
4655        eprintln!("ignoring careless call to dc_accounts_new()");
4656        return ptr::null_mut();
4657    }
4658
4659    let accs = block_on(Accounts::new(as_path(dir).into(), writable != 0));
4660
4661    match accs {
4662        Ok(accs) => Arc::into_raw(Arc::new(RwLock::new(accs))),
4663        Err(err) => {
4664            // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
4665            eprintln!("failed to create accounts: {err:#}");
4666            ptr::null_mut()
4667        }
4668    }
4669}
4670
4671pub type dc_event_channel_t = Mutex<Option<Events>>;
4672
4673#[no_mangle]
4674pub unsafe extern "C" fn dc_event_channel_new() -> *mut dc_event_channel_t {
4675    Box::into_raw(Box::new(Mutex::new(Some(Events::new()))))
4676}
4677
4678/// Release the events channel structure.
4679///
4680/// This function releases the memory of the `dc_event_channel_t` structure.
4681///
4682/// you can call it after calling dc_accounts_new_with_event_channel,
4683/// which took the events channel out of it already, so this just frees the underlying option.
4684#[no_mangle]
4685pub unsafe extern "C" fn dc_event_channel_unref(event_channel: *mut dc_event_channel_t) {
4686    if event_channel.is_null() {
4687        eprintln!("ignoring careless call to dc_event_channel_unref()");
4688        return;
4689    }
4690    drop(Box::from_raw(event_channel))
4691}
4692
4693#[no_mangle]
4694pub unsafe extern "C" fn dc_event_channel_get_event_emitter(
4695    event_channel: *mut dc_event_channel_t,
4696) -> *mut dc_event_emitter_t {
4697    if event_channel.is_null() {
4698        eprintln!("ignoring careless call to dc_event_channel_get_event_emitter()");
4699        return ptr::null_mut();
4700    }
4701
4702    let Some(event_channel) = &*(*event_channel)
4703        .lock()
4704        .expect("call to dc_event_channel_get_event_emitter() failed: mutex is poisoned")
4705    else {
4706        eprintln!(
4707            "ignoring careless call to dc_event_channel_get_event_emitter() 
4708            -> channel was already consumed, make sure you call this before dc_accounts_new_with_event_channel"
4709        );
4710        return ptr::null_mut();
4711    };
4712
4713    let emitter = event_channel.get_emitter();
4714
4715    Box::into_raw(Box::new(emitter))
4716}
4717
4718#[no_mangle]
4719pub unsafe extern "C" fn dc_accounts_new_with_event_channel(
4720    dir: *const libc::c_char,
4721    writable: libc::c_int,
4722    event_channel: *mut dc_event_channel_t,
4723) -> *const dc_accounts_t {
4724    setup_panic!();
4725
4726    if dir.is_null() || event_channel.is_null() {
4727        eprintln!("ignoring careless call to dc_accounts_new_with_event_channel()");
4728        return ptr::null_mut();
4729    }
4730
4731    // consuming channel enforce that you need to get the event emitter
4732    // before initializing the account manager,
4733    // so that you don't miss events/errors during initialisation.
4734    // It also prevents you from using the same channel on multiple account managers.
4735    let Some(event_channel) = (*event_channel)
4736        .lock()
4737        .expect("call to dc_event_channel_get_event_emitter() failed: mutex is poisoned")
4738        .take()
4739    else {
4740        eprintln!(
4741            "ignoring careless call to dc_accounts_new_with_event_channel()
4742            -> channel was already consumed"
4743        );
4744        return ptr::null_mut();
4745    };
4746
4747    let accs = block_on(Accounts::new_with_events(
4748        as_path(dir).into(),
4749        writable != 0,
4750        event_channel,
4751    ));
4752
4753    match accs {
4754        Ok(accs) => Arc::into_raw(Arc::new(RwLock::new(accs))),
4755        Err(err) => {
4756            // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
4757            eprintln!("failed to create accounts: {err:#}");
4758            ptr::null_mut()
4759        }
4760    }
4761}
4762
4763/// Release the accounts structure.
4764///
4765/// This function releases the memory of the `dc_accounts_t` structure.
4766#[no_mangle]
4767pub unsafe extern "C" fn dc_accounts_unref(accounts: *const dc_accounts_t) {
4768    if accounts.is_null() {
4769        eprintln!("ignoring careless call to dc_accounts_unref()");
4770        return;
4771    }
4772    drop(Arc::from_raw(accounts));
4773}
4774
4775#[no_mangle]
4776pub unsafe extern "C" fn dc_accounts_get_account(
4777    accounts: *const dc_accounts_t,
4778    id: u32,
4779) -> *mut dc_context_t {
4780    if accounts.is_null() {
4781        eprintln!("ignoring careless call to dc_accounts_get_account()");
4782        return ptr::null_mut();
4783    }
4784
4785    let accounts = &*accounts;
4786    block_on(accounts.read())
4787        .get_account(id)
4788        .map(|ctx| Box::into_raw(Box::new(ctx)))
4789        .unwrap_or_else(std::ptr::null_mut)
4790}
4791
4792#[no_mangle]
4793pub unsafe extern "C" fn dc_accounts_get_selected_account(
4794    accounts: *const dc_accounts_t,
4795) -> *mut dc_context_t {
4796    if accounts.is_null() {
4797        eprintln!("ignoring careless call to dc_accounts_get_selected_account()");
4798        return ptr::null_mut();
4799    }
4800
4801    let accounts = &*accounts;
4802    block_on(accounts.read())
4803        .get_selected_account()
4804        .map(|ctx| Box::into_raw(Box::new(ctx)))
4805        .unwrap_or_else(std::ptr::null_mut)
4806}
4807
4808#[no_mangle]
4809pub unsafe extern "C" fn dc_accounts_select_account(
4810    accounts: *const dc_accounts_t,
4811    id: u32,
4812) -> libc::c_int {
4813    if accounts.is_null() {
4814        eprintln!("ignoring careless call to dc_accounts_select_account()");
4815        return 0;
4816    }
4817
4818    let accounts = &*accounts;
4819    block_on(async move {
4820        let mut accounts = accounts.write().await;
4821        match accounts.select_account(id).await {
4822            Ok(()) => 1,
4823            Err(err) => {
4824                accounts.emit_event(EventType::Error(format!(
4825                    "Failed to select account: {err:#}"
4826                )));
4827                0
4828            }
4829        }
4830    })
4831}
4832
4833#[no_mangle]
4834pub unsafe extern "C" fn dc_accounts_add_account(accounts: *const dc_accounts_t) -> u32 {
4835    if accounts.is_null() {
4836        eprintln!("ignoring careless call to dc_accounts_add_account()");
4837        return 0;
4838    }
4839
4840    let accounts = &*accounts;
4841
4842    block_on(async move {
4843        let mut accounts = accounts.write().await;
4844        match accounts.add_account().await {
4845            Ok(id) => id,
4846            Err(err) => {
4847                accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}")));
4848                0
4849            }
4850        }
4851    })
4852}
4853
4854#[no_mangle]
4855pub unsafe extern "C" fn dc_accounts_add_closed_account(accounts: *const dc_accounts_t) -> u32 {
4856    if accounts.is_null() {
4857        eprintln!("ignoring careless call to dc_accounts_add_closed_account()");
4858        return 0;
4859    }
4860
4861    let accounts = &*accounts;
4862
4863    block_on(async move {
4864        let mut accounts = accounts.write().await;
4865        match accounts.add_closed_account().await {
4866            Ok(id) => id,
4867            Err(err) => {
4868                accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}")));
4869                0
4870            }
4871        }
4872    })
4873}
4874
4875#[no_mangle]
4876pub unsafe extern "C" fn dc_accounts_remove_account(
4877    accounts: *const dc_accounts_t,
4878    id: u32,
4879) -> libc::c_int {
4880    if accounts.is_null() {
4881        eprintln!("ignoring careless call to dc_accounts_remove_account()");
4882        return 0;
4883    }
4884
4885    let accounts = &*accounts;
4886
4887    block_on(async move {
4888        let mut accounts = accounts.write().await;
4889        match accounts.remove_account(id).await {
4890            Ok(()) => 1,
4891            Err(err) => {
4892                accounts.emit_event(EventType::Error(format!(
4893                    "Failed to remove account: {err:#}"
4894                )));
4895                0
4896            }
4897        }
4898    })
4899}
4900
4901#[no_mangle]
4902pub unsafe extern "C" fn dc_accounts_migrate_account(
4903    accounts: *const dc_accounts_t,
4904    dbfile: *const libc::c_char,
4905) -> u32 {
4906    if accounts.is_null() || dbfile.is_null() {
4907        eprintln!("ignoring careless call to dc_accounts_migrate_account()");
4908        return 0;
4909    }
4910
4911    let accounts = &*accounts;
4912    let dbfile = to_string_lossy(dbfile);
4913
4914    block_on(async move {
4915        let mut accounts = accounts.write().await;
4916        match accounts
4917            .migrate_account(std::path::PathBuf::from(dbfile))
4918            .await
4919        {
4920            Ok(id) => id,
4921            Err(err) => {
4922                accounts.emit_event(EventType::Error(format!(
4923                    "Failed to migrate account: {err:#}"
4924                )));
4925                0
4926            }
4927        }
4928    })
4929}
4930
4931#[no_mangle]
4932pub unsafe extern "C" fn dc_accounts_get_all(accounts: *const dc_accounts_t) -> *mut dc_array_t {
4933    if accounts.is_null() {
4934        eprintln!("ignoring careless call to dc_accounts_get_all()");
4935        return ptr::null_mut();
4936    }
4937
4938    let accounts = &*accounts;
4939    let list = block_on(accounts.read()).get_all();
4940    let array: dc_array_t = list.into();
4941
4942    Box::into_raw(Box::new(array))
4943}
4944
4945#[no_mangle]
4946pub unsafe extern "C" fn dc_accounts_start_io(accounts: *const dc_accounts_t) {
4947    if accounts.is_null() {
4948        eprintln!("ignoring careless call to dc_accounts_start_io()");
4949        return;
4950    }
4951
4952    let accounts = &*accounts;
4953    block_on(async move { accounts.write().await.start_io().await });
4954}
4955
4956#[no_mangle]
4957pub unsafe extern "C" fn dc_accounts_stop_io(accounts: *const dc_accounts_t) {
4958    if accounts.is_null() {
4959        eprintln!("ignoring careless call to dc_accounts_stop_io()");
4960        return;
4961    }
4962
4963    let accounts = &*accounts;
4964    block_on(async move { accounts.read().await.stop_io().await });
4965}
4966
4967#[no_mangle]
4968pub unsafe extern "C" fn dc_accounts_maybe_network(accounts: *const dc_accounts_t) {
4969    if accounts.is_null() {
4970        eprintln!("ignoring careless call to dc_accounts_maybe_network()");
4971        return;
4972    }
4973
4974    let accounts = &*accounts;
4975    block_on(async move { accounts.read().await.maybe_network().await });
4976}
4977
4978#[no_mangle]
4979pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *const dc_accounts_t) {
4980    if accounts.is_null() {
4981        eprintln!("ignoring careless call to dc_accounts_maybe_network_lost()");
4982        return;
4983    }
4984
4985    let accounts = &*accounts;
4986    block_on(async move { accounts.read().await.maybe_network_lost().await });
4987}
4988
4989#[no_mangle]
4990pub unsafe extern "C" fn dc_accounts_background_fetch(
4991    accounts: *const dc_accounts_t,
4992    timeout_in_seconds: u64,
4993) -> libc::c_int {
4994    if accounts.is_null() || timeout_in_seconds <= 2 {
4995        eprintln!("ignoring careless call to dc_accounts_background_fetch()");
4996        return 0;
4997    }
4998
4999    let accounts = &*accounts;
5000    let background_fetch_future = {
5001        let lock = block_on(accounts.read());
5002        lock.background_fetch(Duration::from_secs(timeout_in_seconds))
5003    };
5004    // At this point account manager is not locked anymore.
5005    block_on(background_fetch_future);
5006    1
5007}
5008
5009#[no_mangle]
5010pub unsafe extern "C" fn dc_accounts_stop_background_fetch(accounts: *const dc_accounts_t) {
5011    if accounts.is_null() {
5012        eprintln!("ignoring careless call to dc_accounts_stop_background_fetch()");
5013        return;
5014    }
5015
5016    let accounts = &*accounts;
5017    block_on(accounts.read()).stop_background_fetch();
5018}
5019
5020#[no_mangle]
5021pub unsafe extern "C" fn dc_accounts_set_push_device_token(
5022    accounts: *const dc_accounts_t,
5023    token: *const libc::c_char,
5024) {
5025    if accounts.is_null() {
5026        eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
5027        return;
5028    }
5029
5030    let accounts = &*accounts;
5031    let token = to_string_lossy(token);
5032
5033    block_on(async move {
5034        let accounts = accounts.read().await;
5035        if let Err(err) = accounts.set_push_device_token(&token).await {
5036            accounts.emit_event(EventType::Error(format!(
5037                "Failed to set notify token: {err:#}."
5038            )));
5039        }
5040    })
5041}
5042
5043#[no_mangle]
5044pub unsafe extern "C" fn dc_accounts_get_event_emitter(
5045    accounts: *const dc_accounts_t,
5046) -> *mut dc_event_emitter_t {
5047    if accounts.is_null() {
5048        eprintln!("ignoring careless call to dc_accounts_get_event_emitter()");
5049        return ptr::null_mut();
5050    }
5051
5052    let accounts = &*accounts;
5053    let emitter = block_on(accounts.read()).get_event_emitter();
5054
5055    Box::into_raw(Box::new(emitter))
5056}
5057
5058pub struct dc_jsonrpc_instance_t {
5059    receiver: OutReceiver,
5060    handle: RpcSession<CommandApi>,
5061}
5062
5063#[no_mangle]
5064pub unsafe extern "C" fn dc_jsonrpc_init(
5065    account_manager: *const dc_accounts_t,
5066) -> *mut dc_jsonrpc_instance_t {
5067    if account_manager.is_null() {
5068        eprintln!("ignoring careless call to dc_jsonrpc_init()");
5069        return ptr::null_mut();
5070    }
5071
5072    let account_manager = ManuallyDrop::new(Arc::from_raw(account_manager));
5073    let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc(Arc::clone(
5074        &account_manager,
5075    )));
5076
5077    let (request_handle, receiver) = RpcClient::new();
5078    let handle = RpcSession::new(request_handle, cmd_api);
5079
5080    let instance = dc_jsonrpc_instance_t { receiver, handle };
5081
5082    Box::into_raw(Box::new(instance))
5083}
5084
5085#[no_mangle]
5086pub unsafe extern "C" fn dc_jsonrpc_unref(jsonrpc_instance: *mut dc_jsonrpc_instance_t) {
5087    if jsonrpc_instance.is_null() {
5088        eprintln!("ignoring careless call to dc_jsonrpc_unref()");
5089        return;
5090    }
5091    drop(Box::from_raw(jsonrpc_instance));
5092}
5093
5094fn spawn_handle_jsonrpc_request(handle: RpcSession<CommandApi>, request: String) {
5095    spawn(async move {
5096        handle.handle_incoming(&request).await;
5097    });
5098}
5099
5100#[no_mangle]
5101pub unsafe extern "C" fn dc_jsonrpc_request(
5102    jsonrpc_instance: *mut dc_jsonrpc_instance_t,
5103    request: *const libc::c_char,
5104) {
5105    if jsonrpc_instance.is_null() || request.is_null() {
5106        eprintln!("ignoring careless call to dc_jsonrpc_request()");
5107        return;
5108    }
5109
5110    let handle = &(*jsonrpc_instance).handle;
5111    let request = to_string_lossy(request);
5112    spawn_handle_jsonrpc_request(handle.clone(), request);
5113}
5114
5115#[no_mangle]
5116pub unsafe extern "C" fn dc_jsonrpc_next_response(
5117    jsonrpc_instance: *mut dc_jsonrpc_instance_t,
5118) -> *mut libc::c_char {
5119    if jsonrpc_instance.is_null() {
5120        eprintln!("ignoring careless call to dc_jsonrpc_next_response()");
5121        return ptr::null_mut();
5122    }
5123    let api = &*jsonrpc_instance;
5124    block_on(api.receiver.recv())
5125        .map(|result| serde_json::to_string(&result).unwrap_or_default().strdup())
5126        .unwrap_or(ptr::null_mut())
5127}
5128
5129#[no_mangle]
5130pub unsafe extern "C" fn dc_jsonrpc_blocking_call(
5131    jsonrpc_instance: *mut dc_jsonrpc_instance_t,
5132    input: *const libc::c_char,
5133) -> *mut libc::c_char {
5134    if jsonrpc_instance.is_null() {
5135        eprintln!("ignoring careless call to dc_jsonrpc_blocking_call()");
5136        return ptr::null_mut();
5137    }
5138    let api = &*jsonrpc_instance;
5139    let input = to_string_lossy(input);
5140    let res = block_on(api.handle.process_incoming(&input));
5141    match res {
5142        Some(message) => {
5143            if let Ok(message) = serde_json::to_string(&message) {
5144                message.strdup()
5145            } else {
5146                ptr::null_mut()
5147            }
5148        }
5149        None => ptr::null_mut(),
5150    }
5151}