mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
this PR adds a function that can be used to create any QR code, in a raw
form.
this can be used to create add-contact as well as add-second-device QR
codes (eg. `dc_create_qr_svg(dc_get_securejoin_qr())`) - as well as for
other QR codes as proxies.
the disadvantage of the rich-formatted QR codes as created by
`dc_get_securejoin_qr_svg()` and `dc_backup_provider_get_qr_svg()` were:
- they do not look good and cannot interact with UI layout wise (but
also tapping eg. an address is not easily possible)
- esp. text really looks bad. even with
[some](e5dc8fe3d8)
[hacks](https://github.com/deltachat/deltachat-android/pull/2215) it
[stays buggy](https://github.com/deltachat/deltachat-ios/issues/2200);
the bugs mainly come from different SVG implementation, all need their
own quirks
- accessibility is probably bad as well
we thought that time, SVG is a great thing for QR codes, but apart from
basic geometrics, it is not.
so, we avoid text, this also means to avoid putting an avatar in the
middle of the QR code (we can put some generic symbol there, eg.
different ones for add-contact and add-second-device).
while this looks like a degradation, also other messengers use more raw
QR codes. also, we removed many data from the QR code anyway, eg. the
email address is no longer there. that time, sharing QR images was more
a thing, meanwhile we have invite links, that are much better for that
purpose.
in theory, we could also leave the SVG path completely and go for PNG -
which we did not that time as PNG and text looks bad, as the system font
is not easily usable :) but going for PNG would add further challenges
as passing binary data around, and also UI-implemtation-wise, that would
be a larger step. so, let's stay with SVG in a form we know is
compatible.
the old QR code functions are deprecated.
5034 lines
142 KiB
Rust
5034 lines
142 KiB
Rust
#![recursion_limit = "256"]
|
|
#![warn(unused, clippy::all)]
|
|
#![allow(
|
|
non_camel_case_types,
|
|
non_snake_case,
|
|
non_upper_case_globals,
|
|
clippy::missing_safety_doc,
|
|
clippy::expect_fun_call
|
|
)]
|
|
|
|
#[macro_use]
|
|
extern crate human_panic;
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::convert::TryFrom;
|
|
use std::fmt::Write;
|
|
use std::future::Future;
|
|
use std::ops::Deref;
|
|
use std::ptr;
|
|
use std::str::FromStr;
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, SystemTime};
|
|
|
|
use anyhow::Context as _;
|
|
use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration, ProtectionStatus};
|
|
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
|
use deltachat::contact::{Contact, ContactId, Origin};
|
|
use deltachat::context::{Context, ContextBuilder};
|
|
use deltachat::ephemeral::Timer as EphemeralTimer;
|
|
use deltachat::imex::BackupProvider;
|
|
use deltachat::key::preconfigure_keypair;
|
|
use deltachat::message::MsgId;
|
|
use deltachat::qr_code_generator::{create_qr_svg, generate_backup_qr, get_securejoin_qr_svg};
|
|
use deltachat::stock_str::StockMessage;
|
|
use deltachat::webxdc::StatusUpdateSerial;
|
|
use deltachat::*;
|
|
use deltachat::{accounts::Accounts, log::LogExt};
|
|
use num_traits::{FromPrimitive, ToPrimitive};
|
|
use once_cell::sync::Lazy;
|
|
use rand::Rng;
|
|
use tokio::runtime::Runtime;
|
|
use tokio::sync::RwLock;
|
|
use tokio::task::JoinHandle;
|
|
|
|
mod dc_array;
|
|
mod lot;
|
|
|
|
mod string;
|
|
use deltachat::chatlist::Chatlist;
|
|
|
|
use self::string::*;
|
|
|
|
// as C lacks a good and portable error handling,
|
|
// in general, the C Interface is forgiving wrt to bad parameters.
|
|
// - objects returned by some functions
|
|
// should be passable to the functions handling that object.
|
|
// - if in doubt, the empty string is returned on failures;
|
|
// this avoids panics if the ui just forgets to handle a case
|
|
// - finally, this behaviour matches the old core-c API and UIs already depend on it
|
|
|
|
const DC_GCM_ADDDAYMARKER: u32 = 0x01;
|
|
const DC_GCM_INFO_ONLY: u32 = 0x02;
|
|
|
|
// dc_context_t
|
|
|
|
/// Struct representing the deltachat context.
|
|
pub type dc_context_t = Context;
|
|
|
|
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("unable to create tokio runtime"));
|
|
|
|
fn block_on<T>(fut: T) -> T::Output
|
|
where
|
|
T: Future,
|
|
{
|
|
RT.block_on(fut)
|
|
}
|
|
|
|
fn spawn<T>(fut: T) -> JoinHandle<T::Output>
|
|
where
|
|
T: Future + Send + 'static,
|
|
T::Output: Send + 'static,
|
|
{
|
|
RT.spawn(fut)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_new(
|
|
_os_name: *const libc::c_char,
|
|
dbfile: *const libc::c_char,
|
|
blobdir: *const libc::c_char,
|
|
) -> *mut dc_context_t {
|
|
setup_panic!();
|
|
|
|
if dbfile.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_new()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let ctx = if blobdir.is_null() || *blobdir == 0 {
|
|
// generate random ID as this functionality is not yet available on the C-api.
|
|
let id = rand::thread_rng().gen();
|
|
block_on(
|
|
ContextBuilder::new(as_path(dbfile).to_path_buf())
|
|
.with_id(id)
|
|
.open(),
|
|
)
|
|
} else {
|
|
eprintln!("blobdir can not be defined explicitly anymore");
|
|
return ptr::null_mut();
|
|
};
|
|
match ctx {
|
|
Ok(ctx) => Box::into_raw(Box::new(ctx)),
|
|
Err(err) => {
|
|
eprintln!("failed to create context: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *mut dc_context_t {
|
|
setup_panic!();
|
|
|
|
if dbfile.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_new_closed()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let id = rand::thread_rng().gen();
|
|
match block_on(
|
|
ContextBuilder::new(as_path(dbfile).to_path_buf())
|
|
.with_id(id)
|
|
.build(),
|
|
) {
|
|
Ok(context) => Box::into_raw(Box::new(context)),
|
|
Err(err) => {
|
|
eprintln!("failed to create context: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_open(
|
|
context: *mut dc_context_t,
|
|
passphrase: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_open()");
|
|
return 0;
|
|
}
|
|
|
|
let ctx = &*context;
|
|
let passphrase = to_string_lossy(passphrase);
|
|
block_on(ctx.open(passphrase))
|
|
.context("dc_context_open() failed")
|
|
.log_err(ctx)
|
|
.map(|b| b as libc::c_int)
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_change_passphrase(
|
|
context: *mut dc_context_t,
|
|
passphrase: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_change_passphrase()");
|
|
return 0;
|
|
}
|
|
|
|
let ctx = &*context;
|
|
let passphrase = to_string_lossy(passphrase);
|
|
block_on(ctx.change_passphrase(passphrase))
|
|
.context("dc_context_change_passphrase() failed")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_is_open()");
|
|
return 0;
|
|
}
|
|
|
|
let ctx = &*context;
|
|
block_on(ctx.is_open()) as libc::c_int
|
|
}
|
|
|
|
/// Release the context structure.
|
|
///
|
|
/// This function releases the memory of the `dc_context_t` structure.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_context_unref()");
|
|
return;
|
|
}
|
|
drop(Box::from_raw(context));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_blobdir(context: *mut dc_context_t) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_blobdir()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
ctx.get_blobdir().to_string_lossy().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_config(
|
|
context: *mut dc_context_t,
|
|
key: *const libc::c_char,
|
|
value: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || key.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_config()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let key = to_string_lossy(key);
|
|
let value = to_opt_string_lossy(value);
|
|
|
|
block_on(async move {
|
|
if key.starts_with("ui.") {
|
|
ctx.set_ui_config(&key, value.as_deref())
|
|
.await
|
|
.with_context(|| format!("dc_set_config failed: Can't set {key} to {value:?}"))
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
} else {
|
|
match config::Config::from_str(&key) {
|
|
Ok(key) => ctx
|
|
.set_config(key, value.as_deref())
|
|
.await
|
|
.with_context(|| {
|
|
format!("dc_set_config() failed: Can't set {key} to {value:?}")
|
|
})
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int,
|
|
Err(_) => {
|
|
warn!(ctx, "dc_set_config(): invalid key");
|
|
0
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_config(
|
|
context: *mut dc_context_t,
|
|
key: *const libc::c_char,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() || key.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_config()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let key = to_string_lossy(key);
|
|
|
|
block_on(async move {
|
|
if key.starts_with("ui.") {
|
|
ctx.get_ui_config(&key)
|
|
.await
|
|
.context("Can't get ui-config")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
} else {
|
|
match config::Config::from_str(&key) {
|
|
Ok(key) => ctx
|
|
.get_config(key)
|
|
.await
|
|
.context("Can't get config")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.unwrap_or_default()
|
|
.strdup(),
|
|
Err(_) => {
|
|
warn!(ctx, "dc_get_config(): invalid key '{}'", &key);
|
|
"".strdup()
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_stock_translation(
|
|
context: *mut dc_context_t,
|
|
stock_id: u32,
|
|
stock_msg: *mut libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || stock_msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_stock_string");
|
|
return 0;
|
|
}
|
|
let msg = to_string_lossy(stock_msg);
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match StockMessage::from_u32(stock_id) {
|
|
Some(id) => match ctx.set_stock_translation(id, msg).await {
|
|
Ok(()) => 1,
|
|
Err(err) => {
|
|
warn!(ctx, "set_stock_translation failed: {err:#}");
|
|
0
|
|
}
|
|
},
|
|
None => {
|
|
warn!(ctx, "invalid stock message id {stock_id}");
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_config_from_qr(
|
|
context: *mut dc_context_t,
|
|
qr: *mut libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || qr.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_config_from_qr");
|
|
return 0;
|
|
}
|
|
let qr = to_string_lossy(qr);
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match qr::set_config_from_qr(ctx, &qr).await {
|
|
Ok(()) => 1,
|
|
Err(err) => {
|
|
error!(ctx, "Failed to create account from QR code: {err:#}");
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_info(context: *const dc_context_t) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_info()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
block_on(async move {
|
|
match ctx.get_info().await {
|
|
Ok(info) => render_info(info).unwrap_or_default().strdup(),
|
|
Err(err) => {
|
|
warn!(ctx, "failed to get info: {err:#}");
|
|
"".strdup()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn render_info(
|
|
info: BTreeMap<&'static str, String>,
|
|
) -> std::result::Result<String, std::fmt::Error> {
|
|
let mut res = String::new();
|
|
for (key, value) in &info {
|
|
writeln!(&mut res, "{key}={value}")?;
|
|
}
|
|
|
|
Ok(res)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_connectivity()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(ctx.get_connectivity()) as u32 as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_connectivity_html(
|
|
context: *const dc_context_t,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_connectivity_html()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
block_on(async move {
|
|
match ctx.get_connectivity_html().await {
|
|
Ok(html) => html.strdup(),
|
|
Err(err) => {
|
|
error!(ctx, "Failed to get connectivity html: {err:#}");
|
|
"".strdup()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_push_state()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(ctx.push_state()) as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_all_work_done()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(async move { ctx.all_work_done().await as libc::c_int })
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_oauth2_url(
|
|
context: *mut dc_context_t,
|
|
addr: *const libc::c_char,
|
|
redirect: *const libc::c_char,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_oauth2_url()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "unknown"
|
|
}
|
|
let ctx = &*context;
|
|
let addr = to_string_lossy(addr);
|
|
let redirect = to_string_lossy(redirect);
|
|
|
|
block_on(async move {
|
|
match oauth2::get_oauth2_url(ctx, &addr, &redirect)
|
|
.await
|
|
.context("dc_get_oauth2_url failed")
|
|
.log_err(ctx)
|
|
{
|
|
Ok(Some(res)) => res.strdup(),
|
|
Ok(None) | Err(_) => ptr::null_mut(),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn spawn_configure(ctx: Context) {
|
|
spawn(async move {
|
|
ctx.configure()
|
|
.await
|
|
.context("Configure failed")
|
|
.log_err(&ctx)
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_configure(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_configure()");
|
|
return;
|
|
}
|
|
|
|
let ctx = &*context;
|
|
spawn_configure(ctx.clone());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_is_configured()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ctx.is_configured()
|
|
.await
|
|
.context("failed to get configured state")
|
|
.log_err(ctx)
|
|
.unwrap_or_default() as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_start_io(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
return;
|
|
}
|
|
let ctx = &mut *context;
|
|
|
|
block_on(ctx.start_io())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_id(context: *mut dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
ctx.get_id() as libc::c_int
|
|
}
|
|
|
|
pub type dc_event_t = Event;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_unref(a: *mut dc_event_t) {
|
|
if a.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(a));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int {
|
|
if event.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_get_id()");
|
|
return 0;
|
|
}
|
|
|
|
let event = &*event;
|
|
match event.typ {
|
|
EventType::Info(_) => 100,
|
|
EventType::SmtpConnected(_) => 101,
|
|
EventType::ImapConnected(_) => 102,
|
|
EventType::SmtpMessageSent(_) => 103,
|
|
EventType::ImapMessageDeleted(_) => 104,
|
|
EventType::ImapMessageMoved(_) => 105,
|
|
EventType::ImapInboxIdle => 106,
|
|
EventType::NewBlobFile(_) => 150,
|
|
EventType::DeletedBlobFile(_) => 151,
|
|
EventType::Warning(_) => 300,
|
|
EventType::Error(_) => 400,
|
|
EventType::ErrorSelfNotInGroup(_) => 410,
|
|
EventType::MsgsChanged { .. } => 2000,
|
|
EventType::ReactionsChanged { .. } => 2001,
|
|
EventType::IncomingReaction { .. } => 2002,
|
|
EventType::IncomingMsg { .. } => 2005,
|
|
EventType::IncomingMsgBunch { .. } => 2006,
|
|
EventType::MsgsNoticed { .. } => 2008,
|
|
EventType::MsgDelivered { .. } => 2010,
|
|
EventType::MsgFailed { .. } => 2012,
|
|
EventType::MsgRead { .. } => 2015,
|
|
EventType::MsgDeleted { .. } => 2016,
|
|
EventType::ChatModified(_) => 2020,
|
|
EventType::ChatEphemeralTimerModified { .. } => 2021,
|
|
EventType::ContactsChanged(_) => 2030,
|
|
EventType::LocationChanged(_) => 2035,
|
|
EventType::ConfigureProgress { .. } => 2041,
|
|
EventType::ImexProgress(_) => 2051,
|
|
EventType::ImexFileWritten(_) => 2052,
|
|
EventType::SecurejoinInviterProgress { .. } => 2060,
|
|
EventType::SecurejoinJoinerProgress { .. } => 2061,
|
|
EventType::ConnectivityChanged => 2100,
|
|
EventType::SelfavatarChanged => 2110,
|
|
EventType::ConfigSynced { .. } => 2111,
|
|
EventType::WebxdcStatusUpdate { .. } => 2120,
|
|
EventType::WebxdcInstanceDeleted { .. } => 2121,
|
|
EventType::WebxdcRealtimeData { .. } => 2150,
|
|
EventType::WebxdcRealtimeAdvertisementReceived { .. } => 2151,
|
|
EventType::AccountsBackgroundFetchDone => 2200,
|
|
EventType::ChatlistChanged => 2300,
|
|
EventType::ChatlistItemChanged { .. } => 2301,
|
|
EventType::EventChannelOverflow { .. } => 2400,
|
|
#[allow(unreachable_patterns)]
|
|
#[cfg(test)]
|
|
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc::c_int {
|
|
if event.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_get_data1_int()");
|
|
return 0;
|
|
}
|
|
|
|
let event = &(*event).typ;
|
|
match event {
|
|
EventType::Info(_)
|
|
| EventType::SmtpConnected(_)
|
|
| EventType::ImapConnected(_)
|
|
| EventType::SmtpMessageSent(_)
|
|
| EventType::ImapMessageDeleted(_)
|
|
| EventType::ImapMessageMoved(_)
|
|
| EventType::ImapInboxIdle
|
|
| EventType::NewBlobFile(_)
|
|
| EventType::DeletedBlobFile(_)
|
|
| EventType::Warning(_)
|
|
| EventType::Error(_)
|
|
| EventType::ConnectivityChanged
|
|
| EventType::SelfavatarChanged
|
|
| EventType::ConfigSynced { .. }
|
|
| EventType::IncomingMsgBunch { .. }
|
|
| EventType::ErrorSelfNotInGroup(_)
|
|
| EventType::AccountsBackgroundFetchDone => 0,
|
|
EventType::ChatlistChanged => 0,
|
|
EventType::IncomingReaction { contact_id, .. } => contact_id.to_u32() as libc::c_int,
|
|
EventType::MsgsChanged { chat_id, .. }
|
|
| EventType::ReactionsChanged { chat_id, .. }
|
|
| EventType::IncomingMsg { chat_id, .. }
|
|
| EventType::MsgsNoticed(chat_id)
|
|
| EventType::MsgDelivered { chat_id, .. }
|
|
| EventType::MsgFailed { chat_id, .. }
|
|
| EventType::MsgRead { chat_id, .. }
|
|
| EventType::MsgDeleted { chat_id, .. }
|
|
| EventType::ChatModified(chat_id)
|
|
| EventType::ChatEphemeralTimerModified { chat_id, .. } => chat_id.to_u32() as libc::c_int,
|
|
EventType::ContactsChanged(id) | EventType::LocationChanged(id) => {
|
|
let id = id.unwrap_or_default();
|
|
id.to_u32() as libc::c_int
|
|
}
|
|
EventType::ConfigureProgress { progress, .. } | EventType::ImexProgress(progress) => {
|
|
*progress as libc::c_int
|
|
}
|
|
EventType::ImexFileWritten(_) => 0,
|
|
EventType::SecurejoinInviterProgress { contact_id, .. }
|
|
| EventType::SecurejoinJoinerProgress { contact_id, .. } => {
|
|
contact_id.to_u32() as libc::c_int
|
|
}
|
|
EventType::WebxdcRealtimeData { msg_id, .. }
|
|
| EventType::WebxdcStatusUpdate { msg_id, .. }
|
|
| EventType::WebxdcRealtimeAdvertisementReceived { msg_id }
|
|
| EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
|
|
EventType::ChatlistItemChanged { chat_id } => {
|
|
chat_id.unwrap_or_default().to_u32() as libc::c_int
|
|
}
|
|
EventType::EventChannelOverflow { n } => *n as libc::c_int,
|
|
#[allow(unreachable_patterns)]
|
|
#[cfg(test)]
|
|
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc::c_int {
|
|
if event.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_get_data2_int()");
|
|
return 0;
|
|
}
|
|
|
|
let event = &(*event).typ;
|
|
|
|
match event {
|
|
EventType::Info(_)
|
|
| EventType::SmtpConnected(_)
|
|
| EventType::ImapConnected(_)
|
|
| EventType::SmtpMessageSent(_)
|
|
| EventType::ImapMessageDeleted(_)
|
|
| EventType::ImapMessageMoved(_)
|
|
| EventType::ImapInboxIdle
|
|
| EventType::NewBlobFile(_)
|
|
| EventType::DeletedBlobFile(_)
|
|
| EventType::Warning(_)
|
|
| EventType::Error(_)
|
|
| EventType::ErrorSelfNotInGroup(_)
|
|
| EventType::ContactsChanged(_)
|
|
| EventType::LocationChanged(_)
|
|
| EventType::ConfigureProgress { .. }
|
|
| EventType::ImexProgress(_)
|
|
| EventType::ImexFileWritten(_)
|
|
| EventType::MsgsNoticed(_)
|
|
| EventType::ConnectivityChanged
|
|
| EventType::WebxdcInstanceDeleted { .. }
|
|
| EventType::IncomingMsgBunch { .. }
|
|
| EventType::SelfavatarChanged
|
|
| EventType::AccountsBackgroundFetchDone
|
|
| EventType::ChatlistChanged
|
|
| EventType::ChatlistItemChanged { .. }
|
|
| EventType::ConfigSynced { .. }
|
|
| EventType::ChatModified(_)
|
|
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
|
|
| EventType::EventChannelOverflow { .. } => 0,
|
|
EventType::MsgsChanged { msg_id, .. }
|
|
| EventType::ReactionsChanged { msg_id, .. }
|
|
| EventType::IncomingReaction { msg_id, .. }
|
|
| EventType::IncomingMsg { msg_id, .. }
|
|
| EventType::MsgDelivered { msg_id, .. }
|
|
| EventType::MsgFailed { msg_id, .. }
|
|
| EventType::MsgRead { msg_id, .. }
|
|
| EventType::MsgDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
|
|
EventType::SecurejoinInviterProgress { progress, .. }
|
|
| EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int,
|
|
EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int,
|
|
EventType::WebxdcStatusUpdate {
|
|
status_update_serial,
|
|
..
|
|
} => status_update_serial.to_u32() as libc::c_int,
|
|
EventType::WebxdcRealtimeData { data, .. } => data.len() as libc::c_int,
|
|
#[allow(unreachable_patterns)]
|
|
#[cfg(test)]
|
|
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut libc::c_char {
|
|
if event.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_get_data2_str()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let event = &(*event).typ;
|
|
|
|
match event {
|
|
EventType::Info(msg)
|
|
| EventType::SmtpConnected(msg)
|
|
| EventType::ImapConnected(msg)
|
|
| EventType::SmtpMessageSent(msg)
|
|
| EventType::ImapMessageDeleted(msg)
|
|
| EventType::ImapMessageMoved(msg)
|
|
| EventType::NewBlobFile(msg)
|
|
| EventType::DeletedBlobFile(msg)
|
|
| EventType::Warning(msg)
|
|
| EventType::Error(msg)
|
|
| EventType::ErrorSelfNotInGroup(msg) => {
|
|
let data2 = msg.to_c_string().unwrap_or_default();
|
|
data2.into_raw()
|
|
}
|
|
EventType::MsgsChanged { .. }
|
|
| EventType::ReactionsChanged { .. }
|
|
| EventType::IncomingMsg { .. }
|
|
| EventType::ImapInboxIdle
|
|
| EventType::MsgsNoticed(_)
|
|
| EventType::MsgDelivered { .. }
|
|
| EventType::MsgFailed { .. }
|
|
| EventType::MsgRead { .. }
|
|
| EventType::MsgDeleted { .. }
|
|
| EventType::ChatModified(_)
|
|
| EventType::ContactsChanged(_)
|
|
| EventType::LocationChanged(_)
|
|
| EventType::ImexProgress(_)
|
|
| EventType::SecurejoinInviterProgress { .. }
|
|
| EventType::SecurejoinJoinerProgress { .. }
|
|
| EventType::ConnectivityChanged
|
|
| EventType::SelfavatarChanged
|
|
| EventType::WebxdcStatusUpdate { .. }
|
|
| EventType::WebxdcInstanceDeleted { .. }
|
|
| EventType::AccountsBackgroundFetchDone
|
|
| EventType::ChatEphemeralTimerModified { .. }
|
|
| EventType::IncomingMsgBunch { .. }
|
|
| EventType::ChatlistItemChanged { .. }
|
|
| EventType::ChatlistChanged
|
|
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
|
|
| EventType::EventChannelOverflow { .. } => ptr::null_mut(),
|
|
EventType::ConfigureProgress { comment, .. } => {
|
|
if let Some(comment) = comment {
|
|
comment.to_c_string().unwrap_or_default().into_raw()
|
|
} else {
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
EventType::ImexFileWritten(file) => {
|
|
let data2 = file.to_c_string().unwrap_or_default();
|
|
data2.into_raw()
|
|
}
|
|
EventType::ConfigSynced { key } => {
|
|
let data2 = key.to_string().to_c_string().unwrap_or_default();
|
|
data2.into_raw()
|
|
}
|
|
EventType::WebxdcRealtimeData { data, .. } => {
|
|
let ptr = libc::malloc(data.len());
|
|
libc::memcpy(ptr, data.as_ptr() as *mut libc::c_void, data.len());
|
|
ptr as *mut libc::c_char
|
|
}
|
|
EventType::IncomingReaction { reaction, .. } => reaction
|
|
.as_str()
|
|
.to_c_string()
|
|
.unwrap_or_default()
|
|
.into_raw(),
|
|
#[allow(unreachable_patterns)]
|
|
#[cfg(test)]
|
|
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_get_account_id(event: *mut dc_event_t) -> u32 {
|
|
if event.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_get_account_id()");
|
|
return 0;
|
|
}
|
|
|
|
(*event).id
|
|
}
|
|
|
|
pub type dc_event_emitter_t = EventEmitter;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_event_emitter(
|
|
context: *mut dc_context_t,
|
|
) -> *mut dc_event_emitter_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_event_emitter()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
Box::into_raw(Box::new(ctx.get_event_emitter()))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_event_emitter_unref(emitter: *mut dc_event_emitter_t) {
|
|
if emitter.is_null() {
|
|
eprintln!("ignoring careless call to dc_event_emitter_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(emitter));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_next_event(events: *mut dc_event_emitter_t) -> *mut dc_event_t {
|
|
if events.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_next_event()");
|
|
return ptr::null_mut();
|
|
}
|
|
let events = &*events;
|
|
|
|
block_on(async move {
|
|
events
|
|
.recv()
|
|
.await
|
|
.map(|ev| Box::into_raw(Box::new(ev)))
|
|
.unwrap_or_else(ptr::null_mut)
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_stop_io(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_stop_io()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ctx.stop_io().await;
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_maybe_network()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move { ctx.maybe_network().await })
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_preconfigure_keypair(
|
|
context: *mut dc_context_t,
|
|
secret_data: *const libc::c_char,
|
|
) -> i32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_preconfigure_keypair()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let secret_data = to_string_lossy(secret_data);
|
|
block_on(preconfigure_keypair(ctx, &secret_data))
|
|
.context("Failed to save keypair")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chatlist(
|
|
context: *mut dc_context_t,
|
|
flags: libc::c_int,
|
|
query_str: *const libc::c_char,
|
|
query_id: u32,
|
|
) -> *mut dc_chatlist_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chatlist()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let qs = to_opt_string_lossy(query_str);
|
|
|
|
let qi = if query_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ContactId::new(query_id))
|
|
};
|
|
|
|
block_on(async move {
|
|
match chatlist::Chatlist::try_load(ctx, flags as usize, qs.as_deref(), qi)
|
|
.await
|
|
.context("Failed to get chatlist")
|
|
.log_err(ctx)
|
|
{
|
|
Ok(list) => {
|
|
let ffi_list = ChatlistWrapper { context, list };
|
|
Box::into_raw(Box::new(ffi_list))
|
|
}
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_create_chat_by_contact_id(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_create_chat_by_contact_id()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::create_for_contact(ctx, ContactId::new(contact_id))
|
|
.await
|
|
.context("Failed to create chat from contact_id")
|
|
.log_err(ctx)
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or(0)
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_id_by_contact_id()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::lookup_by_contact(ctx, ContactId::new(contact_id))
|
|
.await
|
|
.context("Failed to get chat for contact_id")
|
|
.log_err(ctx)
|
|
.unwrap_or_default() // unwraps the Result
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or(0) // unwraps the Option
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_prepare_msg(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg: *mut dc_msg_t,
|
|
) -> u32 {
|
|
if context.is_null() || chat_id == 0 || msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_prepare_msg()");
|
|
return 0;
|
|
}
|
|
let ctx = &mut *context;
|
|
let ffi_msg: &mut MessageWrapper = &mut *msg;
|
|
|
|
block_on(async move {
|
|
chat::prepare_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed to prepare message")
|
|
})
|
|
.to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_msg(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg: *mut dc_msg_t,
|
|
) -> u32 {
|
|
if context.is_null() || msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_send_msg()");
|
|
return 0;
|
|
}
|
|
let ctx = &mut *context;
|
|
let ffi_msg = &mut *msg;
|
|
|
|
block_on(async move {
|
|
chat::send_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed to send message")
|
|
})
|
|
.to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_msg_sync(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg: *mut dc_msg_t,
|
|
) -> u32 {
|
|
if context.is_null() || msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_send_msg_sync()");
|
|
return 0;
|
|
}
|
|
let ctx = &mut *context;
|
|
let ffi_msg = &mut *msg;
|
|
|
|
block_on(async move {
|
|
chat::send_msg_sync(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed to send message")
|
|
})
|
|
.to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_text_msg(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
text_to_send: *const libc::c_char,
|
|
) -> u32 {
|
|
if context.is_null() || text_to_send.is_null() {
|
|
eprintln!("ignoring careless call to dc_send_text_msg()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let text_to_send = to_string_lossy(text_to_send);
|
|
|
|
block_on(async move {
|
|
chat::send_text_msg(ctx, ChatId::new(chat_id), text_to_send)
|
|
.await
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.unwrap_or_log_default(ctx, "Failed to send text message")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_videochat_invitation(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_send_videochat_invitation()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
chat::send_videochat_invitation(ctx, ChatId::new(chat_id))
|
|
.await
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.unwrap_or_log_default(ctx, "Failed to send video chat invitation")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_webxdc_status_update(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
json: *const libc::c_char,
|
|
descr: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_send_webxdc_status_update()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(ctx.send_webxdc_status_update(
|
|
MsgId::new(msg_id),
|
|
&to_string_lossy(json),
|
|
&to_string_lossy(descr),
|
|
))
|
|
.context("Failed to send webxdc update")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_webxdc_status_updates(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
last_known_serial: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_webxdc_status_updates()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(ctx.get_webxdc_status_updates(
|
|
MsgId::new(msg_id),
|
|
StatusUpdateSerial::new(last_known_serial),
|
|
))
|
|
.unwrap_or_else(|_| "".to_string())
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_webxdc_integration(
|
|
context: *mut dc_context_t,
|
|
file: *const libc::c_char,
|
|
) {
|
|
if context.is_null() || file.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_webxdc_integration()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(ctx.set_webxdc_integration(&to_string_lossy(file)))
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_init_webxdc_integration(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_init_webxdc_integration()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
|
|
block_on(ctx.init_webxdc_integration(chat_id))
|
|
.log_err(ctx)
|
|
.map(|msg_id| msg_id.map(|id| id.to_u32()).unwrap_or_default())
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_draft(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg: *mut dc_msg_t,
|
|
) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_draft()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
let msg = if msg.is_null() {
|
|
None
|
|
} else {
|
|
let ffi_msg: &mut MessageWrapper = &mut *msg;
|
|
Some(&mut ffi_msg.message)
|
|
};
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.set_draft(ctx, msg)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to set draft");
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_add_device_msg(
|
|
context: *mut dc_context_t,
|
|
label: *const libc::c_char,
|
|
msg: *mut dc_msg_t,
|
|
) -> u32 {
|
|
if context.is_null() || (label.is_null() && msg.is_null()) {
|
|
eprintln!("ignoring careless call to dc_add_device_msg()");
|
|
return 0;
|
|
}
|
|
let ctx = &mut *context;
|
|
let msg = if msg.is_null() {
|
|
None
|
|
} else {
|
|
let ffi_msg: &mut MessageWrapper = &mut *msg;
|
|
Some(&mut ffi_msg.message)
|
|
};
|
|
|
|
block_on(async move {
|
|
chat::add_device_msg(ctx, to_opt_string_lossy(label).as_deref(), msg)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed to add device message")
|
|
})
|
|
.to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_was_device_msg_ever_added(
|
|
context: *mut dc_context_t,
|
|
label: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || label.is_null() {
|
|
eprintln!("ignoring careless call to dc_was_device_msg_ever_added()");
|
|
return 0;
|
|
}
|
|
let ctx = &mut *context;
|
|
|
|
block_on(async move {
|
|
chat::was_device_msg_ever_added(ctx, &to_string_lossy(label))
|
|
.await
|
|
.unwrap_or(false) as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) -> *mut dc_msg_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_draft()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "no draft"
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match ChatId::new(chat_id).get_draft(ctx).await {
|
|
Ok(Some(draft)) => {
|
|
let ffi_msg = MessageWrapper {
|
|
context,
|
|
message: draft,
|
|
};
|
|
Box::into_raw(Box::new(ffi_msg))
|
|
}
|
|
Ok(None) => ptr::null_mut(),
|
|
Err(err) => {
|
|
error!(ctx, "Failed to get draft for chat #{chat_id}: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_msgs(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
flags: u32,
|
|
_marker1before: u32,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_msgs()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let info_only = (flags & DC_GCM_INFO_ONLY) != 0;
|
|
let add_daymarker = (flags & DC_GCM_ADDDAYMARKER) != 0;
|
|
block_on(async move {
|
|
Box::into_raw(Box::new(
|
|
chat::get_chat_msgs_ex(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
MessageListOptions {
|
|
info_only,
|
|
add_daymarker,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get chat msgs")
|
|
.into(),
|
|
))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_msg_cnt()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.get_msg_cnt(ctx)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get msg count") as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_fresh_msg_cnt()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.get_fresh_msg_cnt(ctx)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get fresh msg cnt") as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_similar_chatlist(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut dc_chatlist_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_similar_chatlist()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let chat_id = ChatId::new(chat_id);
|
|
match block_on(chat_id.get_similar_chatlist(ctx))
|
|
.context("failed to get similar chatlist")
|
|
.log_err(ctx)
|
|
{
|
|
Ok(list) => {
|
|
let ffi_list = ChatlistWrapper { context, list };
|
|
Box::into_raw(Box::new(ffi_list))
|
|
}
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_estimate_deletion_cnt(
|
|
context: *mut dc_context_t,
|
|
from_server: libc::c_int,
|
|
seconds: i64,
|
|
) -> libc::c_int {
|
|
if context.is_null() || seconds < 0 {
|
|
eprintln!("ignoring careless call to dc_estimate_deletion_cnt()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(async move {
|
|
message::estimate_deletion_cnt(ctx, from_server != 0, seconds)
|
|
.await
|
|
.unwrap_or(0) as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_fresh_msgs(
|
|
context: *mut dc_context_t,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_fresh_msgs()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
let arr = dc_array_t::from(
|
|
ctx.get_fresh_msgs()
|
|
.await
|
|
.context("Failed to get fresh messages")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.iter()
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
);
|
|
Box::into_raw(Box::new(arr))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_next_msgs(context: *mut dc_context_t) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_next_msgs()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let msg_ids = block_on(ctx.get_next_msgs())
|
|
.context("failed to get next messages")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
let arr = dc_array_t::from(
|
|
msg_ids
|
|
.iter()
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
);
|
|
Box::into_raw(Box::new(arr))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_wait_next_msgs(
|
|
context: *mut dc_context_t,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_wait_next_msgs()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let msg_ids = block_on(ctx.wait_next_msgs())
|
|
.context("failed to wait for next messages")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
let arr = dc_array_t::from(
|
|
msg_ids
|
|
.iter()
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
);
|
|
Box::into_raw(Box::new(arr))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id: u32) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_marknoticed_chat()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
chat::marknoticed_chat(ctx, ChatId::new(chat_id))
|
|
.await
|
|
.context("Failed marknoticed chat")
|
|
.log_err(ctx)
|
|
.unwrap_or(())
|
|
})
|
|
}
|
|
|
|
fn from_prim<S, T>(s: S) -> Option<T>
|
|
where
|
|
T: FromPrimitive,
|
|
S: Into<i64>,
|
|
{
|
|
FromPrimitive::from_i64(s.into())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_media(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg_type: libc::c_int,
|
|
or_msg_type2: libc::c_int,
|
|
or_msg_type3: libc::c_int,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_media()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {msg_type}"));
|
|
let or_msg_type2 =
|
|
from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {or_msg_type2}"));
|
|
let or_msg_type3 =
|
|
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {or_msg_type3}"));
|
|
|
|
block_on(async move {
|
|
Box::into_raw(Box::new(
|
|
chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed get_chat_media")
|
|
.into(),
|
|
))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_chat_visibility(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
archive: libc::c_int,
|
|
) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_chat_visibility()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
let visibility = match archive {
|
|
0 => ChatVisibility::Normal,
|
|
1 => ChatVisibility::Archived,
|
|
2 => ChatVisibility::Pinned,
|
|
_ => {
|
|
warn!(
|
|
ctx,
|
|
"ignoring careless call to dc_set_chat_visibility(): unknown archived state",
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.set_visibility(ctx, visibility)
|
|
.await
|
|
.context("Failed setting chat visibility")
|
|
.log_err(ctx)
|
|
.unwrap_or(())
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_delete_chat()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.delete(ctx)
|
|
.await
|
|
.context("Failed chat delete")
|
|
.log_err(ctx)
|
|
.ok();
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_block_chat(context: *mut dc_context_t, chat_id: u32) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_block_chat()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.block(ctx)
|
|
.await
|
|
.context("Failed chat block")
|
|
.log_err(ctx)
|
|
.ok();
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accept_chat(context: *mut dc_context_t, chat_id: u32) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_accept_chat()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.accept(ctx)
|
|
.await
|
|
.context("Failed chat accept")
|
|
.log_err(ctx)
|
|
.ok();
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_contacts(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_contacts()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
let arr = dc_array_t::from(
|
|
chat::get_chat_contacts(ctx, ChatId::new(chat_id))
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed get_chat_contacts")
|
|
.iter()
|
|
.map(|id| id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
);
|
|
Box::into_raw(Box::new(arr))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_search_msgs(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
query: *const libc::c_char,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() || query.is_null() {
|
|
eprintln!("ignoring careless call to dc_search_msgs()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
|
|
block_on(async move {
|
|
let arr = dc_array_t::from(
|
|
ctx.search_msgs(chat_id, &to_string_lossy(query))
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed search_msgs")
|
|
.iter()
|
|
.map(|msg_id| msg_id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
);
|
|
Box::into_raw(Box::new(arr))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) -> *mut dc_chat_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)).await {
|
|
Ok(chat) => {
|
|
let ffi_chat = ChatWrapper { context, chat };
|
|
Box::into_raw(Box::new(ffi_chat))
|
|
}
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_create_group_chat(
|
|
context: *mut dc_context_t,
|
|
protect: libc::c_int,
|
|
name: *const libc::c_char,
|
|
) -> u32 {
|
|
if context.is_null() || name.is_null() {
|
|
eprintln!("ignoring careless call to dc_create_group_chat()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let protect = if let Some(s) = ProtectionStatus::from_i32(protect) {
|
|
s
|
|
} else {
|
|
warn!(ctx, "bad protect-value for dc_create_group_chat()");
|
|
return 0;
|
|
};
|
|
|
|
block_on(async move {
|
|
chat::create_group_chat(ctx, protect, &to_string_lossy(name))
|
|
.await
|
|
.context("Failed to create group chat")
|
|
.log_err(ctx)
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or(0)
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_create_broadcast_list(context: *mut dc_context_t) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_create_broadcast_list()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(chat::create_broadcast_list(ctx))
|
|
.context("Failed to create broadcast list")
|
|
.log_err(ctx)
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_is_contact_in_chat(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
contact_id: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_is_contact_in_chat()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(chat::is_contact_in_chat(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
ContactId::new(contact_id),
|
|
))
|
|
.context("is_contact_in_chat failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_add_contact_to_chat(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
contact_id: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_add_contact_to_chat()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(chat::add_contact_to_chat(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
ContactId::new(contact_id),
|
|
))
|
|
.context("Failed to add contact")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_remove_contact_from_chat(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
contact_id: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_remove_contact_from_chat()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(chat::remove_contact_from_chat(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
ContactId::new(contact_id),
|
|
))
|
|
.context("Failed to remove contact")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_chat_name(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
name: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() || name.is_null()
|
|
{
|
|
eprintln!("ignoring careless call to dc_set_chat_name()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
chat::set_chat_name(ctx, ChatId::new(chat_id), &to_string_lossy(name))
|
|
.await
|
|
.map(|_| 1)
|
|
.unwrap_or_log_default(ctx, "Failed to set chat name")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_chat_profile_image(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
image: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() {
|
|
eprintln!("ignoring careless call to dc_set_chat_profile_image()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
chat::set_chat_profile_image(ctx, ChatId::new(chat_id), &to_string_lossy(image))
|
|
.await
|
|
.map(|_| 1)
|
|
.unwrap_or_log_default(ctx, "Failed to set profile image")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_chat_mute_duration(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
duration: i64,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_chat_mute_duration()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let muteDuration = match duration {
|
|
0 => MuteDuration::NotMuted,
|
|
-1 => MuteDuration::Forever,
|
|
n if n > 0 => SystemTime::now()
|
|
.checked_add(Duration::from_secs(duration as u64))
|
|
.map_or(MuteDuration::Forever, MuteDuration::Until),
|
|
_ => {
|
|
warn!(
|
|
ctx,
|
|
"dc_chat_set_mute_duration(): Can not use negative duration other than -1",
|
|
);
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
block_on(async move {
|
|
chat::set_muted(ctx, ChatId::new(chat_id), muteDuration)
|
|
.await
|
|
.map(|_| 1)
|
|
.unwrap_or_log_default(ctx, "Failed to set mute duration")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_encrinfo(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_encrinfo()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.get_encryption_info(ctx)
|
|
.await
|
|
.map(|s| s.strdup())
|
|
.unwrap_or_else(|e| {
|
|
error!(ctx, "{e:#}");
|
|
ptr::null_mut()
|
|
})
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_chat_ephemeral_timer(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> u32 {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_chat_ephemeral_timer()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
// Timer value 0 is returned in the rare case of a database error,
|
|
// but it is not dangerous since it is only meant to be used as a
|
|
// default when changing the value. Such errors should not be
|
|
// ignored when ephemeral timer value is used to construct
|
|
// message headers.
|
|
block_on(async move { ChatId::new(chat_id).get_ephemeral_timer(ctx).await })
|
|
.context("Failed to get ephemeral timer")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_chat_ephemeral_timer(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
timer: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_chat_ephemeral_timer()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
ChatId::new(chat_id)
|
|
.set_ephemeral_timer(ctx, EphemeralTimer::from_u32(timer))
|
|
.await
|
|
.context("Failed to set ephemeral timer")
|
|
.log_err(ctx)
|
|
.is_ok() as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_msg_info(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_msg_info()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
let msg_id = MsgId::new(msg_id);
|
|
block_on(msg_id.get_info(ctx))
|
|
.unwrap_or_log_default(ctx, "failed to get msg id")
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_msg_html(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_msg_html()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(MsgId::new(msg_id).get_html(ctx))
|
|
.unwrap_or_log_default(ctx, "Failed get_msg_html")
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_mime_headers(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_mime_headers()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "no mime headers"
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
let mime = message::get_mime_headers(ctx, MsgId::new(msg_id))
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get mime headers");
|
|
if mime.is_empty() {
|
|
return ptr::null_mut();
|
|
}
|
|
mime.strdup()
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_delete_msgs(
|
|
context: *mut dc_context_t,
|
|
msg_ids: *const u32,
|
|
msg_cnt: libc::c_int,
|
|
) {
|
|
if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
|
|
eprintln!("ignoring careless call to dc_delete_msgs()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
|
|
|
|
block_on(message::delete_msgs(ctx, &msg_ids))
|
|
.context("failed dc_delete_msgs() call")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_forward_msgs(
|
|
context: *mut dc_context_t,
|
|
msg_ids: *const u32,
|
|
msg_cnt: libc::c_int,
|
|
chat_id: u32,
|
|
) {
|
|
if context.is_null()
|
|
|| msg_ids.is_null()
|
|
|| msg_cnt <= 0
|
|
|| chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32()
|
|
{
|
|
eprintln!("ignoring careless call to dc_forward_msgs()");
|
|
return;
|
|
}
|
|
let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
chat::forward_msgs(ctx, &msg_ids[..], ChatId::new(chat_id))
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed to forward message")
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_resend_msgs(
|
|
context: *mut dc_context_t,
|
|
msg_ids: *const u32,
|
|
msg_cnt: libc::c_int,
|
|
) -> libc::c_int {
|
|
if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
|
|
eprintln!("ignoring careless call to dc_resend_msgs()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
|
|
|
|
if let Err(err) = block_on(chat::resend_msgs(ctx, &msg_ids)) {
|
|
error!(ctx, "Resending failed: {err:#}");
|
|
0
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_markseen_msgs(
|
|
context: *mut dc_context_t,
|
|
msg_ids: *const u32,
|
|
msg_cnt: libc::c_int,
|
|
) {
|
|
if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
|
|
eprintln!("ignoring careless call to dc_markseen_msgs()");
|
|
return;
|
|
}
|
|
let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
|
|
let ctx = &*context;
|
|
|
|
block_on(message::markseen_msgs(ctx, msg_ids))
|
|
.context("failed dc_markseen_msgs() call")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) -> *mut dc_msg_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_msg()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
let message = match message::Message::load_from_db(ctx, MsgId::new(msg_id)).await {
|
|
Ok(msg) => msg,
|
|
Err(e) => {
|
|
if msg_id <= constants::DC_MSG_ID_LAST_SPECIAL {
|
|
// C-core API returns empty messages, do the same
|
|
warn!(
|
|
ctx,
|
|
"dc_get_msg called with special msg_id={msg_id}, returning empty msg"
|
|
);
|
|
message::Message::default()
|
|
} else {
|
|
warn!(ctx, "dc_get_msg could not retrieve msg_id {msg_id}: {e:#}");
|
|
return ptr::null_mut();
|
|
}
|
|
}
|
|
};
|
|
let ffi_msg = MessageWrapper { context, message };
|
|
Box::into_raw(Box::new(ffi_msg))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_download_full_msg(context: *mut dc_context_t, msg_id: u32) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_download_full_msg()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(MsgId::new(msg_id).download_full(ctx))
|
|
.context("Failed to download message fully.")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *const libc::c_char) -> libc::c_int {
|
|
if addr.is_null() {
|
|
eprintln!("ignoring careless call to dc_may_be_valid_addr()");
|
|
return 0;
|
|
}
|
|
|
|
contact::may_be_valid_addr(&to_string_lossy(addr)) as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lookup_contact_id_by_addr(
|
|
context: *mut dc_context_t,
|
|
addr: *const libc::c_char,
|
|
) -> u32 {
|
|
if context.is_null() || addr.is_null() {
|
|
eprintln!("ignoring careless call to dc_lookup_contact_id_by_addr()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
Contact::lookup_id_by_addr(ctx, &to_string_lossy(addr), Origin::IncomingReplyTo)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to lookup id")
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or_default()
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_create_contact(
|
|
context: *mut dc_context_t,
|
|
name: *const libc::c_char,
|
|
addr: *const libc::c_char,
|
|
) -> u32 {
|
|
if context.is_null() || addr.is_null() {
|
|
eprintln!("ignoring careless call to dc_create_contact()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let name = to_string_lossy(name);
|
|
|
|
block_on(Contact::create(ctx, &name, &to_string_lossy(addr)))
|
|
.context("Cannot create contact")
|
|
.log_err(ctx)
|
|
.map(|id| id.to_u32())
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_add_address_book(
|
|
context: *mut dc_context_t,
|
|
addr_book: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || addr_book.is_null() {
|
|
eprintln!("ignoring careless call to dc_add_address_book()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match Contact::add_address_book(ctx, &to_string_lossy(addr_book)).await {
|
|
Ok(cnt) => cnt as libc::c_int,
|
|
Err(_) => 0,
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_contacts(
|
|
context: *mut dc_context_t,
|
|
flags: u32,
|
|
query: *const libc::c_char,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_contacts()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let query = to_opt_string_lossy(query);
|
|
|
|
block_on(async move {
|
|
match Contact::get_all(ctx, flags, query.as_deref()).await {
|
|
Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from(
|
|
contacts.iter().map(|id| id.to_u32()).collect::<Vec<u32>>(),
|
|
))),
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_blocked_cnt(context: *mut dc_context_t) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_blocked_cnt()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
Contact::get_all_blocked(ctx)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get blocked count")
|
|
.len() as libc::c_int
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_blocked_contacts(
|
|
context: *mut dc_context_t,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_blocked_contacts()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
Box::into_raw(Box::new(dc_array_t::from(
|
|
Contact::get_all_blocked(ctx)
|
|
.await
|
|
.context("Can't get blocked contacts")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.iter()
|
|
.map(|id| id.to_u32())
|
|
.collect::<Vec<u32>>(),
|
|
)))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_block_contact(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
block: libc::c_int,
|
|
) {
|
|
let contact_id = ContactId::new(contact_id);
|
|
if context.is_null() || contact_id.is_special() {
|
|
eprintln!("ignoring careless call to dc_block_contact()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(async move {
|
|
if block == 0 {
|
|
Contact::unblock(ctx, contact_id)
|
|
.await
|
|
.context("Can't unblock contact")
|
|
.log_err(ctx)
|
|
.ok();
|
|
} else {
|
|
Contact::block(ctx, contact_id)
|
|
.await
|
|
.context("Can't block contact")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_contact_encrinfo(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_contact_encrinfo()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
Contact::get_encrinfo(ctx, ContactId::new(contact_id))
|
|
.await
|
|
.map(|s| s.strdup())
|
|
.unwrap_or_else(|e| {
|
|
error!(ctx, "{e:#}");
|
|
ptr::null_mut()
|
|
})
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_delete_contact(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
) -> libc::c_int {
|
|
let contact_id = ContactId::new(contact_id);
|
|
if context.is_null() || contact_id.is_special() {
|
|
eprintln!("ignoring careless call to dc_delete_contact()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match Contact::delete(ctx, contact_id).await {
|
|
Ok(_) => 1,
|
|
Err(err) => {
|
|
error!(ctx, "cannot delete contact: {err:#}");
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_contact(
|
|
context: *mut dc_context_t,
|
|
contact_id: u32,
|
|
) -> *mut dc_contact_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_contact()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
Contact::get_by_id(ctx, ContactId::new(contact_id))
|
|
.await
|
|
.map(|contact| Box::into_raw(Box::new(ContactWrapper { context, contact })))
|
|
.unwrap_or_else(|_| ptr::null_mut())
|
|
})
|
|
}
|
|
|
|
fn spawn_imex(ctx: Context, what: imex::ImexMode, param1: String, passphrase: Option<String>) {
|
|
spawn(async move {
|
|
imex::imex(&ctx, what, param1.as_ref(), passphrase)
|
|
.await
|
|
.context("IMEX failed")
|
|
.log_err(&ctx)
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_imex(
|
|
context: *mut dc_context_t,
|
|
what_raw: libc::c_int,
|
|
param1: *const libc::c_char,
|
|
param2: *const libc::c_char,
|
|
) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_imex()");
|
|
return;
|
|
}
|
|
let what = match imex::ImexMode::from_i32(what_raw) {
|
|
Some(what) => what,
|
|
None => {
|
|
eprintln!("ignoring invalid argument {what_raw} to dc_imex");
|
|
return;
|
|
}
|
|
};
|
|
let passphrase = to_opt_string_lossy(param2);
|
|
|
|
let ctx = &*context;
|
|
|
|
if let Some(param1) = to_opt_string_lossy(param1) {
|
|
spawn_imex(ctx.clone(), what, param1, passphrase);
|
|
} else {
|
|
eprintln!("dc_imex called without a valid directory");
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_imex_has_backup(
|
|
context: *mut dc_context_t,
|
|
dir: *const libc::c_char,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() || dir.is_null() {
|
|
eprintln!("ignoring careless call to dc_imex_has_backup()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "has no backup"
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match imex::has_backup(ctx, to_string_lossy(dir).as_ref()).await {
|
|
Ok(res) => res.strdup(),
|
|
Err(err) => {
|
|
// do not bubble up error to the user,
|
|
// the ui will expect that the file does not exist or cannot be accessed
|
|
warn!(ctx, "dc_imex_has_backup: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_initiate_key_transfer(context: *mut dc_context_t) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_initiate_key_transfer()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "error"
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match imex::initiate_key_transfer(ctx).await {
|
|
Ok(res) => res.strdup(),
|
|
Err(err) => {
|
|
error!(ctx, "dc_initiate_key_transfer(): {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_continue_key_transfer(
|
|
context: *mut dc_context_t,
|
|
msg_id: u32,
|
|
setup_code: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() || msg_id <= constants::DC_MSG_ID_LAST_SPECIAL || setup_code.is_null() {
|
|
eprintln!("ignoring careless call to dc_continue_key_transfer()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
match imex::continue_key_transfer(ctx, MsgId::new(msg_id), &to_string_lossy(setup_code))
|
|
.await
|
|
{
|
|
Ok(()) => 1,
|
|
Err(err) => {
|
|
warn!(ctx, "dc_continue_key_transfer: {err:#}");
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_stop_ongoing_process()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
block_on(ctx.stop_ongoing());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_check_qr(
|
|
context: *mut dc_context_t,
|
|
qr: *const libc::c_char,
|
|
) -> *mut dc_lot_t {
|
|
if context.is_null() || qr.is_null() {
|
|
eprintln!("ignoring careless call to dc_check_qr()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
let lot = match block_on(qr::check_qr(ctx, &to_string_lossy(qr))) {
|
|
Ok(qr) => qr.into(),
|
|
Err(err) => err.into(),
|
|
};
|
|
Box::into_raw(Box::new(lot))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_securejoin_qr(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_securejoin_qr()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
|
|
block_on(securejoin::get_securejoin_qr(ctx, chat_id))
|
|
.unwrap_or_else(|_| "".to_string())
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_securejoin_qr_svg(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to generate_verification_qr()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
|
|
block_on(get_securejoin_qr_svg(ctx, chat_id))
|
|
.unwrap_or_else(|_| "".to_string())
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_join_securejoin(
|
|
context: *mut dc_context_t,
|
|
qr: *const libc::c_char,
|
|
) -> u32 {
|
|
if context.is_null() || qr.is_null() {
|
|
eprintln!("ignoring careless call to dc_join_securejoin()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
securejoin::join_securejoin(ctx, &to_string_lossy(qr))
|
|
.await
|
|
.map(|chatid| chatid.to_u32())
|
|
.context("failed dc_join_securejoin() call")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_send_locations_to_chat(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
seconds: libc::c_int,
|
|
) {
|
|
if context.is_null() || chat_id <= constants::DC_CHAT_ID_LAST_SPECIAL.to_u32() || seconds < 0 {
|
|
eprintln!("ignoring careless call to dc_send_locations_to_chat()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(location::send_locations_to_chat(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
seconds as i64,
|
|
))
|
|
.context("Failed dc_send_locations_to_chat()")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_is_sending_locations_to_chat()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
|
|
block_on(location::is_sending_locations_to_chat(ctx, chat_id))
|
|
.unwrap_or_log_default(ctx, "Failed dc_is_sending_locations_to_chat()") as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_set_location(
|
|
context: *mut dc_context_t,
|
|
latitude: libc::c_double,
|
|
longitude: libc::c_double,
|
|
accuracy: libc::c_double,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_set_location()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
location::set(ctx, latitude, longitude, accuracy)
|
|
.await
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
}) as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_locations(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
contact_id: u32,
|
|
timestamp_begin: i64,
|
|
timestamp_end: i64,
|
|
) -> *mut dc_array::dc_array_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_locations()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let chat_id = if chat_id == 0 {
|
|
None
|
|
} else {
|
|
Some(ChatId::new(chat_id))
|
|
};
|
|
let contact_id = if contact_id == 0 {
|
|
None
|
|
} else {
|
|
Some(contact_id)
|
|
};
|
|
|
|
block_on(async move {
|
|
let res = location::get_range(ctx, chat_id, contact_id, timestamp_begin, timestamp_end)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "Failed get_locations");
|
|
Box::into_raw(Box::new(dc_array_t::from(res)))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_delete_all_locations()");
|
|
return;
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
location::delete_all(ctx)
|
|
.await
|
|
.context("Failed to delete locations")
|
|
.log_err(ctx)
|
|
.ok()
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_create_qr_svg(payload: *const libc::c_char) -> *mut libc::c_char {
|
|
if payload.is_null() {
|
|
eprintln!("ignoring careless call to dc_create_qr_svg()");
|
|
return "".strdup();
|
|
}
|
|
|
|
create_qr_svg(&to_string_lossy(payload))
|
|
.unwrap_or_else(|_| "".to_string())
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_get_last_error(context: *mut dc_context_t) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_last_error()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
ctx.get_last_error().strdup()
|
|
}
|
|
|
|
// dc_array_t
|
|
|
|
pub type dc_array_t = dc_array::dc_array_t;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) {
|
|
if a.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(a));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_cnt()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).len()
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_id(array: *const dc_array_t, index: libc::size_t) -> u32 {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_id()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).get_id(index)
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_latitude(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_double {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_latitude()");
|
|
return 0.0;
|
|
}
|
|
|
|
(*array).get_location(index).latitude
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_longitude(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_double {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_longitude()");
|
|
return 0.0;
|
|
}
|
|
|
|
(*array).get_location(index).longitude
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_accuracy(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_double {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_accuracy()");
|
|
return 0.0;
|
|
}
|
|
|
|
(*array).get_location(index).accuracy
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_timestamp(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> i64 {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_timestamp()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).get_timestamp(index).unwrap_or_default()
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_chat_id(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_uint {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_chat_id()");
|
|
return 0;
|
|
}
|
|
(*array).get_location(index).chat_id.to_u32()
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_contact_id(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_uint {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_contact_id()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).get_location(index).contact_id.to_u32()
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_msg_id(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_uint {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_msg_id()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).get_location(index).msg_id
|
|
}
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_get_marker(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> *mut libc::c_char {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_get_marker()");
|
|
return std::ptr::null_mut(); // NULL explicitly defined as "no markers"
|
|
}
|
|
|
|
if let Some(s) = (*array).get_marker(index) {
|
|
s.strdup()
|
|
} else {
|
|
std::ptr::null_mut()
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_array_search_id(
|
|
array: *const dc_array_t,
|
|
needle: libc::c_uint,
|
|
ret_index: *mut libc::size_t,
|
|
) -> libc::c_int {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_search_id()");
|
|
return 0;
|
|
}
|
|
|
|
if let Some(i) = (*array).search_id(needle) {
|
|
if !ret_index.is_null() {
|
|
*ret_index = i
|
|
}
|
|
1
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
// Return the independent-state of the location at the given index.
|
|
// Independent locations do not belong to the track of the user.
|
|
// Returns 1 if location belongs to the track of the user,
|
|
// 0 if location was reported independently.
|
|
#[no_mangle]
|
|
pub unsafe fn dc_array_is_independent(
|
|
array: *const dc_array_t,
|
|
index: libc::size_t,
|
|
) -> libc::c_int {
|
|
if array.is_null() {
|
|
eprintln!("ignoring careless call to dc_array_is_independent()");
|
|
return 0;
|
|
}
|
|
|
|
(*array).get_location(index).independent as libc::c_int
|
|
}
|
|
|
|
// dc_chatlist_t
|
|
|
|
/// FFI struct for [dc_chatlist_t]
|
|
///
|
|
/// This is the structure behind [dc_chatlist_t] which is the opaque
|
|
/// structure representing a chatlist in the FFI API. It exists
|
|
/// because the FFI API has a reference from the message to the
|
|
/// context, but the Rust API does not, so the FFI layer needs to glue
|
|
/// these together.
|
|
pub struct ChatlistWrapper {
|
|
context: *const dc_context_t,
|
|
list: chatlist::Chatlist,
|
|
}
|
|
|
|
pub type dc_chatlist_t = ChatlistWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_unref(chatlist: *mut dc_chatlist_t) {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_unref()");
|
|
return;
|
|
}
|
|
drop(Box::from_raw(chatlist));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_cnt(chatlist: *mut dc_chatlist_t) -> libc::size_t {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_cnt()");
|
|
return 0;
|
|
}
|
|
let ffi_list = &*chatlist;
|
|
ffi_list.list.len() as libc::size_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_chat_id(
|
|
chatlist: *mut dc_chatlist_t,
|
|
index: libc::size_t,
|
|
) -> u32 {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_chat_id()");
|
|
return 0;
|
|
}
|
|
let ffi_list = &*chatlist;
|
|
let ctx = &*ffi_list.context;
|
|
match ffi_list.list.get_chat_id(index) {
|
|
Ok(chat_id) => chat_id.to_u32(),
|
|
Err(err) => {
|
|
warn!(ctx, "get_chat_id failed: {err:#}");
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_msg_id(
|
|
chatlist: *mut dc_chatlist_t,
|
|
index: libc::size_t,
|
|
) -> u32 {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_msg_id()");
|
|
return 0;
|
|
}
|
|
let ffi_list = &*chatlist;
|
|
let ctx = &*ffi_list.context;
|
|
match ffi_list.list.get_msg_id(index) {
|
|
Ok(msg_id) => msg_id.map_or(0, |msg_id| msg_id.to_u32()),
|
|
Err(err) => {
|
|
warn!(ctx, "get_msg_id failed: {err:#}");
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_summary(
|
|
chatlist: *mut dc_chatlist_t,
|
|
index: libc::size_t,
|
|
chat: *mut dc_chat_t,
|
|
) -> *mut dc_lot_t {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_summary()");
|
|
return ptr::null_mut();
|
|
}
|
|
let maybe_chat = if chat.is_null() {
|
|
None
|
|
} else {
|
|
let ffi_chat = &*chat;
|
|
Some(&ffi_chat.chat)
|
|
};
|
|
let ffi_list = &*chatlist;
|
|
let ctx = &*ffi_list.context;
|
|
|
|
block_on(async move {
|
|
let summary = ffi_list
|
|
.list
|
|
.get_summary(ctx, index, maybe_chat)
|
|
.await
|
|
.context("get_summary failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
Box::into_raw(Box::new(summary.into()))
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_summary2(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
msg_id: u32,
|
|
) -> *mut dc_lot_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_summary2()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
let msg_id = if msg_id == 0 {
|
|
None
|
|
} else {
|
|
Some(MsgId::new(msg_id))
|
|
};
|
|
let summary = block_on(Chatlist::get_summary2(
|
|
ctx,
|
|
ChatId::new(chat_id),
|
|
msg_id,
|
|
None,
|
|
))
|
|
.context("get_summary2 failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
Box::into_raw(Box::new(summary.into()))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chatlist_get_context(
|
|
chatlist: *mut dc_chatlist_t,
|
|
) -> *const dc_context_t {
|
|
if chatlist.is_null() {
|
|
eprintln!("ignoring careless call to dc_chatlist_get_context()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_list = &*chatlist;
|
|
ffi_list.context
|
|
}
|
|
|
|
// dc_chat_t
|
|
|
|
/// FFI struct for [dc_chat_t]
|
|
///
|
|
/// This is the structure behind [dc_chat_t] which is the opaque
|
|
/// structure representing a chat in the FFI API. It exists
|
|
/// because the FFI API has a reference from the message to the
|
|
/// context, but the Rust API does not, so the FFI layer needs to glue
|
|
/// these together.
|
|
pub struct ChatWrapper {
|
|
context: *const dc_context_t,
|
|
chat: chat::Chat,
|
|
}
|
|
|
|
pub type dc_chat_t = ChatWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(chat));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_id()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.get_id().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_type()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.get_type() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_name()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.get_name().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_mailinglist_addr(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_mailinglist_addr()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat
|
|
.chat
|
|
.get_mailinglist_addr()
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_profile_image()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "no image"
|
|
}
|
|
let ffi_chat = &*chat;
|
|
let ctx = &*ffi_chat.context;
|
|
|
|
block_on(async move {
|
|
match ffi_chat.chat.get_profile_image(ctx).await {
|
|
Ok(Some(p)) => p.to_string_lossy().strdup(),
|
|
Ok(None) => ptr::null_mut(),
|
|
Err(err) => {
|
|
error!(ctx, "failed to get profile image: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_color()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
let ctx = &*ffi_chat.context;
|
|
|
|
block_on(ffi_chat.chat.get_color(ctx)).unwrap_or_log_default(ctx, "Failed get_color")
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_visibility(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_visibility()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
match ffi_chat.chat.visibility {
|
|
ChatVisibility::Normal => 0,
|
|
ChatVisibility::Archived => 1,
|
|
ChatVisibility::Pinned => 2,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_contact_request(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_contact_request()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_contact_request() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_unpromoted()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_unpromoted() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_self_talk()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_self_talk() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_device_talk(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_device_talk()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_device_talk() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_can_send()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
let ctx = &*ffi_chat.context;
|
|
block_on(ffi_chat.chat.can_send(ctx))
|
|
.context("can_send failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_protected(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_protected()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_protected() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_protection_broken(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_protection_broken()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_protection_broken() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_sending_locations()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_sending_locations() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_is_muted(chat: *mut dc_chat_t) -> libc::c_int {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_is_muted()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
ffi_chat.chat.is_muted() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_remaining_mute_duration(chat: *mut dc_chat_t) -> i64 {
|
|
if chat.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_remaining_mute_duration()");
|
|
return 0;
|
|
}
|
|
let ffi_chat = &*chat;
|
|
if !ffi_chat.chat.is_muted() {
|
|
return 0;
|
|
}
|
|
// If the chat was muted to before the epoch, it is not muted.
|
|
match ffi_chat.chat.mute_duration {
|
|
MuteDuration::NotMuted => 0,
|
|
MuteDuration::Forever => -1,
|
|
MuteDuration::Until(when) => when
|
|
.duration_since(SystemTime::now())
|
|
.map(|d| d.as_secs() as i64)
|
|
.unwrap_or(0),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_chat_get_info_json(
|
|
context: *mut dc_context_t,
|
|
chat_id: u32,
|
|
) -> *mut libc::c_char {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_chat_get_info_json()");
|
|
return "".strdup();
|
|
}
|
|
let ctx = &*context;
|
|
|
|
block_on(async move {
|
|
let chat = match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)).await {
|
|
Ok(chat) => chat,
|
|
Err(err) => {
|
|
error!(ctx, "dc_get_chat_info_json() failed to load chat: {err:#}");
|
|
return "".strdup();
|
|
}
|
|
};
|
|
let info = match chat.get_info(ctx).await {
|
|
Ok(info) => info,
|
|
Err(err) => {
|
|
error!(
|
|
ctx,
|
|
"dc_get_chat_info_json() failed to get chat info: {err:#}"
|
|
);
|
|
return "".strdup();
|
|
}
|
|
};
|
|
serde_json::to_string(&info)
|
|
.unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json")
|
|
.strdup()
|
|
})
|
|
}
|
|
|
|
// dc_msg_t
|
|
|
|
/// FFI struct for [dc_msg_t]
|
|
///
|
|
/// This is the structure behind [dc_msg_t] which is the opaque
|
|
/// structure representing a message in the FFI API. It exists
|
|
/// because the FFI API has a reference from the message to the
|
|
/// context, but the Rust API does not, so the FFI layer needs to glue
|
|
/// these together.
|
|
pub struct MessageWrapper {
|
|
context: *const dc_context_t,
|
|
message: message::Message,
|
|
}
|
|
|
|
pub type dc_msg_t = MessageWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_new(
|
|
context: *mut dc_context_t,
|
|
viewtype: libc::c_int,
|
|
) -> *mut dc_msg_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_new()");
|
|
return ptr::null_mut();
|
|
}
|
|
let context = &*context;
|
|
let viewtype = from_prim(viewtype).expect(&format!("invalid viewtype = {viewtype}"));
|
|
let msg = MessageWrapper {
|
|
context,
|
|
message: message::Message::new(viewtype),
|
|
};
|
|
Box::into_raw(Box::new(msg))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_unref(msg: *mut dc_msg_t) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(msg));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg_t) -> u32 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_id()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_id().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_from_id(msg: *mut dc_msg_t) -> u32 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_from_id()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_from_id().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_chat_id()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_chat_id().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_viewtype()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg
|
|
.message
|
|
.get_viewtype()
|
|
.to_i64()
|
|
.expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_state()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_state() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_download_state(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_download_state()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.download_state() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_timestamp(msg: *mut dc_msg_t) -> i64 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_received_timestamp()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_timestamp()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_received_timestamp(msg: *mut dc_msg_t) -> i64 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_received_timestamp()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_received_timestamp()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_sort_timestamp(msg: *mut dc_msg_t) -> i64 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_sort_timestamp()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_sort_timestamp()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_text()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_text().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_subject(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_subject()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_subject().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_file()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
ffi_msg
|
|
.message
|
|
.get_file(ctx)
|
|
.map(|p| p.to_string_lossy().strdup())
|
|
.unwrap_or_else(|| "".strdup())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_save_file(
|
|
msg: *mut dc_msg_t,
|
|
path: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if msg.is_null() || path.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_save_file()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
let path = to_string_lossy(path);
|
|
let r = block_on(
|
|
ffi_msg
|
|
.message
|
|
.save_file(ctx, &std::path::PathBuf::from(path)),
|
|
);
|
|
match r {
|
|
Ok(()) => 1,
|
|
Err(_) => {
|
|
r.context("Failed to save file from message")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_filename()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_filename().unwrap_or_default().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_webxdc_blob(
|
|
msg: *mut dc_msg_t,
|
|
filename: *const libc::c_char,
|
|
ret_bytes: *mut libc::size_t,
|
|
) -> *mut libc::c_char {
|
|
if msg.is_null() || filename.is_null() || ret_bytes.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_webxdc_blob()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
let blob = block_on(async move {
|
|
ffi_msg
|
|
.message
|
|
.get_webxdc_blob(ctx, &to_string_lossy(filename))
|
|
.await
|
|
});
|
|
match blob {
|
|
Ok(blob) => {
|
|
*ret_bytes = blob.len();
|
|
let ptr = libc::malloc(*ret_bytes);
|
|
libc::memcpy(ptr, blob.as_ptr() as *mut libc::c_void, *ret_bytes);
|
|
ptr as *mut libc::c_char
|
|
}
|
|
Err(err) => {
|
|
eprintln!("failed read blob from archive: {err}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_webxdc_info(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_webxdc_info()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
block_on(async move {
|
|
let info = match ffi_msg.message.get_webxdc_info(ctx).await {
|
|
Ok(info) => info,
|
|
Err(err) => {
|
|
error!(ctx, "dc_msg_get_webxdc_info() failed to get info: {err:#}");
|
|
return "".strdup();
|
|
}
|
|
};
|
|
serde_json::to_string(&info)
|
|
.unwrap_or_log_default(ctx, "dc_msg_get_webxdc_info() failed to serialise to json")
|
|
.strdup()
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_filemime(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_filemime()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
if let Some(x) = ffi_msg.message.get_filemime() {
|
|
x.strdup()
|
|
} else {
|
|
"".strdup()
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg_t) -> u64 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_filebytes()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
block_on(ffi_msg.message.get_filebytes(ctx))
|
|
.unwrap_or_log_default(ctx, "Cannot get file size")
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_width(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_width()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_width()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_height(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_height()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_height()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_duration(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_duration()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_duration()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_showpadlock()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_showpadlock() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_bot(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_bot()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_bot() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_ephemeral_timer(msg: *mut dc_msg_t) -> u32 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_ephemeral_timer().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_ephemeral_timestamp(msg: *mut dc_msg_t) -> i64 {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_ephemeral_timer()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_ephemeral_timestamp()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_summary(
|
|
msg: *mut dc_msg_t,
|
|
chat: *mut dc_chat_t,
|
|
) -> *mut dc_lot_t {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_summary()");
|
|
return ptr::null_mut();
|
|
}
|
|
let maybe_chat = if chat.is_null() {
|
|
None
|
|
} else {
|
|
let ffi_chat = &*chat;
|
|
Some(&ffi_chat.chat)
|
|
};
|
|
let ffi_msg = &mut *msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
let summary = block_on(ffi_msg.message.get_summary(ctx, maybe_chat))
|
|
.context("dc_msg_get_summary failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
Box::into_raw(Box::new(summary.into()))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_summarytext(
|
|
msg: *mut dc_msg_t,
|
|
approx_characters: libc::c_int,
|
|
) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_summarytext()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
let summary = block_on(ffi_msg.message.get_summary(ctx, None))
|
|
.context("dc_msg_get_summarytext failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default();
|
|
match usize::try_from(approx_characters) {
|
|
Ok(chars) => summary.truncated_text(chars).strdup(),
|
|
Err(_) => summary.text.strdup(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_override_sender_name(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_override_sender_name()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
|
|
ffi_msg.message.get_override_sender_name().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_has_deviating_timestamp(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_has_deviating_timestamp()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.has_deviating_timestamp().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_has_location(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_has_location()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.has_location() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_sent()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_sent().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_forwarded(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_forwarded()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_forwarded().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_info()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_info().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_info_type()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_info_type() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_increation()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_increation().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_is_setupmessage()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.is_setupmessage().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_has_html(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_has_html()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.has_html().into()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_videochat_url(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_videochat_url()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
|
|
ffi_msg
|
|
.message
|
|
.get_videochat_url()
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_videochat_type(msg: *mut dc_msg_t) -> libc::c_int {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_videochat_type()");
|
|
return 0;
|
|
}
|
|
let ffi_msg = &*msg;
|
|
ffi_msg.message.get_videochat_type().unwrap_or_default() as i32
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_setupcodebegin(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_setupcodebegin()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
block_on(ffi_msg.message.get_setupcodebegin(ctx))
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg_t, text: *const libc::c_char) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_text()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_text(to_string_lossy(text))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_html(msg: *mut dc_msg_t, html: *const libc::c_char) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_html()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_html(to_opt_string_lossy(html))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_subject(msg: *mut dc_msg_t, subject: *const libc::c_char) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_subject()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_subject(to_string_lossy(subject));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_override_sender_name(
|
|
msg: *mut dc_msg_t,
|
|
name: *const libc::c_char,
|
|
) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_override_sender_name()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg
|
|
.message
|
|
.set_override_sender_name(to_opt_string_lossy(name))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_file(
|
|
msg: *mut dc_msg_t,
|
|
file: *const libc::c_char,
|
|
filemime: *const libc::c_char,
|
|
) {
|
|
if msg.is_null() || file.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_file()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_file(
|
|
to_string_lossy(file),
|
|
to_opt_string_lossy(filemime).as_deref(),
|
|
)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_dimension(
|
|
msg: *mut dc_msg_t,
|
|
width: libc::c_int,
|
|
height: libc::c_int,
|
|
) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_dimension()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_dimension(width, height)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_duration(msg: *mut dc_msg_t, duration: libc::c_int) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_duration()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_duration(duration)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_location(
|
|
msg: *mut dc_msg_t,
|
|
latitude: libc::c_double,
|
|
longitude: libc::c_double,
|
|
) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_location()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.set_location(latitude, longitude)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_latefiling_mediasize(
|
|
msg: *mut dc_msg_t,
|
|
width: libc::c_int,
|
|
height: libc::c_int,
|
|
duration: libc::c_int,
|
|
) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_latefiling_mediasize()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
let ctx = &*ffi_msg.context;
|
|
|
|
block_on({
|
|
ffi_msg
|
|
.message
|
|
.latefiling_mediasize(ctx, width, height, duration)
|
|
})
|
|
.context("Cannot set media size")
|
|
.log_err(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_error(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_error()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_msg = &*msg;
|
|
match ffi_msg.message.error() {
|
|
Some(error) => error.strdup(),
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_set_quote(msg: *mut dc_msg_t, quote: *const dc_msg_t) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_set_quote()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
let quote_msg = if quote.is_null() {
|
|
None
|
|
} else {
|
|
let ffi_quote = &*quote;
|
|
if ffi_msg.context != ffi_quote.context {
|
|
eprintln!("ignoring attempt to quote message from a different context");
|
|
return;
|
|
}
|
|
Some(&ffi_quote.message)
|
|
};
|
|
|
|
block_on(async move {
|
|
ffi_msg
|
|
.message
|
|
.set_quote(&*ffi_msg.context, quote_msg)
|
|
.await
|
|
.context("failed to set quote")
|
|
.log_err(&*ffi_msg.context)
|
|
.ok();
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_quoted_text(msg: *const dc_msg_t) -> *mut libc::c_char {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_quoted_text()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_msg: &MessageWrapper = &*msg;
|
|
ffi_msg
|
|
.message
|
|
.quoted_text()
|
|
.map_or_else(ptr::null_mut, |s| s.strdup())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_quoted_msg(msg: *const dc_msg_t) -> *mut dc_msg_t {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_get_quoted_msg()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_msg: &MessageWrapper = &*msg;
|
|
let context = &*ffi_msg.context;
|
|
let res = block_on(async move {
|
|
ffi_msg
|
|
.message
|
|
.quoted_message(context)
|
|
.await
|
|
.context("failed to get quoted message")
|
|
.log_err(context)
|
|
.unwrap_or(None)
|
|
});
|
|
|
|
match res {
|
|
Some(message) => Box::into_raw(Box::new(MessageWrapper { context, message })),
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_get_parent(msg: *const dc_msg_t) -> *mut dc_msg_t {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_get_parent()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ffi_msg: &MessageWrapper = &*msg;
|
|
let context = &*ffi_msg.context;
|
|
let res = block_on(async move {
|
|
ffi_msg
|
|
.message
|
|
.parent(context)
|
|
.await
|
|
.context("failed to get parent message")
|
|
.log_err(context)
|
|
.unwrap_or(None)
|
|
});
|
|
|
|
match res {
|
|
Some(message) => Box::into_raw(Box::new(MessageWrapper { context, message })),
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_msg_force_plaintext(msg: *mut dc_msg_t) {
|
|
if msg.is_null() {
|
|
eprintln!("ignoring careless call to dc_msg_force_plaintext()");
|
|
return;
|
|
}
|
|
let ffi_msg = &mut *msg;
|
|
ffi_msg.message.force_plaintext();
|
|
}
|
|
|
|
// dc_contact_t
|
|
|
|
/// FFI struct for [dc_contact_t]
|
|
///
|
|
/// This is the structure behind [dc_contact_t] which is the opaque
|
|
/// structure representing a contact in the FFI API. It exists
|
|
/// because the FFI API has a reference from the message to the
|
|
/// context, but the Rust API does not, so the FFI layer needs to glue
|
|
/// these together.
|
|
pub struct ContactWrapper {
|
|
context: *const dc_context_t,
|
|
contact: contact::Contact,
|
|
}
|
|
|
|
pub type dc_contact_t = ContactWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_unref(contact: *mut dc_contact_t) {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_unref()");
|
|
return;
|
|
}
|
|
drop(Box::from_raw(contact));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_id(contact: *mut dc_contact_t) -> u32 {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_id()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_id().to_u32()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_addr(contact: *mut dc_contact_t) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_addr()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_addr().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_name(contact: *mut dc_contact_t) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_name()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_name().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_auth_name(contact: *mut dc_contact_t) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_auth_name()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_authname().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_display_name(
|
|
contact: *mut dc_contact_t,
|
|
) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_display_name()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_display_name().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_name_n_addr(
|
|
contact: *mut dc_contact_t,
|
|
) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_name_n_addr()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_name_n_addr().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_profile_image(
|
|
contact: *mut dc_contact_t,
|
|
) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_profile_image()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "no profile image"
|
|
}
|
|
let ffi_contact = &*contact;
|
|
let ctx = &*ffi_contact.context;
|
|
|
|
block_on(async move {
|
|
ffi_contact
|
|
.contact
|
|
.get_profile_image(ctx)
|
|
.await
|
|
.unwrap_or_log_default(ctx, "failed to get profile image")
|
|
.map(|p| p.to_string_lossy().strdup())
|
|
.unwrap_or_else(std::ptr::null_mut)
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32 {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_color()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_color()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_status(contact: *mut dc_contact_t) -> *mut libc::c_char {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_status()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.get_status().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_last_seen(contact: *mut dc_contact_t) -> i64 {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_last_seen()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.last_seen()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t) -> libc::c_int {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_was_seen_recently()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.was_seen_recently() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_is_blocked()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
ffi_contact.contact.is_blocked() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> libc::c_int {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_is_verified()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
let ctx = &*ffi_contact.context;
|
|
|
|
if block_on(ffi_contact.contact.is_verified(ctx))
|
|
.context("is_verified failed")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
{
|
|
// Return value is essentially a boolean,
|
|
// but we return 2 for true for backwards compatibility.
|
|
2
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_is_bot(contact: *mut dc_contact_t) -> libc::c_int {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_is_bot()");
|
|
return 0;
|
|
}
|
|
(*contact).contact.is_bot() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_contact_get_verifier_id(contact: *mut dc_contact_t) -> u32 {
|
|
if contact.is_null() {
|
|
eprintln!("ignoring careless call to dc_contact_get_verifier_id()");
|
|
return 0;
|
|
}
|
|
let ffi_contact = &*contact;
|
|
let ctx = &*ffi_contact.context;
|
|
let verifier_contact_id = block_on(ffi_contact.contact.get_verifier_id(ctx))
|
|
.context("failed to get verifier")
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
.unwrap_or_default();
|
|
|
|
verifier_contact_id.to_u32()
|
|
}
|
|
// dc_lot_t
|
|
|
|
pub type dc_lot_t = lot::Lot;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot_t) {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_unref()");
|
|
return;
|
|
}
|
|
|
|
drop(Box::from_raw(lot));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot_t) -> *mut libc::c_char {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_text1()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "there is no such text"
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_text1().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot_t) -> *mut libc::c_char {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_text2()");
|
|
return ptr::null_mut(); // NULL explicitly defined as "there is no such text"
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_text2().strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot_t) -> libc::c_int {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_text1_meaning()");
|
|
return 0;
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_text1_meaning() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_state()");
|
|
return 0;
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_state() as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot_t) -> u32 {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_id()");
|
|
return 0;
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_id()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot_t) -> i64 {
|
|
if lot.is_null() {
|
|
eprintln!("ignoring careless call to dc_lot_get_timestamp()");
|
|
return 0;
|
|
}
|
|
|
|
let lot = &*lot;
|
|
lot.get_timestamp()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
|
|
libc::free(s as *mut _)
|
|
}
|
|
|
|
pub struct BackupProviderWrapper {
|
|
context: *const dc_context_t,
|
|
provider: BackupProvider,
|
|
}
|
|
|
|
pub type dc_backup_provider_t = BackupProviderWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_backup_provider_new(
|
|
context: *mut dc_context_t,
|
|
) -> *mut dc_backup_provider_t {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_backup_provider_new()");
|
|
return ptr::null_mut();
|
|
}
|
|
let ctx = &*context;
|
|
block_on(BackupProvider::prepare(ctx))
|
|
.map(|provider| BackupProviderWrapper {
|
|
context: ctx,
|
|
provider,
|
|
})
|
|
.map(|ffi_provider| Box::into_raw(Box::new(ffi_provider)))
|
|
.context("BackupProvider failed")
|
|
.log_err(ctx)
|
|
.set_last_error(ctx)
|
|
.unwrap_or(ptr::null_mut())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_backup_provider_get_qr(
|
|
provider: *const dc_backup_provider_t,
|
|
) -> *mut libc::c_char {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_backup_provider_qr");
|
|
return "".strdup();
|
|
}
|
|
let ffi_provider = &*provider;
|
|
let ctx = &*ffi_provider.context;
|
|
deltachat::qr::format_backup(&ffi_provider.provider.qr())
|
|
.context("BackupProvider get_qr failed")
|
|
.log_err(ctx)
|
|
.set_last_error(ctx)
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_backup_provider_get_qr_svg(
|
|
provider: *const dc_backup_provider_t,
|
|
) -> *mut libc::c_char {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_backup_provider_qr_svg()");
|
|
return "".strdup();
|
|
}
|
|
let ffi_provider = &*provider;
|
|
let ctx = &*ffi_provider.context;
|
|
let provider = &ffi_provider.provider;
|
|
block_on(generate_backup_qr(ctx, &provider.qr()))
|
|
.context("BackupProvider get_qr_svg failed")
|
|
.log_err(ctx)
|
|
.set_last_error(ctx)
|
|
.unwrap_or_default()
|
|
.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_backup_provider_wait(provider: *mut dc_backup_provider_t) {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_backup_provider_wait()");
|
|
return;
|
|
}
|
|
let ffi_provider = &mut *provider;
|
|
let ctx = &*ffi_provider.context;
|
|
let provider = &mut ffi_provider.provider;
|
|
block_on(provider)
|
|
.context("Failed to await backup provider")
|
|
.log_err(ctx)
|
|
.set_last_error(ctx)
|
|
.ok();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_backup_provider_unref(provider: *mut dc_backup_provider_t) {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_backup_provider_unref()");
|
|
return;
|
|
}
|
|
drop(Box::from_raw(provider));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_receive_backup(
|
|
context: *mut dc_context_t,
|
|
qr: *const libc::c_char,
|
|
) -> libc::c_int {
|
|
if context.is_null() {
|
|
eprintln!("ignoring careless call to dc_receive_backup()");
|
|
return 0;
|
|
}
|
|
let ctx = &*context;
|
|
let qr_text = to_string_lossy(qr);
|
|
receive_backup(ctx.clone(), qr_text)
|
|
}
|
|
|
|
// Because this is a long-running operation make sure we own the Context. This stops a FFI
|
|
// user from deallocating it by calling unref on the object while we are using it.
|
|
fn receive_backup(ctx: Context, qr_text: String) -> libc::c_int {
|
|
let qr = match block_on(qr::check_qr(&ctx, &qr_text))
|
|
.context("Invalid QR code")
|
|
.log_err(&ctx)
|
|
.set_last_error(&ctx)
|
|
{
|
|
Ok(qr) => qr,
|
|
Err(_) => return 0,
|
|
};
|
|
match block_on(imex::get_backup(&ctx, qr))
|
|
.context("Get backup failed")
|
|
.log_err(&ctx)
|
|
.set_last_error(&ctx)
|
|
{
|
|
Ok(_) => 1,
|
|
Err(_) => 0,
|
|
}
|
|
}
|
|
|
|
trait ResultExt<T, E> {
|
|
/// Like `log_err()`, but:
|
|
/// - returns the default value instead of an Err value.
|
|
/// - emits an error instead of a warning for an [Err] result. This means
|
|
/// that the error will be shown to the user in a small pop-up.
|
|
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
|
|
}
|
|
|
|
impl<T: Default, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
|
|
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T {
|
|
match self {
|
|
Ok(t) => t,
|
|
Err(err) => {
|
|
error!(context, "{message}: {err:#}");
|
|
Default::default()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
trait ResultLastError<T, E>
|
|
where
|
|
E: std::fmt::Display,
|
|
{
|
|
/// Sets this `Err` value using [`Context::set_last_error`].
|
|
///
|
|
/// Normally each FFI-API *should* call this if it handles an error from the Rust API:
|
|
/// errors which need to be reported to users in response to an API call need to be
|
|
/// propagated up the Rust API and at the FFI boundary need to be stored into the "last
|
|
/// error" so the FFI users can retrieve an appropriate error message on failure. Often
|
|
/// you will want to combine this with a call to [`LogExt::log_err`].
|
|
///
|
|
/// Since historically calls to the `deltachat::log::error!()` macro were (and sometimes
|
|
/// still are) shown as error toasts to the user, this macro also calls
|
|
/// [`Context::set_last_error`]. It is preferable however to rely on normal error
|
|
/// propagation in Rust code however and only use this `ResultExt::set_last_error` call
|
|
/// in the FFI layer.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// Fully handling an error in the FFI code looks like this currently:
|
|
///
|
|
/// ```no_compile
|
|
/// some_dc_rust_api_call_returning_result()
|
|
/// .context("My API call failed")
|
|
/// .log_err(&context)
|
|
/// .set_last_error(&context)
|
|
/// .unwrap_or_default()
|
|
/// ```
|
|
///
|
|
/// As shows it is a shame the `.log_err()` call currently needs a message instead of
|
|
/// relying on an implicit call to the [`anyhow::Context`] call if needed. This stems
|
|
/// from a time before we fully embraced anyhow. Some day we'll also fix that.
|
|
///
|
|
/// [`Context::set_last_error`]: context::Context::set_last_error
|
|
fn set_last_error(self, context: &context::Context) -> Result<T, E>;
|
|
}
|
|
|
|
impl<T, E> ResultLastError<T, E> for Result<T, E>
|
|
where
|
|
E: std::fmt::Display,
|
|
{
|
|
fn set_last_error(self, context: &context::Context) -> Result<T, E> {
|
|
if let Err(ref err) = self {
|
|
context.set_last_error(&format!("{err:#}"));
|
|
}
|
|
self
|
|
}
|
|
}
|
|
|
|
fn convert_and_prune_message_ids(msg_ids: *const u32, msg_cnt: libc::c_int) -> Vec<MsgId> {
|
|
let ids = unsafe { std::slice::from_raw_parts(msg_ids, msg_cnt as usize) };
|
|
let msg_ids: Vec<MsgId> = ids
|
|
.iter()
|
|
.filter(|id| **id > DC_MSG_ID_LAST_SPECIAL)
|
|
.map(|id| MsgId::new(*id))
|
|
.collect();
|
|
|
|
msg_ids
|
|
}
|
|
|
|
// dc_provider_t
|
|
|
|
pub type dc_provider_t = provider::Provider;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_provider_new_from_email(
|
|
context: *const dc_context_t,
|
|
addr: *const libc::c_char,
|
|
) -> *const dc_provider_t {
|
|
if context.is_null() || addr.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_new_from_email()");
|
|
return ptr::null();
|
|
}
|
|
let addr = to_string_lossy(addr);
|
|
|
|
let ctx = &*context;
|
|
|
|
match block_on(provider::get_provider_info_by_addr(
|
|
ctx,
|
|
addr.as_str(),
|
|
true,
|
|
))
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
{
|
|
Some(provider) => provider,
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_provider_new_from_email_with_dns(
|
|
context: *const dc_context_t,
|
|
addr: *const libc::c_char,
|
|
) -> *const dc_provider_t {
|
|
if context.is_null() || addr.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_new_from_email_with_dns()");
|
|
return ptr::null();
|
|
}
|
|
let addr = to_string_lossy(addr);
|
|
|
|
let ctx = &*context;
|
|
let proxy_enabled = block_on(ctx.get_config_bool(config::Config::ProxyEnabled))
|
|
.context("Can't get config")
|
|
.log_err(ctx);
|
|
|
|
match proxy_enabled {
|
|
Ok(proxy_enabled) => {
|
|
match block_on(provider::get_provider_info_by_addr(
|
|
ctx,
|
|
addr.as_str(),
|
|
proxy_enabled,
|
|
))
|
|
.log_err(ctx)
|
|
.unwrap_or_default()
|
|
{
|
|
Some(provider) => provider,
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_provider_get_overview_page(
|
|
provider: *const dc_provider_t,
|
|
) -> *mut libc::c_char {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_get_overview_page()");
|
|
return "".strdup();
|
|
}
|
|
let provider = &*provider;
|
|
provider.overview_page.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_provider_get_before_login_hint(
|
|
provider: *const dc_provider_t,
|
|
) -> *mut libc::c_char {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_get_before_login_hint()");
|
|
return "".strdup();
|
|
}
|
|
let provider = &*provider;
|
|
provider.before_login_hint.strdup()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> libc::c_int {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_get_status()");
|
|
return 0;
|
|
}
|
|
let provider = &*provider;
|
|
provider.status as libc::c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[allow(clippy::needless_return)]
|
|
pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) {
|
|
if provider.is_null() {
|
|
eprintln!("ignoring careless call to dc_provider_unref()");
|
|
return;
|
|
}
|
|
// currently, there is nothing to free, the provider info is a static object.
|
|
// this may change once we start localizing string.
|
|
}
|
|
|
|
// -- Accounts
|
|
|
|
/// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using
|
|
/// `dc_accounts_t` in multiple threads at once.
|
|
pub struct AccountsWrapper {
|
|
inner: Arc<RwLock<Accounts>>,
|
|
}
|
|
|
|
impl Deref for AccountsWrapper {
|
|
type Target = Arc<RwLock<Accounts>>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl AccountsWrapper {
|
|
fn new(accounts: Accounts) -> Self {
|
|
let inner = Arc::new(RwLock::new(accounts));
|
|
Self { inner }
|
|
}
|
|
}
|
|
|
|
/// Struct representing a list of deltachat accounts.
|
|
pub type dc_accounts_t = AccountsWrapper;
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_new(
|
|
dir: *const libc::c_char,
|
|
writable: libc::c_int,
|
|
) -> *mut dc_accounts_t {
|
|
setup_panic!();
|
|
|
|
if dir.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_new()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let accs = block_on(Accounts::new(as_path(dir).into(), writable != 0));
|
|
|
|
match accs {
|
|
Ok(accs) => Box::into_raw(Box::new(AccountsWrapper::new(accs))),
|
|
Err(err) => {
|
|
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
|
|
eprintln!("failed to create accounts: {err:#}");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Release the accounts structure.
|
|
///
|
|
/// This function releases the memory of the `dc_accounts_t` structure.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_unref(accounts: *mut dc_accounts_t) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_unref()");
|
|
return;
|
|
}
|
|
let _ = Box::from_raw(accounts);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_get_account(
|
|
accounts: *mut dc_accounts_t,
|
|
id: u32,
|
|
) -> *mut dc_context_t {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_get_account()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(accounts.read())
|
|
.get_account(id)
|
|
.map(|ctx| Box::into_raw(Box::new(ctx)))
|
|
.unwrap_or_else(std::ptr::null_mut)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_get_selected_account(
|
|
accounts: *mut dc_accounts_t,
|
|
) -> *mut dc_context_t {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_get_selected_account()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(accounts.read())
|
|
.get_selected_account()
|
|
.map(|ctx| Box::into_raw(Box::new(ctx)))
|
|
.unwrap_or_else(std::ptr::null_mut)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_select_account(
|
|
accounts: *mut dc_accounts_t,
|
|
id: u32,
|
|
) -> libc::c_int {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_select_account()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
match accounts.select_account(id).await {
|
|
Ok(()) => 1,
|
|
Err(err) => {
|
|
accounts.emit_event(EventType::Error(format!(
|
|
"Failed to select account: {err:#}"
|
|
)));
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_add_account(accounts: *mut dc_accounts_t) -> u32 {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_add_account()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &mut *accounts;
|
|
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
match accounts.add_account().await {
|
|
Ok(id) => id,
|
|
Err(err) => {
|
|
accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}")));
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_add_closed_account(accounts: *mut dc_accounts_t) -> u32 {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_add_closed_account()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &mut *accounts;
|
|
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
match accounts.add_closed_account().await {
|
|
Ok(id) => id,
|
|
Err(err) => {
|
|
accounts.emit_event(EventType::Error(format!("Failed to add account: {err:#}")));
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_remove_account(
|
|
accounts: *mut dc_accounts_t,
|
|
id: u32,
|
|
) -> libc::c_int {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_remove_account()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &mut *accounts;
|
|
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
match accounts.remove_account(id).await {
|
|
Ok(()) => 1,
|
|
Err(err) => {
|
|
accounts.emit_event(EventType::Error(format!(
|
|
"Failed to remove account: {err:#}"
|
|
)));
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_migrate_account(
|
|
accounts: *mut dc_accounts_t,
|
|
dbfile: *const libc::c_char,
|
|
) -> u32 {
|
|
if accounts.is_null() || dbfile.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_migrate_account()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &mut *accounts;
|
|
let dbfile = to_string_lossy(dbfile);
|
|
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
match accounts
|
|
.migrate_account(std::path::PathBuf::from(dbfile))
|
|
.await
|
|
{
|
|
Ok(id) => id,
|
|
Err(err) => {
|
|
accounts.emit_event(EventType::Error(format!(
|
|
"Failed to migrate account: {err:#}"
|
|
)));
|
|
0
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_get_all(accounts: *mut dc_accounts_t) -> *mut dc_array_t {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_get_all()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
let list = block_on(accounts.read()).get_all();
|
|
let array: dc_array_t = list.into();
|
|
|
|
Box::into_raw(Box::new(array))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_start_io(accounts: *mut dc_accounts_t) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_start_io()");
|
|
return;
|
|
}
|
|
|
|
let accounts = &mut *accounts;
|
|
block_on(async move { accounts.write().await.start_io().await });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_stop_io(accounts: *mut dc_accounts_t) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_stop_io()");
|
|
return;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(async move { accounts.read().await.stop_io().await });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_maybe_network(accounts: *mut dc_accounts_t) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_maybe_network()");
|
|
return;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(async move { accounts.read().await.maybe_network().await });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *mut dc_accounts_t) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_maybe_network_lost()");
|
|
return;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(async move { accounts.write().await.maybe_network_lost().await });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_background_fetch(
|
|
accounts: *mut dc_accounts_t,
|
|
timeout_in_seconds: u64,
|
|
) -> libc::c_int {
|
|
if accounts.is_null() || timeout_in_seconds <= 2 {
|
|
eprintln!("ignoring careless call to dc_accounts_background_fetch()");
|
|
return 0;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
block_on(async move {
|
|
let accounts = accounts.read().await;
|
|
accounts
|
|
.background_fetch(Duration::from_secs(timeout_in_seconds))
|
|
.await;
|
|
});
|
|
1
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_set_push_device_token(
|
|
accounts: *mut dc_accounts_t,
|
|
token: *const libc::c_char,
|
|
) {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
|
|
return;
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
let token = to_string_lossy(token);
|
|
|
|
block_on(async move {
|
|
let mut accounts = accounts.write().await;
|
|
if let Err(err) = accounts.set_push_device_token(&token).await {
|
|
accounts.emit_event(EventType::Error(format!(
|
|
"Failed to set notify token: {err:#}."
|
|
)));
|
|
}
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
|
|
accounts: *mut dc_accounts_t,
|
|
) -> *mut dc_event_emitter_t {
|
|
if accounts.is_null() {
|
|
eprintln!("ignoring careless call to dc_accounts_get_event_emitter()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let accounts = &*accounts;
|
|
let emitter = block_on(accounts.read()).get_event_emitter();
|
|
|
|
Box::into_raw(Box::new(emitter))
|
|
}
|
|
|
|
#[cfg(feature = "jsonrpc")]
|
|
mod jsonrpc {
|
|
use deltachat_jsonrpc::api::CommandApi;
|
|
use deltachat_jsonrpc::yerpc::{OutReceiver, RpcClient, RpcSession};
|
|
|
|
use super::*;
|
|
|
|
pub struct dc_jsonrpc_instance_t {
|
|
receiver: OutReceiver,
|
|
handle: RpcSession<CommandApi>,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_jsonrpc_init(
|
|
account_manager: *mut dc_accounts_t,
|
|
) -> *mut dc_jsonrpc_instance_t {
|
|
if account_manager.is_null() {
|
|
eprintln!("ignoring careless call to dc_jsonrpc_init()");
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let account_manager = &*account_manager;
|
|
let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc(
|
|
account_manager.inner.clone(),
|
|
));
|
|
|
|
let (request_handle, receiver) = RpcClient::new();
|
|
let handle = RpcSession::new(request_handle, cmd_api);
|
|
|
|
let instance = dc_jsonrpc_instance_t { receiver, handle };
|
|
|
|
Box::into_raw(Box::new(instance))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_jsonrpc_unref(jsonrpc_instance: *mut dc_jsonrpc_instance_t) {
|
|
if jsonrpc_instance.is_null() {
|
|
eprintln!("ignoring careless call to dc_jsonrpc_unref()");
|
|
return;
|
|
}
|
|
drop(Box::from_raw(jsonrpc_instance));
|
|
}
|
|
|
|
fn spawn_handle_jsonrpc_request(handle: RpcSession<CommandApi>, request: String) {
|
|
spawn(async move {
|
|
handle.handle_incoming(&request).await;
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_jsonrpc_request(
|
|
jsonrpc_instance: *mut dc_jsonrpc_instance_t,
|
|
request: *const libc::c_char,
|
|
) {
|
|
if jsonrpc_instance.is_null() || request.is_null() {
|
|
eprintln!("ignoring careless call to dc_jsonrpc_request()");
|
|
return;
|
|
}
|
|
|
|
let handle = &(*jsonrpc_instance).handle;
|
|
let request = to_string_lossy(request);
|
|
spawn_handle_jsonrpc_request(handle.clone(), request);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_jsonrpc_next_response(
|
|
jsonrpc_instance: *mut dc_jsonrpc_instance_t,
|
|
) -> *mut libc::c_char {
|
|
if jsonrpc_instance.is_null() {
|
|
eprintln!("ignoring careless call to dc_jsonrpc_next_response()");
|
|
return ptr::null_mut();
|
|
}
|
|
let api = &*jsonrpc_instance;
|
|
block_on(api.receiver.recv())
|
|
.map(|result| serde_json::to_string(&result).unwrap_or_default().strdup())
|
|
.unwrap_or(ptr::null_mut())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn dc_jsonrpc_blocking_call(
|
|
jsonrpc_instance: *mut dc_jsonrpc_instance_t,
|
|
input: *const libc::c_char,
|
|
) -> *mut libc::c_char {
|
|
if jsonrpc_instance.is_null() {
|
|
eprintln!("ignoring careless call to dc_jsonrpc_blocking_call()");
|
|
return ptr::null_mut();
|
|
}
|
|
let api = &*jsonrpc_instance;
|
|
let input = to_string_lossy(input);
|
|
let res = block_on(api.handle.process_incoming(&input));
|
|
match res {
|
|
Some(message) => {
|
|
if let Ok(message) = serde_json::to_string(&message) {
|
|
message.strdup()
|
|
} else {
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|
|
}
|