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