Skip to main content

deltachat/
lib.rs

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