mirror of
https://github.com/chatmail/core.git
synced 2026-04-20 06:56:29 +03:00
feat: migrate from async-std to tokio
This commit is contained in:
committed by
GitHub
parent
997fb4061a
commit
290ee20e63
109
src/accounts.rs
109
src/accounts.rs
@@ -1,13 +1,12 @@
|
||||
//! # Account manager module.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use async_std::fs;
|
||||
use async_std::path::PathBuf;
|
||||
use uuid::Uuid;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::events::{Event, EventEmitter, EventType, Events};
|
||||
@@ -26,7 +25,7 @@ pub struct Accounts {
|
||||
impl Accounts {
|
||||
/// Loads or creates an accounts folder at the given `dir`.
|
||||
pub async fn new(dir: PathBuf) -> Result<Self> {
|
||||
if !dir.exists().await {
|
||||
if !dir.exists() {
|
||||
Accounts::create(&dir).await?;
|
||||
}
|
||||
|
||||
@@ -47,14 +46,10 @@ impl Accounts {
|
||||
/// Opens an existing accounts structure. Will error if the folder doesn't exist,
|
||||
/// no account exists and no config exists.
|
||||
pub async fn open(dir: PathBuf) -> Result<Self> {
|
||||
ensure!(dir.exists().await, "directory does not exist");
|
||||
ensure!(dir.exists(), "directory does not exist");
|
||||
|
||||
let config_file = dir.join(CONFIG_NAME);
|
||||
ensure!(
|
||||
config_file.exists().await,
|
||||
"{:?} does not exist",
|
||||
config_file
|
||||
);
|
||||
ensure!(config_file.exists(), "{:?} does not exist", config_file);
|
||||
|
||||
let config = Config::from_file(config_file)
|
||||
.await
|
||||
@@ -106,7 +101,7 @@ impl Accounts {
|
||||
let account_config = self.config.new_account(&self.dir).await?;
|
||||
|
||||
let ctx = Context::new(
|
||||
account_config.dbfile().into(),
|
||||
&account_config.dbfile(),
|
||||
account_config.id,
|
||||
self.events.clone(),
|
||||
)
|
||||
@@ -121,7 +116,7 @@ impl Accounts {
|
||||
let account_config = self.config.new_account(&self.dir).await?;
|
||||
|
||||
let ctx = Context::new_closed(
|
||||
account_config.dbfile().into(),
|
||||
&account_config.dbfile(),
|
||||
account_config.id,
|
||||
self.events.clone(),
|
||||
)
|
||||
@@ -148,7 +143,7 @@ impl Accounts {
|
||||
loop {
|
||||
counter += 1;
|
||||
|
||||
if let Err(err) = fs::remove_dir_all(async_std::path::PathBuf::from(&cfg.dir))
|
||||
if let Err(err) = fs::remove_dir_all(&cfg.dir)
|
||||
.await
|
||||
.context("failed to remove account data")
|
||||
{
|
||||
@@ -157,7 +152,7 @@ impl Accounts {
|
||||
}
|
||||
|
||||
// Wait 1 second and try again.
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -173,16 +168,8 @@ impl Accounts {
|
||||
let blobdir = Context::derive_blobdir(&dbfile);
|
||||
let walfile = Context::derive_walfile(&dbfile);
|
||||
|
||||
ensure!(
|
||||
dbfile.exists().await,
|
||||
"no database found: {}",
|
||||
dbfile.display()
|
||||
);
|
||||
ensure!(
|
||||
blobdir.exists().await,
|
||||
"no blobdir found: {}",
|
||||
blobdir.display()
|
||||
);
|
||||
ensure!(dbfile.exists(), "no database found: {}", dbfile.display());
|
||||
ensure!(blobdir.exists(), "no blobdir found: {}", blobdir.display());
|
||||
|
||||
let old_id = self.config.get_selected_account().await;
|
||||
|
||||
@@ -193,7 +180,7 @@ impl Accounts {
|
||||
.await
|
||||
.context("failed to create new account")?;
|
||||
|
||||
let new_dbfile = account_config.dbfile().into();
|
||||
let new_dbfile = account_config.dbfile();
|
||||
let new_blobdir = Context::derive_blobdir(&new_dbfile);
|
||||
let new_walfile = Context::derive_walfile(&new_dbfile);
|
||||
|
||||
@@ -207,7 +194,7 @@ impl Accounts {
|
||||
fs::rename(&blobdir, &new_blobdir)
|
||||
.await
|
||||
.context("failed to rename blobdir")?;
|
||||
if walfile.exists().await {
|
||||
if walfile.exists() {
|
||||
fs::rename(&walfile, &new_walfile)
|
||||
.await
|
||||
.context("failed to rename walfile")?;
|
||||
@@ -217,13 +204,13 @@ impl Accounts {
|
||||
|
||||
match res {
|
||||
Ok(_) => {
|
||||
let ctx = Context::new(new_dbfile, account_config.id, self.events.clone()).await?;
|
||||
let ctx = Context::new(&new_dbfile, account_config.id, self.events.clone()).await?;
|
||||
self.accounts.insert(account_config.id, ctx);
|
||||
Ok(account_config.id)
|
||||
}
|
||||
Err(err) => {
|
||||
// remove temp account
|
||||
fs::remove_dir_all(async_std::path::PathBuf::from(&account_config.dir))
|
||||
fs::remove_dir_all(std::path::PathBuf::from(&account_config.dir))
|
||||
.await
|
||||
.context("failed to remove account data")?;
|
||||
|
||||
@@ -321,7 +308,7 @@ struct InnerConfig {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn new(dir: &PathBuf) -> Result<Self> {
|
||||
pub async fn new(dir: &Path) -> Result<Self> {
|
||||
let inner = InnerConfig {
|
||||
accounts: Vec::new(),
|
||||
selected_account: 0,
|
||||
@@ -355,18 +342,14 @@ impl Config {
|
||||
pub async fn load_accounts(&self, events: &Events) -> Result<BTreeMap<u32, Context>> {
|
||||
let mut accounts = BTreeMap::new();
|
||||
for account_config in &self.inner.accounts {
|
||||
let ctx = Context::new(
|
||||
account_config.dbfile().into(),
|
||||
account_config.id,
|
||||
events.clone(),
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to create context from file {:?}",
|
||||
account_config.dbfile()
|
||||
)
|
||||
})?;
|
||||
let ctx = Context::new(&account_config.dbfile(), account_config.id, events.clone())
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to create context from file {:?}",
|
||||
account_config.dbfile()
|
||||
)
|
||||
})?;
|
||||
|
||||
accounts.insert(account_config.id, ctx);
|
||||
}
|
||||
@@ -375,7 +358,7 @@ impl Config {
|
||||
}
|
||||
|
||||
/// Create a new account in the given root directory.
|
||||
async fn new_account(&mut self, dir: &PathBuf) -> Result<AccountConfig> {
|
||||
async fn new_account(&mut self, dir: &Path) -> Result<AccountConfig> {
|
||||
let id = {
|
||||
let id = self.inner.next_id;
|
||||
let uuid = Uuid::new_v4();
|
||||
@@ -383,7 +366,7 @@ impl Config {
|
||||
|
||||
self.inner.accounts.push(AccountConfig {
|
||||
id,
|
||||
dir: target_dir.into(),
|
||||
dir: target_dir,
|
||||
uuid,
|
||||
});
|
||||
self.inner.next_id += 1;
|
||||
@@ -464,10 +447,10 @@ impl AccountConfig {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_account_new_open() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts1").into();
|
||||
let p: PathBuf = dir.path().join("accounts1");
|
||||
|
||||
let mut accounts1 = Accounts::new(p.clone()).await.unwrap();
|
||||
accounts1.add_account().await.unwrap();
|
||||
@@ -482,10 +465,10 @@ mod tests {
|
||||
assert_eq!(accounts1.accounts.len(), accounts2.accounts.len());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_account_new_add_remove() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone()).await.unwrap();
|
||||
assert_eq!(accounts.accounts.len(), 0);
|
||||
@@ -509,10 +492,10 @@ mod tests {
|
||||
assert_eq!(accounts.accounts.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_accounts_remove_last() -> Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone()).await?;
|
||||
assert!(accounts.get_selected_account().await.is_none());
|
||||
@@ -530,17 +513,17 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_migrate_account() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone()).await.unwrap();
|
||||
assert_eq!(accounts.accounts.len(), 0);
|
||||
assert_eq!(accounts.config.get_selected_account().await, 0);
|
||||
|
||||
let extern_dbfile: PathBuf = dir.path().join("other").into();
|
||||
let ctx = Context::new(extern_dbfile.clone(), 0, Events::new())
|
||||
let extern_dbfile: PathBuf = dir.path().join("other");
|
||||
let ctx = Context::new(&extern_dbfile, 0, Events::new())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.set_config(crate::config::Config::Addr, Some("me@mail.com"))
|
||||
@@ -567,10 +550,10 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Tests that accounts are sorted by ID.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_accounts_sorted() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone()).await.unwrap();
|
||||
|
||||
@@ -585,10 +568,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_accounts_ids_unique_increasing_and_persisted() -> Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
let dummy_accounts = 10;
|
||||
|
||||
let (id0, id1, id2) = {
|
||||
@@ -667,10 +650,10 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_no_accounts_event_emitter() -> Result<()> {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let accounts = Accounts::new(p.clone()).await?;
|
||||
|
||||
@@ -682,7 +665,7 @@ mod tests {
|
||||
|
||||
// Test that event emitter does not return `None` immediately.
|
||||
let duration = std::time::Duration::from_millis(1);
|
||||
assert!(async_std::future::timeout(duration, event_emitter.recv())
|
||||
assert!(tokio::time::timeout(duration, event_emitter.recv())
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
@@ -693,10 +676,10 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_encrypted_account() -> Result<()> {
|
||||
let dir = tempfile::tempdir().context("failed to create tempdir")?;
|
||||
let p: PathBuf = dir.path().join("accounts").into();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone())
|
||||
.await
|
||||
|
||||
323
src/blob.rs
323
src/blob.rs
@@ -4,14 +4,13 @@ use core::cmp::max;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use async_std::prelude::*;
|
||||
use async_std::{fs, io};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{format_err, Context as _, Error, Result};
|
||||
use image::{DynamicImage, ImageFormat};
|
||||
use num_traits::FromPrimitive;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::{fs, io};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
@@ -89,7 +88,7 @@ impl<'a> BlobObject<'a> {
|
||||
Err(err) => {
|
||||
if attempt >= MAX_ATTEMPT {
|
||||
return Err(err).context("failed to create file");
|
||||
} else if attempt == 1 && !dir.exists().await {
|
||||
} else if attempt == 1 && !dir.exists() {
|
||||
fs::create_dir_all(dir).await.ok_or_log(context);
|
||||
} else {
|
||||
name = format!("{}-{}{}", stem, rand::random::<u32>(), ext);
|
||||
@@ -371,108 +370,81 @@ impl<'a> BlobObject<'a> {
|
||||
mut img_wh: u32,
|
||||
max_bytes: Option<usize>,
|
||||
) -> Result<Option<String>> {
|
||||
let mut img = image::open(&blob_abs).context("image recode failure")?;
|
||||
let orientation = self.get_exif_orientation(context);
|
||||
let mut encoded = Vec::new();
|
||||
let mut changed_name = None;
|
||||
tokio::task::block_in_place(move || {
|
||||
let mut img = image::open(&blob_abs).context("image recode failure")?;
|
||||
let orientation = self.get_exif_orientation(context);
|
||||
let mut encoded = Vec::new();
|
||||
let mut changed_name = None;
|
||||
|
||||
fn encode_img(img: &DynamicImage, encoded: &mut Vec<u8>) -> anyhow::Result<()> {
|
||||
encoded.clear();
|
||||
let mut buf = Cursor::new(encoded);
|
||||
img.write_to(&mut buf, image::ImageFormat::Jpeg)?;
|
||||
Ok(())
|
||||
}
|
||||
fn encoded_img_exceeds_bytes(
|
||||
context: &Context,
|
||||
img: &DynamicImage,
|
||||
max_bytes: Option<usize>,
|
||||
encoded: &mut Vec<u8>,
|
||||
) -> anyhow::Result<bool> {
|
||||
if let Some(max_bytes) = max_bytes {
|
||||
encode_img(img, encoded)?;
|
||||
if encoded.len() > max_bytes {
|
||||
info!(
|
||||
context,
|
||||
"image size {}B ({}x{}px) exceeds {}B, need to scale down",
|
||||
encoded.len(),
|
||||
img.width(),
|
||||
img.height(),
|
||||
max_bytes,
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
let exceeds_width = img.width() > img_wh || img.height() > img_wh;
|
||||
let exceeds_width = img.width() > img_wh || img.height() > img_wh;
|
||||
|
||||
let do_scale =
|
||||
exceeds_width || encoded_img_exceeds_bytes(context, &img, max_bytes, &mut encoded)?;
|
||||
let do_rotate = matches!(orientation, Ok(90) | Ok(180) | Ok(270));
|
||||
let do_scale =
|
||||
exceeds_width || encoded_img_exceeds_bytes(context, &img, max_bytes, &mut encoded)?;
|
||||
let do_rotate = matches!(orientation, Ok(90) | Ok(180) | Ok(270));
|
||||
|
||||
if do_scale || do_rotate {
|
||||
if do_rotate {
|
||||
img = match orientation {
|
||||
Ok(90) => img.rotate90(),
|
||||
Ok(180) => img.rotate180(),
|
||||
Ok(270) => img.rotate270(),
|
||||
_ => img,
|
||||
}
|
||||
}
|
||||
|
||||
if do_scale {
|
||||
if !exceeds_width {
|
||||
// The image is already smaller than img_wh, but exceeds max_bytes
|
||||
// We can directly start with trying to scale down to 2/3 of its current width
|
||||
img_wh = max(img.width(), img.height()) * 2 / 3
|
||||
}
|
||||
|
||||
loop {
|
||||
let new_img = img.thumbnail(img_wh, img_wh);
|
||||
|
||||
if encoded_img_exceeds_bytes(context, &new_img, max_bytes, &mut encoded)? {
|
||||
if img_wh < 20 {
|
||||
return Err(format_err!(
|
||||
"Failed to scale image to below {}B",
|
||||
max_bytes.unwrap_or_default()
|
||||
));
|
||||
}
|
||||
|
||||
img_wh = img_wh * 2 / 3;
|
||||
} else {
|
||||
if encoded.is_empty() {
|
||||
encode_img(&new_img, &mut encoded)?;
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Final scaled-down image size: {}B ({}px)",
|
||||
encoded.len(),
|
||||
img_wh
|
||||
);
|
||||
break;
|
||||
if do_scale || do_rotate {
|
||||
if do_rotate {
|
||||
img = match orientation {
|
||||
Ok(90) => img.rotate90(),
|
||||
Ok(180) => img.rotate180(),
|
||||
Ok(270) => img.rotate270(),
|
||||
_ => img,
|
||||
}
|
||||
}
|
||||
|
||||
if do_scale {
|
||||
if !exceeds_width {
|
||||
// The image is already smaller than img_wh, but exceeds max_bytes
|
||||
// We can directly start with trying to scale down to 2/3 of its current width
|
||||
img_wh = max(img.width(), img.height()) * 2 / 3
|
||||
}
|
||||
|
||||
loop {
|
||||
let new_img = img.thumbnail(img_wh, img_wh);
|
||||
|
||||
if encoded_img_exceeds_bytes(context, &new_img, max_bytes, &mut encoded)? {
|
||||
if img_wh < 20 {
|
||||
return Err(format_err!(
|
||||
"Failed to scale image to below {}B",
|
||||
max_bytes.unwrap_or_default()
|
||||
));
|
||||
}
|
||||
|
||||
img_wh = img_wh * 2 / 3;
|
||||
} else {
|
||||
if encoded.is_empty() {
|
||||
encode_img(&new_img, &mut encoded)?;
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Final scaled-down image size: {}B ({}px)",
|
||||
encoded.len(),
|
||||
img_wh
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The file format is JPEG now, we may have to change the file extension
|
||||
if !matches!(ImageFormat::from_path(&blob_abs), Ok(ImageFormat::Jpeg)) {
|
||||
blob_abs = blob_abs.with_extension("jpg");
|
||||
let file_name = blob_abs.file_name().context("No avatar file name (???)")?;
|
||||
let file_name = file_name.to_str().context("Filename is no UTF-8 (???)")?;
|
||||
changed_name = Some(format!("$BLOBDIR/{}", file_name));
|
||||
}
|
||||
|
||||
if encoded.is_empty() {
|
||||
encode_img(&img, &mut encoded)?;
|
||||
}
|
||||
|
||||
std::fs::write(&blob_abs, &encoded)
|
||||
.context("failed to write recoded blob to file")?;
|
||||
}
|
||||
|
||||
// The file format is JPEG now, we may have to change the file extension
|
||||
if !matches!(ImageFormat::from_path(&blob_abs), Ok(ImageFormat::Jpeg)) {
|
||||
blob_abs = blob_abs.with_extension("jpg");
|
||||
let file_name = blob_abs.file_name().context("No avatar file name (???)")?;
|
||||
let file_name = file_name.to_str().context("Filename is no UTF-8 (???)")?;
|
||||
changed_name = Some(format!("$BLOBDIR/{}", file_name));
|
||||
}
|
||||
|
||||
if encoded.is_empty() {
|
||||
encode_img(&img, &mut encoded)?;
|
||||
}
|
||||
|
||||
fs::write(&blob_abs, &encoded)
|
||||
.await
|
||||
.context("failed to write recoded blob to file")?;
|
||||
}
|
||||
|
||||
Ok(changed_name)
|
||||
Ok(changed_name)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_exif_orientation(&self, context: &Context) -> Result<i32, Error> {
|
||||
@@ -500,6 +472,35 @@ impl<'a> fmt::Display for BlobObject<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_img(img: &DynamicImage, encoded: &mut Vec<u8>) -> anyhow::Result<()> {
|
||||
encoded.clear();
|
||||
let mut buf = Cursor::new(encoded);
|
||||
img.write_to(&mut buf, image::ImageFormat::Jpeg)?;
|
||||
Ok(())
|
||||
}
|
||||
fn encoded_img_exceeds_bytes(
|
||||
context: &Context,
|
||||
img: &DynamicImage,
|
||||
max_bytes: Option<usize>,
|
||||
encoded: &mut Vec<u8>,
|
||||
) -> anyhow::Result<bool> {
|
||||
if let Some(max_bytes) = max_bytes {
|
||||
encode_img(img, encoded)?;
|
||||
if encoded.len() > max_bytes {
|
||||
info!(
|
||||
context,
|
||||
"image size {}B ({}x{}px) exceeds {}B, need to scale down",
|
||||
encoded.len(),
|
||||
img.width(),
|
||||
img.height(),
|
||||
max_bytes,
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use fs::File;
|
||||
@@ -513,7 +514,16 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[async_std::test]
|
||||
fn check_image_size(path: impl AsRef<Path>, width: u32, height: u32) -> image::DynamicImage {
|
||||
tokio::task::block_in_place(move || {
|
||||
let img = image::open(path).expect("failed to open image");
|
||||
assert_eq!(img.width(), width, "invalid width");
|
||||
assert_eq!(img.height(), height, "invalid height");
|
||||
img
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create() {
|
||||
let t = TestContext::new().await;
|
||||
let blob = BlobObject::create(&t, "foo", b"hello").await.unwrap();
|
||||
@@ -524,28 +534,28 @@ mod tests {
|
||||
assert_eq!(blob.to_abs_path(), t.get_blobdir().join("foo"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_lowercase_ext() {
|
||||
let t = TestContext::new().await;
|
||||
let blob = BlobObject::create(&t, "foo.TXT", b"hello").await.unwrap();
|
||||
assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_as_file_name() {
|
||||
let t = TestContext::new().await;
|
||||
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
|
||||
assert_eq!(blob.as_file_name(), "foo.txt");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_as_rel_path() {
|
||||
let t = TestContext::new().await;
|
||||
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
|
||||
assert_eq!(blob.as_rel_path(), Path::new("foo.txt"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_suffix() {
|
||||
let t = TestContext::new().await;
|
||||
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
|
||||
@@ -554,16 +564,16 @@ mod tests {
|
||||
assert_eq!(blob.suffix(), None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_dup() {
|
||||
let t = TestContext::new().await;
|
||||
BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
|
||||
let foo_path = t.get_blobdir().join("foo.txt");
|
||||
assert!(foo_path.exists().await);
|
||||
assert!(foo_path.exists());
|
||||
BlobObject::create(&t, "foo.txt", b"world").await.unwrap();
|
||||
let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap();
|
||||
while let Some(dirent) = dir.next().await {
|
||||
let fname = dirent.unwrap().file_name();
|
||||
while let Ok(Some(dirent)) = dir.next_entry().await {
|
||||
let fname = dirent.file_name();
|
||||
if fname == foo_path.file_name().unwrap() {
|
||||
assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
|
||||
} else {
|
||||
@@ -574,20 +584,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_double_ext_preserved() {
|
||||
let t = TestContext::new().await;
|
||||
BlobObject::create(&t, "foo.tar.gz", b"hello")
|
||||
.await
|
||||
.unwrap();
|
||||
let foo_path = t.get_blobdir().join("foo.tar.gz");
|
||||
assert!(foo_path.exists().await);
|
||||
assert!(foo_path.exists());
|
||||
BlobObject::create(&t, "foo.tar.gz", b"world")
|
||||
.await
|
||||
.unwrap();
|
||||
let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap();
|
||||
while let Some(dirent) = dir.next().await {
|
||||
let fname = dirent.unwrap().file_name();
|
||||
while let Ok(Some(dirent)) = dir.next_entry().await {
|
||||
let fname = dirent.file_name();
|
||||
if fname == foo_path.file_name().unwrap() {
|
||||
assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
|
||||
} else {
|
||||
@@ -599,7 +609,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_long_names() {
|
||||
let t = TestContext::new().await;
|
||||
let s = "1".repeat(150);
|
||||
@@ -608,7 +618,7 @@ mod tests {
|
||||
assert!(blobname.len() < 128);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_and_copy() {
|
||||
let t = TestContext::new().await;
|
||||
let src = t.dir.path().join("src");
|
||||
@@ -623,10 +633,10 @@ mod tests {
|
||||
.await
|
||||
.is_err());
|
||||
let whoops = t.get_blobdir().join("whoops");
|
||||
assert!(!whoops.exists().await);
|
||||
assert!(!whoops.exists());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_from_path() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -646,7 +656,7 @@ mod tests {
|
||||
let data = fs::read(blob.to_abs_path()).await.unwrap();
|
||||
assert_eq!(data, b"boo");
|
||||
}
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_from_name_long() {
|
||||
let t = TestContext::new().await;
|
||||
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
|
||||
@@ -709,7 +719,7 @@ mod tests {
|
||||
assert!(!stem.contains('?'));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_selfavatar_outside_blobdir() {
|
||||
let t = TestContext::new().await;
|
||||
let avatar_src = t.dir.path().join("avatar.jpg");
|
||||
@@ -721,22 +731,17 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let avatar_blob = t.get_blobdir().join("avatar.jpg");
|
||||
assert!(!avatar_blob.exists().await);
|
||||
assert!(!avatar_blob.exists());
|
||||
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(avatar_blob.exists().await);
|
||||
assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64);
|
||||
assert!(avatar_blob.exists());
|
||||
assert!(tokio::fs::metadata(&avatar_blob).await.unwrap().len() < avatar_bytes.len() as u64);
|
||||
let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap();
|
||||
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
|
||||
|
||||
let img = image::open(avatar_src).unwrap();
|
||||
assert_eq!(img.width(), 1000);
|
||||
assert_eq!(img.height(), 1000);
|
||||
|
||||
let img = image::open(&avatar_blob).unwrap();
|
||||
assert_eq!(img.width(), BALANCED_AVATAR_SIZE);
|
||||
assert_eq!(img.height(), BALANCED_AVATAR_SIZE);
|
||||
check_image_size(avatar_src, 1000, 1000);
|
||||
check_image_size(&avatar_blob, BALANCED_AVATAR_SIZE, BALANCED_AVATAR_SIZE);
|
||||
|
||||
async fn file_size(path_buf: &PathBuf) -> u64 {
|
||||
let file = File::open(path_buf).await.unwrap();
|
||||
@@ -750,12 +755,14 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(file_size(&avatar_blob).await <= 3000);
|
||||
assert!(file_size(&avatar_blob).await > 2000);
|
||||
let img = image::open(&avatar_blob).unwrap();
|
||||
assert!(img.width() > 130);
|
||||
assert_eq!(img.width(), img.height());
|
||||
tokio::task::block_in_place(move || {
|
||||
let img = image::open(&avatar_blob).unwrap();
|
||||
assert!(img.width() > 130);
|
||||
assert_eq!(img.width(), img.height());
|
||||
});
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_selfavatar_in_blobdir() {
|
||||
let t = TestContext::new().await;
|
||||
let avatar_src = t.get_blobdir().join("avatar.png");
|
||||
@@ -766,9 +773,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let img = image::open(&avatar_src).unwrap();
|
||||
assert_eq!(img.width(), 900);
|
||||
assert_eq!(img.height(), 900);
|
||||
check_image_size(&avatar_src, 900, 900);
|
||||
|
||||
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
|
||||
.await
|
||||
@@ -779,12 +784,10 @@ mod tests {
|
||||
avatar_src.with_extension("jpg").to_str().unwrap()
|
||||
);
|
||||
|
||||
let img = image::open(avatar_cfg).unwrap();
|
||||
assert_eq!(img.width(), BALANCED_AVATAR_SIZE);
|
||||
assert_eq!(img.height(), BALANCED_AVATAR_SIZE);
|
||||
check_image_size(avatar_cfg, BALANCED_AVATAR_SIZE, BALANCED_AVATAR_SIZE);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_selfavatar_copy_without_recode() {
|
||||
let t = TestContext::new().await;
|
||||
let avatar_src = t.dir.path().join("avatar.png");
|
||||
@@ -796,20 +799,20 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let avatar_blob = t.get_blobdir().join("avatar.png");
|
||||
assert!(!avatar_blob.exists().await);
|
||||
assert!(!avatar_blob.exists());
|
||||
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(avatar_blob.exists().await);
|
||||
assert!(avatar_blob.exists());
|
||||
assert_eq!(
|
||||
std::fs::metadata(&avatar_blob).unwrap().len(),
|
||||
tokio::fs::metadata(&avatar_blob).await.unwrap().len(),
|
||||
avatar_bytes.len() as u64
|
||||
);
|
||||
let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap();
|
||||
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_recode_image_1() {
|
||||
let bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
|
||||
// BALANCED_IMAGE_SIZE > 1000, the original image size, so the image is not scaled down:
|
||||
@@ -829,7 +832,7 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_recode_image_2() {
|
||||
// The "-rotated" files are rotated by 270 degrees using the Exif metadata
|
||||
let bytes = include_bytes!("../test-data/image/rectangle2000x1800-rotated.jpg");
|
||||
@@ -855,7 +858,7 @@ mod tests {
|
||||
// Do this in parallel to speed up the test a bit
|
||||
// (it still takes very long though)
|
||||
let bytes2 = bytes.clone();
|
||||
let join_handle = async_std::task::spawn(async move {
|
||||
let join_handle = tokio::task::spawn(async move {
|
||||
let img_rotated = send_image_check_mediaquality(
|
||||
Some("0"),
|
||||
&bytes2,
|
||||
@@ -883,10 +886,10 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_correct_rotation(&img_rotated);
|
||||
|
||||
join_handle.await;
|
||||
join_handle.await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_recode_image_3() {
|
||||
let bytes = include_bytes!("../test-data/image/rectangle200x180-rotated.jpg");
|
||||
let img_rotated = send_image_check_mediaquality(Some("0"), bytes, 200, 180, 270, 180, 200)
|
||||
@@ -934,10 +937,10 @@ mod tests {
|
||||
.await?;
|
||||
let file = alice.get_blobdir().join("file.jpg");
|
||||
|
||||
File::create(&file).await?.write_all(bytes).await?;
|
||||
let img = image::open(&file)?;
|
||||
assert_eq!(img.width(), original_width);
|
||||
assert_eq!(img.height(), original_height);
|
||||
fs::write(&file, &bytes)
|
||||
.await
|
||||
.context("failed to write file")?;
|
||||
check_image_size(&file, original_width, original_height);
|
||||
|
||||
let blob = BlobObject::new_from_path(&alice, &file).await?;
|
||||
assert_eq!(blob.get_exif_orientation(&alice).unwrap_or(0), orientation);
|
||||
@@ -949,9 +952,11 @@ mod tests {
|
||||
let alice_msg = alice.get_last_msg().await;
|
||||
assert_eq!(alice_msg.get_width() as u32, compressed_width);
|
||||
assert_eq!(alice_msg.get_height() as u32, compressed_height);
|
||||
let img = image::open(alice_msg.get_file(&alice).unwrap())?;
|
||||
assert_eq!(img.width() as u32, compressed_width);
|
||||
assert_eq!(img.height() as u32, compressed_height);
|
||||
check_image_size(
|
||||
alice_msg.get_file(&alice).unwrap(),
|
||||
compressed_width,
|
||||
compressed_height,
|
||||
);
|
||||
|
||||
let bob_msg = bob.recv_msg(&sent).await;
|
||||
assert_eq!(bob_msg.get_width() as u32, compressed_width);
|
||||
@@ -961,13 +966,11 @@ mod tests {
|
||||
let blob = BlobObject::new_from_path(&bob, &file).await?;
|
||||
assert_eq!(blob.get_exif_orientation(&bob).unwrap_or(0), 0);
|
||||
|
||||
let img = image::open(file)?;
|
||||
assert_eq!(img.width() as u32, compressed_width);
|
||||
assert_eq!(img.height() as u32, compressed_height);
|
||||
let img = check_image_size(file, compressed_width, compressed_height);
|
||||
Ok(img)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_increation_in_blobdir() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;
|
||||
@@ -986,7 +989,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_increation_not_blobdir() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;
|
||||
|
||||
150
src/chat.rs
150
src/chat.rs
@@ -2,11 +2,11 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -3464,10 +3464,10 @@ mod tests {
|
||||
use crate::contact::Contact;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
use async_std::fs::File;
|
||||
use async_std::prelude::*;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_info() {
|
||||
let t = TestContext::new().await;
|
||||
let chat = t.create_chat_with_contact("bob", "bob@example.com").await;
|
||||
@@ -3498,7 +3498,7 @@ mod tests {
|
||||
assert_eq!(info, loaded);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_draft_no_draft() {
|
||||
let t = TestContext::new().await;
|
||||
let chat = t.get_self_chat().await;
|
||||
@@ -3506,14 +3506,14 @@ mod tests {
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_draft_special_chat_id() {
|
||||
let t = TestContext::new().await;
|
||||
let draft = DC_CHAT_ID_LAST_SPECIAL.get_draft(&t).await.unwrap();
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_draft_no_chat() {
|
||||
// This is a weird case, maybe this should be an error but we
|
||||
// do not get this info from the database currently.
|
||||
@@ -3522,7 +3522,7 @@ mod tests {
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_draft() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = &t.get_self_chat().await.id;
|
||||
@@ -3536,7 +3536,7 @@ mod tests {
|
||||
assert_eq!(msg_text, draft_text);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_draft() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;
|
||||
@@ -3557,7 +3557,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forwarding_draft_failing() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = &t.get_self_chat().await.id;
|
||||
@@ -3571,7 +3571,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_draft_stable_ids() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = &t.get_self_chat().await.id;
|
||||
@@ -3616,7 +3616,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_change_quotes_on_reused_message_object() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
|
||||
@@ -3668,7 +3668,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_contact_to_chat_ex_add_self() {
|
||||
// Adding self to a contact should succeed, even though it's pointless.
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -3681,7 +3681,7 @@ mod tests {
|
||||
assert_eq!(added, false);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_modify_chat_multi_device() -> Result<()> {
|
||||
let a1 = TestContext::new_alice().await;
|
||||
let a2 = TestContext::new_alice().await;
|
||||
@@ -3756,7 +3756,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_modify_chat_disordered() -> Result<()> {
|
||||
// Alice creates a group with Bob, Claire and Daisy and then removes Claire and Daisy
|
||||
// (sleep() is needed as otherwise smeared time from Alice looks to Bob like messages from the future which are all set to "now" then)
|
||||
@@ -3770,47 +3770,47 @@ mod tests {
|
||||
send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?;
|
||||
|
||||
add_contact_to_chat(&alice, alice_chat_id, bob_id).await?;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
let add1 = alice.pop_sent_msg().await;
|
||||
|
||||
add_contact_to_chat(&alice, alice_chat_id, claire_id).await?;
|
||||
let add2 = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
add_contact_to_chat(&alice, alice_chat_id, daisy_id).await?;
|
||||
let add3 = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 4);
|
||||
|
||||
remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?;
|
||||
let remove1 = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?;
|
||||
let remove2 = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2);
|
||||
|
||||
// Bob receives the add and deletion messages out of order
|
||||
let bob = TestContext::new_bob().await;
|
||||
bob.recv_msg(&add1).await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
bob.recv_msg(&add3).await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
let bob_chat_id = bob.recv_msg(&add2).await.chat_id;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 4);
|
||||
|
||||
bob.recv_msg(&remove2).await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
bob.recv_msg(&remove1).await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 2);
|
||||
|
||||
@@ -3818,7 +3818,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test that group updates are robust to lost messages and eventual out of order arrival.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_modify_chat_lost() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -3833,11 +3833,11 @@ mod tests {
|
||||
|
||||
send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?;
|
||||
let add = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?;
|
||||
let remove1 = alice.pop_sent_msg().await;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?;
|
||||
let remove2 = alice.pop_sent_msg().await;
|
||||
@@ -3860,7 +3860,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_leave_group() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -3889,7 +3889,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_remove_contact_for_single() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
let bob = Contact::create(&ctx, "", "bob@f.br").await.unwrap();
|
||||
@@ -3913,7 +3913,7 @@ mod tests {
|
||||
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_self_talk() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat = &t.get_self_chat().await;
|
||||
@@ -3944,7 +3944,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_device_msg_unlabelled() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -3979,7 +3979,7 @@ mod tests {
|
||||
assert_eq!(msg2.chat_id.get_msg_cnt(&t).await.unwrap(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_device_msg_labelled() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -4029,7 +4029,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_device_msg_label_only() {
|
||||
let t = TestContext::new().await;
|
||||
let res = add_device_msg(&t, Some(""), None).await;
|
||||
@@ -4049,7 +4049,7 @@ mod tests {
|
||||
assert!(!msg_id.as_ref().unwrap().is_unset());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_was_device_msg_ever_added() {
|
||||
let t = TestContext::new().await;
|
||||
add_device_msg(&t, Some("some-label"), None).await.ok();
|
||||
@@ -4069,7 +4069,7 @@ mod tests {
|
||||
assert!(was_device_msg_ever_added(&t, "").await.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_device_chat() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -4089,7 +4089,7 @@ mod tests {
|
||||
assert_eq!(chatlist_len(&t, 0).await, 0)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_device_chat_cannot_sent() {
|
||||
let t = TestContext::new().await;
|
||||
t.update_device_chats().await.unwrap();
|
||||
@@ -4106,7 +4106,7 @@ mod tests {
|
||||
assert!(forward_msgs(&t, &[msg_id], device_chat_id).await.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_and_reset_all_device_msgs() {
|
||||
let t = TestContext::new().await;
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
@@ -4138,7 +4138,7 @@ mod tests {
|
||||
.len()
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_archive() {
|
||||
// create two chats
|
||||
let t = TestContext::new().await;
|
||||
@@ -4241,7 +4241,7 @@ mod tests {
|
||||
assert_eq!(chatlist_len(&t, DC_GCL_ARCHIVED_ONLY).await, 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_unarchive_if_muted() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -4335,7 +4335,7 @@ mod tests {
|
||||
result
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pinned() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -4347,9 +4347,9 @@ mod tests {
|
||||
.await
|
||||
.unwrap()
|
||||
.chat_id;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
let chat_id2 = t.get_self_chat().await.id;
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
let chat_id3 = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -4392,7 +4392,7 @@ mod tests {
|
||||
assert_eq!(chatlist, vec![chat_id3, chat_id2, chat_id1]);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_chat_name() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
||||
@@ -4410,7 +4410,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_same_chat_twice() {
|
||||
let context = TestContext::new().await;
|
||||
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de")
|
||||
@@ -4433,7 +4433,7 @@ mod tests {
|
||||
assert_eq!(chat2.name, chat.name);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_shall_attach_selfavatar() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -4451,7 +4451,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_mute_duration() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
||||
@@ -4502,7 +4502,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_info_msg() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -4519,7 +4519,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_info_msg_with_cmd() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -4549,7 +4549,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_protection() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
||||
@@ -4617,7 +4617,7 @@ mod tests {
|
||||
assert_eq!(msg.get_state(), MessageState::OutDelivered); // as bcc-self is disabled and there is nobody else in the chat
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_lookup_by_contact_id() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
|
||||
@@ -4660,7 +4660,7 @@ mod tests {
|
||||
assert!(found.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_lookup_self_by_contact_id() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
|
||||
@@ -4679,7 +4679,7 @@ mod tests {
|
||||
assert_eq!(chat.blocked, Blocked::Not);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_group_with_removed_message_id() -> Result<()> {
|
||||
// Alice creates a group with Bob, sends a message to bob
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -4733,7 +4733,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_marknoticed_chat() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat = t.create_chat_with_contact("bob", "bob@example.org").await;
|
||||
@@ -4778,7 +4778,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_request_fresh_messages() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -4828,7 +4828,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_request_archive() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -4868,7 +4868,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_classic_email_chat() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -4913,7 +4913,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_get_color() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
|
||||
@@ -4956,7 +4956,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sticker_png() -> Result<()> {
|
||||
test_sticker(
|
||||
"sticker.png",
|
||||
@@ -4967,7 +4967,7 @@ mod tests {
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sticker_jpeg() -> Result<()> {
|
||||
test_sticker(
|
||||
"sticker.jpg",
|
||||
@@ -4978,7 +4978,7 @@ mod tests {
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sticker_gif() -> Result<()> {
|
||||
test_sticker(
|
||||
"sticker.gif",
|
||||
@@ -4989,7 +4989,7 @@ mod tests {
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sticker_forward() -> Result<()> {
|
||||
// create chats
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -5020,7 +5020,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forward() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -5041,7 +5041,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forward_info_msg() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -5067,7 +5067,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forward_quote() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -5102,7 +5102,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forward_group() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -5152,7 +5152,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_only_minimal_data_are_forwarded() -> Result<()> {
|
||||
// send a message from Alice to a group with Bob
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -5194,7 +5194,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resend_own_message() -> Result<()> {
|
||||
// Alice creates group with Bob and sends an initial message
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -5247,7 +5247,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resend_foreign_message_fails() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
|
||||
@@ -5266,7 +5266,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resend_opportunistically_encryption() -> Result<()> {
|
||||
// Alice creates group with Bob and sends an initial message
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -5303,7 +5303,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resend_info_message_fails() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
|
||||
@@ -5327,7 +5327,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_can_send_group() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = Contact::create(&alice, "", "bob@f.br").await?;
|
||||
@@ -5353,7 +5353,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_broadcast() -> Result<()> {
|
||||
// create two context, send two messages so both know the other
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -5396,7 +5396,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_for_contact_with_blocked() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let (contact_id, _) =
|
||||
@@ -5430,7 +5430,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_get_encryption_info() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
@@ -376,7 +376,7 @@ mod tests {
|
||||
use crate::stock_str::StockMessage;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_try_load() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
|
||||
@@ -432,7 +432,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sort_self_talk_up_on_forward() {
|
||||
let t = TestContext::new().await;
|
||||
t.update_device_chats().await.unwrap();
|
||||
@@ -457,7 +457,7 @@ mod tests {
|
||||
.is_self_talk());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_special_chat_names() {
|
||||
let t = TestContext::new().await;
|
||||
t.update_device_chats().await.unwrap();
|
||||
@@ -488,7 +488,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_single_chat() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -548,7 +548,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -610,7 +610,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_summary_unwrap() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
|
||||
|
||||
@@ -454,7 +454,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_media_quality_config_option() {
|
||||
let t = TestContext::new().await;
|
||||
let media_quality = t.get_config_int(Config::MediaQuality).await.unwrap();
|
||||
@@ -471,7 +471,7 @@ mod tests {
|
||||
assert_eq!(media_quality, constants::MediaQuality::Worse);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ui_config() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -493,7 +493,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Regression test for https://github.com/deltachat/deltachat-core-rust/issues/3012
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_config_bool() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -505,7 +505,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_self_addrs() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -558,7 +558,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_change_primary_self_addr() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
|
||||
@@ -6,9 +6,10 @@ mod read_url;
|
||||
mod server_params;
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::prelude::*;
|
||||
use async_std::task;
|
||||
use futures::FutureExt;
|
||||
use futures_lite::FutureExt as _;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use tokio::task;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
@@ -55,8 +56,6 @@ impl Context {
|
||||
|
||||
/// Configures this account with the currently set parameters.
|
||||
pub async fn configure(&self) -> Result<()> {
|
||||
use futures::future::FutureExt;
|
||||
|
||||
ensure!(
|
||||
self.scheduler.read().await.is_none(),
|
||||
"cannot configure, already running"
|
||||
@@ -404,7 +403,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
progress!(ctx, 850);
|
||||
|
||||
// Wait for SMTP configuration
|
||||
match smtp_config_task.await {
|
||||
match smtp_config_task.await.unwrap() {
|
||||
Ok(smtp_param) => {
|
||||
param.smtp = smtp_param;
|
||||
}
|
||||
@@ -447,7 +446,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
ctx.interrupt_inbox(InterruptInfo::new(false)).await;
|
||||
|
||||
progress!(ctx, 940);
|
||||
update_device_chats_handle.await?;
|
||||
update_device_chats_handle.await??;
|
||||
|
||||
ctx.sql.set_raw_config_bool("configured", true).await?;
|
||||
|
||||
@@ -549,7 +548,7 @@ async fn try_imap_one_param(
|
||||
);
|
||||
info!(context, "Trying: {}", inf);
|
||||
|
||||
let (_s, r) = async_std::channel::bounded(1);
|
||||
let (_s, r) = async_channel::bounded(1);
|
||||
|
||||
let mut imap = match Imap::new(param, socks5_config.clone(), addr, provider_strict_tls, r).await
|
||||
{
|
||||
@@ -634,10 +633,13 @@ async fn nicer_configuration_error(context: &Context, errors: Vec<ConfigurationE
|
||||
return "no error".to_string();
|
||||
};
|
||||
|
||||
if errors
|
||||
.iter()
|
||||
.all(|e| e.msg.to_lowercase().contains("could not resolve"))
|
||||
{
|
||||
if errors.iter().all(|e| {
|
||||
e.msg.to_lowercase().contains("could not resolve")
|
||||
|| e.msg
|
||||
.to_lowercase()
|
||||
.contains("temporary failure in name resolution")
|
||||
|| e.msg.to_lowercase().contains("name or service not known")
|
||||
}) {
|
||||
return stock_str::error_no_network(context).await;
|
||||
}
|
||||
|
||||
@@ -678,7 +680,7 @@ mod tests {
|
||||
use crate::config::Config;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_no_panic_on_bad_credentials() {
|
||||
let t = TestContext::new().await;
|
||||
t.set_config(Config::Addr, Some("probably@unexistant.addr"))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::context::Context;
|
||||
use anyhow::{anyhow, format_err};
|
||||
|
||||
use anyhow::format_err;
|
||||
use anyhow::Context as _;
|
||||
use crate::context::Context;
|
||||
|
||||
pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
match read_url_inner(context, url).await {
|
||||
@@ -16,24 +15,27 @@ pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_url_inner(context: &Context, mut url: &str) -> anyhow::Result<String> {
|
||||
let mut _temp; // For the borrow checker
|
||||
pub async fn read_url_inner(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
let client = reqwest::Client::new();
|
||||
let mut url = url.to_string();
|
||||
|
||||
// Follow up to 10 http-redirects
|
||||
for _i in 0..10 {
|
||||
let mut response = surf::get(url).send().await.map_err(|e| e.into_inner())?;
|
||||
let response = client.get(&url).send().await?;
|
||||
if response.status().is_redirection() {
|
||||
_temp = response
|
||||
.header("location")
|
||||
.context("Redirection doesn't have a target location")?
|
||||
let headers = response.headers();
|
||||
let header = headers
|
||||
.get_all("location")
|
||||
.iter()
|
||||
.last()
|
||||
.to_string();
|
||||
info!(context, "Following redirect to {}", _temp);
|
||||
url = &_temp;
|
||||
.ok_or_else(|| anyhow!("Redirection doesn't have a target location"))?
|
||||
.to_str()?;
|
||||
info!(context, "Following redirect to {}", header);
|
||||
url = header.to_string();
|
||||
continue;
|
||||
}
|
||||
|
||||
return response.body_string().await.map_err(|e| e.into_inner());
|
||||
return response.text().await.map_err(Into::into);
|
||||
}
|
||||
|
||||
Err(format_err!("Followed 10 redirections"))
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
@@ -1438,8 +1438,8 @@ fn split_address_book(book: &str) -> Vec<(&str, &str)> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_std::fs::File;
|
||||
use async_std::io::WriteExt;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -1508,7 +1508,7 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_contacts() -> Result<()> {
|
||||
let context = TestContext::new().await;
|
||||
|
||||
@@ -1572,7 +1572,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_is_self_addr() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(t.is_self_addr("me@me.org").await?, false);
|
||||
@@ -1584,7 +1584,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_or_lookup() {
|
||||
// add some contacts, this also tests add_address_book()
|
||||
let t = TestContext::new().await;
|
||||
@@ -1685,7 +1685,7 @@ mod tests {
|
||||
assert!(!contact.is_blocked());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_name_changes() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -1797,7 +1797,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -1825,7 +1825,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remote_authnames() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -1876,7 +1876,7 @@ mod tests {
|
||||
assert_eq!(contact.get_display_name(), "bob3");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remote_authnames_create_empty() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -1925,7 +1925,7 @@ mod tests {
|
||||
///
|
||||
/// In the past, "Not Bob" name was stuck until "Bob" changed the name to "Not Bob" and back in
|
||||
/// the "From:" field or user set the name to empty string manually.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remote_authnames_update_to() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -1958,7 +1958,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remote_authnames_edit_empty() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -1995,7 +1995,7 @@ mod tests {
|
||||
assert!(addr_cmp(" mailto:AA@AA.ORG", "Aa@Aa.orG"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_name_in_address() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -2034,7 +2034,7 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_lookup_id_by_addr() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -2059,7 +2059,7 @@ mod tests {
|
||||
assert_eq!(id, Some(ContactId::SELF));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_get_color() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let contact_id = Contact::create(&t, "name", "name@example.net").await?;
|
||||
@@ -2078,7 +2078,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_contact_get_encrinfo() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -2123,7 +2123,7 @@ CCCB 5AA9 F6E1 141C 9431
|
||||
|
||||
/// Tests that status is synchronized when sending encrypted BCC-self messages and not
|
||||
/// synchronized when the message is not encrypted.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_synchronize_status() -> Result<()> {
|
||||
// Alice has two devices.
|
||||
let alice1 = TestContext::new_alice().await;
|
||||
@@ -2188,7 +2188,7 @@ CCCB 5AA9 F6E1 141C 9431
|
||||
}
|
||||
|
||||
/// Tests that DC_EVENT_SELFAVATAR_CHANGED is emitted on avatar changes.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_selfavatar_changed_event() -> Result<()> {
|
||||
// Alice has two devices.
|
||||
let alice1 = TestContext::new_alice().await;
|
||||
@@ -2247,7 +2247,7 @@ CCCB 5AA9 F6E1 141C 9431
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_last_seen() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ffi::OsString;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use async_std::{
|
||||
channel::{self, Receiver, Sender},
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
|
||||
use crate::chat::{get_chat_cnt, ChatId};
|
||||
use crate::config::Config;
|
||||
@@ -75,7 +74,7 @@ pub struct InnerContext {
|
||||
/// The text of the last error logged and emitted as an event.
|
||||
/// If the ui wants to display an error after a failure,
|
||||
/// `last_error` should be used to avoid races with the event thread.
|
||||
pub(crate) last_error: RwLock<String>,
|
||||
pub(crate) last_error: std::sync::RwLock<String>,
|
||||
}
|
||||
|
||||
/// The state of ongoing process.
|
||||
@@ -115,7 +114,7 @@ pub fn get_info() -> BTreeMap<&'static str, String> {
|
||||
|
||||
impl Context {
|
||||
/// Creates new context and opens the database.
|
||||
pub async fn new(dbfile: PathBuf, id: u32, events: Events) -> Result<Context> {
|
||||
pub async fn new(dbfile: &Path, id: u32, events: Events) -> Result<Context> {
|
||||
let context = Self::new_closed(dbfile, id, events).await?;
|
||||
|
||||
// Open the database if is not encrypted.
|
||||
@@ -126,15 +125,15 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Creates new context without opening the database.
|
||||
pub async fn new_closed(dbfile: PathBuf, id: u32, events: Events) -> Result<Context> {
|
||||
pub async fn new_closed(dbfile: &Path, id: u32, events: Events) -> Result<Context> {
|
||||
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().await {
|
||||
async_std::fs::create_dir_all(&blobdir).await?;
|
||||
if !blobdir.exists() {
|
||||
tokio::fs::create_dir_all(&blobdir).await?;
|
||||
}
|
||||
let context = Context::with_blobdir(dbfile, blobdir, id, events).await?;
|
||||
let context = Context::with_blobdir(dbfile.into(), blobdir, id, events).await?;
|
||||
Ok(context)
|
||||
}
|
||||
|
||||
@@ -172,7 +171,7 @@ impl Context {
|
||||
events: Events,
|
||||
) -> Result<Context> {
|
||||
ensure!(
|
||||
blobdir.is_dir().await,
|
||||
blobdir.is_dir(),
|
||||
"Blobdir does not exist: {}",
|
||||
blobdir.display()
|
||||
);
|
||||
@@ -193,7 +192,7 @@ impl Context {
|
||||
quota: RwLock::new(None),
|
||||
creation_time: std::time::SystemTime::now(),
|
||||
last_full_folder_scan: Mutex::new(None),
|
||||
last_error: RwLock::new("".to_string()),
|
||||
last_error: std::sync::RwLock::new("".to_string()),
|
||||
};
|
||||
|
||||
let ctx = Context {
|
||||
@@ -643,14 +642,14 @@ impl Context {
|
||||
Ok(mvbox.as_deref() == Some(folder_name))
|
||||
}
|
||||
|
||||
pub(crate) fn derive_blobdir(dbfile: &PathBuf) -> PathBuf {
|
||||
pub(crate) fn derive_blobdir(dbfile: &Path) -> PathBuf {
|
||||
let mut blob_fname = OsString::new();
|
||||
blob_fname.push(dbfile.file_name().unwrap_or_default());
|
||||
blob_fname.push("-blobs");
|
||||
dbfile.with_file_name(blob_fname)
|
||||
}
|
||||
|
||||
pub(crate) fn derive_walfile(dbfile: &PathBuf) -> PathBuf {
|
||||
pub(crate) fn derive_walfile(dbfile: &Path) -> PathBuf {
|
||||
let mut wal_fname = OsString::new();
|
||||
wal_fname.push(dbfile.file_name().unwrap_or_default());
|
||||
wal_fname.push("-wal");
|
||||
@@ -679,19 +678,19 @@ mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_wrong_db() -> Result<()> {
|
||||
let tmp = tempfile::tempdir()?;
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
std::fs::write(&dbfile, b"123")?;
|
||||
let res = Context::new(dbfile.into(), 1, Events::new()).await?;
|
||||
tokio::fs::write(&dbfile, b"123").await?;
|
||||
let res = Context::new(&dbfile, 1, Events::new()).await?;
|
||||
|
||||
// Broken database is indistinguishable from encrypted one.
|
||||
assert_eq!(res.is_open().await, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_fresh_msgs() {
|
||||
let t = TestContext::new().await;
|
||||
let fresh = t.get_fresh_msgs().await.unwrap();
|
||||
@@ -718,7 +717,7 @@ mod tests {
|
||||
dc_receive_imf(t, msg.as_bytes(), false).await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_fresh_msgs_and_muted_chats() {
|
||||
// receive various mails in 3 chats
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -768,7 +767,7 @@ mod tests {
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 9); // claire is counted again
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_fresh_msgs_and_muted_until() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let bob = t.create_chat_with_contact("", "bob@g.it").await;
|
||||
@@ -826,61 +825,61 @@ mod tests {
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_blobdir_exists() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
Context::new(dbfile.into(), 1, Events::new()).await.unwrap();
|
||||
Context::new(&dbfile, 1, Events::new()).await.unwrap();
|
||||
let blobdir = tmp.path().join("db.sqlite-blobs");
|
||||
assert!(blobdir.is_dir());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async 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(dbfile.into(), 1, Events::new()).await;
|
||||
tokio::fs::write(&blobdir, b"123").await.unwrap();
|
||||
let res = Context::new(&dbfile, 1, Events::new()).await;
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async 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(dbfile.into(), 1, Events::new()).await.unwrap();
|
||||
Context::new(&dbfile, 1, Events::new()).await.unwrap();
|
||||
assert!(subdir.is_dir());
|
||||
assert!(dbfile2.is_file());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_with_empty_blobdir() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
let blobdir = PathBuf::new();
|
||||
let res = Context::with_blobdir(dbfile.into(), blobdir, 1, Events::new()).await;
|
||||
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new()).await;
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async 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(dbfile.into(), blobdir.into(), 1, Events::new()).await;
|
||||
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new()).await;
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn no_crashes_on_context_deref() {
|
||||
let t = TestContext::new().await;
|
||||
std::mem::drop(t);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_info() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -896,7 +895,7 @@ mod tests {
|
||||
assert_eq!(info.get("level").unwrap(), "awesome");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_info_completeness() {
|
||||
// For easier debugging,
|
||||
// get_info() shall return all important information configurable by the Config-values.
|
||||
@@ -944,7 +943,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_search_msgs() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let self_talk = ChatId::create_for_contact(&alice, ContactId::SELF).await?;
|
||||
@@ -1000,7 +999,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_limit_search_msgs() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let chat = alice
|
||||
@@ -1033,13 +1032,13 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_check_passphrase() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
|
||||
let id = 1;
|
||||
let context = Context::new_closed(dbfile.clone().into(), id, Events::new())
|
||||
let context = Context::new_closed(&dbfile, id, Events::new())
|
||||
.await
|
||||
.context("failed to create context")?;
|
||||
assert_eq!(context.open("foo".to_string()).await?, true);
|
||||
@@ -1047,7 +1046,7 @@ mod tests {
|
||||
drop(context);
|
||||
|
||||
let id = 2;
|
||||
let context = Context::new(dbfile.into(), id, Events::new())
|
||||
let context = Context::new(&dbfile, id, Events::new())
|
||||
.await
|
||||
.context("failed to create context")?;
|
||||
assert_eq!(context.is_open().await, false);
|
||||
@@ -1058,7 +1057,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ongoing() -> Result<()> {
|
||||
let context = TestContext::new().await;
|
||||
|
||||
|
||||
@@ -2203,8 +2203,8 @@ async fn add_or_lookup_contact_by_addr(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use async_std::fs::{self, File};
|
||||
use async_std::io::WriteExt;
|
||||
use tokio::fs::{self, File};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -2216,7 +2216,7 @@ mod tests {
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_grpid_simple() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
@@ -2234,7 +2234,7 @@ mod tests {
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_grpid_from_multiple() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
@@ -2283,7 +2283,7 @@ mod tests {
|
||||
\n\
|
||||
hello\n";
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_chats_only() {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(t.get_config_int(Config::ShowEmails).await.unwrap(), 0);
|
||||
@@ -2306,7 +2306,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_accepted_contact_unknown() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
|
||||
@@ -2317,7 +2317,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 0);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_accepted_contact_known() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
|
||||
@@ -2330,7 +2330,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 0);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_accepted_contact_accepted() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
|
||||
@@ -2368,7 +2368,7 @@ mod tests {
|
||||
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_all() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -2387,7 +2387,7 @@ mod tests {
|
||||
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_read_receipt_and_unarchive() -> Result<()> {
|
||||
// create alice's account
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -2501,7 +2501,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_no_from() {
|
||||
// if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
// are very rare, however, we have to add them to the database
|
||||
@@ -2533,7 +2533,7 @@ mod tests {
|
||||
assert!(chats.get_msg_id(0).is_ok());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_escaped_from() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
|
||||
@@ -2566,7 +2566,7 @@ mod tests {
|
||||
assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_escaped_recipients() {
|
||||
let t = TestContext::new_alice().await;
|
||||
Contact::create(&t, "foobar", "foobar@example.com")
|
||||
@@ -2608,7 +2608,7 @@ mod tests {
|
||||
assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cc_to_contact() {
|
||||
let t = TestContext::new_alice().await;
|
||||
Contact::create(&t, "foobar", "foobar@example.com")
|
||||
@@ -2643,7 +2643,7 @@ mod tests {
|
||||
assert_eq!(contact.get_display_name(), "Carl");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_tiscali() {
|
||||
test_parse_ndn(
|
||||
"alice@tiscali.it",
|
||||
@@ -2655,7 +2655,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_testrun() {
|
||||
test_parse_ndn(
|
||||
"alice@testrun.org",
|
||||
@@ -2667,7 +2667,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_yahoo() {
|
||||
test_parse_ndn(
|
||||
"alice@yahoo.com",
|
||||
@@ -2679,7 +2679,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_gmail() {
|
||||
test_parse_ndn(
|
||||
"alice@gmail.com",
|
||||
@@ -2691,7 +2691,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_gmx() {
|
||||
test_parse_ndn(
|
||||
"alice@gmx.com",
|
||||
@@ -2703,7 +2703,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_posteo() {
|
||||
test_parse_ndn(
|
||||
"alice@posteo.org",
|
||||
@@ -2715,7 +2715,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_testrun_2() {
|
||||
test_parse_ndn(
|
||||
"alice@example.org",
|
||||
@@ -2781,7 +2781,7 @@ mod tests {
|
||||
assert_eq!(msg.error(), error_msg.map(|error| error.to_string()));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_group_msg() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr("alice@gmail.com").await;
|
||||
@@ -2841,7 +2841,7 @@ mod tests {
|
||||
Message::load_from_db(context, msg_id).await.unwrap()
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_only_mail() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let msg = load_imf_email(&t, include_bytes!("../test-data/message/wrong-html.eml")).await;
|
||||
@@ -2874,7 +2874,7 @@ mod tests {
|
||||
\n\
|
||||
hello back\n";
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_github_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -2941,7 +2941,7 @@ mod tests {
|
||||
\n\
|
||||
body 4\n";
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_classic_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
@@ -2987,7 +2987,7 @@ Hello mailinglist!\r\n"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_other_device_writes_to_mailinglist() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -3037,7 +3037,7 @@ Hello mailinglist!\r\n"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_block_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
@@ -3071,7 +3071,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_block_then_unblock() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3104,7 +3104,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_not_now() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
@@ -3137,7 +3137,7 @@ Hello mailinglist!\r\n"
|
||||
assert!(chat.is_contact_request());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_accept() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
@@ -3165,7 +3165,7 @@ Hello mailinglist!\r\n"
|
||||
assert!(chat.can_send(&t.ctx).await.unwrap());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_majordomo_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3214,7 +3214,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailchimp_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3244,7 +3244,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(chat.name, "Atlas Obscura");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dhl_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3269,7 +3269,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(chat.name, "DHL Paket");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dpd_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3294,7 +3294,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(chat.name, "DPD");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xt_local_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -3324,7 +3324,7 @@ Hello mailinglist!\r\n"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xing_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -3345,7 +3345,7 @@ Hello mailinglist!\r\n"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ttline_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -3366,7 +3366,7 @@ Hello mailinglist!\r\n"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3397,7 +3397,7 @@ Hello mailinglist!\r\n"
|
||||
assert_eq!(chat.name, "Intern");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3422,7 +3422,7 @@ Hello mailinglist!\r\n"
|
||||
|
||||
/// Test that the changes from apply_mailinglist_changes() are also applied
|
||||
/// if the message is assigned to the chat by In-Reply-To
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_apply_mailinglist_changes_assigned_by_reply() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3464,7 +3464,7 @@ Hello mailinglist!\r\n"
|
||||
)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_show_tokens_in_contacts_list() {
|
||||
check_dont_show_in_contacts_list(
|
||||
"reply+OGHVYCLVBEGATYBICAXBIRQATABUOTUCERABERAHNO@reply.github.com",
|
||||
@@ -3472,7 +3472,7 @@ Hello mailinglist!\r\n"
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_show_noreply_in_contacts_list() {
|
||||
check_dont_show_in_contacts_list("noreply@github.com").await;
|
||||
}
|
||||
@@ -3510,7 +3510,7 @@ YEAAAAAA!.
|
||||
assert!(contacts.is_empty()); // The contact should not have been added to the db
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pdf_filename_simple() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let msg = load_imf_email(
|
||||
@@ -3523,7 +3523,7 @@ YEAAAAAA!.
|
||||
assert_eq!(msg.param.get(Param::File).unwrap(), "$BLOBDIR/simple.pdf");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pdf_filename_continuation() {
|
||||
// test filenames split across multiple header lines, see rfc 2231
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -3550,7 +3550,7 @@ YEAAAAAA!.
|
||||
/// or mua may use multipart/related not correctly -
|
||||
/// so this test is in competition with parse_thunderbird_html_embedded_image()
|
||||
/// that wants the image to be kept in the chat.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_many_images() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -3571,7 +3571,7 @@ YEAAAAAA!.
|
||||
|
||||
/// Test that classical MUA messages are assigned to group chats based on the `In-Reply-To`
|
||||
/// header.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_in_reply_to() {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr("bob@example.com").await;
|
||||
@@ -3636,7 +3636,7 @@ YEAAAAAA!.
|
||||
|
||||
/// Test that classical MUA messages are assigned to group chats
|
||||
/// based on the `In-Reply-To` header for two-member groups.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_in_reply_to_two_member_group() {
|
||||
let t = TestContext::new().await;
|
||||
t.configure_addr("bob@example.com").await;
|
||||
@@ -3738,7 +3738,7 @@ YEAAAAAA!.
|
||||
assert_eq!(msg.get_text().unwrap(), "private reply");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_save_mime_headers_off() -> anyhow::Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -3753,7 +3753,7 @@ YEAAAAAA!.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_save_mime_headers_on() -> anyhow::Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config_bool(Config::SaveMimeHeaders, true).await?;
|
||||
@@ -3924,7 +3924,7 @@ YEAAAAAA!.
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_nondc() {
|
||||
// Bob, the other supporter, answers with a classic MUA.
|
||||
let bob_answer = b"To: support@example.org, claire@example.org\n\
|
||||
@@ -3944,7 +3944,7 @@ YEAAAAAA!.
|
||||
check_alias_reply(bob_answer, false, false).await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_answer_from_dc() {
|
||||
// Bob, the other supporter, answers with Delta Chat.
|
||||
let bob_answer = b"To: support@example.org, claire@example.org\n\
|
||||
@@ -3968,7 +3968,7 @@ YEAAAAAA!.
|
||||
check_alias_reply(bob_answer, false, false).await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_assign_to_trash_by_parent() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -4016,7 +4016,7 @@ YEAAAAAA!.
|
||||
assert_eq!(msg.text.unwrap(), "Reply");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_show_all_outgoing_msgs_in_self_chat() {
|
||||
// Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>:
|
||||
// Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to
|
||||
@@ -4043,7 +4043,7 @@ Message content",
|
||||
assert_ne!(msg.chat_id, t.get_self_chat().await.id);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_classic_mail_creates_chat() {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -4073,7 +4073,7 @@ Message content",
|
||||
assert_eq!(msg.get_text().unwrap(), "Subj – Message content");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_duplicate_message() -> Result<()> {
|
||||
// Test that duplicate messages are ignored based on the Message-ID
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -4132,7 +4132,7 @@ Second signature";
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_private_classical_reply() {
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -4216,7 +4216,7 @@ Private reply"#,
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_private_chat_reply() {
|
||||
for (outgoing_is_classical, outgoing_has_multiple_recipients) in
|
||||
&[(true, true), (false, true), (false, false)]
|
||||
@@ -4312,7 +4312,7 @@ Sent with my Delta Chat Messenger: https://delta.chat
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_nonprivate_classical_reply() {
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -4421,7 +4421,7 @@ Reply to all"#,
|
||||
/// messages have the same recipient lists and only differ in the subject and message contents.
|
||||
/// The messages can be properly assigned to chats only using the In-Reply-To or References
|
||||
/// headers.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_chat_assignment_adhoc() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_alice().await;
|
||||
@@ -4501,7 +4501,7 @@ Second thread."#;
|
||||
}
|
||||
|
||||
/// Test that read receipts don't create chats.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_read_receipts_dont_create_chats() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -4534,7 +4534,7 @@ Second thread."#;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_gmx_forwarded_msg() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -4554,7 +4554,7 @@ Second thread."#;
|
||||
}
|
||||
|
||||
/// Tests that user is notified about new incoming contact requests.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_incoming_contact_request() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -4579,7 +4579,7 @@ Second thread."#;
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_parent_message() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -4642,7 +4642,7 @@ Message with references."#;
|
||||
}
|
||||
|
||||
/// Test a message with RFC 1847 encapsulation as created by Thunderbird.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_rfc1847_encapsulation() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -4668,7 +4668,7 @@ Message with references."#;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_invalid_to_address() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -4680,7 +4680,7 @@ Message with references."#;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_reply_from_different_addr() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
@@ -4745,7 +4745,7 @@ Reply from different address
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_long_filenames() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
@@ -4800,7 +4800,7 @@ Reply from different address
|
||||
}
|
||||
|
||||
/// Tests that contact request is accepted automatically on outgoing message.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_accept_outgoing() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice1 = tcm.alice().await;
|
||||
@@ -4845,7 +4845,7 @@ Reply from different address
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_private_reply_multidevice() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice1 = tcm.alice().await;
|
||||
@@ -4922,7 +4922,7 @@ Reply from different address
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_no_private_reply_to_blocked_account() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
|
||||
@@ -5,20 +5,19 @@ use core::cmp::{max, min};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::from_utf8;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use async_std::prelude::*;
|
||||
use async_std::{fs, io};
|
||||
|
||||
use anyhow::{bail, Error, Result};
|
||||
use chrono::{Local, TimeZone};
|
||||
use futures::StreamExt;
|
||||
use mailparse::dateparse;
|
||||
use mailparse::headers::Headers;
|
||||
use mailparse::MailHeaderMap;
|
||||
use rand::{thread_rng, Rng};
|
||||
use tokio::{fs, io};
|
||||
|
||||
use crate::chat::{add_device_msg, add_device_msg_with_importance};
|
||||
use crate::constants::{DC_ELLIPSIS, DC_OUTDATED_WARNING_DAYS};
|
||||
@@ -278,7 +277,7 @@ pub fn dc_get_filemeta(buf: &[u8]) -> Result<(u32, u32), Error> {
|
||||
///
|
||||
/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path.
|
||||
/// Otherwise, returns path as is.
|
||||
pub(crate) fn dc_get_abs_path<P: AsRef<Path>>(context: &Context, path: P) -> PathBuf {
|
||||
pub(crate) fn dc_get_abs_path(context: &Context, path: impl AsRef<Path>) -> PathBuf {
|
||||
let p: &Path = path.as_ref();
|
||||
if let Ok(p) = p.strip_prefix("$BLOBDIR") {
|
||||
context.get_blobdir().join(p)
|
||||
@@ -297,10 +296,10 @@ pub(crate) async fn dc_get_filebytes(context: &Context, path: impl AsRef<Path>)
|
||||
|
||||
pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) -> bool {
|
||||
let path_abs = dc_get_abs_path(context, &path);
|
||||
if !path_abs.exists().await {
|
||||
if !path_abs.exists() {
|
||||
return false;
|
||||
}
|
||||
if !path_abs.is_file().await {
|
||||
if !path_abs.is_file() {
|
||||
warn!(
|
||||
context,
|
||||
"refusing to delete non-file \"{}\".",
|
||||
@@ -323,8 +322,9 @@ pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) ->
|
||||
}
|
||||
|
||||
pub async fn dc_delete_files_in_dir(context: &Context, path: impl AsRef<Path>) {
|
||||
match async_std::fs::read_dir(path).await {
|
||||
Ok(mut read_dir) => {
|
||||
match tokio::fs::read_dir(path).await {
|
||||
Ok(read_dir) => {
|
||||
let mut read_dir = tokio_stream::wrappers::ReadDirStream::new(read_dir);
|
||||
while let Some(entry) = read_dir.next().await {
|
||||
match entry {
|
||||
Ok(file) => {
|
||||
@@ -344,7 +344,7 @@ pub(crate) async fn dc_create_folder(
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<(), io::Error> {
|
||||
let path_abs = dc_get_abs_path(context, &path);
|
||||
if !path_abs.exists().await {
|
||||
if !path_abs.exists() {
|
||||
match fs::create_dir_all(path_abs).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
@@ -655,7 +655,7 @@ mod tests {
|
||||
assert_eq!(hop_info, expected)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_receive_headers_integration() {
|
||||
let raw = include_bytes!("../test-data/message/mail_with_cc.txt");
|
||||
let expected = r"State: Fresh
|
||||
@@ -873,7 +873,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_file_handling() {
|
||||
let t = TestContext::new().await;
|
||||
let context = &t;
|
||||
@@ -889,8 +889,8 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
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!(dc_file_exist!(context, "$BLOBDIR/foobar"));
|
||||
assert!(!dc_file_exist!(context, "$BLOBDIR/foobarx"));
|
||||
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/foobar").await, 7);
|
||||
|
||||
let abs_path = context
|
||||
@@ -899,23 +899,23 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
assert!(dc_file_exist!(context, &abs_path).await);
|
||||
assert!(dc_file_exist!(context, &abs_path));
|
||||
|
||||
assert!(dc_delete_file(context, "$BLOBDIR/foobar").await);
|
||||
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder")
|
||||
.await
|
||||
.is_ok());
|
||||
assert!(dc_file_exist!(context, "$BLOBDIR/foobar-folder").await);
|
||||
assert!(dc_file_exist!(context, "$BLOBDIR/foobar-folder"));
|
||||
assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder").await);
|
||||
|
||||
let fn0 = "$BLOBDIR/data.data";
|
||||
assert!(dc_write_file(context, &fn0, b"content").await.is_ok());
|
||||
|
||||
assert!(dc_delete_file(context, &fn0).await);
|
||||
assert!(!dc_file_exist!(context, &fn0).await);
|
||||
assert!(!dc_file_exist!(context, &fn0));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_smeared_timestamp() {
|
||||
let t = TestContext::new().await;
|
||||
assert_ne!(
|
||||
@@ -931,7 +931,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_smeared_timestamps() {
|
||||
let t = TestContext::new().await;
|
||||
let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1;
|
||||
@@ -1001,7 +1001,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
assert_eq!(improve_single_line_input("\r\nahte\n\r"), "ahte");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_maybe_warn_on_bad_time() {
|
||||
let t = TestContext::new().await;
|
||||
let timestamp_now = time();
|
||||
@@ -1064,7 +1064,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_maybe_warn_on_outdated() {
|
||||
let t = TestContext::new().await;
|
||||
let timestamp_now: i64 = time();
|
||||
|
||||
@@ -382,7 +382,7 @@ mod tests {
|
||||
assert_eq!(txt.trim(), "two\nlines");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_quote_div() {
|
||||
let input = include_str!("../test-data/message/gmx-quote-body.eml");
|
||||
let dehtml = dehtml(input).unwrap();
|
||||
|
||||
@@ -280,7 +280,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_download_limit() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -303,7 +303,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_update_download_state() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat = t.create_chat_with_contact("Bob", "bob@example.org").await;
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_partial_receive_imf() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -376,7 +376,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_partial_download_and_ephemeral() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = t
|
||||
|
||||
10
src/e2ee.rs
10
src/e2ee.rs
@@ -422,7 +422,7 @@ mod tests {
|
||||
mod ensure_secret_key_exists {
|
||||
use super::*;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_prexisting() {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(
|
||||
@@ -431,7 +431,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_not_configured() {
|
||||
let t = TestContext::new().await;
|
||||
assert!(ensure_secret_key_exists(&t).await.is_err());
|
||||
@@ -480,7 +480,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_encrypted_no_autocrypt() -> anyhow::Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -588,7 +588,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
vec![(Some(peerstate), addr)]
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_should_encrypt() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let encrypt_helper = EncryptHelper::new(&t).await.unwrap();
|
||||
@@ -615,7 +615,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mixed_up_mime() -> Result<()> {
|
||||
// "Mixed Up" mail as received when sending an encrypted
|
||||
// message using Delta Chat Desktop via ProtonMail IMAP/SMTP
|
||||
|
||||
@@ -62,9 +62,9 @@ use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use async_std::channel::Receiver;
|
||||
use async_std::future::timeout;
|
||||
use async_channel::Receiver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::time::timeout;
|
||||
|
||||
use crate::chat::{send_msg, ChatId};
|
||||
use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH};
|
||||
@@ -581,7 +581,7 @@ mod tests {
|
||||
dc_tools::IsNoneOrEmpty,
|
||||
};
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_ephemeral_messages() {
|
||||
let context = TestContext::new().await;
|
||||
|
||||
@@ -711,7 +711,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test enabling and disabling ephemeral timer remotely.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_enable_disable() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -743,7 +743,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test that timer is enabled even if the message explicitly enabling the timer is lost.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_enable_lost() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -785,7 +785,7 @@ mod tests {
|
||||
|
||||
/// Test that Alice replying to the chat without a timer at the same time as Bob enables the
|
||||
/// timer does not result in disabling the timer on the Bob's side.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_timer_rollback() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -859,7 +859,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_delete_msgs() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let self_chat = t.get_self_chat().await;
|
||||
@@ -985,7 +985,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_expired_imap_messages() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
const HOUR: i64 = 60 * 60;
|
||||
@@ -1096,7 +1096,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Regression test for a bug in the timer rollback protection.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ephemeral_timer_references() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! # Events specification.
|
||||
|
||||
use async_std::channel::{self, Receiver, Sender, TrySendError};
|
||||
use async_std::path::PathBuf;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use async_channel::{self as channel, Receiver, Sender, TrySendError};
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::contact::ContactId;
|
||||
@@ -61,23 +62,18 @@ impl Events {
|
||||
///
|
||||
/// [`Context`]: crate::context::Context
|
||||
/// [`Context::get_event_emitter`]: crate::context::Context::get_event_emitter
|
||||
/// [`Stream`]: async_std::stream::Stream
|
||||
/// [`Stream`]: futures::stream::Stream
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventEmitter(Receiver<Event>);
|
||||
|
||||
impl EventEmitter {
|
||||
/// Blocking recv of an event. Return `None` if the `Sender` has been droped.
|
||||
pub fn recv_sync(&self) -> Option<Event> {
|
||||
async_std::task::block_on(self.recv())
|
||||
}
|
||||
|
||||
/// Async recv of an event. Return `None` if the `Sender` has been droped.
|
||||
pub async fn recv(&self) -> Option<Event> {
|
||||
self.0.recv().await.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl async_std::stream::Stream for EventEmitter {
|
||||
impl futures::stream::Stream for EventEmitter {
|
||||
type Item = Event;
|
||||
|
||||
fn poll_next(
|
||||
|
||||
@@ -203,7 +203,7 @@ mod tests {
|
||||
assert_eq!(format_flowed_quote(quote), expected);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_quotes() -> anyhow::Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
26
src/html.rs
26
src/html.rs
@@ -284,7 +284,7 @@ mod tests {
|
||||
use crate::message::{MessengerMessage, Viewtype};
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_plain_unspecified() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml");
|
||||
@@ -300,7 +300,7 @@ This message does not have Content-Type nor Subject.<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_plain_iso88591() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_plain_iso88591.eml");
|
||||
@@ -316,7 +316,7 @@ message with a non-UTF-8 encoding: äöüßÄÖÜ<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_plain_flowed() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_plain_flowed.eml");
|
||||
@@ -336,7 +336,7 @@ and will be wrapped as usual.<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_alt_plain() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain.eml");
|
||||
@@ -355,7 +355,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_html.eml");
|
||||
@@ -373,7 +373,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_alt_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_html.eml");
|
||||
@@ -388,7 +388,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_alt_plain_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
|
||||
@@ -405,7 +405,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_htmlparse_apple_cid_jpg() {
|
||||
// load raw mime html-data with related image-part (cid:)
|
||||
// and make sure, Content-Id has angle-brackets that are removed correctly.
|
||||
@@ -424,14 +424,14 @@ test some special html-characters as < > and & but also " and &#x
|
||||
assert!(!parser.html.contains("cid:"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_html_invalid_msgid() {
|
||||
let t = TestContext::new().await;
|
||||
let msg_id = MsgId::new(100);
|
||||
assert!(msg_id.get_html(&t).await.is_err())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_forwarding() {
|
||||
// alice receives a non-delta html-message
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -478,7 +478,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
assert!(html.contains("this is <b>html</b>"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_forwarding_encrypted() {
|
||||
// Alice receives a non-delta html-message
|
||||
// (`ShowEmails=1` lets Alice actually receive non-delta messages for known contacts,
|
||||
@@ -515,7 +515,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
assert!(html.contains("this is <b>html</b>"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_html() {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -547,7 +547,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
assert!(html.contains("<b>html</b> text"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cp1252_html() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
18
src/imap.rs
18
src/imap.rs
@@ -11,11 +11,11 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{bail, format_err, Context as _, Result};
|
||||
use async_channel::Receiver;
|
||||
use async_imap::types::{
|
||||
Fetch, Flag, Mailbox, Name, NameAttribute, Quota, QuotaRoot, UnsolicitedResponse,
|
||||
};
|
||||
use async_std::channel::Receiver;
|
||||
use async_std::prelude::*;
|
||||
use futures::StreamExt;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::chat::{self, ChatId, ChatIdBlocked};
|
||||
@@ -1922,7 +1922,7 @@ fn get_folder_meaning_by_name(folder_name: &str) -> FolderMeaning {
|
||||
|
||||
fn get_folder_meaning(folder_name: &Name) -> FolderMeaning {
|
||||
for attr in folder_name.attributes() {
|
||||
if let NameAttribute::Custom(ref label) = attr {
|
||||
if let NameAttribute::Extension(ref label) = attr {
|
||||
match label.as_ref() {
|
||||
"\\Trash" => return FolderMeaning::Other,
|
||||
"\\Sent" => return FolderMeaning::Sent,
|
||||
@@ -2388,7 +2388,7 @@ mod tests {
|
||||
assert_eq!(get_folder_meaning_by_name("SPAM"), FolderMeaning::Spam);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_uid_next_validity() {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 0);
|
||||
@@ -2570,7 +2570,7 @@ mod tests {
|
||||
("Spam", true, true, "DeltaChat"),
|
||||
];
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_target_folder_incoming_accepted() -> Result<()> {
|
||||
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
|
||||
check_target_folder_combination(
|
||||
@@ -2587,7 +2587,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_target_folder_incoming_request() -> Result<()> {
|
||||
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_REQUEST {
|
||||
check_target_folder_combination(
|
||||
@@ -2604,7 +2604,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_target_folder_outgoing() -> Result<()> {
|
||||
// Test outgoing emails
|
||||
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
|
||||
@@ -2622,7 +2622,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_target_folder_setupmsg() -> Result<()> {
|
||||
// Test setupmessages
|
||||
for (folder, mvbox_move, chat_msg, _expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
|
||||
@@ -2640,7 +2640,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_imap_search_command() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::{Context as _, Result};
|
||||
use async_imap::Client as ImapClient;
|
||||
|
||||
use async_smtp::ServerAddress;
|
||||
use async_std::net::{self, TcpStream};
|
||||
use tokio::net::{self, TcpStream};
|
||||
|
||||
use super::session::Session;
|
||||
use crate::login_param::{dc_build_tls, Socks5Config};
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::Imap;
|
||||
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use async_imap::extensions::idle::IdleResponse;
|
||||
use async_std::prelude::*;
|
||||
use futures_lite::FutureExt;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::{context::Context, scheduler::InterruptInfo};
|
||||
@@ -87,9 +87,7 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
let session = handle
|
||||
.done()
|
||||
.timeout(Duration::from_secs(15))
|
||||
let session = tokio::time::timeout(Duration::from_secs(15), handle.done())
|
||||
.await?
|
||||
.context("IMAP IDLE protocol timed out")?;
|
||||
self.session = Some(Session { inner: session });
|
||||
@@ -121,7 +119,7 @@ impl Imap {
|
||||
|
||||
// check every minute if there are new messages
|
||||
// TODO: grow sleep durations / make them more flexible
|
||||
let mut interval = async_std::stream::interval(Duration::from_secs(60));
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(60));
|
||||
|
||||
enum Event {
|
||||
Tick,
|
||||
@@ -131,7 +129,7 @@ impl Imap {
|
||||
let info = loop {
|
||||
use futures::future::FutureExt;
|
||||
match interval
|
||||
.next()
|
||||
.tick()
|
||||
.map(|_| Event::Tick)
|
||||
.race(
|
||||
self.idle_interrupt
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::{collections::BTreeMap, time::Instant};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::imap::Imap;
|
||||
use crate::log::LogExt;
|
||||
use crate::{context::Context, imap::FolderMeaning};
|
||||
|
||||
use async_std::stream::StreamExt;
|
||||
|
||||
use super::{get_folder_meaning, get_folder_meaning_by_name};
|
||||
|
||||
impl Imap {
|
||||
@@ -104,7 +103,7 @@ impl Imap {
|
||||
let list = session
|
||||
.list(Some(""), Some("*"))
|
||||
.await?
|
||||
.filter_map(|f| f.ok_or_log_msg(context, "list_folders() can't get folder"));
|
||||
.filter_map(|f| async { f.ok_or_log_msg(context, "list_folders() can't get folder") });
|
||||
Ok(list.collect().await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::ops::{Deref, DerefMut};
|
||||
|
||||
use async_imap::Session as ImapSession;
|
||||
use async_native_tls::TlsStream;
|
||||
use async_std::net::TcpStream;
|
||||
use fast_socks5::client::Socks5Stream;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Session {
|
||||
@@ -11,7 +11,7 @@ pub(crate) struct Session {
|
||||
}
|
||||
|
||||
pub(crate) trait SessionStream:
|
||||
async_std::io::Read + async_std::io::Write + Unpin + Send + Sync + std::fmt::Debug
|
||||
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + Sync + std::fmt::Debug
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
118
src/imex.rs
118
src/imex.rs
@@ -2,16 +2,15 @@
|
||||
|
||||
use std::any::Any;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ::pgp::types::KeyTrait;
|
||||
use anyhow::{bail, ensure, format_err, Context as _, Result};
|
||||
use async_std::{
|
||||
fs::{self, File},
|
||||
path::{Path, PathBuf},
|
||||
prelude::*,
|
||||
};
|
||||
use async_tar::Archive;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use futures_lite::FutureExt;
|
||||
use rand::{thread_rng, Rng};
|
||||
use tokio::fs::{self, File};
|
||||
use tokio_tar::Archive;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId};
|
||||
@@ -109,24 +108,22 @@ pub async fn imex(
|
||||
|
||||
/// Returns the filename of the backup found (otherwise an error)
|
||||
pub async fn has_backup(_context: &Context, dir_name: &Path) -> Result<String> {
|
||||
let mut dir_iter = async_std::fs::read_dir(dir_name).await?;
|
||||
let mut dir_iter = tokio::fs::read_dir(dir_name).await?;
|
||||
let mut newest_backup_name = "".to_string();
|
||||
let mut newest_backup_path: Option<PathBuf> = None;
|
||||
|
||||
while let Some(dirent) = dir_iter.next().await {
|
||||
if let Ok(dirent) = dirent {
|
||||
let path = dirent.path();
|
||||
let name = dirent.file_name();
|
||||
let name: String = name.to_string_lossy().into();
|
||||
if name.starts_with("delta-chat")
|
||||
&& name.ends_with(".tar")
|
||||
&& (newest_backup_name.is_empty() || name > newest_backup_name)
|
||||
{
|
||||
// We just use string comparison to determine which backup is newer.
|
||||
// This works fine because the filenames have the form ...delta-chat-backup-2020-07-24-00.tar
|
||||
newest_backup_path = Some(path);
|
||||
newest_backup_name = name;
|
||||
}
|
||||
while let Ok(Some(dirent)) = dir_iter.next_entry().await {
|
||||
let path = dirent.path();
|
||||
let name = dirent.file_name();
|
||||
let name: String = name.to_string_lossy().into();
|
||||
if name.starts_with("delta-chat")
|
||||
&& name.ends_with(".tar")
|
||||
&& (newest_backup_name.is_empty() || name > newest_backup_name)
|
||||
{
|
||||
// We just use string comparison to determine which backup is newer.
|
||||
// This works fine because the filenames have the form ...delta-chat-backup-2020-07-24-00.tar
|
||||
newest_backup_path = Some(path);
|
||||
newest_backup_name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +174,7 @@ async fn do_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
let msg_id = chat::send_msg(context, chat_id, &mut msg).await?;
|
||||
info!(context, "Wait for setup message being sent ...",);
|
||||
while !context.shall_stop_ongoing().await {
|
||||
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
if let Ok(msg) = Message::load_from_db(context, msg_id).await {
|
||||
if msg.is_sent() {
|
||||
info!(context, "... setup message sent.",);
|
||||
@@ -446,7 +443,7 @@ async fn import_backup(
|
||||
|
||||
context.sql.config_cache.write().await.clear();
|
||||
|
||||
let archive = Archive::new(backup_file);
|
||||
let mut archive = Archive::new(backup_file);
|
||||
|
||||
let mut entries = archive.entries()?;
|
||||
let mut last_progress = 0;
|
||||
@@ -477,7 +474,7 @@ async fn import_backup(
|
||||
// async_tar will unpack to blobdir/BLOBS_BACKUP_NAME, so we move the file afterwards.
|
||||
f.unpack_in(context.get_blobdir()).await?;
|
||||
let from_path = context.get_blobdir().join(f.path()?);
|
||||
if from_path.is_file().await {
|
||||
if from_path.is_file() {
|
||||
if let Some(name) = from_path.file_name() {
|
||||
fs::rename(&from_path, context.get_blobdir().join(name)).await?;
|
||||
} else {
|
||||
@@ -499,10 +496,7 @@ async fn import_backup(
|
||||
/// Returns Ok((temp_db_path, temp_path, dest_path)) on success. Unencrypted database can be
|
||||
/// written to temp_db_path. The backup can then be written to temp_path. If the backup succeeded,
|
||||
/// it can be renamed to dest_path. This guarantees that the backup is complete.
|
||||
async fn get_next_backup_path(
|
||||
folder: &Path,
|
||||
backup_time: i64,
|
||||
) -> Result<(PathBuf, PathBuf, PathBuf)> {
|
||||
fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, PathBuf, PathBuf)> {
|
||||
let folder = PathBuf::from(folder);
|
||||
let stem = chrono::NaiveDateTime::from_timestamp(backup_time, 0)
|
||||
// Don't change this file name format, in has_backup() we use string comparison to determine which backup is newer:
|
||||
@@ -520,7 +514,7 @@ async fn get_next_backup_path(
|
||||
let mut destfile = folder.clone();
|
||||
destfile.push(format!("{}-{:02}.tar", stem, i));
|
||||
|
||||
if !tempdbfile.exists().await && !tempfile.exists().await && !destfile.exists().await {
|
||||
if !tempdbfile.exists() && !tempfile.exists() && !destfile.exists() {
|
||||
return Ok((tempdbfile, tempfile, destfile));
|
||||
}
|
||||
}
|
||||
@@ -530,7 +524,7 @@ async fn get_next_backup_path(
|
||||
async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Result<()> {
|
||||
// get a fine backup file name (the name includes the date so that multiple backup instances are possible)
|
||||
let now = time();
|
||||
let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, now).await?;
|
||||
let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, now)?;
|
||||
let _d1 = DeleteOnDrop(temp_db_path.clone());
|
||||
let _d2 = DeleteOnDrop(temp_path.clone());
|
||||
|
||||
@@ -584,7 +578,8 @@ impl Drop for DeleteOnDrop {
|
||||
fn drop(&mut self) {
|
||||
let file = self.0.clone();
|
||||
// Not using dc_delete_file() here because it would send a DeletedBlobFile event
|
||||
async_std::task::block_on(fs::remove_file(file)).ok();
|
||||
// Hack to avoid panic in nested runtime calls of tokio
|
||||
std::fs::remove_file(file).ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,19 +590,21 @@ async fn export_backup_inner(
|
||||
) -> Result<()> {
|
||||
let file = File::create(temp_path).await?;
|
||||
|
||||
let mut builder = async_tar::Builder::new(file);
|
||||
let mut builder = tokio_tar::Builder::new(file);
|
||||
|
||||
builder
|
||||
.append_path_with_name(temp_db_path, DBFILE_BACKUP_NAME)
|
||||
.await?;
|
||||
|
||||
let read_dir: Vec<_> = fs::read_dir(context.get_blobdir()).await?.collect().await;
|
||||
let read_dir: Vec<_> =
|
||||
tokio_stream::wrappers::ReadDirStream::new(fs::read_dir(context.get_blobdir()).await?)
|
||||
.try_collect()
|
||||
.await?;
|
||||
let count = read_dir.len();
|
||||
let mut written_files = 0;
|
||||
|
||||
let mut last_progress = 0;
|
||||
for entry in read_dir.into_iter() {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name();
|
||||
if !entry.file_type().await?.is_file() {
|
||||
warn!(
|
||||
@@ -648,9 +645,9 @@ async fn import_self_keys(context: &Context, dir: &Path) -> Result<()> {
|
||||
let mut imported_cnt = 0;
|
||||
|
||||
let dir_name = dir.to_string_lossy();
|
||||
let mut dir_handle = async_std::fs::read_dir(&dir).await?;
|
||||
while let Some(entry) = dir_handle.next().await {
|
||||
let entry_fn = entry?.file_name();
|
||||
let mut dir_handle = tokio::fs::read_dir(&dir).await?;
|
||||
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
||||
let entry_fn = entry.file_name();
|
||||
let name_f = entry_fn.to_string_lossy();
|
||||
let path_plus_name = dir.join(&entry_fn);
|
||||
match dc_get_filesuffix_lc(&name_f) {
|
||||
@@ -800,7 +797,7 @@ mod tests {
|
||||
|
||||
use ::pgp::armor::BlockType;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_render_setup_file() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let msg = render_setup_file(&t, "hello").await.unwrap();
|
||||
@@ -817,7 +814,7 @@ mod tests {
|
||||
assert!(msg.contains("-----END PGP MESSAGE-----\n"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_render_setup_file_newline_replace() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_stock_translation(StockMessage::AcSetupMsgBody, "hello\r\nthere".to_string())
|
||||
@@ -828,7 +825,7 @@ mod tests {
|
||||
assert!(msg.contains("<p>hello<br>there</p>"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_setup_code() {
|
||||
let t = TestContext::new().await;
|
||||
let setupcode = create_setup_code(&t);
|
||||
@@ -843,7 +840,7 @@ mod tests {
|
||||
assert_eq!(setupcode.chars().nth(39).unwrap(), '-');
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_export_public_key_to_asc_file() {
|
||||
let context = TestContext::new().await;
|
||||
let key = alice_keypair().public;
|
||||
@@ -853,12 +850,12 @@ mod tests {
|
||||
.is_ok());
|
||||
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
|
||||
let filename = format!("{}/public-key-default.asc", blobdir);
|
||||
let bytes = async_std::fs::read(&filename).await.unwrap();
|
||||
let bytes = tokio::fs::read(&filename).await.unwrap();
|
||||
|
||||
assert_eq!(bytes, key.to_asc(None).into_bytes());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_export_private_key_to_asc_file() {
|
||||
let context = TestContext::new().await;
|
||||
let key = alice_keypair().secret;
|
||||
@@ -868,12 +865,12 @@ mod tests {
|
||||
.is_ok());
|
||||
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
|
||||
let filename = format!("{}/private-key-default.asc", blobdir);
|
||||
let bytes = async_std::fs::read(&filename).await.unwrap();
|
||||
let bytes = tokio::fs::read(&filename).await.unwrap();
|
||||
|
||||
assert_eq!(bytes, key.to_asc(None).into_bytes());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_export_and_import_key() {
|
||||
let context = TestContext::new_alice().await;
|
||||
let blobdir = context.ctx.get_blobdir();
|
||||
@@ -887,7 +884,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_export_and_import_backup() -> Result<()> {
|
||||
let backup_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -896,26 +893,21 @@ mod tests {
|
||||
|
||||
let context2 = TestContext::new().await;
|
||||
assert!(!context2.is_configured().await?);
|
||||
assert!(has_backup(&context2, backup_dir.path().as_ref())
|
||||
.await
|
||||
.is_err());
|
||||
assert!(has_backup(&context2, backup_dir.path()).await.is_err());
|
||||
|
||||
// export from context1
|
||||
assert!(imex(
|
||||
&context1,
|
||||
ImexMode::ExportBackup,
|
||||
backup_dir.path().as_ref(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.is_ok());
|
||||
assert!(
|
||||
imex(&context1, ImexMode::ExportBackup, backup_dir.path(), None)
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
let _event = context1
|
||||
.evtracker
|
||||
.get_matching(|evt| matches!(evt, EventType::ImexProgress(1000)))
|
||||
.await;
|
||||
|
||||
// import to context2
|
||||
let backup = has_backup(&context2, backup_dir.path().as_ref()).await?;
|
||||
let backup = has_backup(&context2, backup_dir.path()).await?;
|
||||
|
||||
// Import of unencrypted backup with incorrect "foobar" backup passphrase fails.
|
||||
assert!(imex(
|
||||
@@ -961,7 +953,7 @@ mod tests {
|
||||
const S_EM_SETUPCODE: &str = "1742-0185-6197-1303-7016-8412-3581-4441-0597";
|
||||
const S_EM_SETUPFILE: &str = include_str!("../test-data/message/stress.txt");
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_split_and_decrypt() {
|
||||
let buf_1 = S_EM_SETUPFILE.as_bytes().to_vec();
|
||||
let (typ, headers, base64) = split_armored_data(&buf_1).unwrap();
|
||||
@@ -984,20 +976,20 @@ mod tests {
|
||||
assert!(headers.get(HEADER_SETUPCODE).is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_key_transfer() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
let alice_clone = alice.clone();
|
||||
let key_transfer_task = async_std::task::spawn(async move {
|
||||
let key_transfer_task = tokio::task::spawn(async move {
|
||||
let ctx = alice_clone;
|
||||
initiate_key_transfer(&ctx).await
|
||||
});
|
||||
|
||||
// Wait for the message to be added to the queue.
|
||||
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
let sent = alice.pop_sent_msg().await;
|
||||
let setup_code = key_transfer_task.await?;
|
||||
let setup_code = key_transfer_task.await??;
|
||||
|
||||
// Alice sets up a second device.
|
||||
let alice2 = TestContext::new().await;
|
||||
|
||||
@@ -448,7 +448,7 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_next_job_two() -> Result<()> {
|
||||
// We want to ensure that loading jobs skips over jobs which
|
||||
// fails to load from the database instead of failing to load
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_next_job_one() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
|
||||
32
src/key.rs
32
src/key.rs
@@ -11,6 +11,7 @@ use num_traits::FromPrimitive;
|
||||
use pgp::composed::Deserializable;
|
||||
use pgp::ser::Serialize;
|
||||
use pgp::types::{KeyTrait, SecretKeyTrait};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::constants::KeyGenType;
|
||||
@@ -219,9 +220,10 @@ async fn generate_keypair(context: &Context) -> Result<KeyPair> {
|
||||
let keytype = KeyGenType::from_i32(context.get_config_int(Config::KeyGenType).await?)
|
||||
.unwrap_or_default();
|
||||
info!(context, "Generating keypair with type {}", keytype);
|
||||
let keypair =
|
||||
async_std::task::spawn_blocking(move || crate::pgp::create_keypair(addr, keytype))
|
||||
.await?;
|
||||
let keypair = Handle::current()
|
||||
.spawn_blocking(move || crate::pgp::create_keypair(addr, keytype))
|
||||
.await??;
|
||||
|
||||
store_self_keypair(context, &keypair, KeyPairUse::Default).await?;
|
||||
info!(
|
||||
context,
|
||||
@@ -397,8 +399,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::{alice_keypair, TestContext};
|
||||
|
||||
use async_std::sync::Arc;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
|
||||
static KEYPAIR: Lazy<KeyPair> = Lazy::new(alice_keypair);
|
||||
|
||||
@@ -520,7 +522,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert_eq!(key, key2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_self_existing() {
|
||||
let alice = alice_keypair();
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -530,7 +532,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert_eq!(alice.secret, seckey);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_self_generate_public() {
|
||||
let t = TestContext::new().await;
|
||||
t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
|
||||
@@ -540,7 +542,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert!(key.is_ok());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_self_generate_secret() {
|
||||
let t = TestContext::new().await;
|
||||
t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
|
||||
@@ -550,7 +552,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert!(key.is_ok());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_load_self_generate_concurrent() {
|
||||
use std::thread;
|
||||
|
||||
@@ -560,11 +562,19 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
.unwrap();
|
||||
let thr0 = {
|
||||
let ctx = t.clone();
|
||||
thread::spawn(move || async_std::task::block_on(SignedPublicKey::load_self(&ctx)))
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(SignedPublicKey::load_self(&ctx))
|
||||
})
|
||||
};
|
||||
let thr1 = {
|
||||
let ctx = t.clone();
|
||||
thread::spawn(move || async_std::task::block_on(SignedPublicKey::load_self(&ctx)))
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(SignedPublicKey::load_self(&ctx))
|
||||
})
|
||||
};
|
||||
let res0 = thr0.join().unwrap();
|
||||
let res1 = thr1.join().unwrap();
|
||||
@@ -577,7 +587,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_save_self_key_twice() {
|
||||
// Saving the same key twice should result in only one row in
|
||||
// the keypairs table.
|
||||
|
||||
@@ -76,7 +76,7 @@ mod tests {
|
||||
assert_eq!(sec_ring.keys(), [alice.secret]);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_keyring_load_self() {
|
||||
// new_self() implies load_self()
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! # Delta Chat Core Library.
|
||||
|
||||
#![recursion_limit = "256"]
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(
|
||||
unused,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! Location handling.
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use async_std::channel::Receiver;
|
||||
use async_std::future::timeout;
|
||||
use async_channel::Receiver;
|
||||
use bitflags::bitflags;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
|
||||
use crate::chat::{self, ChatId};
|
||||
use crate::contact::ContactId;
|
||||
@@ -731,7 +731,7 @@ mod tests {
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_kml_parse() {
|
||||
let context = TestContext::new().await;
|
||||
|
||||
@@ -763,7 +763,7 @@ mod tests {
|
||||
assert_eq!(locations_ref[1].timestamp, 1544739072);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_message_kml() {
|
||||
let context = TestContext::new().await;
|
||||
let timestamp = 1598490000;
|
||||
@@ -791,7 +791,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Tests that location.kml is hidden.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn receive_location_kml() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
|
||||
23
src/log.rs
23
src/log.rs
@@ -1,7 +1,6 @@
|
||||
//! # Logging.
|
||||
|
||||
use crate::context::Context;
|
||||
use async_std::task::block_on;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
@@ -49,15 +48,13 @@ impl Context {
|
||||
/// Set last error string.
|
||||
/// Implemented as blocking as used from macros in different, not always async blocks.
|
||||
pub fn set_last_error(&self, error: &str) {
|
||||
block_on(async move {
|
||||
let mut last_error = self.last_error.write().await;
|
||||
*last_error = error.to_string();
|
||||
});
|
||||
let mut last_error = self.last_error.write().unwrap();
|
||||
*last_error = error.to_string();
|
||||
}
|
||||
|
||||
/// Get last error string.
|
||||
pub async fn get_last_error(&self) -> String {
|
||||
let last_error = &*self.last_error.read().await;
|
||||
pub fn get_last_error(&self) -> String {
|
||||
let last_error = &*self.last_error.read().unwrap();
|
||||
last_error.clone()
|
||||
}
|
||||
}
|
||||
@@ -159,24 +156,24 @@ mod tests {
|
||||
use crate::test_utils::TestContext;
|
||||
use anyhow::Result;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_last_error() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
assert_eq!(t.get_last_error().await, "");
|
||||
assert_eq!(t.get_last_error(), "");
|
||||
|
||||
error!(t, "foo-error");
|
||||
assert_eq!(t.get_last_error().await, "foo-error");
|
||||
assert_eq!(t.get_last_error(), "foo-error");
|
||||
|
||||
warn!(t, "foo-warning");
|
||||
assert_eq!(t.get_last_error().await, "foo-error");
|
||||
assert_eq!(t.get_last_error(), "foo-error");
|
||||
|
||||
info!(t, "foo-info");
|
||||
assert_eq!(t.get_last_error().await, "foo-error");
|
||||
assert_eq!(t.get_last_error(), "foo-error");
|
||||
|
||||
error!(t, "bar-error");
|
||||
error!(t, "baz-error");
|
||||
assert_eq!(t.get_last_error().await, "baz-error");
|
||||
assert_eq!(t.get_last_error(), "baz-error");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,18 +4,16 @@ use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
|
||||
use crate::provider::{get_provider_by_id, Provider};
|
||||
use crate::{context::Context, provider::Socket};
|
||||
use anyhow::{ensure, Result};
|
||||
|
||||
use async_std::io;
|
||||
use async_std::net::TcpStream;
|
||||
|
||||
use async_native_tls::Certificate;
|
||||
pub use async_smtp::ServerAddress;
|
||||
use fast_socks5::client::Socks5Stream;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::{io, net::TcpStream};
|
||||
|
||||
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
|
||||
use crate::provider::{get_provider_by_id, Provider};
|
||||
use crate::{context::Context, provider::Socket};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Display, FromPrimitive, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
@@ -424,7 +422,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_save_load_login_param() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -460,7 +458,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_build_tls() -> Result<()> {
|
||||
// we are using some additional root certificates.
|
||||
// make sure, they do not break construction of TlsConnector
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! # Messages and their identifiers.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{ensure, format_err, Context as _, Result};
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use rusqlite::types::ValueRef;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -1857,7 +1857,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_prepare_message_and_send() {
|
||||
use crate::config::Config;
|
||||
|
||||
@@ -1879,7 +1879,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Tests that message cannot be prepared if account has no configured address.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_prepare_not_configured() {
|
||||
let d = test::TestContext::new().await;
|
||||
let ctx = &d.ctx;
|
||||
@@ -1891,7 +1891,7 @@ mod tests {
|
||||
assert!(chat::prepare_msg(ctx, chat.id, &mut msg).await.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webrtc_instance() {
|
||||
let (webrtc_type, url) = Message::parse_webrtc_instance("basicwebrtc:https://foo/bar");
|
||||
assert_eq!(webrtc_type, VideochatType::BasicWebrtc);
|
||||
@@ -1910,7 +1910,7 @@ mod tests {
|
||||
assert_eq!(url, "https://j.si/foo");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_webrtc_instance() {
|
||||
// webrtc_instance may come from an input field of the ui, be pretty tolerant on input
|
||||
let instance = Message::create_webrtc_instance("https://meet.jit.si/", "123");
|
||||
@@ -1947,7 +1947,7 @@ mod tests {
|
||||
assert_eq!(instance, "basicwebrtc:https://basic.stuff/12345ab");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_webrtc_instance_noroom() {
|
||||
// webrtc_instance may come from an input field of the ui, be pretty tolerant on input
|
||||
let instance = Message::create_webrtc_instance("bla.foo$NOROOM", "123");
|
||||
@@ -1967,7 +1967,7 @@ mod tests {
|
||||
assert_eq!(instance, "https://bla.foo/?$NOROOM=123");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_width_height() {
|
||||
let t = test::TestContext::new().await;
|
||||
|
||||
@@ -1997,7 +1997,7 @@ mod tests {
|
||||
assert!(has_image);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_quote() {
|
||||
use crate::config::Config;
|
||||
|
||||
@@ -2033,7 +2033,7 @@ mod tests {
|
||||
assert!(quoted_msg.get_text() == msg2.quoted_text());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_chat_id() {
|
||||
// Alice receives a message that pops up as a contact request
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -2057,7 +2057,7 @@ mod tests {
|
||||
assert_eq!(msg.get_text().unwrap(), "hello".to_string());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_override_sender_name() {
|
||||
// send message with overridden sender name
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -2105,7 +2105,7 @@ mod tests {
|
||||
assert_ne!(chat.typ, Chattype::Mailinglist);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_markseen_msgs() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -2178,7 +2178,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_state() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -2234,7 +2234,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_is_bot() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::fs;
|
||||
use chrono::TimeZone;
|
||||
use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder};
|
||||
use tokio::fs;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::Chat;
|
||||
@@ -1451,9 +1451,9 @@ fn maybe_encode_words(words: &str) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_std::fs::File;
|
||||
use async_std::prelude::*;
|
||||
use mailparse::{addrparse_header, MailHeaderMap};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::chat::{
|
||||
@@ -1556,7 +1556,7 @@ mod tests {
|
||||
assert_eq!(maybe_encode_words("äöü"), "=?utf-8?b?w6TDtsO8?=");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_from_mua() {
|
||||
// 1.: Receive a mail from an MUA
|
||||
assert_eq!(
|
||||
@@ -1590,7 +1590,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_from_dc() {
|
||||
// 2. Receive a message from Delta Chat
|
||||
assert_eq!(
|
||||
@@ -1610,7 +1610,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_outgoing() {
|
||||
// 3. Send the first message to a new contact
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -1624,7 +1624,7 @@ mod tests {
|
||||
assert_eq!(first_subject_str(t).await, "Message from Alice");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_unicode() {
|
||||
// 4. Receive messages with unicode characters and make sure that we do not panic (we do not care about the result)
|
||||
msg_to_subject_str(
|
||||
@@ -1656,7 +1656,7 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_mdn() {
|
||||
// 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -1706,7 +1706,7 @@ mod tests {
|
||||
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_in_group() -> Result<()> {
|
||||
async fn send_msg_get_subject(
|
||||
t: &TestContext,
|
||||
@@ -1914,7 +1914,7 @@ mod tests {
|
||||
new_msg
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
// This test could still be extended
|
||||
async fn test_render_reply() {
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -2008,7 +2008,7 @@ mod tests {
|
||||
assert!(!headers.lines().any(|l| l.trim().is_empty()));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_selfavatar_unencrypted() -> anyhow::Result<()> {
|
||||
// create chat with bob, set selfavatar
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -2065,7 +2065,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test that removed member address does not go into the `To:` field.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remove_member_bcc() -> Result<()> {
|
||||
// Alice creates a group with Bob and Claire and then removes Bob.
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -2096,7 +2096,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Tests that standard IMF header "From:" comes before non-standard "Autocrypt:" header.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_from_before_autocrypt() -> Result<()> {
|
||||
// create chat with bob
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
use std::io::Cursor;
|
||||
use std::pin::Pin;
|
||||
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
@@ -1029,9 +1028,8 @@ impl MimeMessage {
|
||||
if decoded_data.is_empty() {
|
||||
return;
|
||||
}
|
||||
let reader = Cursor::new(decoded_data);
|
||||
let msg_type = if context
|
||||
.is_webxdc_file(filename, reader)
|
||||
.is_webxdc_file(filename, decoded_data)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
{
|
||||
@@ -1750,7 +1748,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mimeparser_fromheader() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
|
||||
@@ -1808,7 +1806,7 @@ mod tests {
|
||||
assert_eq!(contact.display_name, Some("Götz C".to_string()));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dc_mimeparser_crash() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/issue_523.txt");
|
||||
@@ -1820,7 +1818,7 @@ mod tests {
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_rfc724_mid_exists() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/mail_with_message_id.txt");
|
||||
@@ -1834,7 +1832,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_rfc724_mid_not_exists() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/issue_523.txt");
|
||||
@@ -1876,7 +1874,7 @@ mod tests {
|
||||
mail
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1887,7 +1885,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("test.html".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_encoded_words() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1898,7 +1896,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("Maßnahmen Okt. 2020.html".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_encoded_words_binary() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1909,7 +1907,7 @@ mod tests {
|
||||
assert_eq!(filename, Some(" § 165 Abs".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_encoded_words_windows1251() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1920,7 +1918,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("file Что нового 2020.pdf".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_encoded_words_cont() {
|
||||
// test continued encoded-words and also test apostropes work that way
|
||||
let t = TestContext::new().await;
|
||||
@@ -1932,7 +1930,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("Maßn'ah'men Okt. 2020.html".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_encoded_words_bad_delimiter() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1944,7 +1942,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("=?utf-8?q?foo?=.bar".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_apostrophed() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1955,7 +1953,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("Maßnahmen Okt. 2021.html".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_apostrophed_cont() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1966,7 +1964,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("Maßnahmen März 2022.html".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_apostrophed_windows1251() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1977,7 +1975,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("программирование.HTM".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_apostrophed_cp1252() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1988,7 +1986,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("Auftragsbestätigung.pdf".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_apostrophed_invalid() {
|
||||
let t = TestContext::new().await;
|
||||
let mail = load_mail_with_attachment(
|
||||
@@ -1999,7 +1997,7 @@ mod tests {
|
||||
assert_eq!(filename, Some("somedäüta.html.zip".to_string()))
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_attachment_filename_combined() {
|
||||
// test that if `filename` and `filename*0` are given, the filename is not doubled
|
||||
let t = TestContext::new().await;
|
||||
@@ -2024,7 +2022,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_first_addr() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"From: hello@one.org, world@two.org\n\
|
||||
@@ -2045,7 +2043,7 @@ mod tests {
|
||||
assert!(mimeparser.chat_disposition_notification_to.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_parent_timestamp() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"From: foo@example.org\n\
|
||||
@@ -2078,7 +2076,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mimeparser_with_context() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"From: hello\n\
|
||||
@@ -2130,7 +2128,7 @@ mod tests {
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mimeparser_with_avatars() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -2171,7 +2169,7 @@ mod tests {
|
||||
assert!(mimeparser.group_avatar.unwrap().is_change());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mimeparser_with_videochat() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -2193,7 +2191,7 @@ mod tests {
|
||||
assert_eq!(mimeparser.group_avatar, None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mimeparser_message_kml() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Chat-Version: 1.0\n\
|
||||
@@ -2238,7 +2236,7 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_mdn() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
@@ -2288,7 +2286,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
///
|
||||
/// RFC 6522 specifically allows MDNs to be nested inside
|
||||
/// multipart MIME messages.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_multiple_mdns() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
@@ -2364,7 +2362,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
assert_eq!(message.mdn_reports.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_mdn_with_additional_message_ids() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
@@ -2419,7 +2417,7 @@ Additional-Message-IDs: <foo@example.com> <foo@example.net>\n\
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_inline_attachment() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC)
|
||||
@@ -2459,7 +2457,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
assert_eq!(message.parts[0].msg, "Mail with inline attachment – Hello!");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_hide_html_without_content() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC)
|
||||
@@ -2503,12 +2501,12 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let f = async_std::fs::File::open(blob.to_abs_path()).await.unwrap();
|
||||
let f = tokio::fs::File::open(blob.to_abs_path()).await.unwrap();
|
||||
let size = f.metadata().await.unwrap().len();
|
||||
assert_eq!(size, 154);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_inline_image() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br#"Message-ID: <foobar@example.org>
|
||||
@@ -2554,7 +2552,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
assert_eq!(message.parts[0].msg, "example – Test");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_thunderbird_html_embedded_image() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br#"To: Alice <alice@example.org>
|
||||
@@ -2627,7 +2625,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
}
|
||||
|
||||
// Outlook specifies filename in the "name" attribute of Content-Type
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_outlook_html_embedded_image() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br##"From: Anonymous <anonymous@example.org>
|
||||
@@ -2766,7 +2764,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
assert!(test.is_empty());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_format_flowed_quote() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
@@ -2802,7 +2800,7 @@ Reply
|
||||
assert_eq!(message.parts[0].msg, "Reply");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_quote_without_reply() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
@@ -2834,7 +2832,7 @@ From: alice <alice@example.org>
|
||||
assert_eq!(message.parts[0].msg, "");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn parse_quote_top_posting() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
@@ -2865,7 +2863,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert_eq!(message.parts[0].msg, "A reply.");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_attachment_quote() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/quote_attach.eml");
|
||||
@@ -2883,7 +2881,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::File);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_quote_div() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/gmx-quote.eml");
|
||||
@@ -2892,7 +2890,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert_eq!(mimeparser.parts[0].param.get(Param::Quote).unwrap(), "Now?");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_allinkl_blockquote() {
|
||||
// all-inkl.com puts quotes into `<blockquote> </blockquote>`.
|
||||
let t = TestContext::new().await;
|
||||
@@ -2905,7 +2903,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_subj_to_multimedia_msg() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
@@ -2938,7 +2936,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert_eq!(msg.get_filemime().unwrap(), "image/png");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_plain() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml");
|
||||
@@ -2950,7 +2948,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_alt_plain_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
|
||||
@@ -2962,7 +2960,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_alt_plain() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain.eml");
|
||||
@@ -2977,7 +2975,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_alt_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_html.eml");
|
||||
@@ -2989,7 +2987,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_html() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = include_bytes!("../test-data/message/text_html.eml");
|
||||
@@ -3001,7 +2999,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mime_modified_large_plain() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -3022,7 +3020,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert!(mimemsg.parts[0].msg.len() <= DC_DESIRED_TEXT_LEN + DC_ELLIPSIS.len());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_x_microsoft_original_message_id() {
|
||||
let t = TestContext::new().await;
|
||||
let message = MimeMessage::from_bytes(&t, b"Date: Wed, 17 Feb 2021 15:45:15 +0000\n\
|
||||
@@ -3045,7 +3043,7 @@ On 2020-10-25, Bob wrote:
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_long_in_reply_to() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -3088,7 +3086,7 @@ Some reply
|
||||
}
|
||||
|
||||
// Test that WantsMdn parameter is not set on outgoing messages.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_outgoing_wants_mdn() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -3119,7 +3117,7 @@ Message.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ignore_read_receipt_to_self() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -3189,7 +3187,7 @@ Message.
|
||||
///
|
||||
/// It does not have required Original-Message-ID field, so it is useless, but we want to
|
||||
/// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ms_exchange_mdn() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
@@ -156,21 +156,21 @@ pub async fn dc_get_oauth2_access_token(
|
||||
}
|
||||
|
||||
// ... and POST
|
||||
let mut req = surf::post(post_url).build();
|
||||
if let Err(err) = req.body_form(&post_param) {
|
||||
warn!(context, "Error calling OAuth2 at {}: {:?}", token_url, err);
|
||||
return Ok(None);
|
||||
}
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let client = surf::Client::new();
|
||||
let parsed: Result<Response, _> = client.recv_json(req).await;
|
||||
let response = match parsed {
|
||||
Ok(response) => response,
|
||||
let response: Response = match client.post(post_url).form(&post_param).send().await {
|
||||
Ok(resp) => match resp.json().await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err
|
||||
);
|
||||
warn!(context, "Error calling OAuth2 at {}: {:?}", token_url, err);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
@@ -288,8 +288,14 @@ impl Oauth2 {
|
||||
// "verified_email": true,
|
||||
// "picture": "https://lh4.googleusercontent.com/-Gj5jh_9R0BY/AAAAAAAAAAI/AAAAAAAAAAA/IAjtjfjtjNA/photo.jpg"
|
||||
// }
|
||||
let response: Result<HashMap<String, serde_json::Value>, surf::Error> =
|
||||
surf::get(userinfo_url).recv_json().await;
|
||||
let response = match reqwest::get(userinfo_url).await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
warn!(context, "failed to get userinfo: {}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let response: Result<HashMap<String, serde_json::Value>, _> = response.json().await;
|
||||
let parsed = match response {
|
||||
Ok(parsed) => parsed,
|
||||
Err(err) => {
|
||||
@@ -360,7 +366,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_oauth_from_address() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(
|
||||
@@ -382,7 +388,7 @@ mod tests {
|
||||
assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_oauth_from_mx() {
|
||||
// youtube staff seems to use "google workspace with oauth2", figures this out by MX lookup
|
||||
let t = TestContext::new().await;
|
||||
@@ -397,7 +403,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dc_get_oauth2_addr() {
|
||||
let ctx = TestContext::new().await;
|
||||
let addr = "dignifiedquire@gmail.com";
|
||||
@@ -407,7 +413,7 @@ mod tests {
|
||||
assert_eq!(res, None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dc_get_oauth2_url() {
|
||||
let ctx = TestContext::new().await;
|
||||
let addr = "dignifiedquire@gmail.com";
|
||||
@@ -419,7 +425,7 @@ mod tests {
|
||||
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dc_get_oauth2_token() {
|
||||
let ctx = TestContext::new().await;
|
||||
let addr = "dignifiedquire@gmail.com";
|
||||
|
||||
20
src/param.rs
20
src/param.rs
@@ -1,9 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
|
||||
use anyhow::{bail, Error, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use num_traits::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -434,12 +434,13 @@ impl<'a> ParamsFile<'a> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_std::fs;
|
||||
use async_std::path::Path;
|
||||
use tokio::fs;
|
||||
|
||||
use crate::test_utils::TestContext;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_dc_param() {
|
||||
@@ -485,7 +486,7 @@ mod tests {
|
||||
assert_eq!(params.to_string().parse::<Params>().unwrap(), params);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_params_file_fs_path() {
|
||||
let t = TestContext::new().await;
|
||||
if let ParamsFile::FsPath(p) = ParamsFile::from_param(&t, "/foo/bar/baz").unwrap() {
|
||||
@@ -495,7 +496,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_params_file_blob() {
|
||||
let t = TestContext::new().await;
|
||||
if let ParamsFile::Blob(b) = ParamsFile::from_param(&t, "$BLOBDIR/foo").unwrap() {
|
||||
@@ -506,7 +507,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Tests for Params::get_file(), Params::get_path() and Params::get_blob().
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_params_get_fileparam() {
|
||||
let t = TestContext::new().await;
|
||||
let fname = t.dir.path().join("foo");
|
||||
@@ -514,10 +515,9 @@ mod tests {
|
||||
p.set(Param::File, fname.to_str().unwrap());
|
||||
|
||||
let file = p.get_file(Param::File, &t).unwrap().unwrap();
|
||||
assert_eq!(file, ParamsFile::FsPath(fname.clone().into()));
|
||||
assert_eq!(file, ParamsFile::FsPath(fname.clone()));
|
||||
|
||||
let path: PathBuf = p.get_path(Param::File, &t).unwrap().unwrap();
|
||||
let fname: PathBuf = fname.into();
|
||||
assert_eq!(path, fname);
|
||||
|
||||
// Blob does not exist yet, expect error.
|
||||
@@ -539,7 +539,7 @@ mod tests {
|
||||
assert!(p.get_blob(Param::File, &t, false).await.unwrap().is_none());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_params_unknown_key() -> Result<()> {
|
||||
// 'Z' is used as a key that is known to be unused; these keys should be ignored silently by definition.
|
||||
let p = Params::from_str("w=12\nZ=13\nh=14")?;
|
||||
|
||||
@@ -552,7 +552,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::alice_keypair;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peerstate_save_to_db() {
|
||||
let ctx = crate::test_utils::TestContext::new().await;
|
||||
let addr = "hello@mail.com";
|
||||
@@ -596,7 +596,7 @@ mod tests {
|
||||
assert_eq!(peerstate, peerstate_new2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peerstate_double_create() {
|
||||
let ctx = crate::test_utils::TestContext::new().await;
|
||||
let addr = "hello@mail.com";
|
||||
@@ -628,7 +628,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peerstate_with_empty_gossip_key_save_to_db() {
|
||||
let ctx = crate::test_utils::TestContext::new().await;
|
||||
let addr = "hello@mail.com";
|
||||
@@ -665,7 +665,7 @@ mod tests {
|
||||
assert_eq!(Some(peerstate), peerstate_new);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peerstate_load_db_defaults() {
|
||||
let ctx = crate::test_utils::TestContext::new().await;
|
||||
let addr = "hello@mail.com";
|
||||
@@ -694,7 +694,7 @@ mod tests {
|
||||
assert_eq!(peerstate.verified_key_fingerprint, None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peerstate_degrade_reordering() {
|
||||
let addr = "example@example.org";
|
||||
let pub_key = alice_keypair().public;
|
||||
|
||||
66
src/pgp.rs
66
src/pgp.rs
@@ -15,6 +15,7 @@ use pgp::types::{
|
||||
CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey,
|
||||
};
|
||||
use rand::{thread_rng, CryptoRng, Rng};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use crate::constants::KeyGenType;
|
||||
use crate::dc_tools::EmailAddress;
|
||||
@@ -224,32 +225,33 @@ pub async fn pk_encrypt(
|
||||
) -> Result<String> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
|
||||
async_std::task::spawn_blocking(move || {
|
||||
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
|
||||
.keys()
|
||||
.iter()
|
||||
.filter_map(select_pk_for_encryption)
|
||||
.collect();
|
||||
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
|
||||
Handle::current()
|
||||
.spawn_blocking(move || {
|
||||
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
|
||||
.keys()
|
||||
.iter()
|
||||
.filter_map(select_pk_for_encryption)
|
||||
.collect();
|
||||
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// TODO: measure time
|
||||
let encrypted_msg = if let Some(ref skey) = private_key_for_signing {
|
||||
lit_msg
|
||||
.sign(skey, || "".into(), Default::default())
|
||||
.and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB))
|
||||
.and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs))
|
||||
} else {
|
||||
lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)
|
||||
};
|
||||
// TODO: measure time
|
||||
let encrypted_msg = if let Some(ref skey) = private_key_for_signing {
|
||||
lit_msg
|
||||
.sign(skey, || "".into(), Default::default())
|
||||
.and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB))
|
||||
.and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs))
|
||||
} else {
|
||||
lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)
|
||||
};
|
||||
|
||||
let msg = encrypted_msg?;
|
||||
let encoded_msg = msg.to_armored_string(None)?;
|
||||
let msg = encrypted_msg?;
|
||||
let encoded_msg = msg.to_armored_string(None)?;
|
||||
|
||||
Ok(encoded_msg)
|
||||
})
|
||||
.await
|
||||
Ok(encoded_msg)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
/// Decrypts the message with keys from the private key keyring.
|
||||
@@ -268,7 +270,7 @@ pub async fn pk_decrypt(
|
||||
) -> Result<(Vec<u8>, HashSet<Fingerprint>)> {
|
||||
let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
|
||||
|
||||
let msgs = async_std::task::spawn_blocking(move || {
|
||||
let msgs = tokio::task::spawn_blocking(move || {
|
||||
let cursor = Cursor::new(ctext);
|
||||
let (msg, _) = Message::from_armor_single(cursor)?;
|
||||
|
||||
@@ -277,7 +279,7 @@ pub async fn pk_decrypt(
|
||||
let (decryptor, _) = msg.decrypt(|| "".into(), || "".into(), &skeys[..])?;
|
||||
decryptor.collect::<pgp::errors::Result<Vec<_>>>()
|
||||
})
|
||||
.await?;
|
||||
.await??;
|
||||
|
||||
if let Some(msg) = msgs.into_iter().next() {
|
||||
// get_content() will decompress the message if needed,
|
||||
@@ -342,7 +344,7 @@ pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
let passphrase = passphrase.to_string();
|
||||
|
||||
async_std::task::spawn_blocking(move || {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut rng = thread_rng();
|
||||
let s2k = StringToKey::new_default(&mut rng);
|
||||
let msg =
|
||||
@@ -352,7 +354,7 @@ pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
||||
|
||||
Ok(encoded_msg)
|
||||
})
|
||||
.await
|
||||
.await?
|
||||
}
|
||||
|
||||
/// Symmetric decryption.
|
||||
@@ -363,7 +365,7 @@ pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
|
||||
let (enc_msg, _) = Message::from_armor_single(ctext)?;
|
||||
|
||||
let passphrase = passphrase.to_string();
|
||||
async_std::task::spawn_blocking(move || {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let decryptor = enc_msg.decrypt_with_password(|| passphrase)?;
|
||||
|
||||
let msgs = decryptor.collect::<pgp::errors::Result<Vec<_>>>()?;
|
||||
@@ -376,7 +378,7 @@ pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
|
||||
bail!("No valid messages found")
|
||||
}
|
||||
})
|
||||
.await
|
||||
.await?
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -487,7 +489,7 @@ mod tests {
|
||||
assert!(CTEXT_UNSIGNED.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_singed() {
|
||||
// Check decrypting as Alice
|
||||
let mut decrypt_keyring: Keyring<SignedSecretKey> = Keyring::new();
|
||||
@@ -520,7 +522,7 @@ mod tests {
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_no_sig_check() {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_secret.clone());
|
||||
@@ -533,7 +535,7 @@ mod tests {
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_signed_no_key() {
|
||||
// The validation does not have the public key of the signer.
|
||||
let mut decrypt_keyring = Keyring::new();
|
||||
@@ -551,7 +553,7 @@ mod tests {
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_unsigned() {
|
||||
let mut decrypt_keyring = Keyring::new();
|
||||
decrypt_keyring.add(KEYS.bob_secret.clone());
|
||||
|
||||
@@ -99,7 +99,7 @@ impl PlainText {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html() {
|
||||
let html = PlainText {
|
||||
text: r##"line 1
|
||||
@@ -127,7 +127,7 @@ line with <a href="https://link-mid-of-line.org">https://link-mid-of-line.org</a
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_encapsulated() {
|
||||
let html = PlainText {
|
||||
text: r#"line with <http://encapsulated.link/?foo=_bar> here!"#.to_string(),
|
||||
@@ -146,7 +146,7 @@ line with <<a href="http://encapsulated.link/?foo=_bar">http://encapsulated.l
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_nolink() {
|
||||
let html = PlainText {
|
||||
text: r#"line with nohttp://no.link here"#.to_string(),
|
||||
@@ -165,7 +165,7 @@ line with nohttp://no.link here<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_mailto() {
|
||||
let html = PlainText {
|
||||
text: r#"just an address: foo@bar.org another@one.de"#.to_string(),
|
||||
@@ -184,7 +184,7 @@ just an address: <a href="mailto:foo@bar.org">foo@bar.org</a> <a href="mailto:an
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_flowed() {
|
||||
let html = PlainText {
|
||||
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),
|
||||
@@ -206,7 +206,7 @@ line still line<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_flowed_delsp() {
|
||||
let html = PlainText {
|
||||
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),
|
||||
@@ -228,7 +228,7 @@ linestill line<br/>
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_plain_to_html_fixed() {
|
||||
let html = PlainText {
|
||||
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED};
|
||||
use anyhow::Result;
|
||||
use async_std_resolver::{config, resolver, resolver_from_system_conf, AsyncStdResolver};
|
||||
use chrono::{NaiveDateTime, NaiveTime};
|
||||
use trust_dns_resolver::{config, AsyncResolver, TokioAsyncResolver};
|
||||
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
@@ -85,18 +85,17 @@ pub struct Provider {
|
||||
|
||||
/// Get resolver to query MX records.
|
||||
///
|
||||
/// We first try resolver_from_system_conf() which reads the system's resolver from `/etc/resolv.conf`.
|
||||
/// This does not work at least on some Androids, therefore we use use ResolverConfig::default()
|
||||
/// which default eg. to google's 8.8.8.8 or 8.8.4.4 as a fallback.
|
||||
async fn get_resolver() -> Result<AsyncStdResolver> {
|
||||
if let Ok(resolver) = resolver_from_system_conf().await {
|
||||
/// We first try to read the system's resolver from `/etc/resolv.conf`.
|
||||
/// This does not work at least on some Androids, therefore we fallback
|
||||
/// to the default `ResolverConfig` which uses eg. to google's `8.8.8.8` or `8.8.4.4`.
|
||||
fn get_resolver() -> Result<TokioAsyncResolver> {
|
||||
if let Ok(resolver) = AsyncResolver::tokio_from_system_conf() {
|
||||
return Ok(resolver);
|
||||
}
|
||||
let resolver = resolver(
|
||||
let resolver = AsyncResolver::tokio(
|
||||
config::ResolverConfig::default(),
|
||||
config::ResolverOpts::default(),
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
Ok(resolver)
|
||||
}
|
||||
|
||||
@@ -141,7 +140,7 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> {
|
||||
///
|
||||
/// For security reasons, only Gmail can be configured this way.
|
||||
pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'static Provider> {
|
||||
if let Ok(resolver) = get_resolver().await {
|
||||
if let Ok(resolver) = get_resolver() {
|
||||
let mut fqdn: String = domain.to_string();
|
||||
if !fqdn.ends_with('.') {
|
||||
fqdn.push('.');
|
||||
@@ -242,7 +241,7 @@ mod tests {
|
||||
assert!(provider.id == "gmail");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_provider_info() {
|
||||
let t = TestContext::new().await;
|
||||
assert!(get_provider_info(&t, "", false).await.is_none());
|
||||
@@ -270,9 +269,9 @@ mod tests {
|
||||
assert!(get_provider_update_timestamp() > timestamp_past);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_get_resolver() -> Result<()> {
|
||||
assert!(get_resolver().await.is_ok());
|
||||
#[test]
|
||||
fn test_get_resolver() -> Result<()> {
|
||||
assert!(get_resolver().is_ok());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
50
src/qr.rs
50
src/qr.rs
@@ -1,6 +1,6 @@
|
||||
//! # QR code module.
|
||||
|
||||
use anyhow::{bail, ensure, format_err, Context as _, Error, Result};
|
||||
use anyhow::{bail, ensure, Context as _, Error, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use serde::Deserialize;
|
||||
@@ -355,13 +355,13 @@ struct CreateAccountResponse {
|
||||
async fn set_account_from_qr(context: &Context, qr: &str) -> Result<()> {
|
||||
let url_str = &qr[DCACCOUNT_SCHEME.len()..];
|
||||
|
||||
let parsed: CreateAccountResponse = surf::post(url_str).recv_json().await.map_err(|err| {
|
||||
format_err!(
|
||||
"Cannot create account, request to {:?} failed: {}",
|
||||
url_str,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
let parsed: CreateAccountResponse = reqwest::Client::new()
|
||||
.post(url_str)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await
|
||||
.with_context(|| format!("Cannot create account, request to {:?} failed", url_str))?;
|
||||
|
||||
context
|
||||
.set_config(Config::Addr, Some(&parsed.email))
|
||||
@@ -565,7 +565,7 @@ mod tests {
|
||||
use crate::test_utils::{alice_keypair, TestContext};
|
||||
use anyhow::Result;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_http() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -580,7 +580,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_https() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -595,7 +595,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_text() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -610,7 +610,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_vcard() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -632,7 +632,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_matmsg() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -652,7 +652,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_mailto() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -682,7 +682,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_smtp() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -698,7 +698,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_openpgp_group() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
let qr = check_qr(
|
||||
@@ -741,7 +741,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_openpgp_secure_join() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -788,7 +788,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_openpgp_fingerprint() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -850,7 +850,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_openpgp_without_addr() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -886,7 +886,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_withdraw_verifycontact() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let qr = dc_get_securejoin_qr(&alice, None).await?;
|
||||
@@ -920,7 +920,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_withdraw_verifygroup() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -953,7 +953,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_account() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -985,7 +985,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_webrtc_instance() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
@@ -1011,7 +1011,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decode_account_bad_scheme() {
|
||||
let ctx = TestContext::new().await;
|
||||
let res = check_qr(
|
||||
@@ -1030,7 +1030,7 @@ mod tests {
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_config_from_qr() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ async fn generate_join_group_qr_code(context: &Context, chat_id: ChatId) -> Resu
|
||||
let avatar = match chat.get_profile_image(context).await? {
|
||||
Some(path) => {
|
||||
let avatar_blob = BlobObject::from_path(context, &path)?;
|
||||
Some(std::fs::read(avatar_blob.to_abs_path())?)
|
||||
Some(tokio::fs::read(avatar_blob.to_abs_path()).await?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
@@ -45,7 +45,7 @@ async fn generate_verification_qr(context: &Context) -> Result<String> {
|
||||
let avatar = match contact.get_profile_image(context).await? {
|
||||
Some(path) => {
|
||||
let avatar_blob = BlobObject::from_path(context, &path)?;
|
||||
Some(std::fs::read(avatar_blob.to_abs_path())?)
|
||||
Some(tokio::fs::read(avatar_blob.to_abs_path()).await?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
@@ -266,7 +266,7 @@ fn inner_generate_secure_join_qr_code(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_svg_escaping() {
|
||||
let svg = inner_generate_secure_join_qr_code(
|
||||
"descr123 \" < > &",
|
||||
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
QUOTA_WARN_THRESHOLD_PERCENTAGE,
|
||||
};
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_needs_quota_warning() -> Result<()> {
|
||||
assert!(!needs_quota_warning(0, 0));
|
||||
assert!(!needs_quota_warning(10, 0));
|
||||
@@ -199,7 +199,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_quota_thresholds() -> anyhow::Result<()> {
|
||||
assert!(QUOTA_ALLCLEAR_PERCENTAGE > 50);
|
||||
assert!(QUOTA_ALLCLEAR_PERCENTAGE < QUOTA_WARN_THRESHOLD_PERCENTAGE);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use async_std::prelude::*;
|
||||
use async_std::{
|
||||
channel::{self, Receiver, Sender},
|
||||
future, task,
|
||||
};
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use futures::{join, try_join};
|
||||
use futures_lite::FutureExt;
|
||||
use tokio::task;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
@@ -338,7 +337,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
"smtp got rate limited, waiting for {} until can send again",
|
||||
duration_to_str(duration_until_can_send)
|
||||
);
|
||||
async_std::future::timeout(duration_until_can_send, async {
|
||||
tokio::time::timeout(duration_until_can_send, async {
|
||||
idle_interrupt_receiver.recv().await.unwrap_or_default()
|
||||
})
|
||||
.await
|
||||
@@ -367,7 +366,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
"smtp has messages to retry, planning to retry {} seconds later", timeout
|
||||
);
|
||||
let duration = std::time::Duration::from_secs(timeout);
|
||||
async_std::future::timeout(duration, async {
|
||||
tokio::time::timeout(duration, async {
|
||||
idle_interrupt_receiver.recv().await.unwrap_or_default()
|
||||
})
|
||||
.await
|
||||
@@ -493,13 +492,12 @@ impl Scheduler {
|
||||
};
|
||||
|
||||
// wait for all loops to be started
|
||||
if let Err(err) = inbox_start_recv
|
||||
.recv()
|
||||
.try_join(mvbox_start_recv.recv())
|
||||
.try_join(sentbox_start_recv.recv())
|
||||
.try_join(smtp_start_recv.recv())
|
||||
.await
|
||||
{
|
||||
if let Err(err) = try_join!(
|
||||
inbox_start_recv.recv(),
|
||||
mvbox_start_recv.recv(),
|
||||
sentbox_start_recv.recv(),
|
||||
smtp_start_recv.recv()
|
||||
) {
|
||||
bail!("failed to start scheduler: {}", err);
|
||||
}
|
||||
|
||||
@@ -508,19 +506,21 @@ impl Scheduler {
|
||||
}
|
||||
|
||||
async fn maybe_network(&self) {
|
||||
self.interrupt_inbox(InterruptInfo::new(true))
|
||||
.join(self.interrupt_mvbox(InterruptInfo::new(true)))
|
||||
.join(self.interrupt_sentbox(InterruptInfo::new(true)))
|
||||
.join(self.interrupt_smtp(InterruptInfo::new(true)))
|
||||
.await;
|
||||
join!(
|
||||
self.interrupt_inbox(InterruptInfo::new(true)),
|
||||
self.interrupt_mvbox(InterruptInfo::new(true)),
|
||||
self.interrupt_sentbox(InterruptInfo::new(true)),
|
||||
self.interrupt_smtp(InterruptInfo::new(true))
|
||||
);
|
||||
}
|
||||
|
||||
async fn maybe_network_lost(&self) {
|
||||
self.interrupt_inbox(InterruptInfo::new(false))
|
||||
.join(self.interrupt_mvbox(InterruptInfo::new(false)))
|
||||
.join(self.interrupt_sentbox(InterruptInfo::new(false)))
|
||||
.join(self.interrupt_smtp(InterruptInfo::new(false)))
|
||||
.await;
|
||||
join!(
|
||||
self.interrupt_inbox(InterruptInfo::new(false)),
|
||||
self.interrupt_mvbox(InterruptInfo::new(false)),
|
||||
self.interrupt_sentbox(InterruptInfo::new(false)),
|
||||
self.interrupt_smtp(InterruptInfo::new(false))
|
||||
);
|
||||
}
|
||||
|
||||
async fn interrupt_inbox(&self, info: InterruptInfo) {
|
||||
@@ -564,24 +564,24 @@ impl Scheduler {
|
||||
|
||||
// Actually shutdown tasks.
|
||||
let timeout_duration = std::time::Duration::from_secs(30);
|
||||
future::timeout(timeout_duration, self.inbox_handle)
|
||||
tokio::time::timeout(timeout_duration, self.inbox_handle)
|
||||
.await
|
||||
.ok_or_log(context);
|
||||
if let Some(mvbox_handle) = self.mvbox_handle.take() {
|
||||
future::timeout(timeout_duration, mvbox_handle)
|
||||
tokio::time::timeout(timeout_duration, mvbox_handle)
|
||||
.await
|
||||
.ok_or_log(context);
|
||||
}
|
||||
if let Some(sentbox_handle) = self.sentbox_handle.take() {
|
||||
future::timeout(timeout_duration, sentbox_handle)
|
||||
tokio::time::timeout(timeout_duration, sentbox_handle)
|
||||
.await
|
||||
.ok_or_log(context);
|
||||
}
|
||||
future::timeout(timeout_duration, self.smtp_handle)
|
||||
tokio::time::timeout(timeout_duration, self.smtp_handle)
|
||||
.await
|
||||
.ok_or_log(context);
|
||||
self.ephemeral_handle.cancel().await;
|
||||
self.location_handle.cancel().await;
|
||||
self.ephemeral_handle.abort();
|
||||
self.location_handle.abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::fmt;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use async_std::sync::{Mutex, RwLockReadGuard};
|
||||
use tokio::sync::{Mutex, RwLockReadGuard};
|
||||
|
||||
use crate::dc_tools::time;
|
||||
use crate::events::EventType;
|
||||
@@ -239,7 +239,7 @@ pub(crate) async fn maybe_network_lost(
|
||||
|
||||
impl fmt::Debug for ConnectivityStore {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(guard) = self.0.try_lock() {
|
||||
if let Ok(guard) = self.0.try_lock() {
|
||||
write!(f, "ConnectivityStore {:?}", &*guard)
|
||||
} else {
|
||||
write!(f, "ConnectivityStore [LOCKED]")
|
||||
|
||||
@@ -696,7 +696,7 @@ mod tests {
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::test_utils::{TestContext, TestContextManager};
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_setup_contact() {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
@@ -903,14 +903,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_setup_contact_bad_qr() {
|
||||
let bob = TestContext::new_bob().await;
|
||||
let ret = dc_join_securejoin(&bob.ctx, "not a qr code").await;
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_setup_contact_bob_knows_alice() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
@@ -1035,7 +1035,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_setup_contact_concurrent_calls() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
@@ -1066,7 +1066,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_secure_join() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
@@ -1290,7 +1290,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_no_qr() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::{bail, format_err, Context as _, Error, Result};
|
||||
use async_smtp::smtp::client::net::ClientTlsParameters;
|
||||
use async_smtp::smtp::response::{Category, Code, Detail};
|
||||
use async_smtp::{smtp, EmailAddress, ServerAddress};
|
||||
use async_std::task;
|
||||
use tokio::task;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::contact::{Contact, ContactId};
|
||||
|
||||
273
src/sql.rs
273
src/sql.rs
@@ -1,16 +1,14 @@
|
||||
//! # SQLite wrapper.
|
||||
|
||||
use async_std::path::Path;
|
||||
use async_std::sync::RwLock;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use async_std::prelude::*;
|
||||
use rusqlite::{config::DbConfig, Connection, OpenFlags};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{add_device_msg, update_device_icon, update_saved_messages_icon};
|
||||
@@ -126,18 +124,21 @@ impl Sql {
|
||||
.to_str()
|
||||
.with_context(|| format!("path {:?} is not valid unicode", path))?;
|
||||
let conn = self.get_conn().await?;
|
||||
conn.execute(
|
||||
"ATTACH DATABASE ? AS backup KEY ?",
|
||||
paramsv![path_str, passphrase],
|
||||
)
|
||||
.context("failed to attach backup database")?;
|
||||
let res = conn
|
||||
.query_row("SELECT sqlcipher_export('backup')", [], |_row| Ok(()))
|
||||
.context("failed to export to attached backup database");
|
||||
conn.execute("DETACH DATABASE backup", [])
|
||||
.context("failed to detach backup database")?;
|
||||
res?;
|
||||
Ok(())
|
||||
tokio::task::block_in_place(move || {
|
||||
conn.execute(
|
||||
"ATTACH DATABASE ? AS backup KEY ?",
|
||||
paramsv![path_str, passphrase],
|
||||
)
|
||||
.context("failed to attach backup database")?;
|
||||
let res = conn
|
||||
.query_row("SELECT sqlcipher_export('backup')", [], |_row| Ok(()))
|
||||
.context("failed to export to attached backup database");
|
||||
conn.execute("DETACH DATABASE backup", [])
|
||||
.context("failed to detach backup database")?;
|
||||
res?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Imports the database from a separate file with the given passphrase.
|
||||
@@ -147,40 +148,42 @@ impl Sql {
|
||||
.with_context(|| format!("path {:?} is not valid unicode", path))?;
|
||||
let conn = self.get_conn().await?;
|
||||
|
||||
// Check that backup passphrase is correct before resetting our database.
|
||||
conn.execute(
|
||||
"ATTACH DATABASE ? AS backup KEY ?",
|
||||
paramsv![path_str, passphrase],
|
||||
)
|
||||
.context("failed to attach backup database")?;
|
||||
if let Err(err) = conn
|
||||
.query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(()))
|
||||
.context("backup passphrase is not correct")
|
||||
{
|
||||
tokio::task::block_in_place(move || {
|
||||
// Check that backup passphrase is correct before resetting our database.
|
||||
conn.execute(
|
||||
"ATTACH DATABASE ? AS backup KEY ?",
|
||||
paramsv![path_str, passphrase],
|
||||
)
|
||||
.context("failed to attach backup database")?;
|
||||
if let Err(err) = conn
|
||||
.query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(()))
|
||||
.context("backup passphrase is not correct")
|
||||
{
|
||||
conn.execute("DETACH DATABASE backup", [])
|
||||
.context("failed to detach backup database")?;
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// Reset the database without reopening it. We don't want to reopen the database because we
|
||||
// don't have main database passphrase at this point.
|
||||
// See <https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html> for documentation.
|
||||
// Without resetting import may fail due to existing tables.
|
||||
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)
|
||||
.context("failed to set SQLITE_DBCONFIG_RESET_DATABASE")?;
|
||||
conn.execute("VACUUM", [])
|
||||
.context("failed to vacuum the database")?;
|
||||
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)
|
||||
.context("failed to unset SQLITE_DBCONFIG_RESET_DATABASE")?;
|
||||
let res = conn
|
||||
.query_row("SELECT sqlcipher_export('main', 'backup')", [], |_row| {
|
||||
Ok(())
|
||||
})
|
||||
.context("failed to import from attached backup database");
|
||||
conn.execute("DETACH DATABASE backup", [])
|
||||
.context("failed to detach backup database")?;
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// Reset the database without reopening it. We don't want to reopen the database because we
|
||||
// don't have main database passphrase at this point.
|
||||
// See <https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html> for documentation.
|
||||
// Without resetting import may fail due to existing tables.
|
||||
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)
|
||||
.context("failed to set SQLITE_DBCONFIG_RESET_DATABASE")?;
|
||||
conn.execute("VACUUM", [])
|
||||
.context("failed to vacuum the database")?;
|
||||
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)
|
||||
.context("failed to unset SQLITE_DBCONFIG_RESET_DATABASE")?;
|
||||
let res = conn
|
||||
.query_row("SELECT sqlcipher_export('main', 'backup')", [], |_row| {
|
||||
Ok(())
|
||||
})
|
||||
.context("failed to import from attached backup database");
|
||||
conn.execute("DETACH DATABASE backup", [])
|
||||
.context("failed to detach backup database")?;
|
||||
res?;
|
||||
Ok(())
|
||||
res?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn new_pool(
|
||||
@@ -224,20 +227,22 @@ impl Sql {
|
||||
|
||||
{
|
||||
let conn = self.get_conn().await?;
|
||||
tokio::task::block_in_place(move || -> Result<()> {
|
||||
// Try to enable auto_vacuum. This will only be
|
||||
// applied if the database is new or after successful
|
||||
// VACUUM, which usually happens before backup export.
|
||||
// When auto_vacuum is INCREMENTAL, it is possible to
|
||||
// use PRAGMA incremental_vacuum to return unused
|
||||
// database pages to the filesystem.
|
||||
conn.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;
|
||||
|
||||
// Try to enable auto_vacuum. This will only be
|
||||
// applied if the database is new or after successful
|
||||
// VACUUM, which usually happens before backup export.
|
||||
// When auto_vacuum is INCREMENTAL, it is possible to
|
||||
// use PRAGMA incremental_vacuum to return unused
|
||||
// database pages to the filesystem.
|
||||
conn.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;
|
||||
// journal_mode is persisted, it is sufficient to change it only for one handle.
|
||||
conn.pragma_update(None, "journal_mode", &"WAL".to_string())?;
|
||||
|
||||
// journal_mode is persisted, it is sufficient to change it only for one handle.
|
||||
conn.pragma_update(None, "journal_mode", &"WAL".to_string())?;
|
||||
|
||||
// Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode.
|
||||
conn.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
|
||||
// Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode.
|
||||
conn.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
self.run_migrations(context).await?;
|
||||
@@ -343,15 +348,19 @@ impl Sql {
|
||||
/// Execute the given query, returning the number of affected rows.
|
||||
pub async fn execute(&self, query: &str, params: impl rusqlite::Params) -> Result<usize> {
|
||||
let conn = self.get_conn().await?;
|
||||
let res = conn.execute(query, params)?;
|
||||
Ok(res)
|
||||
tokio::task::block_in_place(move || {
|
||||
let res = conn.execute(query, params)?;
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
|
||||
/// Executes the given query, returning the last inserted row ID.
|
||||
pub async fn insert(&self, query: &str, params: impl rusqlite::Params) -> Result<i64> {
|
||||
let conn = self.get_conn().await?;
|
||||
conn.execute(query, params)?;
|
||||
Ok(conn.last_insert_rowid())
|
||||
tokio::task::block_in_place(move || {
|
||||
conn.execute(query, params)?;
|
||||
Ok(conn.last_insert_rowid())
|
||||
})
|
||||
}
|
||||
|
||||
/// Prepares and executes the statement and maps a function over the resulting rows.
|
||||
@@ -369,9 +378,11 @@ impl Sql {
|
||||
G: FnMut(rusqlite::MappedRows<F>) -> Result<H>,
|
||||
{
|
||||
let conn = self.get_conn().await?;
|
||||
let mut stmt = conn.prepare(sql)?;
|
||||
let res = stmt.query_map(params, f)?;
|
||||
g(res)
|
||||
tokio::task::block_in_place(move || {
|
||||
let mut stmt = conn.prepare(sql)?;
|
||||
let res = stmt.query_map(params, f)?;
|
||||
g(res)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_conn(
|
||||
@@ -408,8 +419,10 @@ impl Sql {
|
||||
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
{
|
||||
let conn = self.get_conn().await?;
|
||||
let res = conn.query_row(query, params, f)?;
|
||||
Ok(res)
|
||||
tokio::task::block_in_place(move || {
|
||||
let res = conn.query_row(query, params, f)?;
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute the function inside a transaction.
|
||||
@@ -422,49 +435,55 @@ impl Sql {
|
||||
G: Send + 'static + FnOnce(&mut rusqlite::Transaction<'_>) -> anyhow::Result<H>,
|
||||
{
|
||||
let mut conn = self.get_conn().await?;
|
||||
let mut transaction = conn.transaction()?;
|
||||
let ret = callback(&mut transaction);
|
||||
tokio::task::block_in_place(move || {
|
||||
let mut transaction = conn.transaction()?;
|
||||
let ret = callback(&mut transaction);
|
||||
|
||||
match ret {
|
||||
Ok(ret) => {
|
||||
transaction.commit()?;
|
||||
Ok(ret)
|
||||
match ret {
|
||||
Ok(ret) => {
|
||||
transaction.commit()?;
|
||||
Ok(ret)
|
||||
}
|
||||
Err(err) => {
|
||||
transaction.rollback()?;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
transaction.rollback()?;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Query the database if the requested table already exists.
|
||||
pub async fn table_exists(&self, name: &str) -> anyhow::Result<bool> {
|
||||
let conn = self.get_conn().await?;
|
||||
let mut exists = false;
|
||||
conn.pragma(None, "table_info", &name.to_string(), |_row| {
|
||||
// will only be executed if the info was found
|
||||
exists = true;
|
||||
Ok(())
|
||||
})?;
|
||||
tokio::task::block_in_place(move || {
|
||||
let mut exists = false;
|
||||
conn.pragma(None, "table_info", &name.to_string(), |_row| {
|
||||
// will only be executed if the info was found
|
||||
exists = true;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(exists)
|
||||
Ok(exists)
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a column exists in a given table.
|
||||
pub async fn col_exists(&self, table_name: &str, col_name: &str) -> anyhow::Result<bool> {
|
||||
let conn = self.get_conn().await?;
|
||||
let mut exists = false;
|
||||
// `PRAGMA table_info` returns one row per column,
|
||||
// each row containing 0=cid, 1=name, 2=type, 3=notnull, 4=dflt_value
|
||||
conn.pragma(None, "table_info", &table_name.to_string(), |row| {
|
||||
let curr_name: String = row.get(1)?;
|
||||
if col_name == curr_name {
|
||||
exists = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
tokio::task::block_in_place(move || {
|
||||
let mut exists = false;
|
||||
// `PRAGMA table_info` returns one row per column,
|
||||
// each row containing 0=cid, 1=name, 2=type, 3=notnull, 4=dflt_value
|
||||
conn.pragma(None, "table_info", &table_name.to_string(), |row| {
|
||||
let curr_name: String = row.get(1)?;
|
||||
if col_name == curr_name {
|
||||
exists = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(exists)
|
||||
Ok(exists)
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute a query which is expected to return zero or one row.
|
||||
@@ -478,12 +497,15 @@ impl Sql {
|
||||
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
{
|
||||
let conn = self.get_conn().await?;
|
||||
let res = match conn.query_row(sql.as_ref(), params, f) {
|
||||
Ok(res) => Ok(Some(res)),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(rusqlite::Error::InvalidColumnType(_, _, rusqlite::types::Type::Null)) => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
}?;
|
||||
let res =
|
||||
tokio::task::block_in_place(move || match conn.query_row(sql.as_ref(), params, f) {
|
||||
Ok(res) => Ok(Some(res)),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(rusqlite::Error::InvalidColumnType(_, _, rusqlite::types::Type::Null)) => {
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -717,7 +739,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
info!(context, "{} files in use.", files_in_use.len(),);
|
||||
/* go through directory and delete unused files */
|
||||
let p = context.get_blobdir();
|
||||
match async_std::fs::read_dir(p).await {
|
||||
match tokio::fs::read_dir(p).await {
|
||||
Ok(mut dir_handle) => {
|
||||
/* avoid deletion of files that are just created to build a message object */
|
||||
let diff = std::time::Duration::from_secs(60 * 60);
|
||||
@@ -725,11 +747,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
.checked_sub(diff)
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
|
||||
while let Some(entry) = dir_handle.next().await {
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(_) => break,
|
||||
};
|
||||
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
||||
let name_f = entry.file_name();
|
||||
let name_s = name_f.to_string_lossy();
|
||||
|
||||
@@ -743,7 +761,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
|
||||
unreferenced_count += 1;
|
||||
|
||||
if let Ok(stats) = async_std::fs::metadata(entry.path()).await {
|
||||
if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
|
||||
let recently_created =
|
||||
stats.created().map_or(false, |t| t > keep_files_newer_than);
|
||||
let recently_modified = stats
|
||||
@@ -860,8 +878,9 @@ pub fn repeat_vars(count: usize) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_std::channel;
|
||||
use async_std::fs::File;
|
||||
use async_channel as channel;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::{test_utils::TestContext, EventType};
|
||||
@@ -894,14 +913,14 @@ mod tests {
|
||||
assert!(is_file_in_use(&files, Some("-suffix"), "world.txt-suffix"));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_table_exists() {
|
||||
let t = TestContext::new().await;
|
||||
assert!(t.ctx.sql.table_exists("msgs").await.unwrap());
|
||||
assert!(!t.ctx.sql.table_exists("foobar").await.unwrap());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_col_exists() {
|
||||
let t = TestContext::new().await;
|
||||
assert!(t.ctx.sql.col_exists("msgs", "mime_modified").await.unwrap());
|
||||
@@ -910,7 +929,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Tests that auto_vacuum is enabled for new databases.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_auto_vacuum() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -925,7 +944,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_housekeeping_db_closed() {
|
||||
let t = TestContext::new().await;
|
||||
|
||||
@@ -945,14 +964,14 @@ mod tests {
|
||||
t.add_event_sender(event_sink).await;
|
||||
|
||||
let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap();
|
||||
assert_eq!(avatar_bytes, &async_std::fs::read(&a).await.unwrap()[..]);
|
||||
assert_eq!(avatar_bytes, &tokio::fs::read(&a).await.unwrap()[..]);
|
||||
|
||||
t.sql.close().await;
|
||||
housekeeping(&t).await.unwrap_err(); // housekeeping should fail as the db is closed
|
||||
t.sql.open(&t, "".to_string()).await.unwrap();
|
||||
|
||||
let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap();
|
||||
assert_eq!(avatar_bytes, &async_std::fs::read(&a).await.unwrap()[..]);
|
||||
assert_eq!(avatar_bytes, &tokio::fs::read(&a).await.unwrap()[..]);
|
||||
|
||||
while let Ok(event) = event_source.try_recv() {
|
||||
match event.typ {
|
||||
@@ -969,7 +988,7 @@ mod tests {
|
||||
|
||||
/// Regression test for a bug where housekeeping deleted drafts since their
|
||||
/// `hidden` flag is set.
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_housekeeping_dont_delete_drafts() {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -996,7 +1015,7 @@ mod tests {
|
||||
///
|
||||
/// Statements were not finalized due to a bug in sqlx:
|
||||
/// <https://github.com/launchbadge/sqlx/issues/1147>
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_db_reopen() -> Result<()> {
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -1006,7 +1025,7 @@ mod tests {
|
||||
// Create a separate empty database for testing.
|
||||
let dir = tempdir()?;
|
||||
let dbfile = dir.path().join("testdb.sqlite");
|
||||
let sql = Sql::new(dbfile.into());
|
||||
let sql = Sql::new(dbfile);
|
||||
|
||||
// Create database with all the tables.
|
||||
sql.open(&t, "".to_string()).await.unwrap();
|
||||
@@ -1028,7 +1047,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_migration_flags() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
t.evtracker.get_info_contains("Opened database").await;
|
||||
@@ -1067,7 +1086,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_check_passphrase() -> Result<()> {
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -1077,7 +1096,7 @@ mod tests {
|
||||
// Create a separate empty database for testing.
|
||||
let dir = tempdir()?;
|
||||
let dbfile = dir.path().join("testdb.sqlite");
|
||||
let sql = Sql::new(dbfile.clone().into());
|
||||
let sql = Sql::new(dbfile.clone());
|
||||
|
||||
sql.check_passphrase("foo".to_string()).await?;
|
||||
sql.open(&t, "foo".to_string())
|
||||
@@ -1086,7 +1105,7 @@ mod tests {
|
||||
sql.close().await;
|
||||
|
||||
// Reopen the database
|
||||
let sql = Sql::new(dbfile.into());
|
||||
let sql = Sql::new(dbfile);
|
||||
|
||||
// Test that we can't open encrypted database without a passphrase.
|
||||
assert!(sql.open(&t, "".to_string()).await.is_err());
|
||||
|
||||
@@ -1167,7 +1167,7 @@ mod tests {
|
||||
assert_eq!(StockMessage::NoMessages.fallback(), "No messages.");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_stock_translation() {
|
||||
let t = TestContext::new().await;
|
||||
t.set_stock_translation(StockMessage::NoMessages, "xyz".to_string())
|
||||
@@ -1176,7 +1176,7 @@ mod tests {
|
||||
assert_eq!(no_messages(&t).await, "xyz")
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_stock_translation_wrong_replacements() {
|
||||
let t = TestContext::new().await;
|
||||
assert!(t
|
||||
@@ -1191,13 +1191,13 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_str() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(no_messages(&t).await, "No messages.");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_string_repl_str() {
|
||||
let t = TestContext::new().await;
|
||||
let contact_id = Contact::create(&t.ctx, "Someone", "someone@example.org")
|
||||
@@ -1212,13 +1212,13 @@ mod tests {
|
||||
// We have no string using %1$d to test...
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_string_repl_str2() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(msg_action_by_user(&t, "foo", "bar").await, "foo by bar.");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_system_msg_simple() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(
|
||||
@@ -1227,7 +1227,7 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_system_msg_add_member_by_me() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(
|
||||
@@ -1236,7 +1236,7 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_system_msg_add_member_by_me_with_displayname() {
|
||||
let t = TestContext::new().await;
|
||||
Contact::create(&t, "Alice", "alice@example.org")
|
||||
@@ -1248,7 +1248,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_stock_system_msg_add_member_by_other_with_displayname() {
|
||||
let t = TestContext::new().await;
|
||||
let contact_id = {
|
||||
@@ -1265,7 +1265,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_quota_exceeding_stock_str() -> anyhow::Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let str = quota_exceeding(&t, 81).await;
|
||||
@@ -1275,7 +1275,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_partial_download_msg_body() -> anyhow::Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let str = partial_download_msg_body(&t, 1024 * 1024).await;
|
||||
@@ -1283,7 +1283,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_update_device_chats() {
|
||||
let t = TestContext::new().await;
|
||||
t.update_device_chats().await.ok();
|
||||
|
||||
@@ -189,7 +189,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils as test;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_summary_text() {
|
||||
let d = test::TestContext::new().await;
|
||||
let ctx = &d.ctx;
|
||||
|
||||
12
src/sync.rs
12
src/sync.rs
@@ -267,7 +267,7 @@ mod tests {
|
||||
use crate::token::Namespace;
|
||||
use anyhow::bail;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_is_sync_sending_enabled() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert!(!t.is_sync_sending_enabled().await?);
|
||||
@@ -278,7 +278,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_build_sync_json() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config_bool(Config::SendSyncMsgs, true).await?;
|
||||
@@ -323,7 +323,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_build_sync_json_sync_msgs_off() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config_bool(Config::SendSyncMsgs, false).await?;
|
||||
@@ -337,7 +337,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_sync_items() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -422,7 +422,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_execute_sync_items() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -451,7 +451,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_sync_msg() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config_bool(Config::SendSyncMsgs, true).await?;
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::panic;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ansi_term::Color;
|
||||
use async_std::channel::{self, Receiver, Sender};
|
||||
use async_std::prelude::*;
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use async_std::task;
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use chat::ChatItem;
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::Rng;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::task;
|
||||
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::chatlist::Chatlist;
|
||||
@@ -250,6 +250,17 @@ impl TestContext {
|
||||
Self::builder().configure_fiona().build().await
|
||||
}
|
||||
|
||||
/// Print current chat state.
|
||||
pub async fn print_chats(&self) {
|
||||
println!("\n========== Chats of {}: ==========", self.name());
|
||||
if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await {
|
||||
for (chat, _) in chats.iter() {
|
||||
self.print_chat(*chat).await;
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Internal constructor.
|
||||
///
|
||||
/// `name` is used to identify this context in e.g. log output. This is useful mostly
|
||||
@@ -266,7 +277,7 @@ impl TestContext {
|
||||
let mut context_names = CONTEXT_NAMES.write().unwrap();
|
||||
context_names.insert(id, name);
|
||||
}
|
||||
let ctx = Context::new(dbfile.into(), id, Events::new())
|
||||
let ctx = Context::new(&dbfile, id, Events::new())
|
||||
.await
|
||||
.expect("failed to create context");
|
||||
|
||||
@@ -382,7 +393,7 @@ impl TestContext {
|
||||
break row;
|
||||
}
|
||||
if start.elapsed() < Duration::from_secs(3) {
|
||||
async_std::task::sleep(Duration::from_millis(100)).await;
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
} else {
|
||||
panic!("no sent message found in jobs table");
|
||||
}
|
||||
@@ -670,20 +681,6 @@ impl Deref for TestContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestContext {
|
||||
fn drop(&mut self) {
|
||||
async_std::task::block_on(async {
|
||||
println!("\n========== Chats of {}: ==========", self.name());
|
||||
if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await {
|
||||
for (chat, _) in chats.iter() {
|
||||
self.print_chat(*chat).await;
|
||||
}
|
||||
}
|
||||
println!();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LogEvent {
|
||||
/// Logged event.
|
||||
Event(Event),
|
||||
@@ -828,15 +825,14 @@ impl EventTracker {
|
||||
/// If no matching events are ready this will wait for new events to arrive and time out
|
||||
/// after 10 seconds.
|
||||
pub async fn get_matching<F: Fn(&EventType) -> bool>(&self, event_matcher: F) -> EventType {
|
||||
async move {
|
||||
tokio::time::timeout(Duration::from_secs(10), async move {
|
||||
loop {
|
||||
let event = self.0.recv().await.unwrap();
|
||||
if event_matcher(&event.typ) {
|
||||
return event.typ;
|
||||
}
|
||||
}
|
||||
}
|
||||
.timeout(Duration::from_secs(10))
|
||||
})
|
||||
.await
|
||||
.expect("timeout waiting for event match")
|
||||
}
|
||||
@@ -1011,21 +1007,21 @@ mod tests {
|
||||
// The following three tests demonstrate, when made to fail, the log output being
|
||||
// directed to the correct test output.
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_with_alice() {
|
||||
let alice = TestContext::builder().configure_alice().build().await;
|
||||
alice.ctx.emit_event(EventType::Info("hello".into()));
|
||||
// panic!("Alice fails");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_with_bob() {
|
||||
let bob = TestContext::builder().configure_bob().build().await;
|
||||
bob.ctx.emit_event(EventType::Info("there".into()));
|
||||
// panic!("Bob fails");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_with_both() {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice = tcm.alice().await;
|
||||
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
use crate::dc_tools::time;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_params_update_timestamp() -> Result<()> {
|
||||
let mut params = Params::new();
|
||||
let ts = time();
|
||||
@@ -85,7 +85,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_out_of_order_subject() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -126,7 +126,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_out_of_order_group_name() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
|
||||
184
src/webxdc.rs
184
src/webxdc.rs
@@ -1,26 +1,27 @@
|
||||
//! # Handle webxdc messages.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, format_err, Result};
|
||||
use deltachat_derive::FromSql;
|
||||
use lettre_email::mime;
|
||||
use lettre_email::PartBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::chat::Chat;
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{dc_create_smeared_timestamp, dc_open_file_std};
|
||||
use crate::dc_tools::dc_create_smeared_timestamp;
|
||||
use crate::dc_tools::dc_get_abs_path;
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::Param;
|
||||
use crate::param::Params;
|
||||
use crate::scheduler::InterruptInfo;
|
||||
use crate::{chat, EventType};
|
||||
use anyhow::{bail, ensure, format_err, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use deltachat_derive::FromSql;
|
||||
use lettre_email::mime;
|
||||
use lettre_email::PartBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use zip::ZipArchive;
|
||||
|
||||
/// The current API version.
|
||||
/// If `min_api` in manifest.toml is set to a larger value,
|
||||
@@ -139,16 +140,12 @@ pub(crate) struct StatusUpdateItemAndSerial {
|
||||
|
||||
impl Context {
|
||||
/// check if a file is an acceptable webxdc for sending or receiving.
|
||||
pub(crate) async fn is_webxdc_file<R>(&self, filename: &str, mut reader: R) -> Result<bool>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
pub(crate) async fn is_webxdc_file(&self, filename: &str, file: &[u8]) -> Result<bool> {
|
||||
if !filename.ends_with(WEBXDC_SUFFIX) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let size = reader.seek(SeekFrom::End(0))?;
|
||||
if size > WEBXDC_RECEIVING_LIMIT {
|
||||
if file.len() as u64 > WEBXDC_RECEIVING_LIMIT {
|
||||
info!(
|
||||
self,
|
||||
"{} exceeds receiving limit of {} bytes", &filename, WEBXDC_RECEIVING_LIMIT
|
||||
@@ -156,8 +153,7 @@ impl Context {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
reader.seek(SeekFrom::Start(0))?;
|
||||
let mut archive = match zip::ZipArchive::new(reader) {
|
||||
let archive = match async_zip::read::mem::ZipFileReader::new(file).await {
|
||||
Ok(archive) => archive,
|
||||
Err(_) => {
|
||||
info!(self, "{} cannot be opened as zip-file", &filename);
|
||||
@@ -165,7 +161,7 @@ impl Context {
|
||||
}
|
||||
};
|
||||
|
||||
if archive.by_name("index.html").is_err() {
|
||||
if archive.entry("index.html").is_none() {
|
||||
info!(self, "{} misses index.html", &filename);
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -176,18 +172,12 @@ impl Context {
|
||||
/// ensure that a file is an acceptable webxdc for sending
|
||||
/// (sending has more strict size limits).
|
||||
pub(crate) async fn ensure_sendable_webxdc_file(&self, path: &PathBuf) -> Result<()> {
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
if !self
|
||||
.is_webxdc_file(path.to_str().unwrap_or_default(), &mut file)
|
||||
.await?
|
||||
{
|
||||
bail!(
|
||||
"{} is not a valid webxdc file",
|
||||
path.to_str().unwrap_or_default()
|
||||
);
|
||||
let filename = path.to_str().unwrap_or_default();
|
||||
if !filename.ends_with(WEBXDC_SUFFIX) {
|
||||
bail!("{} is not a valid webxdc file", filename);
|
||||
}
|
||||
|
||||
let size = file.seek(SeekFrom::End(0))?;
|
||||
let size = tokio::fs::metadata(path).await?.len();
|
||||
if size > WEBXDC_SENDING_LIMIT {
|
||||
bail!(
|
||||
"webxdc {} exceeds acceptable size of {} bytes",
|
||||
@@ -195,6 +185,26 @@ impl Context {
|
||||
WEBXDC_SENDING_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
let valid = match async_zip::read::fs::ZipFileReader::new(path).await {
|
||||
Ok(archive) => {
|
||||
if archive.entry("index.html").is_none() {
|
||||
info!(self, "{} misses index.html", filename);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
info!(self, "{} cannot be opened as zip-file", filename);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !valid {
|
||||
bail!("{} is not a valid webxdc file", filename);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -588,22 +598,28 @@ fn parse_webxdc_manifest(bytes: &[u8]) -> Result<WebxdcManifest> {
|
||||
Ok(manifest)
|
||||
}
|
||||
|
||||
fn get_blob(archive: &mut ZipArchive<File>, name: &str) -> Result<Vec<u8>> {
|
||||
let mut file = archive.by_name(name)?;
|
||||
async fn get_blob(archive: &mut async_zip::read::fs::ZipFileReader, name: &str) -> Result<Vec<u8>> {
|
||||
let (i, _) = archive
|
||||
.entry(name)
|
||||
.ok_or_else(|| anyhow!("no entry found for {}", name))?;
|
||||
let mut reader = archive.entry_reader(i).await?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
reader.read_to_end(&mut buf).await?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Get handle to a webxdc ZIP-archive.
|
||||
/// To check for file existance use archive.by_name(), to read a file, use get_blob(archive).
|
||||
async fn get_webxdc_archive(&self, context: &Context) -> Result<ZipArchive<File>> {
|
||||
async fn get_webxdc_archive(
|
||||
&self,
|
||||
context: &Context,
|
||||
) -> Result<async_zip::read::fs::ZipFileReader> {
|
||||
let path = self
|
||||
.get_file(context)
|
||||
.ok_or_else(|| format_err!("No webxdc instance file."))?;
|
||||
let file = dc_open_file_std(context, path)?;
|
||||
let archive = zip::ZipArchive::new(file)?;
|
||||
let path_abs = dc_get_abs_path(context, &path);
|
||||
let archive = async_zip::read::fs::ZipFileReader::new(path_abs).await?;
|
||||
Ok(archive)
|
||||
}
|
||||
|
||||
@@ -627,7 +643,7 @@ impl Message {
|
||||
let mut archive = self.get_webxdc_archive(context).await?;
|
||||
|
||||
if name == "index.html" {
|
||||
if let Ok(bytes) = get_blob(&mut archive, "manifest.toml") {
|
||||
if let Ok(bytes) = get_blob(&mut archive, "manifest.toml").await {
|
||||
if let Ok(manifest) = parse_webxdc_manifest(&bytes) {
|
||||
if let Some(min_api) = manifest.min_api {
|
||||
if min_api > WEBXDC_API_VERSION {
|
||||
@@ -640,7 +656,7 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
get_blob(&mut archive, name)
|
||||
get_blob(&mut archive, name).await
|
||||
}
|
||||
|
||||
/// Return info from manifest.toml or from fallbacks.
|
||||
@@ -648,7 +664,7 @@ impl Message {
|
||||
ensure!(self.viewtype == Viewtype::Webxdc, "No webxdc instance.");
|
||||
let mut archive = self.get_webxdc_archive(context).await?;
|
||||
|
||||
let mut manifest = if let Ok(bytes) = get_blob(&mut archive, "manifest.toml") {
|
||||
let mut manifest = if let Ok(bytes) = get_blob(&mut archive, "manifest.toml").await {
|
||||
if let Ok(manifest) = parse_webxdc_manifest(&bytes) {
|
||||
manifest
|
||||
} else {
|
||||
@@ -680,9 +696,9 @@ impl Message {
|
||||
} else {
|
||||
self.get_filename().unwrap_or_default()
|
||||
},
|
||||
icon: if archive.by_name("icon.png").is_ok() {
|
||||
icon: if archive.entry("icon.png").is_some() {
|
||||
"icon.png".to_string()
|
||||
} else if archive.by_name("icon.jpg").is_ok() {
|
||||
} else if archive.entry("icon.jpg").is_some() {
|
||||
"icon.jpg".to_string()
|
||||
} else {
|
||||
WEBXDC_DEFAULT_ICON.to_string()
|
||||
@@ -708,10 +724,8 @@ impl Message {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use async_std::fs::File;
|
||||
use async_std::io::WriteExt;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::chat::{
|
||||
add_contact_to_chat, create_group_chat, forward_msgs, resend_msgs, send_msg, send_text_msg,
|
||||
@@ -726,7 +740,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_file_limits() -> Result<()> {
|
||||
assert!(WEBXDC_SENDING_LIMIT >= 32768);
|
||||
assert!(WEBXDC_SENDING_LIMIT < 16777216);
|
||||
@@ -735,41 +749,41 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_is_webxdc_file() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
assert!(
|
||||
!t.is_webxdc_file(
|
||||
"bad-ext-no-zip.txt",
|
||||
Cursor::new(include_bytes!("../test-data/message/issue_523.txt"))
|
||||
include_bytes!("../test-data/message/issue_523.txt")
|
||||
)
|
||||
.await?
|
||||
);
|
||||
assert!(
|
||||
!t.is_webxdc_file(
|
||||
"bad-ext-good-zip.txt",
|
||||
Cursor::new(include_bytes!("../test-data/webxdc/minimal.xdc"))
|
||||
include_bytes!("../test-data/webxdc/minimal.xdc")
|
||||
)
|
||||
.await?
|
||||
);
|
||||
assert!(
|
||||
!t.is_webxdc_file(
|
||||
"good-ext-no-zip.xdc",
|
||||
Cursor::new(include_bytes!("../test-data/message/issue_523.txt"))
|
||||
include_bytes!("../test-data/message/issue_523.txt")
|
||||
)
|
||||
.await?
|
||||
);
|
||||
assert!(
|
||||
!t.is_webxdc_file(
|
||||
"good-ext-no-index-html.xdc",
|
||||
Cursor::new(include_bytes!("../test-data/webxdc/no-index-html.xdc"))
|
||||
include_bytes!("../test-data/webxdc/no-index-html.xdc")
|
||||
)
|
||||
.await?
|
||||
);
|
||||
assert!(
|
||||
t.is_webxdc_file(
|
||||
"good-ext-good-zip.xdc",
|
||||
Cursor::new(include_bytes!("../test-data/webxdc/minimal.xdc"))
|
||||
include_bytes!("../test-data/webxdc/minimal.xdc")
|
||||
)
|
||||
.await?
|
||||
);
|
||||
@@ -796,7 +810,7 @@ mod tests {
|
||||
Message::load_from_db(t, instance_msg_id).await
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_webxdc_instance() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -820,7 +834,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_invalid_webxdc() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -852,7 +866,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_forward_webxdc_instance() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -895,7 +909,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resend_webxdc_instance_and_info() -> Result<()> {
|
||||
// Alice uses webxdc in a group
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -940,7 +954,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_webxdc_instance() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
dc_receive_imf(
|
||||
@@ -966,7 +980,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_contact_request() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -1010,7 +1024,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_delete_webxdc_instance() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1048,7 +1062,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_status_update_record() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1142,7 +1156,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_status_update() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1238,7 +1252,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_webxdc_status_update() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config_bool(Config::BccSelf, true).await?;
|
||||
@@ -1338,7 +1352,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_render_webxdc_status_update_object() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
|
||||
@@ -1364,7 +1378,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_render_webxdc_status_update_object_range() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
|
||||
@@ -1402,7 +1416,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_pop_status_update() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
|
||||
@@ -1462,7 +1476,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -1529,7 +1543,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_webxdc_status_update_to_non_webxdc() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1541,7 +1555,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1558,7 +1572,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_default_icon() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1570,7 +1584,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_with_absolute_paths() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1583,7 +1597,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_with_subdirs() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1624,7 +1638,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webxdc_manifest() -> Result<()> {
|
||||
let result = parse_webxdc_manifest(r#"key = syntax error"#.as_bytes());
|
||||
assert!(result.is_err());
|
||||
@@ -1655,7 +1669,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webxdc_manifest_min_api() -> Result<()> {
|
||||
let manifest = parse_webxdc_manifest(r#"min_api = 3"#.as_bytes())?;
|
||||
assert_eq!(manifest.min_api, Some(3));
|
||||
@@ -1669,7 +1683,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webxdc_manifest_source_code_url() -> Result<()> {
|
||||
let result = parse_webxdc_manifest(r#"source_code_url = 3"#.as_bytes());
|
||||
assert!(result.is_err());
|
||||
@@ -1683,7 +1697,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_min_api_too_large() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
|
||||
@@ -1702,7 +1716,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_info() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
@@ -1786,7 +1800,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_info_summary() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -1852,7 +1866,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_document_name() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -1894,7 +1908,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_info_msg() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -1985,7 +1999,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_info_msg_cleanup_series() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -2023,7 +2037,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_info_msg_no_cleanup_on_interrupted_series() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "c").await?;
|
||||
@@ -2041,7 +2055,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_opportunistic_encryption() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
@@ -2093,7 +2107,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_chatlist_summary() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
|
||||
@@ -2113,7 +2127,7 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_and_text() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
Reference in New Issue
Block a user