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