deltachat/
lib.rs

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