Skip to main content

deltachat/
lib.rs

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