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