async file io

This commit is contained in:
dignifiedquire
2020-03-14 16:26:15 +01:00
parent 6db253e1cc
commit 7140898db9
19 changed files with 340 additions and 252 deletions

View File

@@ -36,7 +36,7 @@ async fn main() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile); println!("creating database {:?}", dbfile);
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile) let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile.into())
.await .await
.expect("Failed to create context"); .expect("Failed to create context");
let running = Arc::new(RwLock::new(true)); let running = Arc::new(RwLock::new(true));

View File

@@ -2,9 +2,10 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt; use std::fmt;
use std::fs;
use std::io::Write; use async_std::path::{Path, PathBuf};
use std::path::{Path, PathBuf}; use async_std::prelude::*;
use async_std::{fs, io};
use self::image::GenericImageView; use self::image::GenericImageView;
use crate::constants::AVATAR_SIZE; use crate::constants::AVATAR_SIZE;
@@ -43,15 +44,16 @@ impl<'a> BlobObject<'a> {
/// [BlobError::WriteFailure] is used when the file could not /// [BlobError::WriteFailure] is used when the file could not
/// be written to. You can expect [BlobError.cause] to contain an /// be written to. You can expect [BlobError.cause] to contain an
/// underlying error. /// underlying error.
pub fn create( pub async fn create(
context: &'a Context, context: &'a Context,
suggested_name: impl AsRef<str>, suggested_name: impl AsRef<str>,
data: &[u8], data: &[u8],
) -> std::result::Result<BlobObject<'a>, BlobError> { ) -> std::result::Result<BlobObject<'a>, BlobError> {
let blobdir = context.get_blobdir(); let blobdir = context.get_blobdir();
let (stem, ext) = BlobObject::sanitise_name(suggested_name.as_ref()); let (stem, ext) = BlobObject::sanitise_name(suggested_name.as_ref());
let (name, mut file) = BlobObject::create_new_file(&blobdir, &stem, &ext)?; let (name, mut file) = BlobObject::create_new_file(&blobdir, &stem, &ext).await?;
file.write_all(data) file.write_all(data)
.await
.map_err(|err| BlobError::WriteFailure { .map_err(|err| BlobError::WriteFailure {
blobdir: blobdir.to_path_buf(), blobdir: blobdir.to_path_buf(),
blobname: name.clone(), blobname: name.clone(),
@@ -67,7 +69,11 @@ impl<'a> BlobObject<'a> {
} }
// Creates a new file, returning a tuple of the name and the handle. // Creates a new file, returning a tuple of the name and the handle.
fn create_new_file(dir: &Path, stem: &str, ext: &str) -> Result<(String, fs::File), BlobError> { async fn create_new_file(
dir: &Path,
stem: &str,
ext: &str,
) -> Result<(String, fs::File), BlobError> {
let max_attempt = 15; let max_attempt = 15;
let mut name = format!("{}{}", stem, ext); let mut name = format!("{}{}", stem, ext);
for attempt in 0..max_attempt { for attempt in 0..max_attempt {
@@ -76,6 +82,7 @@ impl<'a> BlobObject<'a> {
.create_new(true) .create_new(true)
.write(true) .write(true)
.open(&path) .open(&path)
.await
{ {
Ok(file) => return Ok((name, file)), Ok(file) => return Ok((name, file)),
Err(err) => { Err(err) => {
@@ -113,34 +120,38 @@ impl<'a> BlobObject<'a> {
/// In addition to the errors in [BlobObject::create] the /// In addition to the errors in [BlobObject::create] the
/// [BlobError::CopyFailure] is used when the data can not be /// [BlobError::CopyFailure] is used when the data can not be
/// copied. /// copied.
pub fn create_and_copy( pub async fn create_and_copy(
context: &'a Context, context: &'a Context,
src: impl AsRef<Path>, src: impl AsRef<Path>,
) -> std::result::Result<BlobObject<'a>, BlobError> { ) -> std::result::Result<BlobObject<'a>, BlobError> {
let mut src_file = fs::File::open(src.as_ref()).map_err(|err| BlobError::CopyFailure { let mut src_file =
blobdir: context.get_blobdir().to_path_buf(), fs::File::open(src.as_ref())
blobname: String::from(""), .await
src: src.as_ref().to_path_buf(), .map_err(|err| BlobError::CopyFailure {
cause: err, blobdir: context.get_blobdir().to_path_buf(),
backtrace: failure::Backtrace::new(), blobname: String::from(""),
})?; src: src.as_ref().to_path_buf(),
cause: err,
backtrace: failure::Backtrace::new(),
})?;
let (stem, ext) = BlobObject::sanitise_name(&src.as_ref().to_string_lossy()); let (stem, ext) = BlobObject::sanitise_name(&src.as_ref().to_string_lossy());
let (name, mut dst_file) = BlobObject::create_new_file(context.get_blobdir(), &stem, &ext)?; let (name, mut dst_file) =
BlobObject::create_new_file(context.get_blobdir(), &stem, &ext).await?;
let name_for_err = name.clone(); let name_for_err = name.clone();
std::io::copy(&mut src_file, &mut dst_file).map_err(|err| { if let Err(err) = io::copy(&mut src_file, &mut dst_file).await {
{ {
// Attempt to remove the failed file, swallow errors resulting from that. // Attempt to remove the failed file, swallow errors resulting from that.
let path = context.get_blobdir().join(&name_for_err); let path = context.get_blobdir().join(&name_for_err);
fs::remove_file(path).ok(); fs::remove_file(path).await.ok();
} }
BlobError::CopyFailure { return Err(BlobError::CopyFailure {
blobdir: context.get_blobdir().to_path_buf(), blobdir: context.get_blobdir().to_path_buf(),
blobname: name_for_err, blobname: name_for_err,
src: src.as_ref().to_path_buf(), src: src.as_ref().to_path_buf(),
cause: err, cause: err,
backtrace: failure::Backtrace::new(), backtrace: failure::Backtrace::new(),
} });
})?; }
let blob = BlobObject { let blob = BlobObject {
blobdir: context.get_blobdir(), blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name), name: format!("$BLOBDIR/{}", name),
@@ -163,14 +174,14 @@ impl<'a> BlobObject<'a> {
/// This merely delegates to the [BlobObject::create_and_copy] and /// This merely delegates to the [BlobObject::create_and_copy] and
/// the [BlobObject::from_path] methods. See those for possible /// the [BlobObject::from_path] methods. See those for possible
/// errors. /// errors.
pub fn new_from_path( pub async fn new_from_path(
context: &Context, context: &Context,
src: impl AsRef<Path>, src: impl AsRef<Path>,
) -> std::result::Result<BlobObject, BlobError> { ) -> std::result::Result<BlobObject<'_>, BlobError> {
if src.as_ref().starts_with(context.get_blobdir()) { if src.as_ref().starts_with(context.get_blobdir()) {
BlobObject::from_path(context, src) BlobObject::from_path(context, src)
} else { } else {
BlobObject::create_and_copy(context, src) BlobObject::create_and_copy(context, src).await
} }
} }
@@ -489,9 +500,9 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn test_create() { async fn test_create() {
let t = dummy_context().await; let t = dummy_context().await;
let blob = BlobObject::create(&t.ctx, "foo", b"hello").unwrap(); let blob = BlobObject::create(&t.ctx, "foo", b"hello").await.unwrap();
let fname = t.ctx.get_blobdir().join("foo"); let fname = t.ctx.get_blobdir().join("foo");
let data = fs::read(fname).unwrap(); let data = fs::read(fname).await.unwrap();
assert_eq!(data, b"hello"); assert_eq!(data, b"hello");
assert_eq!(blob.as_name(), "$BLOBDIR/foo"); assert_eq!(blob.as_name(), "$BLOBDIR/foo");
assert_eq!(blob.to_abs_path(), t.ctx.get_blobdir().join("foo")); assert_eq!(blob.to_abs_path(), t.ctx.get_blobdir().join("foo"));
@@ -500,44 +511,57 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn test_lowercase_ext() { async fn test_lowercase_ext() {
let t = dummy_context().await; let t = dummy_context().await;
let blob = BlobObject::create(&t.ctx, "foo.TXT", b"hello").unwrap(); let blob = BlobObject::create(&t.ctx, "foo.TXT", b"hello")
.await
.unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt"); assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt");
} }
#[async_std::test] #[async_std::test]
async fn test_as_file_name() { async fn test_as_file_name() {
let t = dummy_context().await; let t = dummy_context().await;
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello")
.await
.unwrap();
assert_eq!(blob.as_file_name(), "foo.txt"); assert_eq!(blob.as_file_name(), "foo.txt");
} }
#[async_std::test] #[async_std::test]
async fn test_as_rel_path() { async fn test_as_rel_path() {
let t = dummy_context().await; let t = dummy_context().await;
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello")
.await
.unwrap();
assert_eq!(blob.as_rel_path(), Path::new("foo.txt")); assert_eq!(blob.as_rel_path(), Path::new("foo.txt"));
} }
#[async_std::test] #[async_std::test]
async fn test_suffix() { async fn test_suffix() {
let t = dummy_context().await; let t = dummy_context().await;
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello")
.await
.unwrap();
assert_eq!(blob.suffix(), Some("txt")); assert_eq!(blob.suffix(), Some("txt"));
let blob = BlobObject::create(&t.ctx, "bar", b"world").unwrap(); let blob = BlobObject::create(&t.ctx, "bar", b"world").await.unwrap();
assert_eq!(blob.suffix(), None); assert_eq!(blob.suffix(), None);
} }
#[async_std::test] #[async_std::test]
async fn test_create_dup() { async fn test_create_dup() {
let t = dummy_context().await; let t = dummy_context().await;
BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); BlobObject::create(&t.ctx, "foo.txt", b"hello")
.await
.unwrap();
let foo_path = t.ctx.get_blobdir().join("foo.txt"); let foo_path = t.ctx.get_blobdir().join("foo.txt");
assert!(foo_path.exists()); assert!(foo_path.exists().await);
BlobObject::create(&t.ctx, "foo.txt", b"world").unwrap(); BlobObject::create(&t.ctx, "foo.txt", b"world")
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() { .await
.unwrap();
let mut dir = fs::read_dir(t.ctx.get_blobdir()).await.unwrap();
while let Some(dirent) = dir.next().await {
let fname = dirent.unwrap().file_name(); let fname = dirent.unwrap().file_name();
if fname == foo_path.file_name().unwrap() { if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).unwrap(), b"hello"); assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
} else { } else {
let name = fname.to_str().unwrap(); let name = fname.to_str().unwrap();
assert!(name.starts_with("foo")); assert!(name.starts_with("foo"));
@@ -549,14 +573,19 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn test_double_ext_preserved() { async fn test_double_ext_preserved() {
let t = dummy_context().await; let t = dummy_context().await;
BlobObject::create(&t.ctx, "foo.tar.gz", b"hello").unwrap(); BlobObject::create(&t.ctx, "foo.tar.gz", b"hello")
.await
.unwrap();
let foo_path = t.ctx.get_blobdir().join("foo.tar.gz"); let foo_path = t.ctx.get_blobdir().join("foo.tar.gz");
assert!(foo_path.exists()); assert!(foo_path.exists().await);
BlobObject::create(&t.ctx, "foo.tar.gz", b"world").unwrap(); BlobObject::create(&t.ctx, "foo.tar.gz", b"world")
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() { .await
.unwrap();
let mut dir = fs::read_dir(t.ctx.get_blobdir()).await.unwrap();
while let Some(dirent) = dir.next().await {
let fname = dirent.unwrap().file_name(); let fname = dirent.unwrap().file_name();
if fname == foo_path.file_name().unwrap() { if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).unwrap(), b"hello"); assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
} else { } else {
let name = fname.to_str().unwrap(); let name = fname.to_str().unwrap();
println!("{}", name); println!("{}", name);
@@ -570,7 +599,7 @@ mod tests {
async fn test_create_long_names() { async fn test_create_long_names() {
let t = dummy_context().await; let t = dummy_context().await;
let s = "1".repeat(150); let s = "1".repeat(150);
let blob = BlobObject::create(&t.ctx, &s, b"data").unwrap(); let blob = BlobObject::create(&t.ctx, &s, b"data").await.unwrap();
let blobname = blob.as_name().split('/').last().unwrap(); let blobname = blob.as_name().split('/').last().unwrap();
assert!(blobname.len() < 128); assert!(blobname.len() < 128);
} }
@@ -579,16 +608,16 @@ mod tests {
async fn test_create_and_copy() { async fn test_create_and_copy() {
let t = dummy_context().await; let t = dummy_context().await;
let src = t.dir.path().join("src"); let src = t.dir.path().join("src");
fs::write(&src, b"boo").unwrap(); fs::write(&src, b"boo").await.unwrap();
let blob = BlobObject::create_and_copy(&t.ctx, &src).unwrap(); let blob = BlobObject::create_and_copy(&t.ctx, &src).await.unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/src"); assert_eq!(blob.as_name(), "$BLOBDIR/src");
let data = fs::read(blob.to_abs_path()).unwrap(); let data = fs::read(blob.to_abs_path()).await.unwrap();
assert_eq!(data, b"boo"); assert_eq!(data, b"boo");
let whoops = t.dir.path().join("whoops"); let whoops = t.dir.path().join("whoops");
assert!(BlobObject::create_and_copy(&t.ctx, &whoops).is_err()); assert!(BlobObject::create_and_copy(&t.ctx, &whoops).await.is_err());
let whoops = t.ctx.get_blobdir().join("whoops"); let whoops = t.ctx.get_blobdir().join("whoops");
assert!(!whoops.exists()); assert!(!whoops.exists().await);
} }
#[async_std::test] #[async_std::test]
@@ -596,25 +625,25 @@ mod tests {
let t = dummy_context().await; let t = dummy_context().await;
let src_ext = t.dir.path().join("external"); let src_ext = t.dir.path().join("external");
fs::write(&src_ext, b"boo").unwrap(); fs::write(&src_ext, b"boo").await.unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_ext).unwrap(); let blob = BlobObject::new_from_path(&t.ctx, &src_ext).await.unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/external"); assert_eq!(blob.as_name(), "$BLOBDIR/external");
let data = fs::read(blob.to_abs_path()).unwrap(); let data = fs::read(blob.to_abs_path()).await.unwrap();
assert_eq!(data, b"boo"); assert_eq!(data, b"boo");
let src_int = t.ctx.get_blobdir().join("internal"); let src_int = t.ctx.get_blobdir().join("internal");
fs::write(&src_int, b"boo").unwrap(); fs::write(&src_int, b"boo").await.unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_int).unwrap(); let blob = BlobObject::new_from_path(&t.ctx, &src_int).await.unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/internal"); assert_eq!(blob.as_name(), "$BLOBDIR/internal");
let data = fs::read(blob.to_abs_path()).unwrap(); let data = fs::read(blob.to_abs_path()).await.unwrap();
assert_eq!(data, b"boo"); assert_eq!(data, b"boo");
} }
#[async_std::test] #[async_std::test]
async fn test_create_from_name_long() { async fn test_create_from_name_long() {
let t = dummy_context().await; let t = dummy_context().await;
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html"); let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
fs::write(&src_ext, b"boo").unwrap(); fs::write(&src_ext, b"boo").await.unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_ext).unwrap(); let blob = BlobObject::new_from_path(&t.ctx, &src_ext).await.unwrap();
assert_eq!( assert_eq!(
blob.as_name(), blob.as_name(),
"$BLOBDIR/autocrypt-setup-message-4137848473.html" "$BLOBDIR/autocrypt-setup-message-4137848473.html"

View File

@@ -1,9 +1,9 @@
//! # Chat module //! # Chat module
use std::convert::TryFrom; use std::convert::TryFrom;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use async_std::path::{Path, PathBuf};
use itertools::Itertools; use itertools::Itertools;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -326,7 +326,8 @@ impl ChatId {
_ => { _ => {
let blob = msg let blob = msg
.param .param
.get_blob(Param::File, context, !msg.is_increation())? .get_blob(Param::File, context, !msg.is_increation())
.await?
.ok_or_else(|| format_err!("No file stored in params"))?; .ok_or_else(|| format_err!("No file stored in params"))?;
msg.param.set(Param::File, blob.as_name()); msg.param.set(Param::File, blob.as_name());
} }
@@ -697,7 +698,8 @@ impl Chat {
profile_image: self profile_image: self
.get_profile_image(context) .get_profile_image(context)
.await .await
.unwrap_or_else(PathBuf::new), .map(Into::into)
.unwrap_or_else(std::path::PathBuf::new),
subtitle: self.get_subtitle(context).await, subtitle: self.get_subtitle(context).await,
draft, draft,
is_muted: self.is_muted(), is_muted: self.is_muted(),
@@ -1046,7 +1048,7 @@ pub struct ChatInfo {
/// ///
/// If there is no profile image set this will be an empty string /// If there is no profile image set this will be an empty string
/// currently. /// currently.
pub profile_image: PathBuf, pub profile_image: std::path::PathBuf,
/// Subtitle for the chat. /// Subtitle for the chat.
pub subtitle: String, pub subtitle: String,
@@ -1158,7 +1160,7 @@ pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<(),
// if there is no saved-messages chat, there is nothing to update. this is no error. // if there is no saved-messages chat, there is nothing to update. this is no error.
if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_SELF).await { if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_SELF).await {
let icon = include_bytes!("../assets/icon-saved-messages.png"); let icon = include_bytes!("../assets/icon-saved-messages.png");
let blob = BlobObject::create(context, "icon-saved-messages.png".to_string(), icon)?; let blob = BlobObject::create(context, "icon-saved-messages.png".to_string(), icon).await?;
let icon = blob.as_name().to_string(); let icon = blob.as_name().to_string();
let mut chat = Chat::load_from_db(context, chat_id).await?; let mut chat = Chat::load_from_db(context, chat_id).await?;
@@ -1172,7 +1174,7 @@ pub(crate) async fn update_device_icon(context: &Context) -> Result<(), Error> {
// if there is no device-chat, there is nothing to update. this is no error. // if there is no device-chat, there is nothing to update. this is no error.
if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE).await { if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE).await {
let icon = include_bytes!("../assets/icon-device.png"); let icon = include_bytes!("../assets/icon-device.png");
let blob = BlobObject::create(context, "icon-device.png".to_string(), icon)?; let blob = BlobObject::create(context, "icon-device.png".to_string(), icon).await?;
let icon = blob.as_name().to_string(); let icon = blob.as_name().to_string();
let mut chat = Chat::load_from_db(context, chat_id).await?; let mut chat = Chat::load_from_db(context, chat_id).await?;
@@ -1335,13 +1337,14 @@ pub(crate) fn msgtype_has_file(msgtype: Viewtype) -> bool {
} }
} }
fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<(), Error> { async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<(), Error> {
if msg.viewtype == Viewtype::Text { if msg.viewtype == Viewtype::Text {
// the caller should check if the message text is empty // the caller should check if the message text is empty
} else if msgtype_has_file(msg.viewtype) { } else if msgtype_has_file(msg.viewtype) {
let blob = msg let blob = msg
.param .param
.get_blob(Param::File, context, !msg.is_increation())? .get_blob(Param::File, context, !msg.is_increation())
.await?
.ok_or_else(|| { .ok_or_else(|| {
format_err!("Attachment missing for message of type #{}", msg.viewtype) format_err!("Attachment missing for message of type #{}", msg.viewtype)
})?; })?;
@@ -1383,7 +1386,7 @@ async fn prepare_msg_common(
msg: &mut Message, msg: &mut Message,
) -> Result<MsgId, Error> { ) -> Result<MsgId, Error> {
msg.id = MsgId::new_unset(); msg.id = MsgId::new_unset();
prepare_msg_blob(context, msg)?; prepare_msg_blob(context, msg).await?;
chat_id.unarchive(context).await?; chat_id.unarchive(context).await?;
let mut chat = Chat::load_from_db(context, chat_id).await?; let mut chat = Chat::load_from_db(context, chat_id).await?;
@@ -2364,14 +2367,15 @@ pub async fn set_chat_profile_image(
.await, .await,
); );
} else { } else {
let image_blob = BlobObject::from_path(context, Path::new(new_image.as_ref())).or_else( let image_blob = match BlobObject::from_path(context, Path::new(new_image.as_ref())) {
|err| match err { Ok(blob) => Ok(blob),
Err(err) => match err {
BlobError::WrongBlobdir { .. } => { BlobError::WrongBlobdir { .. } => {
BlobObject::create_and_copy(context, Path::new(new_image.as_ref())) BlobObject::create_and_copy(context, Path::new(new_image.as_ref())).await
} }
_ => Err(err), _ => Err(err),
}, },
)?; }?;
image_blob.recode_to_avatar_size(context)?; image_blob.recode_to_avatar_size(context)?;
chat.param.set(Param::ProfileImage, image_blob.as_name()); chat.param.set(Param::ProfileImage, image_blob.as_name());
msg.param.set(Param::Arg, image_blob.as_name()); msg.param.set(Param::Arg, image_blob.as_name());
@@ -2560,7 +2564,7 @@ pub async fn add_device_msg(
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device"); let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
msg.try_calc_and_set_dimensions(context).await.ok(); msg.try_calc_and_set_dimensions(context).await.ok();
prepare_msg_blob(context, msg)?; prepare_msg_blob(context, msg).await?;
chat_id.unarchive(context).await?; chat_id.unarchive(context).await?;
context.sql.execute( context.sql.execute(

View File

@@ -141,7 +141,7 @@ impl Context {
.await?; .await?;
match value { match value {
Some(value) => { Some(value) => {
let blob = BlobObject::new_from_path(&self, value)?; let blob = BlobObject::new_from_path(&self, value).await?;
blob.recode_to_avatar_size(self)?; blob.recode_to_avatar_size(self)?;
self.sql self.sql
.set_raw_config(self, key, Some(blob.as_name())) .set_raw_config(self, key, Some(blob.as_name()))
@@ -231,12 +231,12 @@ mod tests {
.write_all(avatar_bytes) .write_all(avatar_bytes)
.unwrap(); .unwrap();
let avatar_blob = t.ctx.get_blobdir().join("avatar.jpg"); let avatar_blob = t.ctx.get_blobdir().join("avatar.jpg");
assert!(!avatar_blob.exists()); assert!(!avatar_blob.exists().await);
t.ctx t.ctx
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap())) .set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))
.await .await
.unwrap(); .unwrap();
assert!(avatar_blob.exists()); assert!(avatar_blob.exists().await);
assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64); assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64);
let avatar_cfg = t.ctx.get_config(Config::Selfavatar).await; let avatar_cfg = t.ctx.get_config(Config::Selfavatar).await;
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string())); assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));

View File

@@ -1,7 +1,6 @@
//! Contacts module //! Contacts module
use std::path::PathBuf; use async_std::path::PathBuf;
use deltachat_derive::*; use deltachat_derive::*;
use itertools::Itertools; use itertools::Itertools;
use rusqlite; use rusqlite;

View File

@@ -2,9 +2,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use async_std::path::{Path, PathBuf};
use async_std::sync::{Arc, Mutex, RwLock}; use async_std::sync::{Arc, Mutex, RwLock};
use crate::chat::*; use crate::chat::*;
@@ -94,7 +94,7 @@ impl Context {
blob_fname.push(dbfile.file_name().unwrap_or_default()); blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs"); blob_fname.push("-blobs");
let blobdir = dbfile.with_file_name(blob_fname); let blobdir = dbfile.with_file_name(blob_fname);
if !blobdir.exists() { if !blobdir.exists().await {
std::fs::create_dir_all(&blobdir)?; std::fs::create_dir_all(&blobdir)?;
} }
Context::with_blobdir(cb, os_name, dbfile, blobdir).await Context::with_blobdir(cb, os_name, dbfile, blobdir).await
@@ -107,7 +107,7 @@ impl Context {
blobdir: PathBuf, blobdir: PathBuf,
) -> Result<Context> { ) -> Result<Context> {
ensure!( ensure!(
blobdir.is_dir(), blobdir.is_dir().await,
"Blobdir does not exist: {}", "Blobdir does not exist: {}",
blobdir.display() blobdir.display()
); );
@@ -518,7 +518,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap(); std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile).await; let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile.into()).await;
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -533,7 +533,7 @@ mod tests {
async fn test_blobdir_exists() { async fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile) Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into())
.await .await
.unwrap(); .unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
@@ -546,7 +546,7 @@ mod tests {
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap(); std::fs::write(&blobdir, b"123").unwrap();
let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).await; let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into()).await;
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -556,7 +556,7 @@ mod tests {
let subdir = tmp.path().join("subdir"); let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite"); let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone(); let dbfile2 = dbfile.clone();
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile) Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into())
.await .await
.unwrap(); .unwrap();
assert!(subdir.is_dir()); assert!(subdir.is_dir());
@@ -568,8 +568,13 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new(); let blobdir = PathBuf::new();
let res = let res = Context::with_blobdir(
Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir).await; Box::new(|_, _| ()),
"FakeOS".into(),
dbfile.into(),
blobdir.into(),
)
.await;
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -578,8 +583,13 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs"); let blobdir = tmp.path().join("blobs");
let res = let res = Context::with_blobdir(
Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir).await; Box::new(|_, _| ()),
"FakeOS".into(),
dbfile.into(),
blobdir.into(),
)
.await;
assert!(res.is_err()); assert!(res.is_err());
} }

View File

@@ -3,11 +3,12 @@
use core::cmp::{max, min}; use core::cmp::{max, min};
use std::borrow::Cow; use std::borrow::Cow;
use std::path::{Path, PathBuf}; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::SystemTime; use std::time::SystemTime;
use std::{fmt, fs};
use async_std::path::{Path, PathBuf};
use async_std::{fs, io};
use chrono::{Local, TimeZone}; use chrono::{Local, TimeZone};
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
@@ -240,11 +241,8 @@ pub fn dc_get_filemeta(buf: &[u8]) -> Result<(u32, u32), Error> {
/// ///
/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path. /// If `path` starts with "$BLOBDIR", replaces it with the blobdir path.
/// Otherwise, returns path as is. /// Otherwise, returns path as is.
pub(crate) fn dc_get_abs_path<P: AsRef<std::path::Path>>( pub(crate) fn dc_get_abs_path<P: AsRef<Path>>(context: &Context, path: P) -> PathBuf {
context: &Context, let p: &Path = path.as_ref();
path: P,
) -> std::path::PathBuf {
let p: &std::path::Path = path.as_ref();
if let Ok(p) = p.strip_prefix("$BLOBDIR") { if let Ok(p) = p.strip_prefix("$BLOBDIR") {
context.get_blobdir().join(p) context.get_blobdir().join(p)
} else { } else {
@@ -252,20 +250,20 @@ pub(crate) fn dc_get_abs_path<P: AsRef<std::path::Path>>(
} }
} }
pub(crate) fn dc_get_filebytes(context: &Context, path: impl AsRef<std::path::Path>) -> u64 { pub(crate) async fn dc_get_filebytes(context: &Context, path: impl AsRef<Path>) -> u64 {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
match fs::metadata(&path_abs) { match fs::metadata(&path_abs).await {
Ok(meta) => meta.len() as u64, Ok(meta) => meta.len() as u64,
Err(_err) => 0, Err(_err) => 0,
} }
} }
pub(crate) fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path>) -> bool { pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) -> bool {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
if !path_abs.exists() { if !path_abs.exists().await {
return false; return false;
} }
if !path_abs.is_file() { if !path_abs.is_file().await {
warn!( warn!(
context, context,
"refusing to delete non-file \"{}\".", "refusing to delete non-file \"{}\".",
@@ -275,7 +273,7 @@ pub(crate) fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path
} }
let dpath = format!("{}", path.as_ref().to_string_lossy()); let dpath = format!("{}", path.as_ref().to_string_lossy());
match fs::remove_file(path_abs) { match fs::remove_file(path_abs).await {
Ok(_) => { Ok(_) => {
context.call_cb(Event::DeletedBlobFile(dpath)); context.call_cb(Event::DeletedBlobFile(dpath));
true true
@@ -287,13 +285,13 @@ pub(crate) fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path
} }
} }
pub(crate) fn dc_copy_file( pub(crate) async fn dc_copy_file(
context: &Context, context: &Context,
src_path: impl AsRef<std::path::Path>, src_path: impl AsRef<Path>,
dest_path: impl AsRef<std::path::Path>, dest_path: impl AsRef<Path>,
) -> bool { ) -> bool {
let src_abs = dc_get_abs_path(context, &src_path); let src_abs = dc_get_abs_path(context, &src_path);
let mut src_file = match fs::File::open(&src_abs) { let mut src_file = match fs::File::open(&src_abs).await {
Ok(file) => file, Ok(file) => file,
Err(err) => { Err(err) => {
warn!( warn!(
@@ -311,6 +309,7 @@ pub(crate) fn dc_copy_file(
.create_new(true) .create_new(true)
.write(true) .write(true)
.open(&dest_abs) .open(&dest_abs)
.await
{ {
Ok(file) => file, Ok(file) => file,
Err(err) => { Err(err) => {
@@ -324,7 +323,7 @@ pub(crate) fn dc_copy_file(
} }
}; };
match std::io::copy(&mut src_file, &mut dest_file) { match io::copy(&mut src_file, &mut dest_file).await {
Ok(_) => true, Ok(_) => true,
Err(err) => { Err(err) => {
error!( error!(
@@ -336,20 +335,20 @@ pub(crate) fn dc_copy_file(
); );
{ {
// Attempt to remove the failed file, swallow errors resulting from that. // Attempt to remove the failed file, swallow errors resulting from that.
fs::remove_file(dest_abs).ok(); fs::remove_file(dest_abs).await.ok();
} }
false false
} }
} }
} }
pub(crate) fn dc_create_folder( pub(crate) async fn dc_create_folder(
context: &Context, context: &Context,
path: impl AsRef<std::path::Path>, path: impl AsRef<Path>,
) -> Result<(), std::io::Error> { ) -> Result<(), io::Error> {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
if !path_abs.exists() { if !path_abs.exists().await {
match fs::create_dir_all(path_abs) { match fs::create_dir_all(path_abs).await {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => { Err(err) => {
warn!( warn!(
@@ -367,13 +366,13 @@ pub(crate) fn dc_create_folder(
} }
/// Write a the given content to provied file path. /// Write a the given content to provied file path.
pub(crate) fn dc_write_file( pub(crate) async fn dc_write_file(
context: &Context, context: &Context,
path: impl AsRef<Path>, path: impl AsRef<Path>,
buf: &[u8], buf: &[u8],
) -> Result<(), std::io::Error> { ) -> Result<(), io::Error> {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
fs::write(&path_abs, buf).map_err(|err| { fs::write(&path_abs, buf).await.map_err(|err| {
warn!( warn!(
context, context,
"Cannot write {} bytes to \"{}\": {}", "Cannot write {} bytes to \"{}\": {}",
@@ -385,13 +384,10 @@ pub(crate) fn dc_write_file(
}) })
} }
pub fn dc_read_file<P: AsRef<std::path::Path>>( pub async fn dc_read_file<P: AsRef<Path>>(context: &Context, path: P) -> Result<Vec<u8>, Error> {
context: &Context,
path: P,
) -> Result<Vec<u8>, Error> {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
match fs::read(&path_abs) { match fs::read(&path_abs).await {
Ok(bytes) => Ok(bytes), Ok(bytes) => Ok(bytes),
Err(err) => { Err(err) => {
warn!( warn!(
@@ -405,13 +401,31 @@ pub fn dc_read_file<P: AsRef<std::path::Path>>(
} }
} }
pub fn dc_open_file<P: AsRef<std::path::Path>>( pub async fn dc_open_file<P: AsRef<Path>>(context: &Context, path: P) -> Result<fs::File, Error> {
let path_abs = dc_get_abs_path(context, &path);
match fs::File::open(&path_abs).await {
Ok(bytes) => Ok(bytes),
Err(err) => {
warn!(
context,
"Cannot read \"{}\" or file is empty: {}",
path.as_ref().display(),
err
);
Err(err.into())
}
}
}
pub fn dc_open_file_std<P: AsRef<std::path::Path>>(
context: &Context, context: &Context,
path: P, path: P,
) -> Result<std::fs::File, Error> { ) -> Result<std::fs::File, Error> {
let path_abs = dc_get_abs_path(context, &path); let p: PathBuf = path.as_ref().into();
let path_abs = dc_get_abs_path(context, p);
match fs::File::open(&path_abs) { match std::fs::File::open(&path_abs) {
Ok(bytes) => Ok(bytes), Ok(bytes) => Ok(bytes),
Err(err) => { Err(err) => {
warn!( warn!(
@@ -425,7 +439,7 @@ pub fn dc_open_file<P: AsRef<std::path::Path>>(
} }
} }
pub(crate) fn dc_get_next_backup_path( pub(crate) async fn dc_get_next_backup_path(
folder: impl AsRef<Path>, folder: impl AsRef<Path>,
backup_time: i64, backup_time: i64,
) -> Result<PathBuf, Error> { ) -> Result<PathBuf, Error> {
@@ -438,7 +452,7 @@ pub(crate) fn dc_get_next_backup_path(
for i in 0..64 { for i in 0..64 {
let mut path = folder.clone(); let mut path = folder.clone();
path.push(format!("{}-{}.bak", stem, i)); path.push(format!("{}-{}.bak", stem, i));
if !path.exists() { if !path.exists().await {
return Ok(path); return Ok(path);
} }
} }
@@ -715,27 +729,31 @@ mod tests {
async fn test_file_handling() { async fn test_file_handling() {
let t = dummy_context().await; let t = dummy_context().await;
let context = &t.ctx; let context = &t.ctx;
let dc_file_exist = |ctx: &Context, fname: &str| { macro_rules! dc_file_exist {
ctx.get_blobdir() ($ctx:expr, $fname:expr) => {
.join(Path::new(fname).file_name().unwrap()) $ctx.get_blobdir()
.exists() .join(Path::new($fname).file_name().unwrap())
}; .exists()
};
assert!(!dc_delete_file(context, "$BLOBDIR/lkqwjelqkwlje"));
if dc_file_exist(context, "$BLOBDIR/foobar")
|| dc_file_exist(context, "$BLOBDIR/dada")
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
{
dc_delete_file(context, "$BLOBDIR/foobar");
dc_delete_file(context, "$BLOBDIR/dada");
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
dc_delete_file(context, "$BLOBDIR/foobar-folder");
} }
assert!(dc_write_file(context, "$BLOBDIR/foobar", b"content").is_ok());
assert!(dc_file_exist(context, "$BLOBDIR/foobar",)); assert!(!dc_delete_file(context, "$BLOBDIR/lkqwjelqkwlje").await);
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx")); if dc_file_exist!(context, "$BLOBDIR/foobar").await
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/foobar"), 7); || dc_file_exist!(context, "$BLOBDIR/dada").await
|| dc_file_exist!(context, "$BLOBDIR/foobar.dadada").await
|| dc_file_exist!(context, "$BLOBDIR/foobar-folder").await
{
dc_delete_file(context, "$BLOBDIR/foobar").await;
dc_delete_file(context, "$BLOBDIR/dada").await;
dc_delete_file(context, "$BLOBDIR/foobar.dadada").await;
dc_delete_file(context, "$BLOBDIR/foobar-folder").await;
}
assert!(dc_write_file(context, "$BLOBDIR/foobar", b"content")
.await
.is_ok());
assert!(dc_file_exist!(context, "$BLOBDIR/foobar").await);
assert!(!dc_file_exist!(context, "$BLOBDIR/foobarx").await);
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/foobar").await, 7);
let abs_path = context let abs_path = context
.get_blobdir() .get_blobdir()
@@ -743,31 +761,33 @@ mod tests {
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
assert!(dc_file_exist(context, &abs_path)); assert!(dc_file_exist!(context, &abs_path).await);
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",)); assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada").await);
// attempting to copy a second time should fail // attempting to copy a second time should fail
assert!(!dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",)); assert!(!dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada").await);
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7); assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada").await, 7);
let buf = dc_read_file(context, "$BLOBDIR/dada").unwrap(); let buf = dc_read_file(context, "$BLOBDIR/dada").await.unwrap();
assert_eq!(buf.len(), 7); assert_eq!(buf.len(), 7);
assert_eq!(&buf, b"content"); assert_eq!(&buf, b"content");
assert!(dc_delete_file(context, "$BLOBDIR/foobar")); assert!(dc_delete_file(context, "$BLOBDIR/foobar").await);
assert!(dc_delete_file(context, "$BLOBDIR/dada")); assert!(dc_delete_file(context, "$BLOBDIR/dada").await);
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder").is_ok()); assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder")
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",)); .await
assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder")); .is_ok());
assert!(dc_file_exist!(context, "$BLOBDIR/foobar-folder").await);
assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder").await);
let fn0 = "$BLOBDIR/data.data"; let fn0 = "$BLOBDIR/data.data";
assert!(dc_write_file(context, &fn0, b"content").is_ok()); assert!(dc_write_file(context, &fn0, b"content").await.is_ok());
assert!(dc_delete_file(context, &fn0)); assert!(dc_delete_file(context, &fn0).await);
assert!(!dc_file_exist(context, &fn0)); assert!(!dc_file_exist!(context, &fn0).await);
} }
#[test] #[test]

View File

@@ -1,6 +1,6 @@
//! # Events specification //! # Events specification
use std::path::PathBuf; use async_std::path::PathBuf;
use strum::EnumProperty; use strum::EnumProperty;

View File

@@ -1,8 +1,9 @@
//! # Import/export module //! # Import/export module
use core::cmp::{max, min}; use std::cmp::{max, min};
use std::path::Path;
use async_std::path::{Path, PathBuf};
use async_std::prelude::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
@@ -83,10 +84,10 @@ pub async fn imex(context: &Context, what: ImexMode, param1: Option<impl AsRef<P
/// Returns the filename of the backup found (otherwise an error) /// Returns the filename of the backup found (otherwise an error)
pub async fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> { pub async fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> {
let dir_name = dir_name.as_ref(); let dir_name = dir_name.as_ref();
let dir_iter = std::fs::read_dir(dir_name)?; let mut dir_iter = async_std::fs::read_dir(dir_name).await?;
let mut newest_backup_time = 0; let mut newest_backup_time = 0;
let mut newest_backup_path: Option<std::path::PathBuf> = None; let mut newest_backup_path: Option<PathBuf> = None;
for dirent in dir_iter { while let Some(dirent) = dir_iter.next().await {
if let Ok(dirent) = dirent { if let Ok(dirent) = dirent {
let path = dirent.path(); let path = dirent.path();
let name = dirent.file_name(); let name = dirent.file_name();
@@ -133,7 +134,8 @@ async fn do_initiate_key_transfer(context: &Context) -> Result<String> {
context, context,
"autocrypt-setup-message.html", "autocrypt-setup-message.html",
setup_file_content.as_bytes(), setup_file_content.as_bytes(),
)?; )
.await?;
let chat_id = chat::create_by_contact_id(context, DC_CONTACT_ID_SELF).await?; let chat_id = chat::create_by_contact_id(context, DC_CONTACT_ID_SELF).await?;
msg = Message::default(); msg = Message::default();
@@ -270,7 +272,7 @@ pub async fn continue_key_transfer(
); );
if let Some(filename) = msg.get_file(context) { if let Some(filename) = msg.get_file(context) {
let file = dc_open_file(context, filename)?; let file = dc_open_file_std(context, filename)?;
let sc = normalize_setup_code(setup_code); let sc = normalize_setup_code(setup_code);
let armored_key = decrypt_setup_file(context, &sc, file)?; let armored_key = decrypt_setup_file(context, &sc, file)?;
set_self_key(context, &armored_key, true, true).await?; set_self_key(context, &armored_key, true, true).await?;
@@ -383,7 +385,7 @@ pub async fn job_imex_imap(context: &Context, job: &Job) -> Result<()> {
context.free_ongoing().await; context.free_ongoing().await;
bail!("Cannot create private key or private key not available."); bail!("Cannot create private key or private key not available.");
} else { } else {
dc_create_folder(context, &param)?; dc_create_folder(context, &param).await?;
} }
} }
let path = Path::new(param); let path = Path::new(param);
@@ -424,14 +426,14 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) ->
"Cannot import backups to accounts in use." "Cannot import backups to accounts in use."
); );
context.sql.close(&context).await; context.sql.close(&context).await;
dc_delete_file(context, context.get_dbfile()); dc_delete_file(context, context.get_dbfile()).await;
ensure!( ensure!(
!context.get_dbfile().exists(), !context.get_dbfile().exists().await,
"Cannot delete old database." "Cannot delete old database."
); );
ensure!( ensure!(
dc_copy_file(context, backup_to_import.as_ref(), context.get_dbfile()), dc_copy_file(context, backup_to_import.as_ref(), context.get_dbfile()).await,
"could not copy file" "could not copy file"
); );
/* error already logged */ /* error already logged */
@@ -494,7 +496,7 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) ->
} }
let path_filename = context.get_blobdir().join(file_name); let path_filename = context.get_blobdir().join(file_name);
dc_write_file(context, &path_filename, &file_blob)?; dc_write_file(context, &path_filename, &file_blob).await?;
} }
if all_files_extracted { if all_files_extracted {
@@ -520,7 +522,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
// FIXME: we should write to a temporary file first and rename it on success. this would guarantee the backup is complete. // FIXME: we should write to a temporary file first and rename it on success. this would guarantee the backup is complete.
// let dest_path_filename = dc_get_next_backup_file(context, dir, res); // let dest_path_filename = dc_get_next_backup_file(context, dir, res);
let now = time(); let now = time();
let dest_path_filename = dc_get_next_backup_path(dir, now)?; let dest_path_filename = dc_get_next_backup_path(dir, now).await?;
let dest_path_string = dest_path_filename.to_string_lossy().to_string(); let dest_path_string = dest_path_filename.to_string_lossy().to_string();
sql::housekeeping(context).await; sql::housekeeping(context).await;
@@ -535,7 +537,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
context.get_dbfile().display(), context.get_dbfile().display(),
dest_path_filename.display(), dest_path_filename.display(),
); );
let copied = dc_copy_file(context, context.get_dbfile(), &dest_path_filename); let copied = dc_copy_file(context, context.get_dbfile(), &dest_path_filename).await;
context context
.sql .sql
.open(&context, &context.get_dbfile(), false) .open(&context, &context.get_dbfile(), false)
@@ -556,7 +558,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
); );
let res = match add_files_to_export(context, &dest_sql).await { let res = match add_files_to_export(context, &dest_sql).await {
Err(err) => { Err(err) => {
dc_delete_file(context, &dest_path_filename); dc_delete_file(context, &dest_path_filename).await;
error!(context, "backup failed: {}", err); error!(context, "backup failed: {}", err);
Err(err) Err(err)
} }
@@ -586,17 +588,17 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
// copy all files from BLOBDIR into backup-db // copy all files from BLOBDIR into backup-db
let mut total_files_cnt = 0; let mut total_files_cnt = 0;
let dir = context.get_blobdir(); let dir = context.get_blobdir();
let dir_handle = std::fs::read_dir(&dir)?; let dir_handle = async_std::fs::read_dir(&dir).await?;
total_files_cnt += dir_handle.filter(|r| r.is_ok()).count(); total_files_cnt += dir_handle.filter(|r| r.is_ok()).count().await;
info!(context, "EXPORT: total_files_cnt={}", total_files_cnt); info!(context, "EXPORT: total_files_cnt={}", total_files_cnt);
sql.with_conn_async(|conn| async move { sql.with_conn_async(|conn| async move {
// scan directory, pass 2: copy files // scan directory, pass 2: copy files
let dir_handle = std::fs::read_dir(&dir)?; let mut dir_handle = async_std::fs::read_dir(&dir).await?;
let mut processed_files_cnt = 0; let mut processed_files_cnt = 0;
for entry in dir_handle { while let Some(entry) = dir_handle.next().await {
let entry = entry?; let entry = entry?;
if context.shall_stop_ongoing().await { if context.shall_stop_ongoing().await {
return Ok(()); return Ok(());
@@ -612,7 +614,7 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
} }
info!(context, "EXPORT: copying filename={}", name); info!(context, "EXPORT: copying filename={}", name);
let curr_path_filename = context.get_blobdir().join(entry.file_name()); let curr_path_filename = context.get_blobdir().join(entry.file_name());
if let Ok(buf) = dc_read_file(context, &curr_path_filename) { if let Ok(buf) = dc_read_file(context, &curr_path_filename).await {
if buf.is_empty() { if buf.is_empty() {
continue; continue;
} }
@@ -644,8 +646,8 @@ async fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
let mut imported_cnt = 0; let mut imported_cnt = 0;
let dir_name = dir.as_ref().to_string_lossy(); let dir_name = dir.as_ref().to_string_lossy();
let dir_handle = std::fs::read_dir(&dir)?; let mut dir_handle = async_std::fs::read_dir(&dir).await?;
for entry in dir_handle { while let Some(entry) = dir_handle.next().await {
let entry_fn = entry?.file_name(); let entry_fn = entry?.file_name();
let name_f = entry_fn.to_string_lossy(); let name_f = entry_fn.to_string_lossy();
let path_plus_name = dir.as_ref().join(&entry_fn); let path_plus_name = dir.as_ref().join(&entry_fn);
@@ -665,7 +667,7 @@ async fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
continue; continue;
} }
} }
match dc_read_file(context, &path_plus_name) { match dc_read_file(context, &path_plus_name).await {
Ok(buf) => { Ok(buf) => {
let armored = std::string::String::from_utf8_lossy(&buf); let armored = std::string::String::from_utf8_lossy(&buf);
if let Err(err) = set_self_key(context, &armored, set_default, false).await { if let Err(err) = set_self_key(context, &armored, set_default, false).await {
@@ -688,7 +690,7 @@ async fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
async fn export_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> { async fn export_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
let mut export_errors = 0; let mut export_errors = 0;
context let keys = context
.sql .sql
.query_map( .query_map(
"SELECT id, public_key, private_key, is_default FROM keypairs;", "SELECT id, public_key, private_key, is_default FROM keypairs;",
@@ -704,30 +706,36 @@ async fn export_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
Ok((id, public_key, private_key, is_default)) Ok((id, public_key, private_key, is_default))
}, },
|keys| { |keys| {
for key_pair in keys { keys.collect::<std::result::Result<Vec<_>, _>>()
let (id, public_key, private_key, is_default) = key_pair?; .map_err(Into::into)
let id = Some(id).filter(|_| is_default != 0);
if let Some(key) = public_key {
if export_key_to_asc_file(context, &dir, id, &key).is_err() {
export_errors += 1;
}
} else {
export_errors += 1;
}
if let Some(key) = private_key {
if export_key_to_asc_file(context, &dir, id, &key).is_err() {
export_errors += 1;
}
} else {
export_errors += 1;
}
}
Ok(())
}, },
) )
.await?; .await?;
for (id, public_key, private_key, is_default) in keys {
let id = Some(id).filter(|_| is_default != 0);
if let Some(key) = public_key {
if export_key_to_asc_file(context, &dir, id, &key)
.await
.is_err()
{
export_errors += 1;
}
} else {
export_errors += 1;
}
if let Some(key) = private_key {
if export_key_to_asc_file(context, &dir, id, &key)
.await
.is_err()
{
export_errors += 1;
}
} else {
export_errors += 1;
}
}
ensure!(export_errors == 0, "errors while exporting keys"); ensure!(export_errors == 0, "errors while exporting keys");
Ok(()) Ok(())
} }
@@ -735,7 +743,7 @@ async fn export_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
/******************************************************************************* /*******************************************************************************
* Classic key export * Classic key export
******************************************************************************/ ******************************************************************************/
fn export_key_to_asc_file( async fn export_key_to_asc_file(
context: &Context, context: &Context,
dir: impl AsRef<Path>, dir: impl AsRef<Path>,
id: Option<i64>, id: Option<i64>,
@@ -748,9 +756,9 @@ fn export_key_to_asc_file(
dir.as_ref().join(format!("{}-key-{}.asc", kind, &id)) dir.as_ref().join(format!("{}-key-{}.asc", kind, &id))
}; };
info!(context, "Exporting key {}", file_name.display()); info!(context, "Exporting key {}", file_name.display());
dc_delete_file(context, &file_name); dc_delete_file(context, &file_name).await;
let res = key.write_asc_to_file(&file_name, context); let res = key.write_asc_to_file(&file_name, context).await;
if res.is_err() { if res.is_err() {
error!(context, "Cannot write key to {}", file_name.display()); error!(context, "Cannot write key to {}", file_name.display());
} else { } else {
@@ -819,10 +827,12 @@ mod tests {
let context = dummy_context().await; let context = dummy_context().await;
let key = Key::from(alice_keypair().public); let key = Key::from(alice_keypair().public);
let blobdir = "$BLOBDIR"; let blobdir = "$BLOBDIR";
assert!(export_key_to_asc_file(&context.ctx, blobdir, None, &key).is_ok()); assert!(export_key_to_asc_file(&context.ctx, blobdir, None, &key)
.await
.is_ok());
let blobdir = context.ctx.get_blobdir().to_str().unwrap(); let blobdir = context.ctx.get_blobdir().to_str().unwrap();
let filename = format!("{}/public-key-default.asc", blobdir); let filename = format!("{}/public-key-default.asc", blobdir);
let bytes = std::fs::read(&filename).unwrap(); let bytes = async_std::fs::read(&filename).await.unwrap();
assert_eq!(bytes, key.to_asc(None).into_bytes()); assert_eq!(bytes, key.to_asc(None).into_bytes());
} }

View File

@@ -261,7 +261,7 @@ impl Job {
.get_path(Param::File, context) .get_path(Param::File, context)
.map_err(|_| format_err!("Can't get filename"))) .map_err(|_| format_err!("Can't get filename")))
.ok_or_else(|| format_err!("Can't get filename"))); .ok_or_else(|| format_err!("Can't get filename")));
let body = job_try!(dc_read_file(context, &filename)); let body = job_try!(dc_read_file(context, &filename).await);
let recipients = job_try!(self.param.get(Param::Recipients).ok_or_else(|| { let recipients = job_try!(self.param.get(Param::Recipients).ok_or_else(|| {
warn!(context, "Missing recipients for job {}", self.job_id); warn!(context, "Missing recipients for job {}", self.job_id);
format_err!("Missing recipients") format_err!("Missing recipients")
@@ -298,7 +298,7 @@ impl Job {
set_delivered(context, MsgId::new(foreign_id)).await; set_delivered(context, MsgId::new(foreign_id)).await;
} }
// now also delete the generated file // now also delete the generated file
dc_delete_file(context, filename); dc_delete_file(context, filename).await;
Ok(()) Ok(())
} }
}) })
@@ -1086,7 +1086,7 @@ async fn add_smtp_job(
ensure!(!recipients.is_empty(), "no recipients for smtp job set"); ensure!(!recipients.is_empty(), "no recipients for smtp job set");
let mut param = Params::new(); let mut param = Params::new();
let bytes = &rendered_msg.message; let bytes = &rendered_msg.message;
let blob = BlobObject::create(context, &rendered_msg.rfc724_mid, bytes)?; let blob = BlobObject::create(context, &rendered_msg.rfc724_mid, bytes).await?;
let recipients = recipients.join("\x1e"); let recipients = recipients.join("\x1e");
param.set(Param::File, blob.as_name()); param.set(Param::File, blob.as_name());

View File

@@ -2,8 +2,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io::Cursor; use std::io::Cursor;
use std::path::Path;
use async_std::path::Path;
use pgp::composed::Deserializable; use pgp::composed::Deserializable;
use pgp::ser::Serialize; use pgp::ser::Serialize;
use pgp::types::{KeyTrait, SecretKeyTrait}; use pgp::types::{KeyTrait, SecretKeyTrait};
@@ -268,14 +268,14 @@ impl Key {
.expect("failed to serialize key") .expect("failed to serialize key")
} }
pub fn write_asc_to_file( pub async fn write_asc_to_file(
&self, &self,
file: impl AsRef<Path>, file: impl AsRef<Path>,
context: &Context, context: &Context,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let file_content = self.to_asc(None).into_bytes(); let file_content = self.to_asc(None).into_bytes();
let res = dc_write_file(context, &file, &file_content); let res = dc_write_file(context, &file, &file_content).await;
if res.is_err() { if res.is_err() {
error!(context, "Cannot write key to {}", file.as_ref().display()); error!(context, "Cannot write key to {}", file.as_ref().display());
} }

View File

@@ -1,7 +1,6 @@
//! # Messages and their identifiers //! # Messages and their identifiers
use std::path::{Path, PathBuf}; use async_std::path::{Path, PathBuf};
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use failure::Fail; use failure::Fail;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@@ -349,7 +348,7 @@ impl Message {
self.param.set_int(Param::Width, 0); self.param.set_int(Param::Width, 0);
self.param.set_int(Param::Height, 0); self.param.set_int(Param::Height, 0);
if let Ok(buf) = dc_read_file(context, path_and_filename) { if let Ok(buf) = dc_read_file(context, path_and_filename).await {
if let Ok((width, height)) = dc_get_filemeta(&buf) { if let Ok((width, height)) = dc_get_filemeta(&buf) {
self.param.set_int(Param::Width, width as i32); self.param.set_int(Param::Width, width as i32);
self.param.set_int(Param::Height, height as i32); self.param.set_int(Param::Height, height as i32);
@@ -450,12 +449,12 @@ impl Message {
.map(|name| name.to_string_lossy().to_string()) .map(|name| name.to_string_lossy().to_string())
} }
pub fn get_filebytes(&self, context: &Context) -> u64 { pub async fn get_filebytes(&self, context: &Context) -> u64 {
self.param match self.param.get_path(Param::File, context) {
.get_path(Param::File, context) Ok(Some(path)) => dc_get_filebytes(context, &path).await,
.unwrap_or(None) Ok(None) => 0,
.map(|path| dc_get_filebytes(context, &path)) Err(_) => 0,
.unwrap_or_default() }
} }
pub fn get_width(&self) -> i32 { pub fn get_width(&self) -> i32 {
@@ -558,13 +557,13 @@ impl Message {
self.param.get_cmd() == SystemMessage::AutocryptSetupMessage self.param.get_cmd() == SystemMessage::AutocryptSetupMessage
} }
pub fn get_setupcodebegin(&self, context: &Context) -> Option<String> { pub async fn get_setupcodebegin(&self, context: &Context) -> Option<String> {
if !self.is_setupmessage() { if !self.is_setupmessage() {
return None; return None;
} }
if let Some(filename) = self.get_file(context) { if let Some(filename) = self.get_file(context) {
if let Ok(ref buf) = dc_read_file(context, filename) { if let Ok(ref buf) = dc_read_file(context, filename).await {
if let Ok((typ, headers, _)) = split_armored_data(buf) { if let Ok((typ, headers, _)) = split_armored_data(buf) {
if typ == pgp::armor::BlockType::Message { if typ == pgp::armor::BlockType::Message {
return headers.get(crate::pgp::HEADER_SETUPCODE).cloned(); return headers.get(crate::pgp::HEADER_SETUPCODE).cloned();
@@ -909,7 +908,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> String {
} }
if let Some(path) = msg.get_file(context) { if let Some(path) = msg.get_file(context) {
let bytes = dc_get_filebytes(context, &path); let bytes = dc_get_filebytes(context, &path).await;
ret += &format!("\nFile: {}, {}, bytes\n", path.display(), bytes); ret += &format!("\nFile: {}, {}, bytes\n", path.display(), bytes);
} }

View File

@@ -808,7 +808,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
meta.viewtype = Viewtype::Image; meta.viewtype = Viewtype::Image;
meta.param.set(Param::File, grpimage); meta.param.set(Param::File, grpimage);
let (mail, filename_as_sent) = build_body_file(context, &meta, "group-image")?; let (mail, filename_as_sent) = build_body_file(context, &meta, "group-image").await?;
meta_part = Some(mail); meta_part = Some(mail);
protected_headers.push(Header::new("Chat-Group-Avatar".into(), filename_as_sent)); protected_headers.push(Header::new("Chat-Group-Avatar".into(), filename_as_sent));
} }
@@ -879,13 +879,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
// add attachment part // add attachment part
if chat::msgtype_has_file(self.msg.viewtype) { if chat::msgtype_has_file(self.msg.viewtype) {
if !is_file_size_okay(context, &self.msg) { if !is_file_size_okay(context, &self.msg).await {
bail!( bail!(
"Message exceeds the recommended {} MB.", "Message exceeds the recommended {} MB.",
RECOMMENDED_FILE_SIZE / 1_000_000, RECOMMENDED_FILE_SIZE / 1_000_000,
); );
} else { } else {
let (file_part, _) = build_body_file(context, &self.msg, "")?; let (file_part, _) = build_body_file(context, &self.msg, "").await?;
parts.push(file_part); parts.push(file_part);
} }
} }
@@ -1035,14 +1035,15 @@ fn wrapped_base64_encode(buf: &[u8]) -> String {
.join("\r\n") .join("\r\n")
} }
fn build_body_file( async fn build_body_file(
context: &Context, context: &Context,
msg: &Message, msg: &Message,
base_name: &str, base_name: &str,
) -> Result<(PartBuilder, String), Error> { ) -> Result<(PartBuilder, String), Error> {
let blob = msg let blob = msg
.param .param
.get_blob(Param::File, context, true)? .get_blob(Param::File, context, true)
.await?
.ok_or_else(|| format_err!("msg has no filename"))?; .ok_or_else(|| format_err!("msg has no filename"))?;
let suffix = blob.suffix().unwrap_or("dat"); let suffix = blob.suffix().unwrap_or("dat");
@@ -1138,10 +1139,10 @@ fn recipients_contain_addr(recipients: &[(String, String)], addr: &str) -> bool
.any(|(_, cur)| cur.to_lowercase() == addr_lc) .any(|(_, cur)| cur.to_lowercase() == addr_lc)
} }
fn is_file_size_okay(context: &Context, msg: &Message) -> bool { async fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
match msg.param.get_path(Param::File, context).unwrap_or(None) { match msg.param.get_path(Param::File, context).unwrap_or(None) {
Some(path) => { Some(path) => {
let bytes = dc_get_filebytes(context, &path); let bytes = dc_get_filebytes(context, &path).await;
bytes <= UPPER_LIMIT_FILE_SIZE bytes <= UPPER_LIMIT_FILE_SIZE
} }
None => false, None => false,

View File

@@ -463,7 +463,7 @@ impl MimeMessage {
self.parse_mime_recursive(context, &mail).await self.parse_mime_recursive(context, &mail).await
} }
MimeS::Single => self.add_single_part_if_known(context, mail), MimeS::Single => self.add_single_part_if_known(context, mail).await,
} }
} }
.boxed() .boxed()
@@ -578,7 +578,7 @@ impl MimeMessage {
Ok(any_part_added) Ok(any_part_added)
} }
fn add_single_part_if_known( async fn add_single_part_if_known(
&mut self, &mut self,
context: &Context, context: &Context,
mail: &mailparse::ParsedMail<'_>, mail: &mailparse::ParsedMail<'_>,
@@ -600,7 +600,8 @@ impl MimeMessage {
&raw_mime, &raw_mime,
&mail.get_body_raw()?, &mail.get_body_raw()?,
&filename, &filename,
); )
.await;
} }
None => { None => {
match mime_type.type_() { match mime_type.type_() {
@@ -652,7 +653,7 @@ impl MimeMessage {
Ok(self.parts.len() > old_part_count) Ok(self.parts.len() > old_part_count)
} }
fn do_add_single_file_part( async fn do_add_single_file_part(
&mut self, &mut self,
context: &Context, context: &Context,
msg_type: Viewtype, msg_type: Viewtype,
@@ -685,7 +686,7 @@ impl MimeMessage {
/* we have a regular file attachment, /* we have a regular file attachment,
write decoded data to new blob object */ write decoded data to new blob object */
let blob = match BlobObject::create(context, filename, decoded_data) { let blob = match BlobObject::create(context, filename, decoded_data).await {
Ok(blob) => blob, Ok(blob) => blob,
Err(err) => { Err(err) => {
error!( error!(

View File

@@ -1,8 +1,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::path::PathBuf;
use std::str; use std::str;
use async_std::path::PathBuf;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -281,7 +281,7 @@ impl Params {
/// created without copying if the path already referes to a valid /// created without copying if the path already referes to a valid
/// blob. If so a [BlobObject] will be returned regardless of the /// blob. If so a [BlobObject] will be returned regardless of the
/// `create` argument. /// `create` argument.
pub fn get_blob<'a>( pub async fn get_blob<'a>(
&self, &self,
key: Param, key: Param,
context: &'a Context, context: &'a Context,
@@ -294,7 +294,7 @@ impl Params {
let file = ParamsFile::from_param(context, val)?; let file = ParamsFile::from_param(context, val)?;
let blob = match file { let blob = match file {
ParamsFile::FsPath(path) => match create { ParamsFile::FsPath(path) => match create {
true => BlobObject::new_from_path(context, path)?, true => BlobObject::new_from_path(context, path).await?,
false => BlobObject::from_path(context, path)?, false => BlobObject::from_path(context, path)?,
}, },
ParamsFile::Blob(blob) => blob, ParamsFile::Blob(blob) => blob,
@@ -368,8 +368,8 @@ impl<'a> ParamsFile<'a> {
mod tests { mod tests {
use super::*; use super::*;
use std::fs; use async_std::fs;
use std::path::Path; use async_std::path::Path;
use crate::test_utils::*; use crate::test_utils::*;
@@ -446,20 +446,25 @@ mod tests {
p.set(Param::File, fname.to_str().unwrap()); p.set(Param::File, fname.to_str().unwrap());
let file = p.get_file(Param::File, &t.ctx).unwrap().unwrap(); let file = p.get_file(Param::File, &t.ctx).unwrap().unwrap();
assert_eq!(file, ParamsFile::FsPath(fname.clone())); assert_eq!(file, ParamsFile::FsPath(fname.clone().into()));
let path = p.get_path(Param::File, &t.ctx).unwrap().unwrap(); let path: PathBuf = p.get_path(Param::File, &t.ctx).unwrap().unwrap();
let fname: PathBuf = fname.into();
assert_eq!(path, fname); assert_eq!(path, fname);
// Blob does not exist yet, expect BlobError. // Blob does not exist yet, expect BlobError.
let err = p.get_blob(Param::File, &t.ctx, false).unwrap_err(); let err = p.get_blob(Param::File, &t.ctx, false).await.unwrap_err();
match err { match err {
BlobError::WrongBlobdir { .. } => (), BlobError::WrongBlobdir { .. } => (),
_ => panic!("wrong error type/variant: {:?}", err), _ => panic!("wrong error type/variant: {:?}", err),
} }
fs::write(fname, b"boo").unwrap(); fs::write(fname, b"boo").await.unwrap();
let blob = p.get_blob(Param::File, &t.ctx, true).unwrap().unwrap(); let blob = p
.get_blob(Param::File, &t.ctx, true)
.await
.unwrap()
.unwrap();
assert_eq!( assert_eq!(
blob, blob,
BlobObject::from_name(&t.ctx, "foo".to_string()).unwrap() BlobObject::from_name(&t.ctx, "foo".to_string()).unwrap()
@@ -468,7 +473,11 @@ mod tests {
// Blob in blobdir, expect blob. // Blob in blobdir, expect blob.
let bar = t.ctx.get_blobdir().join("bar"); let bar = t.ctx.get_blobdir().join("bar");
p.set(Param::File, bar.to_str().unwrap()); p.set(Param::File, bar.to_str().unwrap());
let blob = p.get_blob(Param::File, &t.ctx, false).unwrap().unwrap(); let blob = p
.get_blob(Param::File, &t.ctx, false)
.await
.unwrap()
.unwrap();
assert_eq!( assert_eq!(
blob, blob,
BlobObject::from_name(&t.ctx, "bar".to_string()).unwrap() BlobObject::from_name(&t.ctx, "bar".to_string()).unwrap()
@@ -477,6 +486,10 @@ mod tests {
p.remove(Param::File); p.remove(Param::File);
assert!(p.get_file(Param::File, &t.ctx).unwrap().is_none()); assert!(p.get_file(Param::File, &t.ctx).unwrap().is_none());
assert!(p.get_path(Param::File, &t.ctx).unwrap().is_none()); assert!(p.get_path(Param::File, &t.ctx).unwrap().is_none());
assert!(p.get_blob(Param::File, &t.ctx, false).unwrap().is_none()); assert!(p
.get_blob(Param::File, &t.ctx, false)
.await
.unwrap()
.is_none());
} }
} }

View File

@@ -96,7 +96,7 @@ impl Sql {
} }
// return true on success, false on failure // return true on success, false on failure
pub async fn open(&self, context: &Context, dbfile: &Path, readonly: bool) -> bool { pub async fn open<T: AsRef<Path>>(&self, context: &Context, dbfile: T, readonly: bool) -> bool {
match open(context, self, dbfile, readonly).await { match open(context, self, dbfile, readonly).await {
Ok(_) => true, Ok(_) => true,
Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false, Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false,
@@ -607,7 +607,7 @@ pub async fn housekeeping(context: &Context) {
entry.file_name() entry.file_name()
); );
let path = entry.path(); let path = entry.path();
dc_delete_file(context, path); dc_delete_file(context, path).await;
} }
} }
Err(err) => { Err(err) => {

View File

@@ -376,7 +376,7 @@ impl Context {
chat::add_device_msg(&self, Some("core-about-device-chat"), Some(&mut msg)).await?; chat::add_device_msg(&self, Some("core-about-device-chat"), Some(&mut msg)).await?;
let image = include_bytes!("../assets/welcome-image.jpg"); let image = include_bytes!("../assets/welcome-image.jpg");
let blob = BlobObject::create(&self, "welcome-image.jpg".to_string(), image)?; let blob = BlobObject::create(&self, "welcome-image.jpg".to_string(), image).await?;
let mut msg = Message::new(Viewtype::Image); let mut msg = Message::new(Viewtype::Image);
msg.param.set(Param::File, blob.as_name()); msg.param.set(Param::File, blob.as_name());
chat::add_device_msg(&self, Some("core-welcome-image"), Some(&mut msg)).await?; chat::add_device_msg(&self, Some("core-welcome-image"), Some(&mut msg)).await?;

View File

@@ -32,7 +32,9 @@ pub(crate) async fn test_context(callback: Option<Box<ContextCallback>>) -> Test
Some(cb) => cb, Some(cb) => cb,
None => Box::new(|_, _| ()), None => Box::new(|_, _| ()),
}; };
let ctx = Context::new(cb, "FakeOs".into(), dbfile).await.unwrap(); let ctx = Context::new(cb, "FakeOs".into(), dbfile.into())
.await
.unwrap();
TestContext { ctx, dir } TestContext { ctx, dir }
} }

View File

@@ -104,7 +104,7 @@ struct TestContext {
async fn create_test_context() -> TestContext { async fn create_test_context() -> TestContext {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile) let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile.into())
.await .await
.unwrap(); .unwrap();
TestContext { ctx, dir } TestContext { ctx, dir }