Merge pull request #1784 from deltachat/feat/multiii

This commit is contained in:
Friedel Ziegelmayer
2020-08-11 12:20:32 +02:00
committed by GitHub
32 changed files with 1513 additions and 326 deletions

533
src/accounts.rs Normal file
View File

@@ -0,0 +1,533 @@
use std::collections::HashMap;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context as TaskContext, Poll};
use async_std::fs;
use async_std::path::PathBuf;
use async_std::sync::{Arc, RwLock};
use uuid::Uuid;
use anyhow::{ensure, Context as _};
use serde::{Deserialize, Serialize};
use crate::context::Context;
use crate::error::Result;
use crate::events::Event;
/// Account manager, that can handle multiple accounts in a single place.
#[derive(Debug, Clone)]
pub struct Accounts {
dir: PathBuf,
config: Config,
accounts: Arc<RwLock<HashMap<u32, Context>>>,
}
impl Accounts {
/// Loads or creates an accounts folder at the given `dir`.
pub async fn new(os_name: String, dir: PathBuf) -> Result<Self> {
if !dir.exists().await {
Accounts::create(os_name, &dir).await?;
}
Accounts::open(dir).await
}
/// Creates a new default structure, including a default account.
pub async fn create(os_name: String, dir: &PathBuf) -> Result<()> {
fs::create_dir_all(dir)
.await
.context("failed to create folder")?;
// create default account
let config = Config::new(os_name.clone(), dir).await?;
let account_config = config.new_account(dir).await?;
Context::new(os_name, account_config.dbfile().into(), account_config.id)
.await
.context("failed to create default account")?;
Ok(())
}
/// 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");
let config_file = dir.join(CONFIG_NAME);
ensure!(config_file.exists().await, "accounts.toml does not exist");
let config = Config::from_file(config_file).await?;
let accounts = config.load_accounts().await?;
Ok(Self {
dir,
config,
accounts: Arc::new(RwLock::new(accounts)),
})
}
/// Get an account by its `id`:
pub async fn get_account(&self, id: u32) -> Option<Context> {
self.accounts.read().await.get(&id).cloned()
}
/// Get the currently selected account.
pub async fn get_selected_account(&self) -> Context {
let id = self.config.get_selected_account().await;
self.accounts
.read()
.await
.get(&id)
.cloned()
.expect("inconsistent state")
}
/// Select the given account.
pub async fn select_account(&self, id: u32) -> Result<()> {
self.config.select_account(id).await?;
Ok(())
}
/// Add a new account.
pub async fn add_account(&self) -> Result<u32> {
let os_name = self.config.os_name().await;
let account_config = self.config.new_account(&self.dir).await?;
let ctx = Context::new(os_name, account_config.dbfile().into(), account_config.id).await?;
self.accounts.write().await.insert(account_config.id, ctx);
Ok(account_config.id)
}
/// Remove an account.
pub async fn remove_account(&self, id: u32) -> Result<()> {
let ctx = self.accounts.write().await.remove(&id);
ensure!(ctx.is_some(), "no account with this id: {}", id);
let ctx = ctx.unwrap();
ctx.stop_io().await;
drop(ctx);
if let Some(cfg) = self.config.get_account(id).await {
fs::remove_dir_all(async_std::path::PathBuf::from(&cfg.dir))
.await
.context("failed to remove account data")?;
}
self.config.remove_account(id).await?;
Ok(())
}
/// Migrate an existing account into this structure.
pub async fn migrate_account(&self, dbfile: PathBuf) -> Result<u32> {
let blobdir = Context::derive_blobdir(&dbfile);
ensure!(
dbfile.exists().await,
"no database found: {}",
dbfile.display()
);
ensure!(
blobdir.exists().await,
"no blobdir found: {}",
blobdir.display()
);
let old_id = self.config.get_selected_account().await;
// create new account
let account_config = self.config.new_account(&self.dir).await?;
let new_dbfile = account_config.dbfile().into();
let new_blobdir = Context::derive_blobdir(&new_dbfile);
let res = {
fs::create_dir_all(&account_config.dir).await?;
fs::rename(&dbfile, &new_dbfile).await?;
fs::rename(&blobdir, &new_blobdir).await?;
Ok(())
};
match res {
Ok(_) => {
let ctx = Context::with_blobdir(
self.config.os_name().await,
new_dbfile,
new_blobdir,
account_config.id,
)
.await?;
self.accounts.write().await.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))
.await
.context("failed to remove account data")?;
self.config.remove_account(account_config.id).await?;
// set selection back
self.select_account(old_id).await?;
Err(err)
}
}
}
/// Get a list of all account ids.
pub async fn get_all(&self) -> Vec<u32> {
self.accounts.read().await.keys().copied().collect()
}
/// Import a backup using a new account and selects it.
pub async fn import_account(&self, file: PathBuf) -> Result<u32> {
let old_id = self.config.get_selected_account().await;
let id = self.add_account().await?;
let ctx = self.get_account(id).await.expect("just added");
match crate::imex::imex(&ctx, crate::imex::ImexMode::ImportBackup, Some(file)).await {
Ok(_) => Ok(id),
Err(err) => {
// remove temp account
self.remove_account(id).await?;
// set selection back
self.select_account(old_id).await?;
Err(err)
}
}
}
pub async fn start_io(&self) {
let accounts = &*self.accounts.read().await;
for account in accounts.values() {
account.start_io().await;
}
}
pub async fn stop_io(&self) {
let accounts = &*self.accounts.read().await;
for account in accounts.values() {
account.stop_io().await;
}
}
pub async fn maybe_network(&self) {
let accounts = &*self.accounts.read().await;
for account in accounts.values() {
account.maybe_network().await;
}
}
/// Unified event emitter.
pub async fn get_event_emitter(&self) -> EventEmitter {
let emitters = self
.accounts
.read()
.await
.iter()
.map(|(id, a)| EmitterWrapper {
id: *id,
emitter: a.get_event_emitter(),
done: AtomicBool::new(false),
})
.collect();
EventEmitter(emitters)
}
}
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> {
futures::future::poll_fn(|cx| Pin::new(self).recv_poll(cx)).await
}
fn recv_poll(self: Pin<&Self>, _cx: &mut TaskContext<'_>) -> Poll<Option<Event>> {
for e in &*self.0 {
if e.done.load(Ordering::Acquire) {
// skip emitters that are already done
continue;
}
match e.emitter.try_recv() {
Ok(event) => return Poll::Ready(Some(event)),
Err(async_std::sync::TryRecvError::Disconnected) => {
e.done.store(true, Ordering::Release);
}
Err(async_std::sync::TryRecvError::Empty) => {}
}
}
Poll::Pending
}
}
#[derive(Debug)]
pub struct EventEmitter(Vec<EmitterWrapper>);
#[derive(Debug)]
struct EmitterWrapper {
id: u32,
emitter: crate::events::EventEmitter,
done: AtomicBool,
}
pub const CONFIG_NAME: &str = "accounts.toml";
pub const DB_NAME: &str = "dc.db";
#[derive(Debug, Clone)]
pub struct Config {
file: PathBuf,
inner: Arc<RwLock<InnerConfig>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
struct InnerConfig {
pub os_name: String,
/// The currently selected account.
pub selected_account: u32,
pub next_id: u32,
pub accounts: Vec<AccountConfig>,
}
impl Config {
pub async fn new(os_name: String, dir: &PathBuf) -> Result<Self> {
let cfg = Config {
file: dir.join(CONFIG_NAME),
inner: Arc::new(RwLock::new(InnerConfig {
os_name,
accounts: Vec::new(),
selected_account: 0,
next_id: 1,
})),
};
cfg.sync().await?;
Ok(cfg)
}
pub async fn os_name(&self) -> String {
self.inner.read().await.os_name.clone()
}
/// Sync the inmemory representation to disk.
async fn sync(&self) -> Result<()> {
fs::write(
&self.file,
toml::to_string_pretty(&*self.inner.read().await)?,
)
.await
.context("failed to write config")
}
/// Read a configuration from the given file into memory.
pub async fn from_file(file: PathBuf) -> Result<Self> {
let bytes = fs::read(&file).await.context("failed to read file")?;
let inner: InnerConfig = toml::from_slice(&bytes).context("failed to parse config")?;
Ok(Config {
file,
inner: Arc::new(RwLock::new(inner)),
})
}
pub async fn load_accounts(&self) -> Result<HashMap<u32, Context>> {
let cfg = &*self.inner.read().await;
let mut accounts = HashMap::with_capacity(cfg.accounts.len());
for account_config in &cfg.accounts {
let ctx = Context::new(
cfg.os_name.clone(),
account_config.dbfile().into(),
account_config.id,
)
.await?;
accounts.insert(account_config.id, ctx);
}
Ok(accounts)
}
/// Create a new account in the given root directory.
pub async fn new_account(&self, dir: &PathBuf) -> Result<AccountConfig> {
let id = {
let inner = &mut self.inner.write().await;
let id = inner.next_id;
let uuid = Uuid::new_v4();
let target_dir = dir.join(uuid.to_simple_ref().to_string());
inner.accounts.push(AccountConfig {
id,
name: String::new(),
dir: target_dir.into(),
uuid,
});
inner.next_id += 1;
id
};
self.sync().await?;
self.select_account(id).await.expect("just added");
let cfg = self.get_account(id).await.expect("just added");
Ok(cfg)
}
/// Removes an existing acccount entirely.
pub async fn remove_account(&self, id: u32) -> Result<()> {
{
let inner = &mut *self.inner.write().await;
if let Some(idx) = inner.accounts.iter().position(|e| e.id == id) {
// remove account from the configs
inner.accounts.remove(idx);
}
if inner.selected_account == id {
// reset selected account
inner.selected_account = inner.accounts.get(0).map(|e| e.id).unwrap_or_default();
}
}
self.sync().await
}
pub async fn get_account(&self, id: u32) -> Option<AccountConfig> {
self.inner
.read()
.await
.accounts
.iter()
.find(|e| e.id == id)
.cloned()
}
pub async fn get_selected_account(&self) -> u32 {
self.inner.read().await.selected_account
}
pub async fn select_account(&self, id: u32) -> Result<()> {
{
let inner = &mut *self.inner.write().await;
ensure!(
inner.accounts.iter().any(|e| e.id == id),
"invalid account id: {}",
id
);
inner.selected_account = id;
}
self.sync().await?;
Ok(())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AccountConfig {
/// Unique id.
pub id: u32,
/// Display name
pub name: String,
/// Root directory for all data for this account.
pub dir: std::path::PathBuf,
pub uuid: Uuid,
}
impl AccountConfig {
/// Get the canoncial dbfile name for this configuration.
pub fn dbfile(&self) -> std::path::PathBuf {
self.dir.join(DB_NAME)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[async_std::test]
async fn test_account_new_open() {
let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts1").into();
let accounts1 = Accounts::new("my_os".into(), p.clone()).await.unwrap();
let accounts2 = Accounts::open(p).await.unwrap();
assert_eq!(accounts1.accounts.read().await.len(), 1);
assert_eq!(accounts1.config.get_selected_account().await, 1);
assert_eq!(accounts1.dir, accounts2.dir);
assert_eq!(
&*accounts1.config.inner.read().await,
&*accounts2.config.inner.read().await,
);
assert_eq!(
accounts1.accounts.read().await.len(),
accounts2.accounts.read().await.len()
);
}
#[async_std::test]
async fn test_account_new_add_remove() {
let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into();
let accounts = Accounts::new("my_os".into(), p.clone()).await.unwrap();
assert_eq!(accounts.accounts.read().await.len(), 1);
assert_eq!(accounts.config.get_selected_account().await, 1);
let id = accounts.add_account().await.unwrap();
assert_eq!(id, 2);
assert_eq!(accounts.config.get_selected_account().await, id);
assert_eq!(accounts.accounts.read().await.len(), 2);
accounts.select_account(1).await.unwrap();
assert_eq!(accounts.config.get_selected_account().await, 1);
accounts.remove_account(1).await.unwrap();
assert_eq!(accounts.config.get_selected_account().await, 2);
assert_eq!(accounts.accounts.read().await.len(), 1);
}
#[async_std::test]
async fn test_migrate_account() {
let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into();
let accounts = Accounts::new("my_os".into(), p.clone()).await.unwrap();
assert_eq!(accounts.accounts.read().await.len(), 1);
assert_eq!(accounts.config.get_selected_account().await, 1);
let extern_dbfile: PathBuf = dir.path().join("other").into();
let ctx = Context::new("my_os".into(), extern_dbfile.clone(), 0)
.await
.unwrap();
ctx.set_config(crate::config::Config::Addr, Some("me@mail.com"))
.await
.unwrap();
drop(ctx);
accounts
.migrate_account(extern_dbfile.clone())
.await
.unwrap();
assert_eq!(accounts.accounts.read().await.len(), 2);
assert_eq!(accounts.config.get_selected_account().await, 2);
let ctx = accounts.get_selected_account().await;
assert_eq!(
"me@mail.com",
ctx.get_config(crate::config::Config::Addr).await.unwrap()
);
}
}

View File

@@ -15,7 +15,7 @@ use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::events::Event;
use crate::events::EventType;
use crate::message;
/// Represents a file in the blob directory.
@@ -67,7 +67,7 @@ impl<'a> BlobObject<'a> {
blobdir,
name: format!("$BLOBDIR/{}", name),
};
context.emit_event(Event::NewBlobFile(blob.as_name().to_string()));
context.emit_event(EventType::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}
@@ -155,7 +155,7 @@ impl<'a> BlobObject<'a> {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),
};
context.emit_event(Event::NewBlobFile(blob.as_name().to_string()));
context.emit_event(EventType::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}

View File

@@ -17,7 +17,7 @@ use crate::context::Context;
use crate::dc_tools::*;
use crate::ephemeral::{delete_expired_messages, schedule_ephemeral_task, Timer as EphemeralTimer};
use crate::error::{bail, ensure, format_err, Error};
use crate::events::Event;
use crate::events::EventType;
use crate::job::{self, Action};
use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId};
use crate::mimeparser::SystemMessage;
@@ -185,7 +185,7 @@ impl ChatId {
)
.await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});
@@ -242,7 +242,7 @@ impl ChatId {
.execute("DELETE FROM chats WHERE id=?;", paramsv![self])
.await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});
@@ -268,7 +268,7 @@ impl ChatId {
};
if changed {
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: self,
msg_id: MsgId::new(0),
});
@@ -789,7 +789,7 @@ impl Chat {
{
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot send message; self not in group.".into())
EventType::ErrorSelfNotInGroup("Cannot send message; self not in group.".into())
);
bail!("Cannot set message; self not in group.");
}
@@ -1146,7 +1146,7 @@ pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId
chat.id.unblock(context).await;
// Sending with 0s as data since multiple messages may have changed.
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -1188,7 +1188,7 @@ pub async fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<
}
};
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -1355,7 +1355,7 @@ pub async fn prepare_msg(
msg.state = MessageState::OutPreparing;
let msg_id = prepare_msg_common(context, chat_id, msg).await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: msg.chat_id,
msg_id: msg.id,
});
@@ -1530,7 +1530,7 @@ pub async fn send_msg_sync(
match status {
job::Status::Finished(Ok(_)) => {
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: msg.chat_id,
msg_id: msg.id,
});
@@ -1558,13 +1558,13 @@ async fn send_msg_inner(
if let Some(send_job) = prepare_send_msg(context, chat_id, msg).await? {
job::add(context, send_job).await;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: msg.chat_id,
msg_id: msg.id,
});
if msg.param.exists(Param::SetLatitude) {
context.emit_event(Event::LocationChanged(Some(DC_CONTACT_ID_SELF)));
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
}
}
@@ -1665,7 +1665,7 @@ pub async fn get_chat_msgs(
// On desktop chatlist is always shown on the side,
// and it is important to update the last message shown
// there.
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
})
@@ -1788,7 +1788,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<(),
)
.await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -1820,7 +1820,7 @@ pub async fn marknoticed_all_chats(context: &Context) -> Result<(), Error> {
)
.await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});
@@ -1991,7 +1991,7 @@ pub async fn create_group_chat(
chat_id.set_draft_raw(context, &mut draft_msg).await;
}
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});
@@ -2091,7 +2091,9 @@ pub(crate) async fn add_contact_to_chat_ex(
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot add contact to group; self not in group.".into())
EventType::ErrorSelfNotInGroup(
"Cannot add contact to group; self not in group.".into()
)
);
bail!("can not add contact because our account is not part of it");
}
@@ -2149,7 +2151,7 @@ pub(crate) async fn add_contact_to_chat_ex(
msg.param.set_int(Param::Arg2, from_handshake.into());
msg.id = send_msg(context, chat_id, &mut msg).await?;
}
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
Ok(true)
}
@@ -2311,7 +2313,7 @@ pub async fn set_muted(
.await
.is_ok()
{
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
} else {
bail!("Failed to set mute duration, chat might not exist -");
}
@@ -2343,7 +2345,7 @@ pub async fn remove_contact_from_chat(
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await {
emit_event!(
context,
Event::ErrorSelfNotInGroup(
EventType::ErrorSelfNotInGroup(
"Cannot remove contact from chat; self not in group.".into()
)
);
@@ -2391,7 +2393,7 @@ pub async fn remove_contact_from_chat(
// removed it first, it would complicate the
// check/encryption logic.
success = remove_from_chat_contacts_table(context, chat_id, contact_id).await;
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
}
}
@@ -2451,7 +2453,7 @@ pub async fn set_chat_name(
} else if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await {
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot set chat name; self not in group".into())
EventType::ErrorSelfNotInGroup("Cannot set chat name; self not in group".into())
);
} else {
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
@@ -2481,12 +2483,12 @@ pub async fn set_chat_name(
msg.param.set(Param::Arg, &chat.name);
}
msg.id = send_msg(context, chat_id, &mut msg).await?;
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id,
msg_id: msg.id,
});
}
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
success = true;
}
}
@@ -2519,7 +2521,9 @@ pub async fn set_chat_profile_image(
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await {
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot set chat profile image; self not in group.".into())
EventType::ErrorSelfNotInGroup(
"Cannot set chat profile image; self not in group.".into()
)
);
bail!("Failed to set profile image");
}
@@ -2558,13 +2562,13 @@ pub async fn set_chat_profile_image(
msg.id = send_msg(context, chat_id, &mut msg).await?;
emit_event!(
context,
Event::MsgsChanged {
EventType::MsgsChanged {
chat_id,
msg_id: msg.id
}
);
}
emit_event!(context, Event::ChatModified(chat_id));
emit_event!(context, EventType::ChatModified(chat_id));
Ok(())
}
@@ -2648,7 +2652,7 @@ pub async fn forward_msgs(
}
}
for (chat_id, msg_id) in created_chats.iter().zip(created_msgs.iter()) {
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: *chat_id,
msg_id: *msg_id,
});
@@ -2774,9 +2778,9 @@ pub async fn add_device_msg_with_importance(
if !msg_id.is_unset() {
if important {
context.emit_event(Event::IncomingMsg { chat_id, msg_id });
context.emit_event(EventType::IncomingMsg { chat_id, msg_id });
} else {
context.emit_event(Event::MsgsChanged { chat_id, msg_id });
context.emit_event(EventType::MsgsChanged { chat_id, msg_id });
}
}
@@ -2864,7 +2868,7 @@ pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl
.get_rowid(context, "msgs", "rfc724_mid", &rfc724_mid)
.await
.unwrap_or_default();
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id,
msg_id: MsgId::new(row_id),
});

