Skip to main content

deltachat/
lib.rs

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