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