Skip to main content

deltachat/
lib.rs

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