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