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