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