View File

@@ -8,7 +8,7 @@ use crate::chat::ChatId;
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_tools::*;
use crate::events::Event;
use crate::events::EventType;
use crate::message::MsgId;
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
use crate::stock::StockMessage;
@@ -226,7 +226,7 @@ impl Context {
Config::DeleteDeviceAfter => {
let ret = self.sql.set_raw_config(self, key, value).await;
// Force chatlist reload to delete old messages immediately.
self.emit_event(Event::MsgsChanged {
self.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});

View File

@@ -28,7 +28,7 @@ macro_rules! progress {
$progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.emit_event($crate::events::Event::ConfigureProgress($progress));
$context.emit_event($crate::events::EventType::ConfigureProgress($progress));
};
}

View File

@@ -13,7 +13,7 @@ use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::error::{bail, ensure, format_err, Result};
use crate::events::Event;
use crate::events::EventType;
use crate::key::{DcKey, SignedPublicKey};
use crate::login_param::LoginParam;
use crate::message::{MessageState, MsgId};
@@ -245,7 +245,7 @@ impl Contact {
let (contact_id, sth_modified) =
Contact::add_or_lookup(context, name, addr, Origin::ManuallyCreated).await?;
let blocked = Contact::is_blocked_load(context, contact_id).await;
context.emit_event(Event::ContactsChanged(
context.emit_event(EventType::ContactsChanged(
if sth_modified == Modifier::Created {
Some(contact_id)
} else {
@@ -273,7 +273,7 @@ impl Contact {
.await
.is_ok()
{
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -533,7 +533,7 @@ impl Contact {
}
}
if modify_cnt > 0 {
context.emit_event(Event::ContactsChanged(None));
context.emit_event(EventType::ContactsChanged(None));
}
Ok(modify_cnt)
@@ -784,7 +784,7 @@ impl Contact {
.await
{
Ok(_) => {
context.emit_event(Event::ContactsChanged(None));
context.emit_event(EventType::ContactsChanged(None));
return Ok(());
}
Err(err) => {
@@ -1093,7 +1093,7 @@ async fn set_block_contact(context: &Context, contact_id: u32, new_blocking: boo
paramsv![new_blocking, 100, contact_id as i32],
).await.is_ok() {
Contact::mark_noticed(context, contact_id).await;
context.emit_event(Event::ContactsChanged(None));
context.emit_event(EventType::ContactsChanged(None));
}
}
}
@@ -1143,7 +1143,7 @@ pub(crate) async fn set_profile_image(
};
if changed {
contact.update_param(context).await?;
context.emit_event(Event::ContactsChanged(Some(contact_id)));
context.emit_event(EventType::ContactsChanged(Some(contact_id)));
}
Ok(())
}

View File

@@ -14,7 +14,7 @@ use crate::constants::*;
use crate::contact::*;
use crate::dc_tools::duration_to_str;
use crate::error::*;
use crate::events::{Event, EventEmitter, Events};
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::key::{DcKey, SignedPublicKey};
use crate::login_param::LoginParam;
use crate::lot::Lot;
@@ -59,6 +59,9 @@ pub struct InnerContext {
pub(crate) scheduler: RwLock<Scheduler>,
pub(crate) ephemeral_task: RwLock<Option<task::JoinHandle<()>>>,
/// Id for this context on the current device.
pub(crate) id: u32,
creation_time: SystemTime,
}
@@ -86,7 +89,7 @@ pub fn get_info() -> BTreeMap<&'static str, String> {
impl Context {
/// Creates new context.
pub async fn new(os_name: String, dbfile: PathBuf) -> Result<Context> {
pub async fn new(os_name: String, dbfile: PathBuf, id: u32) -> Result<Context> {
// pretty_env_logger::try_init_timed().ok();
let mut blob_fname = OsString::new();
@@ -96,13 +99,14 @@ impl Context {
if !blobdir.exists().await {
async_std::fs::create_dir_all(&blobdir).await?;
}
Context::with_blobdir(os_name, dbfile, blobdir).await
Context::with_blobdir(os_name, dbfile, blobdir, id).await
}
pub async fn with_blobdir(
pub(crate) async fn with_blobdir(
os_name: String,
dbfile: PathBuf,
blobdir: PathBuf,
id: u32,
) -> Result<Context> {
ensure!(
blobdir.is_dir().await,
@@ -111,6 +115,7 @@ impl Context {
);
let inner = InnerContext {
id,
blobdir,
dbfile,
os_name: Some(os_name),
@@ -188,8 +193,11 @@ impl Context {
}
/// Emits a single event.
pub fn emit_event(&self, event: Event) {
self.events.emit(event);
pub fn emit_event(&self, event: EventType) {
self.events.emit(Event {
id: self.id,
typ: event,
});
}
/// Get the next queued event.
@@ -197,6 +205,11 @@ impl Context {
self.events.get_emitter()
}
/// Get the ID of this context.
pub fn get_id(&self) -> u32 {
self.id
}
// Ongoing process allocation/free/check
pub async fn alloc_ongoing(&self) -> Result<Receiver<()>> {
@@ -458,6 +471,13 @@ impl Context {
self.get_config(Config::ConfiguredMvboxFolder).await
== Some(folder_name.as_ref().to_string())
}
pub fn derive_blobdir(dbfile: &PathBuf) -> 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)
}
}
impl InnerContext {
@@ -510,7 +530,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new("FakeOs".into(), dbfile.into()).await;
let res = Context::new("FakeOs".into(), dbfile.into(), 1).await;
assert!(res.is_err());
}
@@ -525,7 +545,9 @@ mod tests {
async fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
Context::new("FakeOS".into(), dbfile.into()).await.unwrap();
Context::new("FakeOS".into(), dbfile.into(), 1)
.await
.unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir());
}
@@ -536,7 +558,7 @@ mod tests {
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("FakeOS".into(), dbfile.into()).await;
let res = Context::new("FakeOS".into(), dbfile.into(), 1).await;
assert!(res.is_err());
}
@@ -546,7 +568,9 @@ mod tests {
let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone();
Context::new("FakeOS".into(), dbfile.into()).await.unwrap();
Context::new("FakeOS".into(), dbfile.into(), 1)
.await
.unwrap();
assert!(subdir.is_dir());
assert!(dbfile2.is_file());
}
@@ -556,7 +580,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new();
let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into()).await;
let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into(), 1).await;
assert!(res.is_err());
}
@@ -565,7 +589,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into()).await;
let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into(), 1).await;
assert!(res.is_err());
}

View File

@@ -12,7 +12,7 @@ use crate::context::Context;
use crate::dc_tools::*;
use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
use crate::error::{bail, ensure, format_err, Result};
use crate::events::Event;
use crate::events::EventType;
use crate::headerdef::HeaderDef;
use crate::job::{self, Action};
use crate::message::{self, MessageState, MessengerMessage, MsgId};
@@ -93,8 +93,8 @@ pub async fn dc_receive_imf(
if let Some(create_event_to_send) = create_event_to_send {
for (chat_id, msg_id) in created_db_entries {
let event = match create_event_to_send {
CreateEvent::MsgsChanged => Event::MsgsChanged { msg_id, chat_id },
CreateEvent::IncomingMsg => Event::IncomingMsg { msg_id, chat_id },
CreateEvent::MsgsChanged => EventType::MsgsChanged { msg_id, chat_id },
CreateEvent::IncomingMsg => EventType::IncomingMsg { msg_id, chat_id },
};
context.emit_event(event);
}
@@ -206,7 +206,7 @@ pub async fn dc_receive_imf(
.await
{
Ok(()) => {
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
Err(err) => {
warn!(context, "reveive_imf cannot update profile image: {}", err);
@@ -935,7 +935,7 @@ async fn save_locations(
}
}
if send_event {
context.emit_event(Event::LocationChanged(Some(from_id)));
context.emit_event(EventType::LocationChanged(Some(from_id)));
}
}
@@ -1239,7 +1239,7 @@ async fn create_or_lookup_group(
.await
.is_ok()
{
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
}
}
@@ -1298,7 +1298,7 @@ async fn create_or_lookup_group(
}
if send_EVENT_CHAT_MODIFIED {
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
Ok((chat_id, chat_id_blocked))
}
@@ -1435,7 +1435,7 @@ async fn create_or_lookup_adhoc_group(
chat::add_to_chat_contacts_table(context, new_chat_id, member_id).await;
}
context.emit_event(Event::ChatModified(new_chat_id));
context.emit_event(EventType::ChatModified(new_chat_id));
Ok((new_chat_id, create_blocked))
}

View File

@@ -15,7 +15,7 @@ use rand::{thread_rng, Rng};
use crate::context::Context;
use crate::error::{bail, Error};
use crate::events::Event;
use crate::events::EventType;
pub(crate) fn dc_exactly_one_bit_set(v: i32) -> bool {
0 != v && 0 == v & (v - 1)
@@ -286,7 +286,7 @@ pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) ->
let dpath = format!("{}", path.as_ref().to_string_lossy());
match fs::remove_file(path_abs).await {
Ok(_) => {
context.emit_event(Event::DeletedBlobFile(dpath));
context.emit_event(EventType::DeletedBlobFile(dpath));
true
}
Err(err) => {

View File

@@ -63,7 +63,7 @@ use crate::constants::{
use crate::context::Context;
use crate::dc_tools::time;
use crate::error::{ensure, Error};
use crate::events::Event;
use crate::events::EventType;
use crate::message::{Message, MessageState, MsgId};
use crate::mimeparser::SystemMessage;
use crate::sql;
@@ -177,7 +177,7 @@ impl ChatId {
)
.await?;
context.emit_event(Event::ChatEphemeralTimerModified {
context.emit_event(EventType::ChatEphemeralTimerModified {
chat_id: self,
timer,
});
@@ -379,7 +379,7 @@ pub async fn schedule_ephemeral_task(context: &Context) {
async_std::task::sleep(duration).await;
emit_event!(
context1,
Event::MsgsChanged {
EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0)
}
@@ -390,7 +390,7 @@ pub async fn schedule_ephemeral_task(context: &Context) {
// Emit event immediately
emit_event!(
context,
Event::MsgsChanged {
EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0)
}

View File

@@ -1,5 +1,7 @@
//! # Events specification
use std::ops::Deref;
use async_std::path::PathBuf;
use async_std::sync::{channel, Receiver, Sender, TrySendError};
use strum::EnumProperty;
@@ -54,14 +56,32 @@ impl EventEmitter {
async_std::task::block_on(self.recv())
}
/// Blocking async recv of an event. Return `None` if the `Sender` has been droped.
/// Async recv of an event. Return `None` if the `Sender` has been droped.
pub async fn recv(&self) -> Option<Event> {
// TODO: change once we can use async channels internally.
self.0.recv().await.ok()
}
pub fn try_recv(&self) -> Result<Event, async_std::sync::TryRecvError> {
self.0.try_recv()
}
}
impl Event {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Event {
pub id: u32,
pub typ: EventType,
}
impl Deref for Event {
type Target = EventType;
fn deref(&self) -> &EventType {
&self.typ
}
}
impl EventType {
/// Returns the corresponding Event id.
pub fn as_id(&self) -> i32 {
self.get_str("id")
@@ -72,7 +92,7 @@ impl Event {
}
#[derive(Debug, Clone, PartialEq, Eq, EnumProperty)]
pub enum Event {
pub enum EventType {
/// The library-user may write an informational string to the log.
/// Passed to the callback given to dc_context_new().
/// This event should not be reported to the end-user using a popup or something like that.

View File

@@ -19,7 +19,7 @@ use crate::context::Context;
use crate::dc_receive_imf::{
dc_receive_imf, from_field_to_contact_id, is_msgrmsg_rfc724_mid_in_list,
};
use crate::events::Event;
use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::job::{self, Action};
use crate::login_param::{CertificateChecks, LoginParam};
@@ -284,7 +284,7 @@ impl Imap {
.await
};
// IMAP connection failures are reported to users
emit_event!(context, Event::ErrorNetwork(message));
emit_event!(context, EventType::ErrorNetwork(message));
return Err(Error::ConnectionFailed(err.to_string()));
}
};
@@ -307,7 +307,7 @@ impl Imap {
.await;
warn!(context, "{} ({})", message, err);
emit_event!(context, Event::ErrorNetwork(message.clone()));
emit_event!(context, EventType::ErrorNetwork(message.clone()));
let lock = context.wrong_pw_warning_mutex.lock().await;
if self.login_failed_once
@@ -445,7 +445,7 @@ impl Imap {
self.connected = true;
emit_event!(
context,
Event::ImapConnected(format!(
EventType::ImapConnected(format!(
"IMAP-LOGIN as {}, capabilities: {}",
lp.mail_user, caps_list,
))
@@ -888,7 +888,7 @@ impl Imap {
Ok(_) => {
emit_event!(
context,
Event::ImapMessageMoved(format!(
EventType::ImapMessageMoved(format!(
"IMAP Message {} moved to {}",
display_folder_id, dest_folder
))
@@ -932,7 +932,7 @@ impl Imap {
warn!(context, "Cannot mark {} as \"Deleted\" after copy.", uid);
emit_event!(
context,
Event::ImapMessageMoved(format!(
EventType::ImapMessageMoved(format!(
"IMAP Message {} copied to {} (delete FAILED)",
display_folder_id, dest_folder
))
@@ -942,7 +942,7 @@ impl Imap {
self.config.selected_folder_needs_expunge = true;
emit_event!(
context,
Event::ImapMessageMoved(format!(
EventType::ImapMessageMoved(format!(
"IMAP Message {} copied to {} (delete successfull)",
display_folder_id, dest_folder
))
@@ -1142,7 +1142,7 @@ impl Imap {
} else {
emit_event!(
context,
Event::ImapMessageDeleted(format!(
EventType::ImapMessageDeleted(format!(
"IMAP Message {} marked as deleted [{}]",
display_imap_id, message_id
))
@@ -1316,7 +1316,7 @@ impl Imap {
self.config.selected_folder_needs_expunge = true;
match self.select_folder::<String>(context, None).await {
Ok(()) => {
emit_event!(context, Event::ImapFolderEmptied(folder.to_string()));
emit_event!(context, EventType::ImapFolderEmptied(folder.to_string()));
}
Err(err) => {
error!(context, "expunge failed {}: {:?}", folder, err);

View File

@@ -16,7 +16,7 @@ use crate::context::Context;
use crate::dc_tools::*;
use crate::e2ee;
use crate::error::*;
use crate::events::Event;
use crate::events::EventType;
use crate::key::{self, DcKey, DcSecretKey, SignedPublicKey, SignedSecretKey};
use crate::message::{Message, MsgId};
use crate::mimeparser::SystemMessage;
@@ -369,7 +369,7 @@ async fn imex_inner(
ensure!(param.is_some(), "No Import/export dir/file given.");
info!(context, "Import/export process started.");
context.emit_event(Event::ImexProgress(10));
context.emit_event(EventType::ImexProgress(10));
ensure!(context.sql.is_open().await, "Database not opened.");
@@ -393,11 +393,11 @@ async fn imex_inner(
match success {
Ok(()) => {
info!(context, "IMEX successfully completed");
context.emit_event(Event::ImexProgress(1000));
context.emit_event(EventType::ImexProgress(1000));
Ok(())
}
Err(err) => {
context.emit_event(Event::ImexProgress(0));
context.emit_event(EventType::ImexProgress(0));
bail!("IMEX FAILED to complete: {}", err);
}
}
@@ -487,7 +487,7 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) ->
if permille > 990 {
permille = 990
}
context.emit_event(Event::ImexProgress(permille));
context.emit_event(EventType::ImexProgress(permille));
if file_blob.is_empty() {
continue;
}
@@ -563,7 +563,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
dest_sql
.set_raw_config_int(context, "backup_time", now as i32)
.await?;
context.emit_event(Event::ImexFileWritten(dest_path_filename));
context.emit_event(EventType::ImexFileWritten(dest_path_filename));
Ok(())
}
};
@@ -602,7 +602,7 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
}
processed_files_cnt += 1;
let permille = max(min(processed_files_cnt * 1000 / total_files_cnt, 990), 10);
context.emit_event(Event::ImexProgress(permille));
context.emit_event(EventType::ImexProgress(permille));
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
@@ -769,7 +769,7 @@ where
if res.is_err() {
error!(context, "Cannot write key to {}", file_name.display());
} else {
context.emit_event(Event::ImexFileWritten(file_name));
context.emit_event(EventType::ImexFileWritten(file_name));
}
res
}

View File

@@ -23,7 +23,7 @@ use crate::context::Context;
use crate::dc_tools::*;
use crate::ephemeral::load_imap_deletion_msgid;
use crate::error::{bail, ensure, format_err, Error, Result};
use crate::events::Event;
use crate::events::EventType;
use crate::imap::*;
use crate::location;
use crate::login_param::LoginParam;
@@ -737,7 +737,7 @@ async fn set_delivered(context: &Context, msg_id: MsgId) {
)
.await
.unwrap_or_default();
context.emit_event(Event::MsgDelivered { chat_id, msg_id });
context.emit_event(EventType::MsgDelivered { chat_id, msg_id });
}
/// Constructs a job for sending a message.

View File

@@ -79,6 +79,8 @@ mod dehtml;
pub mod dc_receive_imf;
pub mod dc_tools;
pub mod accounts;
/// if set imap/incoming and smtp/outgoing MIME messages will be printed
pub const DCC_MIME_DEBUG: &str = "DCC_MIME_DEBUG";

View File

@@ -9,7 +9,7 @@ use crate::constants::*;
use crate::context::*;
use crate::dc_tools::*;
use crate::error::{ensure, Error};
use crate::events::Event;
use crate::events::EventType;
use crate::job::{self, Job};
use crate::message::{Message, MsgId};
use crate::mimeparser::SystemMessage;
@@ -227,7 +227,7 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds:
.await;
chat::add_info_msg(context, chat_id, stock_str).await;
}
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
if 0 != seconds {
schedule_maybe_send_locations(context, false).await;
job::add(
@@ -301,7 +301,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
}
}
if continue_streaming {
context.emit_event(Event::LocationChanged(Some(DC_CONTACT_ID_SELF)));
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
};
schedule_maybe_send_locations(context, false).await;
}
@@ -381,7 +381,7 @@ pub async fn delete_all(context: &Context) -> Result<(), Error> {
.sql
.execute("DELETE FROM locations;", paramsv![])
.await?;
context.emit_event(Event::LocationChanged(None));
context.emit_event(EventType::LocationChanged(None));
Ok(())
}
@@ -715,7 +715,7 @@ pub(crate) async fn job_maybe_send_locations_ended(
.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)
.await;
chat::add_info_msg(context, chat_id, stock_str).await;
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
}
job::Status::Finished(Ok(()))

View File

@@ -11,7 +11,7 @@ macro_rules! info {
file = file!(),
line = line!(),
msg = &formatted);
emit_event!($ctx, $crate::Event::Info(full));
emit_event!($ctx, $crate::EventType::Info(full));
}};
}
@@ -26,7 +26,7 @@ macro_rules! warn {
file = file!(),
line = line!(),
msg = &formatted);
emit_event!($ctx, $crate::Event::Warning(full));
emit_event!($ctx, $crate::EventType::Warning(full));
}};
}
@@ -37,7 +37,7 @@ macro_rules! error {
};
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
let formatted = format!($msg, $($args),*);
emit_event!($ctx, $crate::Event::Error(formatted));
emit_event!($ctx, $crate::EventType::Error(formatted));
}};
}
@@ -48,7 +48,7 @@ macro_rules! error_network {
};
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
let formatted = format!($msg, $($args),*);
emit_event!($ctx, $crate::Event::ErrorNetwork(formatted));
emit_event!($ctx, $crate::EventType::ErrorNetwork(formatted));
}};
}

View File

@@ -12,7 +12,7 @@ use crate::contact::*;
use crate::context::*;
use crate::dc_tools::*;
use crate::error::{ensure, Error};
use crate::events::Event;
use crate::events::EventType;
use crate::job::{self, Action};
use crate::lot::{Lot, LotState, Meaning};
use crate::mimeparser::{FailureReport, SystemMessage};
@@ -1118,7 +1118,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) {
}
if !msg_ids.is_empty() {
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -1209,7 +1209,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> bool {
}
if send_event {
context.emit_event(Event::MsgsChanged {
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
@@ -1376,7 +1376,7 @@ pub async fn set_msg_failed(context: &Context, msg_id: MsgId, error: Option<impl
)
.await
{
Ok(_) => context.emit_event(Event::MsgFailed {
Ok(_) => context.emit_event(EventType::MsgFailed {
chat_id: msg.chat_id,
msg_id,
}),
@@ -1551,7 +1551,7 @@ pub(crate) async fn handle_ndn(
.await,
)
.await;
context.emit_event(Event::ChatModified(chat_id));
context.emit_event(EventType::ChatModified(chat_id));
}
}
}

View File

@@ -16,7 +16,7 @@ use crate::dc_tools::*;
use crate::dehtml::dehtml;
use crate::e2ee;
use crate::error::{bail, Result};
use crate::events::Event;
use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::key::Fingerprint;
use crate::location;
@@ -1001,7 +1001,7 @@ impl MimeMessage {
if let Some((chat_id, msg_id)) =
message::handle_mdn(context, from_id, original_message_id, sent_timestamp).await
{
context.emit_event(Event::MsgRead { chat_id, msg_id });
context.emit_event(EventType::MsgRead { chat_id, msg_id });
}
}
}

View File

@@ -12,7 +12,7 @@ use crate::contact::*;
use crate::context::Context;
use crate::e2ee::*;
use crate::error::{bail, Error};
use crate::events::Event;
use crate::events::EventType;
use crate::headerdef::HeaderDef;
use crate::key::{DcKey, Fingerprint, SignedPublicKey};
use crate::lot::LotState;
@@ -32,7 +32,7 @@ macro_rules! joiner_progress {
$progress >= 0 && $progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.emit_event($crate::events::Event::SecurejoinJoinerProgress {
$context.emit_event($crate::events::EventType::SecurejoinJoinerProgress {
contact_id: $contact_id,
progress: $progress,
});
@@ -45,7 +45,7 @@ macro_rules! inviter_progress {
$progress >= 0 && $progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.emit_event($crate::events::Event::SecurejoinInviterProgress {
$context.emit_event($crate::events::EventType::SecurejoinInviterProgress {
contact_id: $contact_id,
progress: $progress,
});
@@ -653,7 +653,7 @@ pub(crate) async fn handle_securejoin_handshake(
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited).await;
info!(context, "Auth verified.",);
secure_connection_established(context, contact_chat_id).await;
emit_event!(context, Event::ContactsChanged(Some(contact_id)));
emit_event!(context, EventType::ContactsChanged(Some(contact_id)));
inviter_progress!(context, contact_id, 600);
if join_vg {
// the vg-member-added message is special:
@@ -773,7 +773,7 @@ pub(crate) async fn handle_securejoin_handshake(
return Ok(abort_retval);
}
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined).await;
emit_event!(context, Event::ContactsChanged(None));
emit_event!(context, EventType::ContactsChanged(None));
let cg_member_added = mime_message
.get(HeaderDef::ChatGroupMemberAdded)
.map(|s| s.as_str())
@@ -960,7 +960,7 @@ async fn secure_connection_established(context: &Context, contact_chat_id: ChatI
.stock_string_repl_str(StockMessage::ContactVerified, addr)
.await;
chat::add_info_msg(context, contact_chat_id, msg).await;
emit_event!(context, Event::ChatModified(contact_chat_id));
emit_event!(context, EventType::ChatModified(contact_chat_id));
}
async fn could_not_establish_secure_connection(
@@ -1073,7 +1073,7 @@ pub async fn handle_degrade_event(
.await;
chat::add_info_msg(context, contact_chat_id, msg).await;
emit_event!(context, Event::ChatModified(contact_chat_id));
emit_event!(context, EventType::ChatModified(contact_chat_id));
}
}
Ok(())

View File

@@ -9,7 +9,7 @@ use async_smtp::*;
use crate::constants::*;
use crate::context::Context;
use crate::events::Event;
use crate::events::EventType;
use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam};
use crate::oauth2::*;
use crate::provider::get_provider_info;
@@ -102,7 +102,7 @@ impl Smtp {
}
if lp.send_server.is_empty() || lp.send_port == 0 {
context.emit_event(Event::ErrorNetwork("SMTP bad parameters.".into()));
context.emit_event(EventType::ErrorNetwork("SMTP bad parameters.".into()));
return Err(Error::BadParameters);
}
@@ -187,14 +187,14 @@ impl Smtp {
)
.await;
emit_event!(context, Event::ErrorNetwork(message));
emit_event!(context, EventType::ErrorNetwork(message));
return Err(Error::ConnectionFailure(err));
}
self.transport = Some(trans);
self.last_success = Some(SystemTime::now());
context.emit_event(Event::SmtpConnected(format!(
context.emit_event(EventType::SmtpConnected(format!(
"SMTP-LOGIN as {} ok",
lp.send_user,
)));

View File

@@ -4,7 +4,7 @@ use super::Smtp;
use async_smtp::*;
use crate::context::Context;
use crate::events::Event;
use crate::events::EventType;
use std::time::Duration;
pub type Result<T> = std::result::Result<T, Error>;
@@ -55,7 +55,7 @@ impl Smtp {
.await
.map_err(Error::SendError)?;
context.emit_event(Event::SmtpMessageSent(format!(
context.emit_event(EventType::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len_bytes, recipients_display
)));

View File

@@ -27,9 +27,14 @@ impl TestContext {
///
/// [Context]: crate::context::Context
pub async fn new() -> Self {
use rand::Rng;
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
let ctx = Context::new("FakeOS".into(), dbfile.into()).await.unwrap();
let id = rand::thread_rng().gen();
let ctx = Context::new("FakeOS".into(), dbfile.into(), id)
.await
.unwrap();
Self { ctx, dir }
}