Remove dc_open call

A new context is now created by calling Context::new and therefore you
always have a valid context.  This is much more in Rust style and will
allow a lot of furture simplifications on the context itself.

The FFI layer has not yet been adjusted in this commit and thus will
fail.
This commit is contained in:
Floris Bruynooghe
2019-09-13 20:05:59 +02:00
committed by Floris Bruynooghe
parent e5699e8ba9
commit afc9a31080
11 changed files with 344 additions and 369 deletions

View File

@@ -34,7 +34,16 @@ use deltachat::*;
pub type dc_context_t = context::Context; pub type dc_context_t = context::Context;
pub type dc_callback_t = types::dc_callback_t; /// Callback function that should be given to dc_context_new().
///
/// @memberof Context
/// @param context The context object as returned by dc_context_new().
/// @param event one of the @ref DC_EVENT constants
/// @param data1 depends on the event parameter
/// @param data2 depends on the event parameter
/// @return return 0 unless stated otherwise in the event parameter documentation
pub type dc_callback_t =
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_context_new( pub unsafe extern "C" fn dc_context_new(

View File

@@ -447,14 +447,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
=============================================" ============================================="
), ),
}, },
"open" => {
ensure!(!arg1.is_empty(), "Argument <file> missing");
dc_close(context);
ensure!(dc_open(context, arg1, None), "Open failed");
}
"close" => {
dc_close(context);
}
"initiate-key-transfer" => { "initiate-key-transfer" => {
let setup_code = dc_initiate_key_transfer(context); let setup_code = dc_initiate_key_transfer(context);
if !setup_code.is_null() { if !setup_code.is_null() {

View File

@@ -15,8 +15,8 @@ extern crate rusqlite;
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::Path;
use std::process::Command; use std::process::Command;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
@@ -44,7 +44,7 @@ use self::cmdline::*;
// Event Handler // Event Handler
unsafe extern "C" fn receive_event( fn receive_event(
_context: &Context, _context: &Context,
event: Event, event: Event,
data1: uintptr_t, data1: uintptr_t,
@@ -387,15 +387,15 @@ impl Highlighter for DcHelper {
impl Helper for DcHelper {} impl Helper for DcHelper {}
fn main_0(args: Vec<String>) -> Result<(), failure::Error> { fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
let mut context = dc_context_new(Some(receive_event), ptr::null_mut(), Some("CLI".into())); if args.len() < 2 {
if args.len() == 2 {
if unsafe { !dc_open(&mut context, &args[1], None) } {
println!("Error: Cannot open {}.", args[0],);
}
} else if args.len() != 1 {
println!("Error: Bad arguments, expected [db-name]."); println!("Error: Bad arguments, expected [db-name].");
return Err(format_err!("No db-name specified"));
} }
let context = Context::new(
Box::new(receive_event),
"CLI".into(),
Path::new(&args[1]).to_path_buf(),
)?;
println!("Delta Chat Core is awaiting your commands."); println!("Delta Chat Core is awaiting your commands.");

View File

@@ -16,7 +16,7 @@ use deltachat::job::{
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs, perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
}; };
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize { fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
println!("[{:?}]", event); println!("[{:?}]", event);
match event { match event {
@@ -39,7 +39,11 @@ extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> us
fn main() { fn main() {
unsafe { unsafe {
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile);
let ctx =
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
let running = Arc::new(RwLock::new(true)); let running = Arc::new(RwLock::new(true));
let info = dc_get_info(&ctx); let info = dc_get_info(&ctx);
let info_s = CStr::from_ptr(info); let info_s = CStr::from_ptr(info);
@@ -73,13 +77,6 @@ fn main() {
} }
}); });
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("opening database {:?}", dbfile);
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
println!("configuring"); println!("configuring");
let args = std::env::args().collect::<Vec<String>>(); let args = std::env::args().collect::<Vec<String>>();
assert_eq!(args.len(), 2, "missing password"); assert_eq!(args.len(), 2, "missing password");
@@ -130,6 +127,5 @@ fn main() {
t2.join().unwrap(); t2.join().unwrap();
println!("closing"); println!("closing");
dc_close(&ctx);
} }
} }

View File

@@ -1,3 +1,6 @@
use std::ffi::OsString;
use std::path::PathBuf;
use std::ptr;
use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::sync::{Arc, Condvar, Mutex, RwLock};
use crate::chat::*; use crate::chat::*;
@@ -5,6 +8,7 @@ use crate::constants::*;
use crate::contact::*; use crate::contact::*;
use crate::dc_receive_imf::*; use crate::dc_receive_imf::*;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::*;
use crate::imap::*; use crate::imap::*;
use crate::job::*; use crate::job::*;
use crate::job_thread::JobThread; use crate::job_thread::JobThread;
@@ -17,9 +21,21 @@ use crate::smtp::*;
use crate::sql::Sql; use crate::sql::Sql;
use crate::types::*; use crate::types::*;
use crate::x::*; use crate::x::*;
use std::ptr;
use std::path::PathBuf; /// Callback function type for [Context]
///
/// # Parameters
///
/// * `context` - The context object as returned by [Context::new].
/// * `event` - One of the [Event] items.
/// * `data1` - Depends on the event parameter, see [Event].
/// * `data2` - Depends on the event parameter, see [Event].
///
/// # Returns
///
/// This callback must return 0 unless stated otherwise in the event
/// description at [Event].
pub type ContextCallback = dyn Fn(&Context, Event, uintptr_t, uintptr_t) -> uintptr_t;
pub struct Context { pub struct Context {
pub userdata: *mut libc::c_void, pub userdata: *mut libc::c_void,
@@ -34,7 +50,7 @@ pub struct Context {
pub smtp: Arc<Mutex<Smtp>>, pub smtp: Arc<Mutex<Smtp>>,
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>, pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
pub oauth2_critical: Arc<Mutex<()>>, pub oauth2_critical: Arc<Mutex<()>>,
pub cb: Option<dc_callback_t>, pub cb: Box<ContextCallback>,
pub os_name: Option<String>, pub os_name: Option<String>,
pub cmdline_sel_chat_id: Arc<RwLock<u32>>, pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
pub bob: Arc<RwLock<BobStatus>>, pub bob: Arc<RwLock<BobStatus>>,
@@ -54,75 +70,32 @@ pub struct RunningState {
} }
impl Context { impl Context {
pub fn has_dbfile(&self) -> bool { pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
self.get_dbfile().is_some() let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs");
let blobdir = dbfile.with_file_name(blob_fname);
if !blobdir.exists() {
std::fs::create_dir_all(&blobdir)?;
}
Context::with_blobdir(cb, os_name, dbfile, blobdir)
} }
pub fn has_blobdir(&self) -> bool { pub fn with_blobdir(
!self.get_blobdir().is_null() cb: Box<ContextCallback>,
} os_name: String,
dbfile: PathBuf,
pub fn get_dbfile(&self) -> Option<PathBuf> { blobdir: PathBuf,
(*self.dbfile.clone().read().unwrap()) ) -> Result<Context> {
.as_ref() ensure!(
.map(|x| x.clone()) blobdir.is_dir(),
} "Blobdir does not exist: {}",
blobdir.display()
pub fn get_blobdir(&self) -> *const libc::c_char { );
*self.blobdir.clone().read().unwrap() let blobdir_c = blobdir.to_c_string()?;
} let ctx = Context {
blobdir: Arc::new(RwLock::new(unsafe { dc_strdup(blobdir_c.as_ptr()) })),
pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t { dbfile: Arc::new(RwLock::new(Some(dbfile))),
if let Some(cb) = self.cb {
unsafe { cb(self, event, data1, data2) }
} else {
0
}
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
dc_close(&self);
}
}
}
impl Default for RunningState {
fn default() -> Self {
RunningState {
ongoing_running: false,
shall_stop_ongoing: true,
}
}
}
#[derive(Default)]
pub struct BobStatus {
pub expects: i32,
pub status: i32,
pub qr_scan: Option<Lot>,
}
#[derive(Default, Debug)]
pub struct SmtpState {
pub idle: bool,
pub suspended: bool,
pub doing_jobs: bool,
pub perform_jobs_needed: i32,
pub probe_network: bool,
}
// create/open/config/information
pub fn dc_context_new(
cb: Option<dc_callback_t>,
userdata: *mut libc::c_void,
os_name: Option<String>,
) -> Context {
Context {
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
dbfile: Arc::new(RwLock::new(None)),
inbox: Arc::new(RwLock::new({ inbox: Arc::new(RwLock::new({
Imap::new( Imap::new(
cb_get_config, cb_get_config,
@@ -131,9 +104,9 @@ pub fn dc_context_new(
cb_receive_imf, cb_receive_imf,
) )
})), })),
userdata, userdata: std::ptr::null_mut(),
cb, cb,
os_name, os_name: Some(os_name),
running_state: Arc::new(RwLock::new(Default::default())), running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(), sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())), smtp: Arc::new(Mutex::new(Smtp::new())),
@@ -165,7 +138,80 @@ pub fn dc_context_new(
probe_imap_network: Arc::new(RwLock::new(false)), probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)), perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
generating_key_mutex: Mutex::new(()), generating_key_mutex: Mutex::new(()),
};
if !ctx
.sql
.open(&ctx, &ctx.dbfile.read().unwrap().as_ref().unwrap(), 0)
{
return Err(format_err!("Failed opening sqlite database"));
} }
Ok(ctx)
}
pub fn has_dbfile(&self) -> bool {
self.get_dbfile().is_some()
}
pub fn has_blobdir(&self) -> bool {
!self.get_blobdir().is_null()
}
pub fn get_dbfile(&self) -> Option<PathBuf> {
(*self.dbfile.clone().read().unwrap())
.as_ref()
.map(|x| x.clone())
}
pub fn get_blobdir(&self) -> *const libc::c_char {
*self.blobdir.clone().read().unwrap()
}
pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t {
(*self.cb)(self, event, data1, data2)
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
info!(self, "disconnecting INBOX-watch",);
self.inbox.read().unwrap().disconnect(self);
info!(self, "disconnecting sentbox-thread",);
self.sentbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting mvbox-thread",);
self.mvbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting SMTP");
self.smtp.clone().lock().unwrap().disconnect();
self.sql.close(self);
let blobdir = self.blobdir.write().unwrap();
free(*blobdir as *mut libc::c_void);
}
}
}
impl Default for RunningState {
fn default() -> Self {
RunningState {
ongoing_running: false,
shall_stop_ongoing: true,
}
}
}
#[derive(Default)]
pub struct BobStatus {
pub expects: i32,
pub status: i32,
pub qr_scan: Option<Lot>,
}
#[derive(Default, Debug)]
pub struct SmtpState {
pub idle: bool,
pub suspended: bool,
pub doing_jobs: bool,
pub perform_jobs_needed: i32,
pub probe_network: bool,
} }
unsafe fn cb_receive_imf( unsafe fn cb_receive_imf(
@@ -251,70 +297,10 @@ fn cb_get_config(context: &Context, key: &str) -> Option<String> {
context.sql.get_config(context, key) context.sql.get_config(context, key)
} }
pub unsafe fn dc_close(context: &Context) {
info!(context, "disconnecting INBOX-watch",);
context.inbox.read().unwrap().disconnect(context);
info!(context, "disconnecting sentbox-thread",);
context
.sentbox_thread
.read()
.unwrap()
.imap
.disconnect(context);
info!(context, "disconnecting mvbox-thread",);
context
.mvbox_thread
.read()
.unwrap()
.imap
.disconnect(context);
info!(context, "disconnecting SMTP");
context.smtp.clone().lock().unwrap().disconnect();
context.sql.close(context);
let mut dbfile = context.dbfile.write().unwrap();
*dbfile = None;
let mut blobdir = context.blobdir.write().unwrap();
free(*blobdir as *mut libc::c_void);
*blobdir = ptr::null_mut();
}
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
context.sql.is_open() as libc::c_int
}
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void { pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
context.userdata as *mut _ context.userdata as *mut _
} }
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
let mut success = false;
if 0 != dc_is_open(context) {
return false;
}
*context.dbfile.write().unwrap() = Some(PathBuf::from(dbfile));
let blobdir = blobdir.unwrap_or_default();
if !blobdir.is_empty() {
let dir = dc_ensure_no_slash_safe(blobdir).strdup();
*context.blobdir.write().unwrap() = dir;
} else {
let dir = dbfile.to_string() + "-blobs";
dc_create_folder(context, &dir);
*context.blobdir.write().unwrap() = dir.strdup();
}
// Create/open sqlite database, this may already use the blobdir
let dbfile_path = std::path::Path::new(dbfile);
if context.sql.open(context, dbfile_path, 0) {
success = true
}
if !success {
dc_close(context);
}
success
}
pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char { pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
dc_strdup(*context.blobdir.clone().read().unwrap()) dc_strdup(*context.blobdir.clone().read().unwrap())
} }
@@ -607,19 +593,59 @@ pub fn do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
mod tests { mod tests {
use super::*; use super::*;
use crate::test_utils::*;
#[test] #[test]
fn no_crashes_on_context_deref() { fn test_wrong_db() {
let ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into())); let tmp = tempfile::tempdir().unwrap();
std::mem::drop(ctx); let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOs".into(), dbfile);
assert!(res.is_err());
} }
#[test] #[test]
fn test_context_double_close() { fn test_blobdir_exists() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let tmp = tempfile::tempdir().unwrap();
unsafe { let dbfile = tmp.path().join("db.sqlite");
dc_close(&ctx); Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
dc_close(&ctx); let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir());
} }
std::mem::drop(ctx);
#[test]
fn test_wrong_blogdir() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap();
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile);
assert!(res.is_err());
}
#[test]
fn test_sqlite_parent_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone();
Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
assert!(subdir.is_dir());
assert!(dbfile2.is_file());
}
#[test]
fn test_with_blobdir_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile, blobdir);
assert!(res.is_err());
}
#[test]
fn no_crashes_on_context_deref() {
let t = dummy_context();
std::mem::drop(t.ctx);
} }
} }

