implement ffi and use public sendme

This commit is contained in:
Floris Bruynooghe
2023-02-13 18:25:12 +01:00
parent 852adbe514
commit 323535584b
5 changed files with 363 additions and 614 deletions

758
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -152,6 +152,3 @@ vendored = [
"rusqlite/bundled-sqlcipher-vendored-openssl",
"reqwest/native-tls-vendored"
]
[patch.'https://github.com/n0-computer/sendme']
sendme = { path = "/home/flub/n0/sendme" }

View File

@@ -24,6 +24,7 @@ typedef struct _dc_provider dc_provider_t;
typedef struct _dc_event dc_event_t;
typedef struct _dc_event_emitter dc_event_emitter_t;
typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t;
typedef struct _dc_backup_provider dc_backup_provider_t;
// Alias for backwards compatibility, use dc_event_emitter_t instead.
typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
@@ -2295,6 +2296,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
#define DC_QR_FPR_MISMATCH 220 // id=contact
#define DC_QR_FPR_WITHOUT_ADDR 230 // test1=formatted fingerprint
#define DC_QR_ACCOUNT 250 // text1=domain
#define DC_QR_BACKUP 251
#define DC_QR_WEBRTC_INSTANCE 260 // text1=domain, text2=instance pattern
#define DC_QR_ADDR 320 // id=contact
#define DC_QR_TEXT 330 // text1=text
@@ -2634,8 +2636,108 @@ char* dc_get_last_error (dc_context_t* context);
void dc_str_unref (char* str);
// TODO: add New stuff
/**
* @class dc_backup_provider_t
*
* Set up another device.
*/
/**
* Creates an object for sending a backup to another device.
*
* The backup is sent to through a peer-to-peer channel which is bootstrapped
* by a QR-code. The backup contains the entire state of the account
* including credentials. This can be used to setup a new device.
*
* Once this function returns the backup is being offered to remove devices.
* To wait until one device transferred the backup, use
* dc_backup_provider_done(). Alternatively abort the operation using
* dc_stop_ongoing_process().
*
* During execution of the job #DC_EVENT_IMEX_PROGRESS is sent out to indicate
* state and progress.
*
* @memberof dc_backup_sender_t
* @param context The context.
* @param folder A Path to a temporary directory where the encrypted database
* export will be created. The directory is not automatically cleaned
* after the backup is sent.
* @return Opaque object for sending the backup.
* On errors, NULL is returned and dc_get_last_error()returns an error that
* should be shown to the user.
*/
dc_backup_provider_t* dc_provide_backup (dc_context_t* context, const chat* folder);
/**
* Returns the QR code text that will offer the backup to other devices.
*
* The QR code contains a ticket which will validate the backup and provide
* authentication for both the provider and the recipient.
*
* The scanning device should call the scanned text to dc_check_qr(). If
* dc_check_qr() returns DC_QR_BACKUP the backup transfer can be started using
* dc_get_backup().
*
* @memberof dc_backup_provider_t
* @param context The context.
* @param backup_provider The backup provider object as created by
* dc_provide_backup().
* @return The text that should be put in the QR code.
* On errors and empty QR code is returned, NULL is never returned.
* the returned string must be released using dc_str_unref() after usage.
*/
char* dc_backup_provider_qr (dc_context_t* context, const dc_backup_provider_t* backup_provider);
/**
* Returns the QR code SVG image that will offer the backup to other devices.
*
* This works like dc_backup_provider_qr() but returns the text of a rendered
* SVG image containing the QR code.
*
* @memberof dc_backup_provider_t
* @param context The context.
* @param backup_provider The backup provider object as created by
* dc_provide_backup().
* @return The text that should be put in the QR code.
* On errors and empty QR code is returned, NULL is never returned.
* the returned string must be released using dc_str_unref() after usage.
*/
char * dc_backup_provider_qr_svg (dc_context_t* context, const dc_backup_provider_t* backup_provider);
/**
* Waits for the sending to finish and frees the backup provider object.
*
* @memberof dc_backup_sender_t
* @param context The context.
* @param backup_sender The backup sender object as created by dc_send_backup().
* If NULL is given nothing is done.
*/
void dc_backup_provider_done (dc_context_t* context, dc_backup_provider_t* backup_provider);
/**
* Gets a backup offered by a dc_backup_provider_t object on another device.
*
* This function is called on a device that scanned the QR code offered by
* dc_backup_sender_qr() or dc_backup_sender_qr_svg(). Typically this is a
* different device than that which provides the backup.
*
* While dc_receive_backup() returns immediately the started job make take a
* while. Use dc_stop_ongoing_process() to abort it early.
*
* During execution of the job #DC_EVENT_IMEX_PROGRESS is sent out to indicate
* state and progress.
*
* @param context The context.
* @param qr The qr code text, dc_check_qr() must have returned DC_QR_BACKUP
* on this text.
* @return 0=failure, 1=success. Success only means the progress was started
* correctly, final success or failure is via the #DC_EVENT_IMEX_PROGRESS
* events.
*/
int dc_receive_backup (dc_context_t* context, const char* qr);
/**
* @class dc_accounts_t

View File

@@ -28,9 +28,10 @@ use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
use deltachat::contact::{Contact, ContactId, Origin};
use deltachat::context::Context;
use deltachat::ephemeral::Timer as EphemeralTimer;
use deltachat::imex::BackupProvider;
use deltachat::key::DcKey;
use deltachat::message::MsgId;
use deltachat::qr_code_generator::get_securejoin_qr_svg;
use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions};
use deltachat::stock_str::StockMessage;
use deltachat::stock_str::StockStrings;
@@ -4130,6 +4131,102 @@ pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
libc::free(s as *mut _)
}
pub type dc_backup_provider_t = BackupProvider;
#[no_mangle]
pub unsafe extern "C" fn dc_provide_backup(
context: *mut dc_context_t,
folder: *const libc::c_char,
) -> *mut dc_backup_provider_t {
if context.is_null() {
eprintln!("ignoring careless call to dc_send_backup()");
return ptr::null_mut();
}
let ctx = &*context;
let dir = as_path(folder);
block_on(async move {
BackupProvider::prepare(ctx, dir)
.await
.map(|provider| Box::into_raw(Box::new(provider)))
.log_err(ctx, "BackupProvider failed")
.unwrap_or(ptr::null_mut())
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_backup_provider_qr(
_context: *mut dc_context_t,
provider: *const dc_backup_provider_t,
) -> *mut libc::c_char {
let provider = &*provider;
deltachat::qr::format_backup(provider.qr())
.unwrap_or_default()
.strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_backup_provider_qr_svg(
context: *mut dc_context_t,
provider: *const dc_backup_provider_t,
) -> *mut libc::c_char {
if context.is_null() {
eprintln!("ignoring careless call to dc_send_backup()");
return ptr::null_mut();
}
let ctx = &*context;
let provider = &*provider;
block_on(async move {
generate_backup_qr(ctx, provider.qr())
.await
.unwrap_or_default()
.strdup()
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_backup_provider_done(
context: *mut dc_context_t,
provider: *mut dc_backup_provider_t,
) {
if context.is_null() {
eprintln!("ignoring careless call to dc_send_backup()");
return;
}
let ctx = &*context;
let provider = Box::from_raw(provider);
block_on(async move {
provider
.join()
.await
.log_err(ctx, "Failed to join provider")
.ok();
});
}
#[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_send_backup()");
return 0;
}
let ctx = &*context;
let qr_text = to_string_lossy(qr);
let qr = match block_on(qr::check_qr(ctx, &qr_text)).log_err(ctx, "Invalid QR code") {
Ok(qr) => qr,
Err(_) => return 0,
};
spawn(async move {
imex::get_backup(ctx, qr)
.await
.log_err(ctx, "Get backup failed")
.ok();
});
1
}
trait ResultExt<T, E> {
/// Like `log_err()`, but:
/// - returns the default value instead of an Err value.

View File

@@ -161,6 +161,19 @@ pub async fn check_qr(context: &Context, qr: &str) -> Result<Qr> {
Ok(qrcode)
}
/// Formats the text of the [`Qr::Backup`] variant.
///
/// This is the inverse of [`check_qr`] for that variant only.
///
/// TODO: Refactor this so all variants have a correct [`Display`] and transform `check_qr`
/// into [`FromStr`].
pub fn format_backup(qr: Qr) -> Result<String> {
match qr {
Qr::Backup { ticket } => Ok(format!("{DCBACKUP_SCHEME}{ticket}")),
_ => Err(anyhow!("Not a backup QR code")),
}
}
/// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH`
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH`
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR`