deltachat/
lib.rs

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