View File

@@ -1110,7 +1110,7 @@ mod tests {
#[test] #[test]
fn test_render_setup_file() { fn test_render_setup_file() {
let t = test_context(Some(logging_cb)); let t = test_context(Some(Box::new(logging_cb)));
configure_alice_keypair(&t.ctx); configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap(); let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
@@ -1128,14 +1128,9 @@ mod tests {
assert!(msg.contains("-----END PGP MESSAGE-----\n")); assert!(msg.contains("-----END PGP MESSAGE-----\n"));
} }
unsafe extern "C" fn ac_setup_msg_cb( fn ac_setup_msg_cb(ctx: &Context, evt: Event, d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
ctx: &Context,
evt: Event,
d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() { if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() {
"hello\r\nthere".strdup() as usize unsafe { "hello\r\nthere".strdup() as usize }
} else { } else {
logging_cb(ctx, evt, d1, d2) logging_cb(ctx, evt, d1, d2)
} }
@@ -1143,7 +1138,7 @@ mod tests {
#[test] #[test]
fn test_render_setup_file_newline_replace() { fn test_render_setup_file_newline_replace() {
let t = test_context(Some(ac_setup_msg_cb)); let t = test_context(Some(Box::new(ac_setup_msg_cb)));
configure_alice_keypair(&t.ctx); configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap(); let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
println!("{}", &msg); println!("{}", &msg);

View File

@@ -22,6 +22,8 @@ pub enum Error {
Image(image_meta::ImageError), Image(image_meta::ImageError),
#[fail(display = "{:?}", _0)] #[fail(display = "{:?}", _0)]
Utf8(std::str::Utf8Error), Utf8(std::str::Utf8Error),
#[fail(display = "{:?}", _0)]
CStringError(crate::dc_tools::CStringError),
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@@ -62,6 +64,12 @@ impl From<image_meta::ImageError> for Error {
} }
} }
impl From<crate::dc_tools::CStringError> for Error {
fn from(err: crate::dc_tools::CStringError) -> Error {
Error::CStringError(err)
}
}
#[macro_export] #[macro_export]
macro_rules! bail { macro_rules! bail {
($e:expr) => { ($e:expr) => {

View File

@@ -237,10 +237,8 @@ mod tests {
use super::*; use super::*;
use crate::test_utils::*; use crate::test_utils::*;
use std::ffi::CString;
use crate::constants::DC_CONTACT_ID_SELF; use crate::constants::DC_CONTACT_ID_SELF;
use crate::context::dc_context_new;
use crate::types::uintptr_t; use crate::types::uintptr_t;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
@@ -258,19 +256,18 @@ mod tests {
#[test] #[test]
fn test_stock_str() { fn test_stock_str() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages."); assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "No messages.");
} }
unsafe extern "C" fn test_stock_str_no_fallback_cb( fn test_stock_str_no_fallback_cb(
_ctx: &Context, _ctx: &Context,
evt: Event, evt: Event,
d1: uintptr_t, d1: uintptr_t,
_d2: uintptr_t, _d2: uintptr_t,
) -> uintptr_t { ) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() { if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() {
let tmp = CString::new("Hello there").unwrap(); unsafe { "Hello there".strdup() as usize }
dc_strdup(tmp.as_ptr()) as usize
} else { } else {
0 0
} }
@@ -278,16 +275,16 @@ mod tests {
#[test] #[test]
fn test_stock_str_no_fallback() { fn test_stock_str_no_fallback() {
let t = test_context(Some(test_stock_str_no_fallback_cb)); let t = test_context(Some(Box::new(test_stock_str_no_fallback_cb)));
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there"); assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there");
} }
#[test] #[test]
fn test_stock_string_repl_str() { fn test_stock_string_repl_str() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
// uses %1$s substitution // uses %1$s substitution
assert_eq!( assert_eq!(
ctx.stock_string_repl_str(StockMessage::Member, "42"), t.ctx.stock_string_repl_str(StockMessage::Member, "42"),
"42 member(s)" "42 member(s)"
); );
// We have no string using %1$d to test... // We have no string using %1$d to test...
@@ -295,36 +292,38 @@ mod tests {
#[test] #[test]
fn test_stock_string_repl_int() { fn test_stock_string_repl_int() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
assert_eq!( assert_eq!(
ctx.stock_string_repl_int(StockMessage::Member, 42), t.ctx.stock_string_repl_int(StockMessage::Member, 42),
"42 member(s)" "42 member(s)"
); );
} }
#[test] #[test]
fn test_stock_string_repl_str2() { fn test_stock_string_repl_str2() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
assert_eq!( assert_eq!(
ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"), t.ctx
.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
"Response from foo: bar" "Response from foo: bar"
); );
} }
#[test] #[test]
fn test_stock_system_msg_simple() { fn test_stock_system_msg_simple() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
assert_eq!( assert_eq!(
ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0), t.ctx
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
"Location streaming enabled." "Location streaming enabled."
) )
} }
#[test] #[test]
fn test_stock_system_msg_add_member_by_me() { fn test_stock_system_msg_add_member_by_me() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None); let t = dummy_context();
assert_eq!( assert_eq!(
ctx.stock_system_msg( t.ctx.stock_system_msg(
StockMessage::MsgAddMember, StockMessage::MsgAddMember,
"alice@example.com", "alice@example.com",
"", "",

View File

@@ -9,9 +9,8 @@ use tempfile::{tempdir, TempDir};
use crate::config::Config; use crate::config::Config;
use crate::constants::{Event, KeyType}; use crate::constants::{Event, KeyType};
use crate::context::{dc_context_new, dc_open, Context}; use crate::context::{Context, ContextCallback};
use crate::key; use crate::key;
use crate::types::dc_callback_t;
/// A Context and temporary directory. /// A Context and temporary directory.
/// ///
@@ -28,18 +27,15 @@ pub struct TestContext {
/// "db.sqlite" in the [TestContext.dir] directory. /// "db.sqlite" in the [TestContext.dir] directory.
/// ///
/// [Context]: crate::context::Context /// [Context]: crate::context::Context
pub fn test_context(cb: Option<dc_callback_t>) -> TestContext { pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
unsafe {
let mut ctx = dc_context_new(cb, std::ptr::null_mut(), None);
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
assert!( let cb: Box<ContextCallback> = match callback {
dc_open(&mut ctx, dbfile.to_str().unwrap(), None), Some(cb) => cb,
"Failed to open {}", None => Box::new(|_, _, _, _| 0),
dbfile.display(), };
); let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap();
TestContext { ctx: ctx, dir: dir } TestContext { ctx: ctx, dir: dir }
}
} }
/// Return a dummy [TestContext]. /// Return a dummy [TestContext].
@@ -51,13 +47,8 @@ pub fn dummy_context() -> TestContext {
test_context(None) test_context(None)
} }
pub unsafe extern "C" fn logging_cb( pub fn logging_cb(_ctx: &Context, evt: Event, _d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
_ctx: &Context, let to_str = unsafe { |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap() };
evt: Event,
_d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
match evt { match evt {
Event::INFO => println!("I: {}", to_str(d2)), Event::INFO => println!("I: {}", to_str(d2)),
Event::WARNING => println!("W: {}", to_str(d2)), Event::WARNING => println!("W: {}", to_str(d2)),

View File

@@ -1,21 +1,9 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use crate::constants::Event;
use crate::context::Context; use crate::context::Context;
pub use mmime::clist::*; pub use mmime::clist::*;
pub use rusqlite::ffi::*; pub use rusqlite::ffi::*;
/// Callback function that should be given to dc_context_new().
///
/// @memberof Context
/// @param context The context object as returned by dc_context_new().
/// @param event one of the @ref DC_EVENT constants
/// @param data1 depends on the event parameter
/// @param data2 depends on the event parameter
/// @return return 0 unless stated otherwise in the event parameter documentation
pub type dc_callback_t =
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
pub type dc_receive_imf_t = unsafe fn( pub type dc_receive_imf_t = unsafe fn(
_: &Context, _: &Context,
_: *const libc::c_char, _: *const libc::c_char,

View File

@@ -30,7 +30,6 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
as *const u8 as *const libc::c_char; as *const u8 as *const libc::c_char;
unsafe fn stress_functions(context: &Context) { unsafe fn stress_functions(context: &Context) {
if 0 != dc_is_open(context) {
if dc_file_exist(context, "$BLOBDIR/foobar") if dc_file_exist(context, "$BLOBDIR/foobar")
|| dc_file_exist(context, "$BLOBDIR/dada") || dc_file_exist(context, "$BLOBDIR/dada")
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada") || dc_file_exist(context, "$BLOBDIR/foobar.dadada")
@@ -126,7 +125,6 @@ unsafe fn stress_functions(context: &Context) {
assert!(dc_delete_file(context, as_path(fn0))); assert!(dc_delete_file(context, as_path(fn0)));
free(fn0 as *mut libc::c_void); free(fn0 as *mut libc::c_void);
free(fn1 as *mut libc::c_void); free(fn1 as *mut libc::c_void);
}
let res = context.get_config(config::Config::SysConfigKeys).unwrap(); let res = context.get_config(config::Config::SysConfigKeys).unwrap();
@@ -567,12 +565,7 @@ fn test_encryption_decryption() {
assert_eq!(plain, original_text); assert_eq!(plain, original_text);
} }
unsafe extern "C" fn cb( fn cb(_context: &Context, _event: Event, _data1: uintptr_t, _data2: uintptr_t) -> uintptr_t {
_context: &Context,
_event: Event,
_data1: uintptr_t,
_data2: uintptr_t,
) -> uintptr_t {
0 0
} }
@@ -582,21 +575,16 @@ struct TestContext {
dir: TempDir, dir: TempDir,
} }
unsafe fn create_test_context() -> TestContext { fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
assert!( let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap();
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"Failed to open {}",
dbfile.display()
);
TestContext { ctx: ctx, dir: dir } TestContext { ctx: ctx, dir: dir }
} }
#[test] #[test]
fn test_dc_get_oauth2_url() { fn test_dc_get_oauth2_url() {
let ctx = unsafe { create_test_context() }; let ctx = create_test_context();
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";
let redirect_uri = "chat.delta:/com.b44t.messenger"; let redirect_uri = "chat.delta:/com.b44t.messenger";
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri); let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
@@ -606,7 +594,7 @@ fn test_dc_get_oauth2_url() {
#[test] #[test]
fn test_dc_get_oauth2_addr() { fn test_dc_get_oauth2_addr() {
let ctx = unsafe { create_test_context() }; let ctx = create_test_context();
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";
let code = "fail"; let code = "fail";
let res = dc_get_oauth2_addr(&ctx.ctx, addr, code); let res = dc_get_oauth2_addr(&ctx.ctx, addr, code);
@@ -616,7 +604,7 @@ fn test_dc_get_oauth2_addr() {
#[test] #[test]
fn test_dc_get_oauth2_token() { fn test_dc_get_oauth2_token() {
let ctx = unsafe { create_test_context() }; let ctx = create_test_context();
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";
let code = "fail"; let code = "fail";
let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, 0); let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, 0);
@@ -634,7 +622,6 @@ fn test_stress_tests() {
#[test] #[test]
fn test_get_contacts() { fn test_get_contacts() {
unsafe {
let context = create_test_context(); let context = create_test_context();
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap(); let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
@@ -647,12 +634,10 @@ fn test_get_contacts() {
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap(); let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
}
} }
#[test] #[test]
fn test_chat() { fn test_chat() {
unsafe {
let context = create_test_context(); let context = create_test_context();
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap(); let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(contact1, 0); assert_ne!(contact1, 0);
@@ -666,18 +651,4 @@ fn test_chat() {
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap(); let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap();
assert_eq!(chat2.name, chat.name); assert_eq!(chat2.name, chat.name);
}
}
#[test]
fn test_wrong_db() {
unsafe {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
assert!(!res);
}
} }