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