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