diff --git a/CHANGELOG.md b/CHANGELOG.md index 5862fb8c6b..c14a308da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ It is used to fix race condition between fetching existing messages and starting the test. #4208 +### API-Changes +- Use `tracing` crate for logging. #3960 ## [1.111.0] - 2023-03-05 diff --git a/Cargo.lock b/Cargo.lock index 3ca53f8efc..91cc3e5531 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1165,7 +1165,6 @@ dependencies = [ "kamadak-exif", "lettre_email", "libc", - "log", "mailparse", "num-derive", "num-traits", @@ -1174,6 +1173,7 @@ dependencies = [ "parking_lot", "percent-encoding", "pgp", + "pin-project-lite", "pretty_env_logger", "proptest", "qrcodegen", @@ -1202,6 +1202,9 @@ dependencies = [ "tokio-stream", "tokio-tar", "toml", + "tracing", + "tracing-futures", + "tracing-subscriber", "trust-dns-resolver", "url", "uuid", @@ -1243,6 +1246,9 @@ dependencies = [ "rusqlite", "rustyline", "tokio", + "tracing", + "tracing-log", + "tracing-subscriber", ] [[package]] @@ -1252,12 +1258,12 @@ dependencies = [ "anyhow", "deltachat", "deltachat-jsonrpc", - "env_logger 0.10.0", "futures-lite", - "log", "serde", "serde_json", "tokio", + "tracing", + "tracing-subscriber", "yerpc", ] @@ -1284,6 +1290,8 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tracing", + "tracing-subscriber", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f24a4f1f6c..b278eb1db4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,8 +66,12 @@ once_cell = "1.17.0" percent-encoding = "2.2" parking_lot = "0.12" pgp = { version = "0.9", default-features = false } +pin-project-lite = { version = "0.2.9" } pretty_env_logger = { version = "0.4", optional = true } qrcodegen = "1.7.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", default-features=false, features = ["std", "env-filter", "registry", "fmt"] } +tracing-futures = "0.2" quick-xml = "0.27" rand = "0.8" regex = "1.7" @@ -98,8 +102,6 @@ uuid = { version = "1", features = ["serde", "v4"] } ansi_term = "0.12.0" criterion = { version = "0.4.0", features = ["async_tokio"] } futures-lite = "1.12" -log = "0.4" -pretty_env_logger = "0.4" proptest = { version = "1", default-features = false, features = ["std"] } tempfile = "3" testdir = "0.7.2" diff --git a/README.md b/README.md index 937967a1c0..710270d483 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ $ curl https://sh.rustup.rs -sSf | sh Compile and run Delta Chat Core command line utility, using `cargo`: ``` -$ RUST_LOG=deltachat_repl=info cargo run -p deltachat-repl -- ~/deltachat-db +$ cargo run -p deltachat-repl -- ~/deltachat-db ``` where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist. diff --git a/benches/send_events.rs b/benches/send_events.rs index ec83f7a865..7cc81eb210 100644 --- a/benches/send_events.rs +++ b/benches/send_events.rs @@ -2,15 +2,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; use deltachat::context::Context; use deltachat::stock_str::StockStrings; -use deltachat::{info, Event, EventType, Events}; +use deltachat::{Event, EventType, Events}; use tempfile::tempdir; async fn send_events_benchmark(context: &Context) { let emitter = context.get_event_emitter(); for _i in 0..1_000_000 { - info!(context, "interesting event..."); + context.emit_event(EventType::Info("interesting event...".to_string())); } - info!(context, "DONE"); + context.emit_event(EventType::Info("DONE".to_string())); loop { match emitter.recv().await.unwrap() { diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index 3e10245e94..c270aa85e4 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -25,6 +25,8 @@ anyhow = "1" thiserror = "1" rand = "0.8" once_cell = "1.17.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", default-features=false, features = ["std", "registry"] } [features] default = ["vendored"] diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 63d8d52bfe..b606bb0acc 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -23,33 +23,51 @@ use std::sync::Arc; use std::time::{Duration, SystemTime}; use anyhow::Context as _; -use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration, ProtectionStatus}; -use deltachat::constants::DC_MSG_ID_LAST_SPECIAL; +use deltachat::chat::{ + self, ChatId, ChatVisibility, MessageListOptions, MuteDuration, ProtectionStatus, +}; +use deltachat::chatlist::{self, Chatlist}; +use deltachat::config; +use deltachat::constants::{self, DC_MSG_ID_LAST_SPECIAL}; +use deltachat::contact; use deltachat::contact::{Contact, ContactId, Origin}; +use deltachat::context::future::ContextIdFutureExt; use deltachat::context::Context; use deltachat::ephemeral::Timer as EphemeralTimer; use deltachat::imex::BackupProvider; +use deltachat::imex::{self, ImexMode}; +use deltachat::key; use deltachat::key::DcKey; -use deltachat::message::MsgId; +use deltachat::location; +use deltachat::message::{self, MsgId}; +use deltachat::oauth2; +use deltachat::provider; +use deltachat::qr; use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg}; use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions}; +use deltachat::securejoin; use deltachat::stock_str::StockMessage; use deltachat::stock_str::StockStrings; +use deltachat::summary; +use deltachat::tools; use deltachat::webxdc::StatusUpdateSerial; -use deltachat::*; use deltachat::{accounts::Accounts, log::LogExt}; +use deltachat::{Event, EventEmitter, EventType, Events}; use num_traits::{FromPrimitive, ToPrimitive}; use once_cell::sync::Lazy; use rand::Rng; use tokio::runtime::Runtime; use tokio::sync::RwLock; use tokio::task::JoinHandle; +use tracing::instrument::WithSubscriber; +use tracing::{error, warn}; +use tracing_subscriber::prelude::*; +use tracing_subscriber::registry::Registry; mod dc_array; mod lot; mod string; -use deltachat::chatlist::Chatlist; use self::string::*; @@ -80,6 +98,15 @@ where RT.block_on(fut) } +fn block_on_with_context(context: &Context, fut: T) -> T::Output +where + T: Future, +{ + let subscriber = Registry::default().with(context.to_layer()); + let context_id = context.get_id(); + RT.block_on(fut.bind_context_id(context_id).with_subscriber(subscriber)) +} + fn spawn(fut: T) -> JoinHandle where T: Future + Send + 'static, @@ -88,6 +115,16 @@ where RT.spawn(fut) } +fn spawn_with_context(context: &Context, fut: T) -> JoinHandle +where + T: Future + Send + 'static, + T::Output: Send + 'static, +{ + let subscriber = Registry::default().with(context.to_layer()); + let context_id = context.get_id(); + spawn(fut.bind_context_id(context_id).with_subscriber(subscriber)) +} + #[no_mangle] pub unsafe extern "C" fn dc_context_new( _os_name: *const libc::c_char, @@ -104,12 +141,13 @@ pub unsafe extern "C" fn dc_context_new( let ctx = if blobdir.is_null() || *blobdir == 0 { // generate random ID as this functionality is not yet available on the C-api. let id = rand::thread_rng().gen(); - block_on(Context::new( - as_path(dbfile), - id, - Events::new(), - StockStrings::new(), - )) + let events = Events::new(); + let subscriber = Registry::default().with(events.to_layer()); + + block_on( + Context::new(as_path(dbfile), id, events, StockStrings::new()) + .with_subscriber(subscriber), + ) } else { eprintln!("blobdir can not be defined explicitly anymore"); return ptr::null_mut(); @@ -133,12 +171,13 @@ pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> * } let id = rand::thread_rng().gen(); - match block_on(Context::new_closed( - as_path(dbfile), - id, - Events::new(), - StockStrings::new(), - )) { + let events = Events::new(); + let subscriber = Registry::default().with(events.to_layer()); + + match block_on( + Context::new_closed(as_path(dbfile), id, events, StockStrings::new()) + .with_subscriber(subscriber), + ) { Ok(context) => Box::into_raw(Box::new(context)), Err(err) => { eprintln!("failed to create context: {err:#}"); @@ -159,7 +198,7 @@ pub unsafe extern "C" fn dc_context_open( let ctx = &*context; let passphrase = to_string_lossy(passphrase); - block_on(ctx.open(passphrase)) + block_on_with_context(ctx, ctx.open(passphrase)) .log_err(ctx, "dc_context_open() failed") .map(|b| b as libc::c_int) .unwrap_or(0) @@ -173,7 +212,7 @@ pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc: } let ctx = &*context; - block_on(ctx.is_open()) as libc::c_int + block_on_with_context(ctx, ctx.is_open()) as libc::c_int } /// Release the context structure. @@ -212,7 +251,7 @@ pub unsafe extern "C" fn dc_set_config( let key = to_string_lossy(key); let value = to_opt_string_lossy(value); - block_on(async move { + block_on_with_context(ctx, async move { if key.starts_with("ui.") { ctx.set_ui_config(&key, value.as_deref()) .await @@ -228,7 +267,7 @@ pub unsafe extern "C" fn dc_set_config( .log_err(ctx, "dc_set_config() failed") .is_ok() as libc::c_int, Err(_) => { - warn!(ctx, "dc_set_config(): invalid key"); + warn!("dc_set_config(): invalid key"); 0 } } @@ -249,7 +288,7 @@ pub unsafe extern "C" fn dc_get_config( let key = to_string_lossy(key); - block_on(async move { + block_on_with_context(ctx, async move { if key.starts_with("ui.") { ctx.get_ui_config(&key) .await @@ -267,7 +306,7 @@ pub unsafe extern "C" fn dc_get_config( .unwrap_or_default() .strdup(), Err(_) => { - warn!(ctx, "dc_get_config(): invalid key '{}'", &key); + warn!("dc_get_config(): invalid key '{}'", &key); "".strdup() } } @@ -288,17 +327,17 @@ pub unsafe extern "C" fn dc_set_stock_translation( let msg = to_string_lossy(stock_msg); let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match StockMessage::from_u32(stock_id) { Some(id) => match ctx.set_stock_translation(id, msg).await { Ok(()) => 1, Err(err) => { - warn!(ctx, "set_stock_translation failed: {err:#}"); + warn!("set_stock_translation failed: {err:#}"); 0 } }, None => { - warn!(ctx, "invalid stock message id {stock_id}"); + warn!("invalid stock message id {stock_id}"); 0 } } @@ -317,11 +356,11 @@ pub unsafe extern "C" fn dc_set_config_from_qr( let qr = to_string_lossy(qr); let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match qr::set_config_from_qr(ctx, &qr).await { Ok(()) => 1, Err(err) => { - error!(ctx, "Failed to create account from QR code: {err:#}"); + error!("Failed to create account from QR code: {err:#}"); 0 } } @@ -335,11 +374,11 @@ pub unsafe extern "C" fn dc_get_info(context: *const dc_context_t) -> *mut libc: return "".strdup(); } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match ctx.get_info().await { Ok(info) => render_info(info).unwrap_or_default().strdup(), Err(err) => { - warn!(ctx, "failed to get info: {err:#}"); + warn!("failed to get info: {err:#}"); "".strdup() } } @@ -364,7 +403,9 @@ pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> li return 0; } let ctx = &*context; - block_on(async move { ctx.get_connectivity().await as u32 as libc::c_int }) + block_on_with_context(ctx, async move { + ctx.get_connectivity().await as u32 as libc::c_int + }) } #[no_mangle] @@ -376,11 +417,11 @@ pub unsafe extern "C" fn dc_get_connectivity_html( return "".strdup(); } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match ctx.get_connectivity_html().await { Ok(html) => html.strdup(), Err(err) => { - error!(ctx, "Failed to get connectivity html: {err:#}"); + error!("Failed to get connectivity html: {err:#}"); "".strdup() } } @@ -394,7 +435,7 @@ pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c return 0; } let ctx = &*context; - block_on(async move { ctx.all_work_done().await as libc::c_int }) + block_on_with_context(ctx, ctx.all_work_done()) as libc::c_int } #[no_mangle] @@ -411,7 +452,7 @@ pub unsafe extern "C" fn dc_get_oauth2_url( let addr = to_string_lossy(addr); let redirect = to_string_lossy(redirect); - block_on(async move { + block_on_with_context(ctx, async move { match oauth2::get_oauth2_url(ctx, &addr, &redirect) .await .log_err(ctx, "dc_get_oauth2_url failed") @@ -423,7 +464,10 @@ pub unsafe extern "C" fn dc_get_oauth2_url( } fn spawn_configure(ctx: Context) { - spawn(async move { ctx.configure().await.log_err(&ctx, "Configure failed") }); + let ctx2 = ctx.clone(); + spawn_with_context(&ctx, async move { + ctx2.configure().await.log_err(&ctx2, "Configure failed") + }); } #[no_mangle] @@ -445,7 +489,7 @@ pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ctx.is_configured() .await .log_err(ctx, "failed to get configured state") @@ -460,7 +504,7 @@ pub unsafe extern "C" fn dc_start_io(context: *mut dc_context_t) { } let ctx = &*context; - block_on(ctx.start_io()) + block_on_with_context(ctx, ctx.start_io()) } #[no_mangle] @@ -750,9 +794,7 @@ pub unsafe extern "C" fn dc_stop_io(context: *mut dc_context_t) { } let ctx = &*context; - block_on(async move { - ctx.stop_io().await; - }) + block_on_with_context(ctx, ctx.stop_io()); } #[no_mangle] @@ -763,7 +805,7 @@ pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) { } let ctx = &*context; - block_on(async move { ctx.maybe_network().await }) + block_on_with_context(ctx, ctx.maybe_network()) } #[no_mangle] @@ -778,7 +820,7 @@ pub unsafe extern "C" fn dc_preconfigure_keypair( return 0; } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let addr = tools::EmailAddress::new(&to_string_lossy(addr))?; let public = key::SignedPublicKey::from_asc(&to_string_lossy(public_data))?.0; let secret = key::SignedSecretKey::from_asc(&to_string_lossy(secret_data))?.0; @@ -814,7 +856,7 @@ pub unsafe extern "C" fn dc_get_chatlist( Some(ContactId::new(query_id)) }; - block_on(async move { + block_on_with_context(ctx, async move { match chatlist::Chatlist::try_load(ctx, flags as usize, qs.as_deref(), qi) .await .log_err(ctx, "Failed to get chatlist") @@ -839,7 +881,7 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::create_for_contact(ctx, ContactId::new(contact_id)) .await .log_err(ctx, "Failed to create chat from contact_id") @@ -859,7 +901,7 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::lookup_by_contact(ctx, ContactId::new(contact_id)) .await .log_err(ctx, "Failed to get chat for contact_id") @@ -882,10 +924,10 @@ pub unsafe extern "C" fn dc_prepare_msg( let ctx = &mut *context; let ffi_msg: &mut MessageWrapper = &mut *msg; - block_on(async move { + block_on_with_context(ctx, async { chat::prepare_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message) .await - .unwrap_or_log_default(ctx, "Failed to prepare message") + .unwrap_or_log_default("Failed to prepare message") }) .to_u32() } @@ -903,10 +945,10 @@ pub unsafe extern "C" fn dc_send_msg( let ctx = &mut *context; let ffi_msg = &mut *msg; - block_on(async move { + block_on_with_context(ctx, async { chat::send_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message) .await - .unwrap_or_log_default(ctx, "Failed to send message") + .unwrap_or_log_default("Failed to send message") }) .to_u32() } @@ -924,10 +966,10 @@ pub unsafe extern "C" fn dc_send_msg_sync( let ctx = &mut *context; let ffi_msg = &mut *msg; - block_on(async move { + block_on_with_context(ctx, async { chat::send_msg_sync(ctx, ChatId::new(chat_id), &mut ffi_msg.message) .await - .unwrap_or_log_default(ctx, "Failed to send message") + .unwrap_or_log_default("Failed to send message") }) .to_u32() } @@ -945,11 +987,11 @@ pub unsafe extern "C" fn dc_send_text_msg( let ctx = &*context; let text_to_send = to_string_lossy(text_to_send); - block_on(async move { + block_on_with_context(ctx, async move { chat::send_text_msg(ctx, ChatId::new(chat_id), text_to_send) .await .map(|msg_id| msg_id.to_u32()) - .unwrap_or_log_default(ctx, "Failed to send text message") + .unwrap_or_log_default("Failed to send text message") }) } @@ -964,11 +1006,11 @@ pub unsafe extern "C" fn dc_send_videochat_invitation( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { chat::send_videochat_invitation(ctx, ChatId::new(chat_id)) .await .map(|msg_id| msg_id.to_u32()) - .unwrap_or_log_default(ctx, "Failed to send video chat invitation") + .unwrap_or_log_default("Failed to send video chat invitation") }) } @@ -984,11 +1026,11 @@ pub unsafe extern "C" fn dc_send_reaction( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { send_reaction(ctx, MsgId::new(msg_id), &to_string_lossy(reaction)) .await .map(|msg_id| msg_id.to_u32()) - .unwrap_or_log_default(ctx, "Failed to send reaction") + .unwrap_or_log_default("Failed to send reaction") }) } @@ -1003,8 +1045,9 @@ pub unsafe extern "C" fn dc_get_msg_reactions( } let ctx = &*context; - let reactions = if let Ok(reactions) = block_on(get_msg_reactions(ctx, MsgId::new(msg_id))) - .log_err(ctx, "failed dc_get_msg_reactions() call") + let reactions = if let Ok(reactions) = + block_on_with_context(ctx, get_msg_reactions(ctx, MsgId::new(msg_id))) + .log_err(ctx, "failed dc_get_msg_reactions() call") { reactions } else { @@ -1027,11 +1070,14 @@ pub unsafe extern "C" fn dc_send_webxdc_status_update( } let ctx = &*context; - block_on(ctx.send_webxdc_status_update( - MsgId::new(msg_id), - &to_string_lossy(json), - &to_string_lossy(descr), - )) + block_on_with_context( + ctx, + ctx.send_webxdc_status_update( + MsgId::new(msg_id), + &to_string_lossy(json), + &to_string_lossy(descr), + ), + ) .log_err(ctx, "Failed to send webxdc update") .is_ok() as libc::c_int } @@ -1048,10 +1094,13 @@ pub unsafe extern "C" fn dc_get_webxdc_status_updates( } let ctx = &*context; - block_on(ctx.get_webxdc_status_updates( - MsgId::new(msg_id), - StatusUpdateSerial::new(last_known_serial), - )) + block_on_with_context( + ctx, + ctx.get_webxdc_status_updates( + MsgId::new(msg_id), + StatusUpdateSerial::new(last_known_serial), + ), + ) .unwrap_or_else(|_| "".to_string()) .strdup() } @@ -1074,11 +1123,11 @@ pub unsafe extern "C" fn dc_set_draft( Some(&mut ffi_msg.message) }; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .set_draft(ctx, msg) .await - .unwrap_or_log_default(ctx, "failed to set draft"); + .unwrap_or_log_default("failed to set draft"); }); } @@ -1100,10 +1149,10 @@ pub unsafe extern "C" fn dc_add_device_msg( Some(&mut ffi_msg.message) }; - block_on(async move { + block_on_with_context(ctx, async { chat::add_device_msg(ctx, to_opt_string_lossy(label).as_deref(), msg) .await - .unwrap_or_log_default(ctx, "Failed to add device message") + .unwrap_or_log_default("Failed to add device message") }) .to_u32() } @@ -1119,7 +1168,7 @@ pub unsafe extern "C" fn dc_was_device_msg_ever_added( } let ctx = &mut *context; - block_on(async move { + block_on_with_context(ctx, async { chat::was_device_msg_ever_added(ctx, &to_string_lossy(label)) .await .unwrap_or(false) as libc::c_int @@ -1134,7 +1183,7 @@ pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match ChatId::new(chat_id).get_draft(ctx).await { Ok(Some(draft)) => { let ffi_msg = MessageWrapper { @@ -1145,7 +1194,7 @@ pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) } Ok(None) => ptr::null_mut(), Err(err) => { - error!(ctx, "Failed to get draft for chat #{chat_id}: {err:#}"); + error!("Failed to get draft for chat #{chat_id}: {err:#}"); ptr::null_mut() } } @@ -1167,7 +1216,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs( let info_only = (flags & DC_GCM_INFO_ONLY) != 0; let add_daymarker = (flags & DC_GCM_ADDDAYMARKER) != 0; - block_on(async move { + block_on_with_context(ctx, async move { Box::into_raw(Box::new( chat::get_chat_msgs_ex( ctx, @@ -1178,7 +1227,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs( }, ) .await - .unwrap_or_log_default(ctx, "failed to get chat msgs") + .unwrap_or_log_default("failed to get chat msgs") .into(), )) }) @@ -1192,11 +1241,11 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32 } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .get_msg_cnt(ctx) .await - .unwrap_or_log_default(ctx, "failed to get msg count") as libc::c_int + .unwrap_or_log_default("failed to get msg count") as libc::c_int }) } @@ -1211,11 +1260,11 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .get_fresh_msg_cnt(ctx) .await - .unwrap_or_log_default(ctx, "failed to get fresh msg cnt") as libc::c_int + .unwrap_or_log_default("failed to get fresh msg cnt") as libc::c_int }) } @@ -1230,7 +1279,7 @@ pub unsafe extern "C" fn dc_estimate_deletion_cnt( return 0; } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { message::estimate_deletion_cnt(ctx, from_server != 0, seconds) .await .unwrap_or(0) as libc::c_int @@ -1247,7 +1296,7 @@ pub unsafe extern "C" fn dc_get_fresh_msgs( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let arr = dc_array_t::from( ctx.get_fresh_msgs() .await @@ -1269,7 +1318,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { chat::marknoticed_chat(ctx, ChatId::new(chat_id)) .await .log_err(ctx, "Failed marknoticed chat") @@ -1309,11 +1358,11 @@ pub unsafe extern "C" fn dc_get_chat_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {or_msg_type3}")); - block_on(async move { + block_on_with_context(ctx, async move { Box::into_raw(Box::new( chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3) .await - .unwrap_or_log_default(ctx, "Failed get_chat_media") + .unwrap_or_log_default("Failed get_chat_media") .into(), )) }) @@ -1345,7 +1394,7 @@ pub unsafe extern "C" fn dc_get_next_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {or_msg_type3}")); - block_on(async move { + block_on_with_context(ctx, async move { chat::get_next_media( ctx, MsgId::new(msg_id), @@ -1374,16 +1423,14 @@ pub unsafe extern "C" fn dc_set_chat_protection( let protect = if let Some(s) = ProtectionStatus::from_i32(protect) { s } else { - warn!(ctx, "bad protect-value for dc_set_chat_protection()"); + warn!("bad protect-value for dc_set_chat_protection()"); return 0; }; - block_on(async move { - match ChatId::new(chat_id).set_protection(ctx, protect).await { - Ok(()) => 1, - Err(_) => 0, - } - }) + match block_on_with_context(ctx, ChatId::new(chat_id).set_protection(ctx, protect)) { + Ok(()) => 1, + Err(_) => 0, + } } #[no_mangle] @@ -1402,15 +1449,12 @@ pub unsafe extern "C" fn dc_set_chat_visibility( 1 => ChatVisibility::Archived, 2 => ChatVisibility::Pinned, _ => { - warn!( - ctx, - "ignoring careless call to dc_set_chat_visibility(): unknown archived state", - ); + warn!("ignoring careless call to dc_set_chat_visibility(): unknown archived state",); return; } }; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .set_visibility(ctx, visibility) .await @@ -1427,7 +1471,7 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .delete(ctx) .await @@ -1443,7 +1487,7 @@ pub unsafe extern "C" fn dc_block_chat(context: *mut dc_context_t, chat_id: u32) } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .block(ctx) .await @@ -1459,7 +1503,7 @@ pub unsafe extern "C" fn dc_accept_chat(context: *mut dc_context_t, chat_id: u32 } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .accept(ctx) .await @@ -1478,11 +1522,11 @@ pub unsafe extern "C" fn dc_get_chat_contacts( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let arr = dc_array_t::from( chat::get_chat_contacts(ctx, ChatId::new(chat_id)) .await - .unwrap_or_log_default(ctx, "Failed get_chat_contacts") + .unwrap_or_log_default("Failed get_chat_contacts") .iter() .map(|id| id.to_u32()) .collect::>(), @@ -1508,11 +1552,11 @@ pub unsafe extern "C" fn dc_search_msgs( Some(ChatId::new(chat_id)) }; - block_on(async move { + block_on_with_context(ctx, async move { let arr = dc_array_t::from( ctx.search_msgs(chat_id, &to_string_lossy(query)) .await - .unwrap_or_log_default(ctx, "Failed search_msgs") + .unwrap_or_log_default("Failed search_msgs") .iter() .map(|msg_id| msg_id.to_u32()) .collect::>(), @@ -1529,7 +1573,7 @@ pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) - } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)).await { Ok(chat) => { let ffi_chat = ChatWrapper { context, chat }; @@ -1554,11 +1598,11 @@ pub unsafe extern "C" fn dc_create_group_chat( let protect = if let Some(s) = ProtectionStatus::from_i32(protect) { s } else { - warn!(ctx, "bad protect-value for dc_create_group_chat()"); + warn!("bad protect-value for dc_create_group_chat()"); return 0; }; - block_on(async move { + block_on_with_context(ctx, async move { chat::create_group_chat(ctx, protect, &to_string_lossy(name)) .await .log_err(ctx, "Failed to create group chat") @@ -1574,7 +1618,7 @@ pub unsafe extern "C" fn dc_create_broadcast_list(context: *mut dc_context_t) -> return 0; } let ctx = &*context; - block_on(chat::create_broadcast_list(ctx)) + block_on_with_context(ctx, chat::create_broadcast_list(ctx)) .log_err(ctx, "Failed to create broadcast list") .map(|id| id.to_u32()) .unwrap_or(0) @@ -1592,11 +1636,10 @@ pub unsafe extern "C" fn dc_is_contact_in_chat( } let ctx = &*context; - block_on(chat::is_contact_in_chat( + block_on_with_context( ctx, - ChatId::new(chat_id), - ContactId::new(contact_id), - )) + chat::is_contact_in_chat(ctx, ChatId::new(chat_id), ContactId::new(contact_id)), + ) .log_err(ctx, "is_contact_in_chat failed") .unwrap_or_default() as libc::c_int } @@ -1613,11 +1656,10 @@ pub unsafe extern "C" fn dc_add_contact_to_chat( } let ctx = &*context; - block_on(chat::add_contact_to_chat( + block_on_with_context( ctx, - ChatId::new(chat_id), - ContactId::new(contact_id), - )) + chat::add_contact_to_chat(ctx, ChatId::new(chat_id), ContactId::new(contact_id)), + ) .log_err(ctx, "Failed to add contact") .is_ok() as libc::c_int } @@ -1634,11 +1676,10 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat( } let ctx = &*context; - block_on(chat::remove_contact_from_chat( + block_on_with_context( ctx, - ChatId::new(chat_id), - ContactId::new(contact_id), - )) + chat::remove_contact_from_chat(ctx, ChatId::new(chat_id), ContactId::new(contact_id)), + ) .log_err(ctx, "Failed to remove contact") .is_ok() as libc::c_int } @@ -1656,11 +1697,11 @@ pub unsafe extern "C" fn dc_set_chat_name( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { chat::set_chat_name(ctx, ChatId::new(chat_id), &to_string_lossy(name)) .await .map(|_| 1) - .unwrap_or_log_default(ctx, "Failed to set chat name") + .unwrap_or_log_default("Failed to set chat name") }) } @@ -1676,11 +1717,11 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { chat::set_chat_profile_image(ctx, ChatId::new(chat_id), &to_string_lossy(image)) .await .map(|_| 1) - .unwrap_or_log_default(ctx, "Failed to set profile image") + .unwrap_or_log_default("Failed to set profile image") }) } @@ -1702,19 +1743,16 @@ pub unsafe extern "C" fn dc_set_chat_mute_duration( .checked_add(Duration::from_secs(duration as u64)) .map_or(MuteDuration::Forever, MuteDuration::Until), _ => { - warn!( - ctx, - "dc_chat_set_mute_duration(): Can not use negative duration other than -1", - ); + warn!("dc_chat_set_mute_duration(): Can not use negative duration other than -1",); return 0; } }; - block_on(async move { + block_on_with_context(ctx, async move { chat::set_muted(ctx, ChatId::new(chat_id), muteDuration) .await .map(|_| 1) - .unwrap_or_log_default(ctx, "Failed to set mute duration") + .unwrap_or_log_default("Failed to set mute duration") }) } @@ -1729,13 +1767,13 @@ pub unsafe extern "C" fn dc_get_chat_encrinfo( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .get_encryption_info(ctx) .await .map(|s| s.strdup()) .unwrap_or_else(|e| { - error!(ctx, "{e:#}"); + error!("{e:#}"); ptr::null_mut() }) }) @@ -1757,10 +1795,12 @@ pub unsafe extern "C" fn dc_get_chat_ephemeral_timer( // default when changing the value. Such errors should not be // ignored when ephemeral timer value is used to construct // message headers. - block_on(async move { ChatId::new(chat_id).get_ephemeral_timer(ctx).await }) - .log_err(ctx, "Failed to get ephemeral timer") - .unwrap_or_default() - .to_u32() + block_on_with_context(ctx, async move { + ChatId::new(chat_id).get_ephemeral_timer(ctx).await + }) + .log_err(ctx, "Failed to get ephemeral timer") + .unwrap_or_default() + .to_u32() } #[no_mangle] @@ -1775,7 +1815,7 @@ pub unsafe extern "C" fn dc_set_chat_ephemeral_timer( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { ChatId::new(chat_id) .set_ephemeral_timer(ctx, EphemeralTimer::from_u32(timer)) .await @@ -1795,10 +1835,10 @@ pub unsafe extern "C" fn dc_get_msg_info( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { message::get_msg_info(ctx, MsgId::new(msg_id)) .await - .unwrap_or_log_default(ctx, "failed to get msg id") + .unwrap_or_log_default("failed to get msg id") .strdup() }) } @@ -1814,8 +1854,8 @@ pub unsafe extern "C" fn dc_get_msg_html( } let ctx = &*context; - block_on(MsgId::new(msg_id).get_html(ctx)) - .unwrap_or_log_default(ctx, "Failed get_msg_html") + block_on_with_context(ctx, MsgId::new(msg_id).get_html(ctx)) + .unwrap_or_log_default("Failed get_msg_html") .strdup() } @@ -1830,10 +1870,10 @@ pub unsafe extern "C" fn dc_get_mime_headers( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let mime = message::get_mime_headers(ctx, MsgId::new(msg_id)) .await - .unwrap_or_log_default(ctx, "failed to get mime headers"); + .unwrap_or_log_default("failed to get mime headers"); if mime.is_empty() { return ptr::null_mut(); } @@ -1854,7 +1894,7 @@ pub unsafe extern "C" fn dc_delete_msgs( let ctx = &*context; let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); - block_on(message::delete_msgs(ctx, &msg_ids)) + block_on_with_context(ctx, message::delete_msgs(ctx, &msg_ids)) .log_err(ctx, "failed dc_delete_msgs() call") .ok(); } @@ -1877,10 +1917,10 @@ pub unsafe extern "C" fn dc_forward_msgs( let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { chat::forward_msgs(ctx, &msg_ids[..], ChatId::new(chat_id)) .await - .unwrap_or_log_default(ctx, "Failed to forward message") + .unwrap_or_log_default("Failed to forward message") }) } @@ -1898,7 +1938,7 @@ pub unsafe extern "C" fn dc_resend_msgs( let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); if let Err(err) = block_on(chat::resend_msgs(ctx, &msg_ids)) { - error!(ctx, "Resending failed: {err:#}"); + error!("Resending failed: {err:#}"); 0 } else { 1 @@ -1918,7 +1958,7 @@ pub unsafe extern "C" fn dc_markseen_msgs( let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt); let ctx = &*context; - block_on(message::markseen_msgs(ctx, msg_ids)) + block_on_with_context(ctx, message::markseen_msgs(ctx, msg_ids)) .log_err(ctx, "failed dc_markseen_msgs() call") .ok(); } @@ -1931,19 +1971,16 @@ pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) -> } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let message = match message::Message::load_from_db(ctx, MsgId::new(msg_id)).await { Ok(msg) => msg, Err(e) => { if msg_id <= constants::DC_MSG_ID_LAST_SPECIAL { // C-core API returns empty messages, do the same - warn!( - ctx, - "dc_get_msg called with special msg_id={msg_id}, returning empty msg" - ); + warn!("dc_get_msg called with special msg_id={msg_id}, returning empty msg"); message::Message::default() } else { - error!(ctx, "dc_get_msg could not retrieve msg_id {msg_id}: {e:#}"); + error!("dc_get_msg could not retrieve msg_id {msg_id}: {e:#}"); return ptr::null_mut(); } } @@ -1960,7 +1997,7 @@ pub unsafe extern "C" fn dc_download_full_msg(context: *mut dc_context_t, msg_id return; } let ctx = &*context; - block_on(MsgId::new(msg_id).download_full(ctx)) + block_on_with_context(ctx, MsgId::new(msg_id).download_full(ctx)) .log_err(ctx, "Failed to download message fully.") .ok(); } @@ -1986,10 +2023,10 @@ pub unsafe extern "C" fn dc_lookup_contact_id_by_addr( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { Contact::lookup_id_by_addr(ctx, &to_string_lossy(addr), Origin::IncomingReplyTo) .await - .unwrap_or_log_default(ctx, "failed to lookup id") + .unwrap_or_log_default("failed to lookup id") .map(|id| id.to_u32()) .unwrap_or_default() }) @@ -2008,7 +2045,7 @@ pub unsafe extern "C" fn dc_create_contact( let ctx = &*context; let name = to_string_lossy(name); - block_on(Contact::create(ctx, &name, &to_string_lossy(addr))) + block_on_with_context(ctx, Contact::create(ctx, &name, &to_string_lossy(addr))) .log_err(ctx, "Cannot create contact") .map(|id| id.to_u32()) .unwrap_or(0) @@ -2025,12 +2062,13 @@ pub unsafe extern "C" fn dc_add_address_book( } let ctx = &*context; - block_on(async move { - match Contact::add_address_book(ctx, &to_string_lossy(addr_book)).await { - Ok(cnt) => cnt as libc::c_int, - Err(_) => 0, - } - }) + match block_on_with_context( + ctx, + Contact::add_address_book(ctx, &to_string_lossy(addr_book)), + ) { + Ok(cnt) => cnt as libc::c_int, + Err(_) => 0, + } } #[no_mangle] @@ -2046,14 +2084,12 @@ pub unsafe extern "C" fn dc_get_contacts( let ctx = &*context; let query = to_opt_string_lossy(query); - block_on(async move { - match Contact::get_all(ctx, flags, query.as_deref()).await { - Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from( - contacts.iter().map(|id| id.to_u32()).collect::>(), - ))), - Err(_) => ptr::null_mut(), - } - }) + match block_on_with_context(ctx, Contact::get_all(ctx, flags, query.as_deref())) { + Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from( + contacts.iter().map(|id| id.to_u32()).collect::>(), + ))), + Err(_) => ptr::null_mut(), + } } #[no_mangle] @@ -2064,10 +2100,10 @@ pub unsafe extern "C" fn dc_get_blocked_cnt(context: *mut dc_context_t) -> libc: } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { Contact::get_all_blocked(ctx) .await - .unwrap_or_log_default(ctx, "failed to get blocked count") + .unwrap_or_log_default("failed to get blocked count") .len() as libc::c_int }) } @@ -2082,7 +2118,7 @@ pub unsafe extern "C" fn dc_get_blocked_contacts( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { Box::into_raw(Box::new(dc_array_t::from( Contact::get_all_blocked(ctx) .await @@ -2107,7 +2143,7 @@ pub unsafe extern "C" fn dc_block_contact( return; } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { if block == 0 { Contact::unblock(ctx, contact_id) .await @@ -2131,12 +2167,12 @@ pub unsafe extern "C" fn dc_get_contact_encrinfo( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { Contact::get_encrinfo(ctx, ContactId::new(contact_id)) .await .map(|s| s.strdup()) .unwrap_or_else(|e| { - error!(ctx, "{e:#}"); + error!("{e:#}"); ptr::null_mut() }) }) @@ -2154,11 +2190,11 @@ pub unsafe extern "C" fn dc_delete_contact( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match Contact::delete(ctx, contact_id).await { Ok(_) => 1, Err(err) => { - error!(ctx, "cannot delete contact: {err:#}"); + error!("cannot delete contact: {err:#}"); 0 } } @@ -2176,7 +2212,7 @@ pub unsafe extern "C" fn dc_get_contact( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { Contact::get_by_id(ctx, ContactId::new(contact_id)) .await .map(|contact| Box::into_raw(Box::new(ContactWrapper { context, contact }))) @@ -2185,7 +2221,8 @@ pub unsafe extern "C" fn dc_get_contact( } fn spawn_imex(ctx: Context, what: imex::ImexMode, param1: String, passphrase: Option) { - spawn(async move { + let ctx2 = ctx.clone(); + spawn_with_context(&ctx2, async move { imex::imex(&ctx, what, param1.as_ref(), passphrase) .await .log_err(&ctx, "IMEX failed") @@ -2203,7 +2240,7 @@ pub unsafe extern "C" fn dc_imex( eprintln!("ignoring careless call to dc_imex()"); return; } - let what = match imex::ImexMode::from_i32(what_raw) { + let what = match ImexMode::from_i32(what_raw) { Some(what) => what, None => { eprintln!("ignoring invalid argument {what_raw} to dc_imex"); @@ -2232,13 +2269,13 @@ pub unsafe extern "C" fn dc_imex_has_backup( } let ctx = &*context; - block_on(async move { - match imex::has_backup(ctx, to_string_lossy(dir).as_ref()).await { + block_on_with_context(ctx, async move { + match imex::has_backup(to_string_lossy(dir).as_ref()).await { Ok(res) => res.strdup(), Err(err) => { // do not bubble up error to the user, // the ui will expect that the file does not exist or cannot be accessed - warn!(ctx, "dc_imex_has_backup: {err:#}"); + warn!("dc_imex_has_backup: {err:#}"); ptr::null_mut() } } @@ -2253,11 +2290,11 @@ pub unsafe extern "C" fn dc_initiate_key_transfer(context: *mut dc_context_t) -> } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match imex::initiate_key_transfer(ctx).await { Ok(res) => res.strdup(), Err(err) => { - error!(ctx, "dc_initiate_key_transfer(): {err:#}"); + error!("dc_initiate_key_transfer(): {err:#}"); ptr::null_mut() } } @@ -2276,13 +2313,13 @@ pub unsafe extern "C" fn dc_continue_key_transfer( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { match imex::continue_key_transfer(ctx, MsgId::new(msg_id), &to_string_lossy(setup_code)) .await { Ok(()) => 1, Err(err) => { - warn!(ctx, "dc_continue_key_transfer: {err:#}"); + warn!("dc_continue_key_transfer: {err:#}"); 0 } } @@ -2296,7 +2333,7 @@ pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) { return; } let ctx = &*context; - block_on(ctx.stop_ongoing()); + block_on_with_context(ctx, ctx.stop_ongoing()); } #[no_mangle] @@ -2310,7 +2347,7 @@ pub unsafe extern "C" fn dc_check_qr( } let ctx = &*context; - let lot = match block_on(qr::check_qr(ctx, &to_string_lossy(qr))) { + let lot = match block_on_with_context(ctx, qr::check_qr(ctx, &to_string_lossy(qr))) { Ok(qr) => qr.into(), Err(err) => err.into(), }; @@ -2333,7 +2370,7 @@ pub unsafe extern "C" fn dc_get_securejoin_qr( Some(ChatId::new(chat_id)) }; - block_on(securejoin::get_securejoin_qr(ctx, chat_id)) + block_on_with_context(ctx, securejoin::get_securejoin_qr(ctx, chat_id)) .unwrap_or_else(|_| "".to_string()) .strdup() } @@ -2354,7 +2391,7 @@ pub unsafe extern "C" fn dc_get_securejoin_qr_svg( Some(ChatId::new(chat_id)) }; - block_on(get_securejoin_qr_svg(ctx, chat_id)) + block_on_with_context(ctx, get_securejoin_qr_svg(ctx, chat_id)) .unwrap_or_else(|_| "".to_string()) .strdup() } @@ -2370,7 +2407,7 @@ pub unsafe extern "C" fn dc_join_securejoin( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { securejoin::join_securejoin(ctx, &to_string_lossy(qr)) .await .map(|chatid| chatid.to_u32()) @@ -2391,11 +2428,10 @@ pub unsafe extern "C" fn dc_send_locations_to_chat( } let ctx = &*context; - block_on(location::send_locations_to_chat( + block_on_with_context( ctx, - ChatId::new(chat_id), - seconds as i64, - )) + location::send_locations_to_chat(ctx, ChatId::new(chat_id), seconds as i64), + ) .log_err(ctx, "Failed dc_send_locations_to_chat()") .ok(); } @@ -2416,8 +2452,8 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat( Some(ChatId::new(chat_id)) }; - block_on(location::is_sending_locations_to_chat(ctx, chat_id)) - .unwrap_or_log_default(ctx, "Failed dc_is_sending_locations_to_chat()") as libc::c_int + block_on_with_context(ctx, location::is_sending_locations_to_chat(ctx, chat_id)) + .unwrap_or_log_default("Failed dc_is_sending_locations_to_chat()") as libc::c_int } #[no_mangle] @@ -2433,7 +2469,7 @@ pub unsafe extern "C" fn dc_set_location( } let ctx = &*context; - block_on(location::set(ctx, latitude, longitude, accuracy)) as _ + block_on_with_context(ctx, location::set(ctx, latitude, longitude, accuracy)) as _ } #[no_mangle] @@ -2460,10 +2496,10 @@ pub unsafe extern "C" fn dc_get_locations( Some(contact_id) }; - block_on(async move { + block_on_with_context(ctx, async move { let res = location::get_range(ctx, chat_id, contact_id, timestamp_begin, timestamp_end) .await - .unwrap_or_log_default(ctx, "Failed get_locations"); + .unwrap_or_log_default("Failed get_locations"); Box::into_raw(Box::new(dc_array_t::from(res))) }) } @@ -2476,7 +2512,7 @@ pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) { } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { location::delete_all(ctx) .await .log_err(ctx, "Failed to delete locations") @@ -2709,11 +2745,10 @@ pub unsafe extern "C" fn dc_chatlist_get_chat_id( return 0; } let ffi_list = &*chatlist; - let ctx = &*ffi_list.context; match ffi_list.list.get_chat_id(index) { Ok(chat_id) => chat_id.to_u32(), Err(err) => { - warn!(ctx, "get_chat_id failed: {err:#}"); + warn!("get_chat_id failed: {err:#}"); 0 } } @@ -2729,11 +2764,10 @@ pub unsafe extern "C" fn dc_chatlist_get_msg_id( return 0; } let ffi_list = &*chatlist; - let ctx = &*ffi_list.context; match ffi_list.list.get_msg_id(index) { Ok(msg_id) => msg_id.map_or(0, |msg_id| msg_id.to_u32()), Err(err) => { - warn!(ctx, "get_msg_id failed: {err:#}"); + warn!("get_msg_id failed: {err:#}"); 0 } } @@ -2758,7 +2792,7 @@ pub unsafe extern "C" fn dc_chatlist_get_summary( let ffi_list = &*chatlist; let ctx = &*ffi_list.context; - block_on(async move { + block_on_with_context(ctx, async move { let summary = ffi_list .list .get_summary(ctx, index, maybe_chat) @@ -2785,12 +2819,10 @@ pub unsafe extern "C" fn dc_chatlist_get_summary2( } else { Some(MsgId::new(msg_id)) }; - let summary = block_on(Chatlist::get_summary2( + let summary = block_on_with_context( ctx, - ChatId::new(chat_id), - msg_id, - None, - )) + Chatlist::get_summary2(ctx, ChatId::new(chat_id), msg_id, None), + ) .log_err(ctx, "get_summary2 failed") .unwrap_or_default(); Box::into_raw(Box::new(summary.into())) @@ -2887,12 +2919,12 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut let ffi_chat = &*chat; let ctx = &*ffi_chat.context; - block_on(async move { + block_on_with_context(ctx, async move { match ffi_chat.chat.get_profile_image(ctx).await { Ok(Some(p)) => p.to_string_lossy().strdup(), Ok(None) => ptr::null_mut(), Err(err) => { - error!(ctx, "failed to get profile image: {err:#}"); + error!("failed to get profile image: {err:#}"); ptr::null_mut() } } @@ -2908,7 +2940,8 @@ pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { let ffi_chat = &*chat; let ctx = &*ffi_chat.context; - block_on(ffi_chat.chat.get_color(ctx)).unwrap_or_log_default(ctx, "Failed get_color") + block_on_with_context(ctx, ffi_chat.chat.get_color(ctx)) + .unwrap_or_log_default("Failed get_color") } #[no_mangle] @@ -2973,7 +3006,7 @@ pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int { } let ffi_chat = &*chat; let ctx = &*ffi_chat.context; - block_on(ffi_chat.chat.can_send(ctx)) + block_on_with_context(ctx, ffi_chat.chat.can_send(ctx)) .log_err(ctx, "can_send failed") .unwrap_or_default() as libc::c_int } @@ -3040,26 +3073,23 @@ pub unsafe extern "C" fn dc_chat_get_info_json( } let ctx = &*context; - block_on(async move { + block_on_with_context(ctx, async move { let chat = match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)).await { Ok(chat) => chat, Err(err) => { - error!(ctx, "dc_get_chat_info_json() failed to load chat: {err:#}"); + error!("dc_get_chat_info_json() failed to load chat: {err:#}"); return "".strdup(); } }; let info = match chat.get_info(ctx).await { Ok(info) => info, Err(err) => { - error!( - ctx, - "dc_get_chat_info_json() failed to get chat info: {err:#}" - ); + error!("dc_get_chat_info_json() failed to get chat info: {err:#}"); return "".strdup(); } }; serde_json::to_string(&info) - .unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json") + .unwrap_or_log_default("dc_get_chat_info_json() failed to serialise to json") .strdup() }) } @@ -3259,7 +3289,7 @@ pub unsafe extern "C" fn dc_msg_get_webxdc_blob( } let ffi_msg = &*msg; let ctx = &*ffi_msg.context; - let blob = block_on(async move { + let blob = block_on_with_context(ctx, async move { ffi_msg .message .get_webxdc_blob(ctx, &to_string_lossy(filename)) @@ -3288,16 +3318,16 @@ pub unsafe extern "C" fn dc_msg_get_webxdc_info(msg: *mut dc_msg_t) -> *mut libc let ffi_msg = &*msg; let ctx = &*ffi_msg.context; - block_on(async move { + block_on_with_context(ctx, async move { let info = match ffi_msg.message.get_webxdc_info(ctx).await { Ok(info) => info, Err(err) => { - error!(ctx, "dc_msg_get_webxdc_info() failed to get info: {err:#}"); + error!("dc_msg_get_webxdc_info() failed to get info: {err:#}"); return "".strdup(); } }; serde_json::to_string(&info) - .unwrap_or_log_default(ctx, "dc_msg_get_webxdc_info() failed to serialise to json") + .unwrap_or_log_default("dc_msg_get_webxdc_info() failed to serialise to json") .strdup() }) } @@ -3325,8 +3355,8 @@ pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg_t) -> u64 { let ffi_msg = &*msg; let ctx = &*ffi_msg.context; - block_on(ffi_msg.message.get_filebytes(ctx)) - .unwrap_or_log_default(ctx, "Cannot get file size") + block_on_with_context(ctx, ffi_msg.message.get_filebytes(ctx)) + .unwrap_or_log_default("Cannot get file size") .unwrap_or_default() } @@ -3418,7 +3448,7 @@ pub unsafe extern "C" fn dc_msg_get_summary( let ffi_msg = &mut *msg; let ctx = &*ffi_msg.context; - let summary = block_on(ffi_msg.message.get_summary(ctx, maybe_chat)) + let summary = block_on_with_context(ctx, ffi_msg.message.get_summary(ctx, maybe_chat)) .log_err(ctx, "dc_msg_get_summary failed") .unwrap_or_default(); Box::into_raw(Box::new(summary.into())) @@ -3436,7 +3466,7 @@ pub unsafe extern "C" fn dc_msg_get_summarytext( let ffi_msg = &mut *msg; let ctx = &*ffi_msg.context; - let summary = block_on(ffi_msg.message.get_summary(ctx, None)) + let summary = block_on_with_context(ctx, ffi_msg.message.get_summary(ctx, None)) .log_err(ctx, "dc_msg_get_summarytext failed") .unwrap_or_default(); match usize::try_from(approx_characters) { @@ -3580,7 +3610,7 @@ pub unsafe extern "C" fn dc_msg_get_setupcodebegin(msg: *mut dc_msg_t) -> *mut l let ffi_msg = &*msg; let ctx = &*ffi_msg.context; - block_on(ffi_msg.message.get_setupcodebegin(ctx)) + block_on_with_context(ctx, ffi_msg.message.get_setupcodebegin(ctx)) .unwrap_or_default() .strdup() } @@ -3699,7 +3729,7 @@ pub unsafe extern "C" fn dc_msg_latefiling_mediasize( let ffi_msg = &mut *msg; let ctx = &*ffi_msg.context; - block_on({ + block_on_with_context(ctx, { ffi_msg .message .latefiling_mediasize(ctx, width, height, duration) @@ -3738,7 +3768,7 @@ pub unsafe extern "C" fn dc_msg_set_quote(msg: *mut dc_msg_t, quote: *const dc_m Some(&ffi_quote.message) }; - block_on(async move { + block_on_with_context(&*ffi_msg.context, async move { ffi_msg .message .set_quote(&*ffi_msg.context, quote_msg) @@ -3769,7 +3799,7 @@ pub unsafe extern "C" fn dc_msg_get_quoted_msg(msg: *const dc_msg_t) -> *mut dc_ } let ffi_msg: &MessageWrapper = &*msg; let context = &*ffi_msg.context; - let res = block_on(async move { + let res = block_on_with_context(context, async move { ffi_msg .message .quoted_message(context) @@ -3792,7 +3822,7 @@ pub unsafe extern "C" fn dc_msg_get_parent(msg: *const dc_msg_t) -> *mut dc_msg_ } let ffi_msg: &MessageWrapper = &*msg; let context = &*ffi_msg.context; - let res = block_on(async move { + let res = block_on_with_context(context, async move { ffi_msg .message .parent(context) @@ -3917,12 +3947,12 @@ pub unsafe extern "C" fn dc_contact_get_profile_image( let ffi_contact = &*contact; let ctx = &*ffi_contact.context; - block_on(async move { + block_on_with_context(ctx, async move { ffi_contact .contact .get_profile_image(ctx) .await - .unwrap_or_log_default(ctx, "failed to get profile image") + .unwrap_or_log_default("failed to get profile image") .map(|p| p.to_string_lossy().strdup()) .unwrap_or_else(std::ptr::null_mut) }) @@ -3987,7 +4017,7 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l let ffi_contact = &*contact; let ctx = &*ffi_contact.context; - block_on(ffi_contact.contact.is_verified(ctx)) + block_on_with_context(ctx, ffi_contact.contact.is_verified(ctx)) .log_err(ctx, "is_verified failed") .unwrap_or_default() as libc::c_int } @@ -4002,7 +4032,7 @@ pub unsafe extern "C" fn dc_contact_get_verifier_addr( } let ffi_contact = &*contact; let ctx = &*ffi_contact.context; - block_on(ffi_contact.contact.get_verifier_addr(ctx)) + block_on_with_context(ctx, ffi_contact.contact.get_verifier_addr(ctx)) .log_err(ctx, "failed to get verifier for contact") .unwrap_or_default() .strdup() @@ -4016,7 +4046,7 @@ pub unsafe extern "C" fn dc_contact_get_verifier_id(contact: *mut dc_contact_t) } let ffi_contact = &*contact; let ctx = &*ffi_contact.context; - let verifier_contact_id = block_on(ffi_contact.contact.get_verifier_id(ctx)) + let verifier_contact_id = block_on_with_context(ctx, ffi_contact.contact.get_verifier_id(ctx)) .log_err(ctx, "failed to get verifier") .unwrap_or_default() .unwrap_or_default(); @@ -4273,15 +4303,15 @@ trait ResultExt { /// - returns the default value instead of an Err value. /// - emits an error instead of a warning for an [Err] result. This means /// that the error will be shown to the user in a small pop-up. - fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T; + fn unwrap_or_log_default(self, message: &str) -> T; } impl ResultExt for Result { - fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T { + fn unwrap_or_log_default(self, message: &str) -> T { match self { Ok(t) => t, Err(err) => { - error!(context, "{message}: {err:#}"); + error!("{message}: {err:#}"); Default::default() } } @@ -4323,14 +4353,14 @@ where /// from a time before we fully embraced anyhow. Some day we'll also fix that. /// /// [`Context::set_last_error`]: context::Context::set_last_error - fn set_last_error(self, context: &context::Context) -> Result; + fn set_last_error(self, context: &Context) -> Result; } impl ResultLastError for Result where E: std::fmt::Display, { - fn set_last_error(self, context: &context::Context) -> Result { + fn set_last_error(self, context: &Context) -> Result { if let Err(ref err) = self { context.set_last_error(&format!("{err:#}")); } @@ -4379,7 +4409,7 @@ pub unsafe extern "C" fn dc_provider_new_from_email( let ctx = &*context; - match block_on(provider::get_provider_info(ctx, addr.as_str(), true)) { + match block_on_with_context(ctx, provider::get_provider_info(addr.as_str(), true)) { Some(provider) => provider, None => ptr::null_mut(), } @@ -4397,7 +4427,7 @@ pub unsafe extern "C" fn dc_provider_new_from_email_with_dns( let addr = to_string_lossy(addr); let ctx = &*context; - let socks5_enabled = block_on(async move { + let socks5_enabled = block_on_with_context(ctx, async move { ctx.get_config_bool(config::Config::Socks5Enabled) .await .log_err(ctx, "Can't get config") @@ -4405,11 +4435,10 @@ pub unsafe extern "C" fn dc_provider_new_from_email_with_dns( match socks5_enabled { Ok(socks5_enabled) => { - match block_on(provider::get_provider_info( + match block_on_with_context( ctx, - addr.as_str(), - socks5_enabled, - )) { + provider::get_provider_info(addr.as_str(), socks5_enabled), + ) { Some(provider) => provider, None => ptr::null_mut(), } @@ -4776,6 +4805,9 @@ mod jsonrpc { receiver: OutReceiver, handle: RpcSession, event_thread: JoinHandle>, + + /// Event channel, used to send log events converted from `tracing` events. + events: Events, } #[no_mangle] @@ -4788,14 +4820,23 @@ mod jsonrpc { } let account_manager = &*account_manager; - let events = block_on(account_manager.read()).get_event_emitter(); let cmd_api = deltachat_jsonrpc::api::CommandApi::from_arc(account_manager.inner.clone()); let (request_handle, receiver) = RpcClient::new(); let handle = RpcSession::new(request_handle.clone(), cmd_api); + let (events, event_emitter) = block_on({ + async { + let am = account_manager.inner.clone(); + let event_emitter = am.read().await.get_event_emitter(); + let events = am.read().await.events(); + drop(am); + (events, event_emitter) + } + }); + let event_thread = spawn(async move { - while let Some(event) = events.recv().await { + while let Some(event) = event_emitter.recv().await { let event = event_to_json_rpc_notification(event); request_handle .send_notification("event", Some(event)) @@ -4809,6 +4850,7 @@ mod jsonrpc { receiver, handle, event_thread, + events, }; Box::into_raw(Box::new(instance)) @@ -4824,10 +4866,20 @@ mod jsonrpc { drop(Box::from_raw(jsonrpc_instance)); } - fn spawn_handle_jsonrpc_request(handle: RpcSession, request: String) { - spawn(async move { - handle.handle_incoming(&request).await; - }); + fn spawn_handle_jsonrpc_request( + handle: RpcSession, + events: Events, + request: String, + ) { + let layer = events.to_layer(); + let subscriber = Registry::default().with(layer); + + spawn( + async move { + handle.handle_incoming(&request).await; + } + .with_subscriber(subscriber), + ); } #[no_mangle] @@ -4841,8 +4893,9 @@ mod jsonrpc { } let handle = &(*jsonrpc_instance).handle; + let events = &(*jsonrpc_instance).events; let request = to_string_lossy(request); - spawn_handle_jsonrpc_request(handle.clone(), request); + spawn_handle_jsonrpc_request(handle.clone(), events.clone(), request); } #[no_mangle] diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index 3ed2605e9b..5b1109ba76 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -15,6 +15,7 @@ use deltachat::{ config::Config, constants::DC_MSG_ID_DAYMARKER, contact::{may_be_valid_addr, Contact, ContactId, Origin}, + context::future::ContextIdFutureExt, context::get_info, ephemeral::Timer, imex, location, @@ -228,13 +229,13 @@ impl CommandApi { async fn start_io(&self, id: u32) -> Result<()> { let ctx = self.get_context(id).await?; - ctx.start_io().await; + ctx.start_io().bind_context_id(id).await; Ok(()) } async fn stop_io(&self, id: u32) -> Result<()> { let ctx = self.get_context(id).await?; - ctx.stop_io().await; + ctx.stop_io().bind_context_id(id).await; Ok(()) } @@ -284,25 +285,27 @@ impl CommandApi { .await?; let provider_info = - get_provider_info(&ctx, email.split('@').last().unwrap_or(""), socks5_enabled).await; + get_provider_info(email.split('@').last().unwrap_or(""), socks5_enabled).await; Ok(ProviderInfo::from_dc_type(provider_info)) } /// Checks if the context is already configured. async fn is_configured(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - ctx.is_configured().await + ctx.is_configured().bind_context_id(account_id).await } /// Get system info for an account. async fn get_info(&self, account_id: u32) -> Result> { let ctx = self.get_context(account_id).await?; - ctx.get_info().await + ctx.get_info().bind_context_id(account_id).await } async fn set_config(&self, account_id: u32, key: String, value: Option) -> Result<()> { let ctx = self.get_context(account_id).await?; - set_config(&ctx, &key, value.as_deref()).await + set_config(&ctx, &key, value.as_deref()) + .bind_context_id(account_id) + .await } async fn batch_set_config( @@ -313,6 +316,7 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; for (key, value) in config.into_iter() { set_config(&ctx, &key, value.as_deref()) + .bind_context_id(account_id) .await .with_context(|| format!("Can't set {key} to {value:?}"))?; } @@ -326,19 +330,23 @@ impl CommandApi { /// Internally, the function will call dc_set_config() with the appropriate keys, async fn set_config_from_qr(&self, account_id: u32, qr_content: String) -> Result<()> { let ctx = self.get_context(account_id).await?; - qr::set_config_from_qr(&ctx, &qr_content).await + qr::set_config_from_qr(&ctx, &qr_content) + .bind_context_id(account_id) + .await } async fn check_qr(&self, account_id: u32, qr_content: String) -> Result { let ctx = self.get_context(account_id).await?; - let qr = qr::check_qr(&ctx, &qr_content).await?; + let qr = qr::check_qr(&ctx, &qr_content) + .bind_context_id(account_id) + .await?; let qr_object = QrObject::from(qr); Ok(qr_object) } async fn get_config(&self, account_id: u32, key: String) -> Result> { let ctx = self.get_context(account_id).await?; - get_config(&ctx, &key).await + get_config(&ctx, &key).bind_context_id(account_id).await } async fn batch_get_config( @@ -349,7 +357,10 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; let mut result: HashMap> = HashMap::new(); for key in keys { - result.insert(key.clone(), get_config(&ctx, &key).await?); + result.insert( + key.clone(), + get_config(&ctx, &key).bind_context_id(account_id).await?, + ); } Ok(result) } @@ -370,22 +381,26 @@ impl CommandApi { /// Setup the credential config before calling this. async fn configure(&self, account_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ctx.stop_io().await; - let result = ctx.configure().await; - if result.is_err() { - if let Ok(true) = ctx.is_configured().await { - ctx.start_io().await; + async { + ctx.stop_io().await; + let result = ctx.configure().await; + if result.is_err() { + if let Ok(true) = ctx.is_configured().await { + ctx.start_io().await; + } + return result; } - return result; + ctx.start_io().await; + Ok(()) } - ctx.start_io().await; - Ok(()) + .bind_context_id(account_id) + .await } /// Signal an ongoing process to stop. async fn stop_ongoing_process(&self, account_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ctx.stop_ongoing().await; + ctx.stop_ongoing().bind_context_id(account_id).await; Ok(()) } @@ -402,6 +417,7 @@ impl CommandApi { path.as_ref(), passphrase, ) + .bind_context_id(account_id) .await } @@ -418,6 +434,7 @@ impl CommandApi { path.as_ref(), passphrase, ) + .bind_context_id(account_id) .await } @@ -436,6 +453,7 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; Ok(ctx .get_fresh_msgs() + .bind_context_id(account_id) .await? .iter() .map(|msg_id| msg_id.to_u32()) @@ -450,7 +468,10 @@ impl CommandApi { /// e.g. using "gray" instead of "red" color. async fn get_fresh_msg_cnt(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).get_fresh_msg_cnt(&ctx).await + ChatId::new(chat_id) + .get_fresh_msg_cnt(&ctx) + .bind_context_id(account_id) + .await } /// Estimate the number of messages that will be deleted @@ -464,7 +485,9 @@ impl CommandApi { seconds: i64, ) -> Result { let ctx = self.get_context(account_id).await?; - message::estimate_deletion_cnt(&ctx, from_server, seconds).await + message::estimate_deletion_cnt(&ctx, from_server, seconds) + .bind_context_id(account_id) + .await } // --------------------------------------------- @@ -473,7 +496,9 @@ impl CommandApi { async fn initiate_autocrypt_key_transfer(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - deltachat::imex::initiate_key_transfer(&ctx).await + deltachat::imex::initiate_key_transfer(&ctx) + .bind_context_id(account_id) + .await } async fn continue_autocrypt_key_transfer( @@ -483,7 +508,9 @@ impl CommandApi { setup_code: String, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - deltachat::imex::continue_key_transfer(&ctx, MsgId::new(message_id), &setup_code).await + deltachat::imex::continue_key_transfer(&ctx, MsgId::new(message_id), &setup_code) + .bind_context_id(account_id) + .await } // --------------------------------------------- @@ -504,6 +531,7 @@ impl CommandApi { query_string.as_deref(), query_contact_id.map(ContactId::new), ) + .bind_context_id(account_id) .await?; let mut l: Vec = Vec::with_capacity(list.len()); for i in 0..list.len() { @@ -527,7 +555,10 @@ impl CommandApi { for entry in entries.iter() { result.insert( entry.0, - match get_chat_list_item_by_id(&ctx, entry).await { + match get_chat_list_item_by_id(&ctx, entry) + .bind_context_id(account_id) + .await + { Ok(res) => res, Err(err) => ChatListItemFetchResult::Error { id: entry.0, @@ -545,24 +576,34 @@ impl CommandApi { async fn get_full_chat_by_id(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - FullChat::try_from_dc_chat_id(&ctx, chat_id).await + FullChat::try_from_dc_chat_id(&ctx, chat_id) + .bind_context_id(account_id) + .await } /// get basic info about a chat, /// use chatlist_get_full_chat_by_id() instead if you need more information async fn get_basic_chat_info(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - BasicChat::try_from_dc_chat_id(&ctx, chat_id).await + BasicChat::try_from_dc_chat_id(&ctx, chat_id) + .bind_context_id(account_id) + .await } async fn accept_chat(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).accept(&ctx).await + ChatId::new(chat_id) + .accept(&ctx) + .bind_context_id(account_id) + .await } async fn block_chat(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).block(&ctx).await + ChatId::new(chat_id) + .block(&ctx) + .bind_context_id(account_id) + .await } /// Delete a chat. @@ -585,7 +626,10 @@ impl CommandApi { /// To leave a chat explicitly, use leave_group() async fn delete_chat(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).delete(&ctx).await + ChatId::new(chat_id) + .delete(&ctx) + .bind_context_id(account_id) + .await } /// Get encryption info for a chat. @@ -595,7 +639,10 @@ impl CommandApi { /// returns Multi-line text async fn get_chat_encryption_info(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).get_encryption_info(&ctx).await + ChatId::new(chat_id) + .get_encryption_info(&ctx) + .bind_context_id(account_id) + .await } /// Get QR code (text and SVG) that will offer an Setup-Contact or Verified-Group invitation. @@ -621,10 +668,14 @@ impl CommandApi { ) -> Result<(String, String)> { let ctx = self.get_context(account_id).await?; let chat = chat_id.map(ChatId::new); - Ok(( - securejoin::get_securejoin_qr(&ctx, chat).await?, - get_securejoin_qr_svg(&ctx, chat).await?, - )) + async { + Ok(( + securejoin::get_securejoin_qr(&ctx, chat).await?, + get_securejoin_qr_svg(&ctx, chat).await?, + )) + } + .bind_context_id(account_id) + .await } /// Continue a Setup-Contact or Verified-Group-Invite protocol @@ -650,13 +701,17 @@ impl CommandApi { /// async fn secure_join(&self, account_id: u32, qr: String) -> Result { let ctx = self.get_context(account_id).await?; - let chat_id = securejoin::join_securejoin(&ctx, &qr).await?; + let chat_id = securejoin::join_securejoin(&ctx, &qr) + .bind_context_id(account_id) + .await?; Ok(chat_id.to_u32()) } async fn leave_group(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::SELF).await + remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::SELF) + .bind_context_id(account_id) + .await } /// Remove a member from a group. @@ -672,7 +727,9 @@ impl CommandApi { contact_id: u32, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await + remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)) + .bind_context_id(account_id) + .await } /// Add a member to a group. @@ -690,7 +747,9 @@ impl CommandApi { contact_id: u32, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - add_contact_to_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await + add_contact_to_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)) + .bind_context_id(account_id) + .await } /// Get the contact IDs belonging to a chat. @@ -710,7 +769,9 @@ impl CommandApi { /// so we could return only SELF or the known members; this is not decided yet) async fn get_chat_contacts(&self, account_id: u32, chat_id: u32) -> Result> { let ctx = self.get_context(account_id).await?; - let contacts = chat::get_chat_contacts(&ctx, ChatId::new(chat_id)).await?; + let contacts = chat::get_chat_contacts(&ctx, ChatId::new(chat_id)) + .bind_context_id(account_id) + .await?; Ok(contacts.iter().map(|id| id.to_u32()).collect::>()) } @@ -740,6 +801,7 @@ impl CommandApi { false => ProtectionStatus::Unprotected, }; chat::create_group_chat(&ctx, protect, &name) + .bind_context_id(account_id) .await .map(|id| id.to_u32()) } @@ -768,6 +830,7 @@ impl CommandApi { async fn create_broadcast_list(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; chat::create_broadcast_list(&ctx) + .bind_context_id(account_id) .await .map(|id| id.to_u32()) } @@ -780,7 +843,9 @@ impl CommandApi { /// Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent. async fn set_chat_name(&self, account_id: u32, chat_id: u32, new_name: String) -> Result<()> { let ctx = self.get_context(account_id).await?; - chat::set_chat_name(&ctx, ChatId::new(chat_id), &new_name).await + chat::set_chat_name(&ctx, ChatId::new(chat_id), &new_name) + .bind_context_id(account_id) + .await } /// Set group profile image. @@ -804,6 +869,7 @@ impl CommandApi { ) -> Result<()> { let ctx = self.get_context(account_id).await?; chat::set_chat_profile_image(&ctx, ChatId::new(chat_id), &image_path.unwrap_or_default()) + .bind_context_id(account_id) .await } @@ -817,6 +883,7 @@ impl CommandApi { ChatId::new(chat_id) .set_visibility(&ctx, visibility.into_core_type()) + .bind_context_id(account_id) .await } @@ -829,6 +896,7 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; ChatId::new(chat_id) .set_ephemeral_timer(&ctx, Timer::from_u32(timer)) + .bind_context_id(account_id) .await } @@ -836,6 +904,7 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; Ok(ChatId::new(chat_id) .get_ephemeral_timer(&ctx) + .bind_context_id(account_id) .await? .to_u32()) } @@ -850,8 +919,9 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; let mut msg = Message::new(Viewtype::Text); msg.set_text(Some(text)); - let message_id = - deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut msg)).await?; + let message_id = deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut msg)) + .bind_context_id(account_id) + .await?; Ok(message_id.to_u32()) } @@ -864,7 +934,9 @@ impl CommandApi { /// See also markseen_msgs(). async fn marknoticed_chat(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - marknoticed_chat(&ctx, ChatId::new(chat_id)).await + marknoticed_chat(&ctx, ChatId::new(chat_id)) + .bind_context_id(account_id) + .await } async fn get_first_unread_message_of_chat( @@ -875,20 +947,24 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; // TODO: implement this in core with an SQL query, that will be way faster - let messages = get_chat_msgs(&ctx, ChatId::new(chat_id)).await?; - let mut first_unread_message_id = None; - for item in messages.into_iter().rev() { - if let ChatItem::Message { msg_id } = item { - match msg_id.get_state(&ctx).await? { - MessageState::InSeen => break, - MessageState::InFresh | MessageState::InNoticed => { - first_unread_message_id = Some(msg_id) + async { + let messages = get_chat_msgs(&ctx, ChatId::new(chat_id)).await?; + let mut first_unread_message_id = None; + for item in messages.into_iter().rev() { + if let ChatItem::Message { msg_id } = item { + match msg_id.get_state(&ctx).await? { + MessageState::InSeen => break, + MessageState::InFresh | MessageState::InNoticed => { + first_unread_message_id = Some(msg_id) + } + _ => continue, } - _ => continue, } } + Ok(first_unread_message_id.map(|id| id.to_u32())) } - Ok(first_unread_message_id.map(|id| id.to_u32())) + .bind_context_id(account_id) + .await } /// Set mute duration of a chat. @@ -910,7 +986,9 @@ impl CommandApi { duration: MuteDuration, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - chat::set_muted(&ctx, ChatId::new(chat_id), duration.try_into_core_type()?).await + chat::set_muted(&ctx, ChatId::new(chat_id), duration.try_into_core_type()?) + .bind_context_id(account_id) + .await } /// Check whether the chat is currently muted (can be changed by set_chat_mute_duration()). @@ -919,6 +997,7 @@ impl CommandApi { async fn is_chat_muted(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; Ok(Chat::load_from_db(&ctx, ChatId::new(chat_id)) + .bind_context_id(account_id) .await? .is_muted()) } @@ -947,7 +1026,9 @@ impl CommandApi { /// One #DC_EVENT_MSGS_NOTICED event is emitted per modified chat. async fn markseen_msgs(&self, account_id: u32, msg_ids: Vec) -> Result<()> { let ctx = self.get_context(account_id).await?; - markseen_msgs(&ctx, msg_ids.into_iter().map(MsgId::new).collect()).await + markseen_msgs(&ctx, msg_ids.into_iter().map(MsgId::new).collect()) + .bind_context_id(account_id) + .await } async fn get_message_ids( @@ -966,6 +1047,7 @@ impl CommandApi { add_daymarker, }, ) + .bind_context_id(account_id) .await?; Ok(msg .iter() @@ -994,6 +1076,7 @@ impl CommandApi { add_daymarker, }, ) + .bind_context_id(account_id) .await?; Ok(msg .iter() @@ -1003,12 +1086,17 @@ impl CommandApi { async fn get_message(&self, account_id: u32, message_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - MessageObject::from_message_id(&ctx, message_id).await + MessageObject::from_message_id(&ctx, message_id) + .bind_context_id(account_id) + .await } async fn get_message_html(&self, account_id: u32, message_id: u32) -> Result> { let ctx = self.get_context(account_id).await?; - MsgId::new(message_id).get_html(&ctx).await + MsgId::new(message_id) + .get_html(&ctx) + .bind_context_id(account_id) + .await } /// get multiple messages in one call, @@ -1023,7 +1111,9 @@ impl CommandApi { let ctx = self.get_context(account_id).await?; let mut messages: HashMap = HashMap::new(); for message_id in message_ids { - let message_result = MessageObject::from_message_id(&ctx, message_id).await; + let message_result = MessageObject::from_message_id(&ctx, message_id) + .bind_context_id(account_id) + .await; messages.insert( message_id, match message_result { @@ -1044,7 +1134,9 @@ impl CommandApi { message_id: u32, ) -> Result { let ctx = self.get_context(account_id).await?; - MessageNotificationInfo::from_msg_id(&ctx, MsgId::new(message_id)).await + MessageNotificationInfo::from_msg_id(&ctx, MsgId::new(message_id)) + .bind_context_id(account_id) + .await } /// Delete messages. The messages are deleted on the current device and @@ -1052,7 +1144,7 @@ impl CommandApi { async fn delete_messages(&self, account_id: u32, message_ids: Vec) -> Result<()> { let ctx = self.get_context(account_id).await?; let msgs: Vec = message_ids.into_iter().map(MsgId::new).collect(); - delete_msgs(&ctx, &msgs).await + delete_msgs(&ctx, &msgs).bind_context_id(account_id).await } /// Get an informational text for a single message. The text is multiline and may @@ -1062,7 +1154,9 @@ impl CommandApi { /// max. text returned by dc_msg_get_text() (about 30000 characters). async fn get_message_info(&self, account_id: u32, message_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - get_msg_info(&ctx, MsgId::new(message_id)).await + get_msg_info(&ctx, MsgId::new(message_id)) + .bind_context_id(account_id) + .await } /// Asks the core to start downloading a message fully. @@ -1078,7 +1172,10 @@ impl CommandApi { /// To reflect these changes a @ref DC_EVENT_MSGS_CHANGED event will be emitted. async fn download_full_message(&self, account_id: u32, message_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - MsgId::new(message_id).download_full(&ctx).await + MsgId::new(message_id) + .download_full(&ctx) + .bind_context_id(account_id) + .await } /// Search messages containing the given query string. @@ -1100,7 +1197,10 @@ impl CommandApi { chat_id: Option, ) -> Result> { let ctx = self.get_context(account_id).await?; - let messages = ctx.search_msgs(chat_id.map(ChatId::new), &query).await?; + let messages = ctx + .search_msgs(chat_id.map(ChatId::new), &query) + .bind_context_id(account_id) + .await?; Ok(messages .iter() .map(|msg_id| msg_id.to_u32()) @@ -1118,7 +1218,9 @@ impl CommandApi { for id in message_ids { results.insert( id, - MessageSearchResult::from_msg_id(&ctx, MsgId::new(id)).await?, + MessageSearchResult::from_msg_id(&ctx, MsgId::new(id)) + .bind_context_id(account_id) + .await?, ); } Ok(results) @@ -1135,7 +1237,9 @@ impl CommandApi { ContactObject::try_from_dc_contact( &ctx, - deltachat::contact::Contact::get_by_id(&ctx, contact_id).await?, + deltachat::contact::Contact::get_by_id(&ctx, contact_id) + .bind_context_id(account_id) + .await?, ) .await } @@ -1155,17 +1259,23 @@ impl CommandApi { "provided email address is not a valid email address" )) } - let contact_id = Contact::create(&ctx, &name.unwrap_or_default(), &email).await?; + let contact_id = Contact::create(&ctx, &name.unwrap_or_default(), &email) + .bind_context_id(account_id) + .await?; Ok(contact_id.to_u32()) } /// Returns contact id of the created or existing DM chat with that contact async fn create_chat_by_contact_id(&self, account_id: u32, contact_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - let contact = Contact::get_by_id(&ctx, ContactId::new(contact_id)).await?; - ChatId::create_for_contact(&ctx, contact.id) - .await - .map(|id| id.to_u32()) + async { + let contact = Contact::get_by_id(&ctx, ContactId::new(contact_id)).await?; + ChatId::create_for_contact(&ctx, contact.id) + .await + .map(|id| id.to_u32()) + } + .bind_context_id(account_id) + .await } async fn block_contact(&self, account_id: u32, contact_id: u32) -> Result<()> { @@ -1175,23 +1285,29 @@ impl CommandApi { async fn unblock_contact(&self, account_id: u32, contact_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - Contact::unblock(&ctx, ContactId::new(contact_id)).await + Contact::unblock(&ctx, ContactId::new(contact_id)) + .bind_context_id(account_id) + .await } async fn get_blocked_contacts(&self, account_id: u32) -> Result> { let ctx = self.get_context(account_id).await?; - let blocked_ids = Contact::get_all_blocked(&ctx).await?; - let mut contacts: Vec = Vec::with_capacity(blocked_ids.len()); - for id in blocked_ids { - contacts.push( - ContactObject::try_from_dc_contact( - &ctx, - deltachat::contact::Contact::get_by_id(&ctx, id).await?, - ) - .await?, - ); + async { + let blocked_ids = Contact::get_all_blocked(&ctx).await?; + let mut contacts: Vec = Vec::with_capacity(blocked_ids.len()); + for id in blocked_ids { + contacts.push( + ContactObject::try_from_dc_contact( + &ctx, + deltachat::contact::Contact::get_by_id(&ctx, id).await?, + ) + .await?, + ); + } + Ok(contacts) } - Ok(contacts) + .bind_context_id(account_id) + .await } async fn get_contact_ids( @@ -1201,7 +1317,9 @@ impl CommandApi { query: Option, ) -> Result> { let ctx = self.get_context(account_id).await?; - let contacts = Contact::get_all(&ctx, list_flags, query.as_deref()).await?; + let contacts = Contact::get_all(&ctx, list_flags, query.as_deref()) + .bind_context_id(account_id) + .await?; Ok(contacts.into_iter().map(|c| c.to_u32()).collect()) } @@ -1214,18 +1332,22 @@ impl CommandApi { query: Option, ) -> Result> { let ctx = self.get_context(account_id).await?; - let contact_ids = Contact::get_all(&ctx, list_flags, query.as_deref()).await?; - let mut contacts: Vec = Vec::with_capacity(contact_ids.len()); - for id in contact_ids { - contacts.push( - ContactObject::try_from_dc_contact( - &ctx, - deltachat::contact::Contact::get_by_id(&ctx, id).await?, - ) - .await?, - ); + async { + let contact_ids = Contact::get_all(&ctx, list_flags, query.as_deref()).await?; + let mut contacts: Vec = Vec::with_capacity(contact_ids.len()); + for id in contact_ids { + contacts.push( + ContactObject::try_from_dc_contact( + &ctx, + deltachat::contact::Contact::get_by_id(&ctx, id).await?, + ) + .await?, + ); + } + Ok(contacts) } - Ok(contacts) + .bind_context_id(account_id) + .await } async fn get_contacts_by_ids( @@ -1235,25 +1357,31 @@ impl CommandApi { ) -> Result> { let ctx = self.get_context(account_id).await?; - let mut contacts = HashMap::with_capacity(ids.len()); - for id in ids { - contacts.insert( - id, - ContactObject::try_from_dc_contact( - &ctx, - deltachat::contact::Contact::get_by_id(&ctx, ContactId::new(id)).await?, - ) - .await?, - ); + async { + let mut contacts = HashMap::with_capacity(ids.len()); + for id in ids { + contacts.insert( + id, + ContactObject::try_from_dc_contact( + &ctx, + deltachat::contact::Contact::get_by_id(&ctx, ContactId::new(id)).await?, + ) + .await?, + ); + } + Ok(contacts) } - Ok(contacts) + .bind_context_id(account_id) + .await } async fn delete_contact(&self, account_id: u32, contact_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; let contact_id = ContactId::new(contact_id); - Contact::delete(&ctx, contact_id).await?; + Contact::delete(&ctx, contact_id) + .bind_context_id(account_id) + .await?; Ok(()) } @@ -1265,10 +1393,14 @@ impl CommandApi { ) -> Result<()> { let ctx = self.get_context(account_id).await?; let contact_id = ContactId::new(contact_id); - let contact = Contact::load_from_db(&ctx, contact_id).await?; - let addr = contact.get_addr(); - Contact::create(&ctx, &name, addr).await?; - Ok(()) + async { + let contact = Contact::load_from_db(&ctx, contact_id).await?; + let addr = contact.get_addr(); + Contact::create(&ctx, &name, addr).await?; + Ok(()) + } + .bind_context_id(account_id) + .await } /// Get encryption info for a contact. @@ -1280,7 +1412,9 @@ impl CommandApi { contact_id: u32, ) -> Result { let ctx = self.get_context(account_id).await?; - Contact::get_encrinfo(&ctx, ContactId::new(contact_id)).await + Contact::get_encrinfo(&ctx, ContactId::new(contact_id)) + .bind_context_id(account_id) + .await } /// Check if an e-mail address belongs to a known and unblocked contact. @@ -1294,7 +1428,9 @@ impl CommandApi { addr: String, ) -> Result> { let ctx = self.get_context(account_id).await?; - let contact_id = Contact::lookup_id_by_addr(&ctx, &addr, Origin::IncomingReplyTo).await?; + let contact_id = Contact::lookup_id_by_addr(&ctx, &addr, Origin::IncomingReplyTo) + .bind_context_id(account_id) + .await?; Ok(contact_id.map(|id| id.to_u32())) } @@ -1329,7 +1465,9 @@ impl CommandApi { let or_msg_type2 = or_message_type2.map_or(Viewtype::Unknown, |v| v.into()); let or_msg_type3 = or_message_type3.map_or(Viewtype::Unknown, |v| v.into()); - let media = get_chat_media(&ctx, chat_id, msg_type, or_msg_type2, or_msg_type3).await?; + let media = get_chat_media(&ctx, chat_id, msg_type, or_msg_type2, or_msg_type3) + .bind_context_id(account_id) + .await?; Ok(media.iter().map(|msg_id| msg_id.to_u32()).collect()) } @@ -1353,29 +1491,33 @@ impl CommandApi { let msg_type2: Viewtype = or_message_type2.map(|v| v.into()).unwrap_or_default(); let msg_type3: Viewtype = or_message_type3.map(|v| v.into()).unwrap_or_default(); - let prev = chat::get_next_media( - &ctx, - MsgId::new(msg_id), - chat::Direction::Backward, - msg_type, - msg_type2, - msg_type3, - ) - .await? - .map(|id| id.to_u32()); - - let next = chat::get_next_media( - &ctx, - MsgId::new(msg_id), - chat::Direction::Forward, - msg_type, - msg_type2, - msg_type3, - ) - .await? - .map(|id| id.to_u32()); + async { + let prev = chat::get_next_media( + &ctx, + MsgId::new(msg_id), + chat::Direction::Backward, + msg_type, + msg_type2, + msg_type3, + ) + .await? + .map(|id| id.to_u32()); + + let next = chat::get_next_media( + &ctx, + MsgId::new(msg_id), + chat::Direction::Forward, + msg_type, + msg_type2, + msg_type3, + ) + .await? + .map(|id| id.to_u32()); - Ok((prev, next)) + Ok((prev, next)) + } + .bind_context_id(account_id) + .await } // --------------------------------------------- @@ -1395,6 +1537,7 @@ impl CommandApi { destination.as_ref(), passphrase, ) + .bind_context_id(account_id) .await } @@ -1411,6 +1554,7 @@ impl CommandApi { path.as_ref(), passphrase, ) + .bind_context_id(account_id) .await } @@ -1510,7 +1654,7 @@ impl CommandApi { /// If the connectivity changes, a #DC_EVENT_CONNECTIVITY_CHANGED will be emitted. async fn get_connectivity(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - Ok(ctx.get_connectivity().await as u32) + Ok(ctx.get_connectivity().bind_context_id(account_id).await as u32) } /// Get an overview of the current connectivity, and possibly more statistics. @@ -1524,7 +1668,9 @@ impl CommandApi { /// and the improvement instantly reaches all UIs. async fn get_connectivity_html(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - ctx.get_connectivity_html().await + ctx.get_connectivity_html() + .bind_context_id(account_id) + .await } // --------------------------------------------- @@ -1548,6 +1694,7 @@ impl CommandApi { timestamp_begin, timestamp_end, ) + .bind_context_id(account_id) .await?; Ok(locations.into_iter().map(|l| l.into()).collect()) @@ -1566,6 +1713,7 @@ impl CommandApi { ) -> Result<()> { let ctx = self.get_context(account_id).await?; ctx.send_webxdc_status_update(MsgId::new(instance_msg_id), &update_str, &description) + .bind_context_id(account_id) .await } @@ -1580,6 +1728,7 @@ impl CommandApi { MsgId::new(instance_msg_id), StatusUpdateSerial::new(last_known_serial), ) + .bind_context_id(account_id) .await } @@ -1590,7 +1739,9 @@ impl CommandApi { instance_msg_id: u32, ) -> Result { let ctx = self.get_context(account_id).await?; - WebxdcMessageInfo::get_for_message(&ctx, MsgId::new(instance_msg_id)).await + WebxdcMessageInfo::get_for_message(&ctx, MsgId::new(instance_msg_id)) + .bind_context_id(account_id) + .await } /// Get blob encoded as base64 from a webxdc message @@ -1603,11 +1754,14 @@ impl CommandApi { path: String, ) -> Result { let ctx = self.get_context(account_id).await?; - let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?; - let blob = message.get_webxdc_blob(&ctx, &path).await?; - - use base64::{engine::general_purpose, Engine as _}; - Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) + async { + let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?; + let blob = message.get_webxdc_blob(&ctx, &path).await?; + use base64::{engine::general_purpose, Engine as _}; + Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) + } + .bind_context_id(account_id) + .await } /// Forward messages to another chat. @@ -1624,7 +1778,9 @@ impl CommandApi { ) -> Result<()> { let ctx = self.get_context(account_id).await?; let message_ids: Vec = message_ids.into_iter().map(MsgId::new).collect(); - forward_msgs(&ctx, &message_ids, ChatId::new(chat_id)).await + forward_msgs(&ctx, &message_ids, ChatId::new(chat_id)) + .bind_context_id(account_id) + .await } async fn send_sticker( @@ -1638,7 +1794,9 @@ impl CommandApi { let mut msg = Message::new(Viewtype::Sticker); msg.set_file(&sticker_path, None); - let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?; + let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg) + .bind_context_id(account_id) + .await?; Ok(message_id.to_u32()) } @@ -1655,50 +1813,56 @@ impl CommandApi { reaction: Vec, ) -> Result { let ctx = self.get_context(account_id).await?; - let message_id = send_reaction(&ctx, MsgId::new(message_id), &reaction.join(" ")).await?; + let message_id = send_reaction(&ctx, MsgId::new(message_id), &reaction.join(" ")) + .bind_context_id(account_id) + .await?; Ok(message_id.to_u32()) } async fn send_msg(&self, account_id: u32, chat_id: u32, data: MessageData) -> Result { let ctx = self.get_context(account_id).await?; - let mut message = Message::new(if let Some(viewtype) = data.viewtype { - viewtype.into() - } else if data.file.is_some() { - Viewtype::File - } else { - Viewtype::Text - }); - if data.text.is_some() { - message.set_text(data.text); - } - if data.html.is_some() { - message.set_html(data.html); - } - if data.override_sender_name.is_some() { - message.set_override_sender_name(data.override_sender_name); - } - if let Some(file) = data.file { - message.set_file(file, None); - } - if let Some((latitude, longitude)) = data.location { - message.set_location(latitude, longitude); - } - if let Some(id) = data.quoted_message_id { - message - .set_quote( - &ctx, - Some( - &Message::load_from_db(&ctx, MsgId::new(id)) - .await - .context("message to quote could not be loaded")?, - ), - ) - .await?; + async { + let mut message = Message::new(if let Some(viewtype) = data.viewtype { + viewtype.into() + } else if data.file.is_some() { + Viewtype::File + } else { + Viewtype::Text + }); + if data.text.is_some() { + message.set_text(data.text); + } + if data.html.is_some() { + message.set_html(data.html); + } + if data.override_sender_name.is_some() { + message.set_override_sender_name(data.override_sender_name); + } + if let Some(file) = data.file { + message.set_file(file, None); + } + if let Some((latitude, longitude)) = data.location { + message.set_location(latitude, longitude); + } + if let Some(id) = data.quoted_message_id { + message + .set_quote( + &ctx, + Some( + &Message::load_from_db(&ctx, MsgId::new(id)) + .await + .context("message to quote could not be loaded")?, + ), + ) + .await?; + } + let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message) + .await? + .to_u32(); + Ok(msg_id) } - let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message) - .await? - .to_u32(); - Ok(msg_id) + .bind_context_id(account_id) + .await } // --------------------------------------------- @@ -1708,24 +1872,32 @@ impl CommandApi { async fn remove_draft(&self, account_id: u32, chat_id: u32) -> Result<()> { let ctx = self.get_context(account_id).await?; - ChatId::new(chat_id).set_draft(&ctx, None).await + ChatId::new(chat_id) + .set_draft(&ctx, None) + .bind_context_id(account_id) + .await } /// Get draft for a chat, if any. async fn get_draft(&self, account_id: u32, chat_id: u32) -> Result> { let ctx = self.get_context(account_id).await?; - if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? { - Ok(Some( - MessageObject::from_msg_id(&ctx, draft.get_id()).await?, - )) - } else { - Ok(None) + async { + if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? { + Ok(Some( + MessageObject::from_msg_id(&ctx, draft.get_id()).await?, + )) + } else { + Ok(None) + } } + .bind_context_id(account_id) + .await } async fn send_videochat_invitation(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; chat::send_videochat_invitation(&ctx, ChatId::new(chat_id)) + .bind_context_id(account_id) .await .map(|msg_id| msg_id.to_u32()) } @@ -1737,16 +1909,20 @@ impl CommandApi { async fn misc_get_sticker_folder(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; - let account_folder = ctx - .get_dbfile() - .parent() - .context("account folder not found")?; - let sticker_folder_path = account_folder.join("stickers"); - fs::create_dir_all(&sticker_folder_path).await?; - sticker_folder_path - .to_str() - .map(|s| s.to_owned()) - .context("path conversion to string failed") + async { + let account_folder = ctx + .get_dbfile() + .parent() + .context("account folder not found")?; + let sticker_folder_path = account_folder.join("stickers"); + fs::create_dir_all(&sticker_folder_path).await?; + sticker_folder_path + .to_str() + .map(|s| s.to_owned()) + .context("path conversion to string failed") + } + .bind_context_id(account_id) + .await } /// save a sticker to a collection/folder in the account's sticker folder @@ -1757,79 +1933,86 @@ impl CommandApi { collection: String, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - let message = Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; - ensure!( - message.get_viewtype() == Viewtype::Sticker, - "message {} is not a sticker", - msg_id - ); - let account_folder = ctx - .get_dbfile() - .parent() - .context("account folder not found")?; - ensure!( - is_sanitized(&collection), - "illegal characters in collection name" - ); - let destination_path = account_folder.join("stickers").join(collection); - fs::create_dir_all(&destination_path).await?; - let file = message.get_file(&ctx).context("no file")?; - fs::copy( - &file, - destination_path.join(format!( - "{}.{}", - msg_id, - file.extension() - .unwrap_or_default() - .to_str() - .unwrap_or_default() - )), - ) - .await?; - Ok(()) + async { + let message = Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + ensure!( + message.get_viewtype() == Viewtype::Sticker, + "message {} is not a sticker", + msg_id + ); + let account_folder = ctx + .get_dbfile() + .parent() + .context("account folder not found")?; + ensure!( + is_sanitized(&collection), + "illegal characters in collection name" + ); + let destination_path = account_folder.join("stickers").join(collection); + fs::create_dir_all(&destination_path).await?; + let file = message.get_file(&ctx).context("no file")?; + fs::copy( + &file, + destination_path.join(format!( + "{}.{}", + msg_id, + file.extension() + .unwrap_or_default() + .to_str() + .unwrap_or_default() + )), + ) + .await?; + Ok(()) + } + .bind_context_id(account_id) + .await } /// for desktop, get stickers from stickers folder, /// grouped by the collection/folder they are in. async fn misc_get_stickers(&self, account_id: u32) -> Result>> { let ctx = self.get_context(account_id).await?; - let account_folder = ctx - .get_dbfile() - .parent() - .context("account folder not found")?; - let sticker_folder_path = account_folder.join("stickers"); - fs::create_dir_all(&sticker_folder_path).await?; - let mut result = HashMap::new(); - - let mut packs = tokio::fs::read_dir(sticker_folder_path).await?; - while let Some(entry) = packs.next_entry().await? { - if !entry.file_type().await?.is_dir() { - continue; - } - let pack_name = entry.file_name().into_string().unwrap_or_default(); - let mut stickers = tokio::fs::read_dir(entry.path()).await?; - let mut sticker_paths = Vec::new(); - while let Some(sticker_entry) = stickers.next_entry().await? { - if !sticker_entry.file_type().await?.is_file() { + async { + let account_folder = ctx + .get_dbfile() + .parent() + .context("account folder not found")?; + let sticker_folder_path = account_folder.join("stickers"); + fs::create_dir_all(&sticker_folder_path).await?; + let mut result = HashMap::new(); + + let mut packs = tokio::fs::read_dir(sticker_folder_path).await?; + while let Some(entry) = packs.next_entry().await? { + if !entry.file_type().await?.is_dir() { continue; } - let sticker_name = sticker_entry.file_name().into_string().unwrap_or_default(); - if sticker_name.ends_with(".png") || sticker_name.ends_with(".webp") { - sticker_paths.push( - sticker_entry - .path() - .to_str() - .map(|s| s.to_owned()) - .context("path conversion to string failed")?, - ); + let pack_name = entry.file_name().into_string().unwrap_or_default(); + let mut stickers = tokio::fs::read_dir(entry.path()).await?; + let mut sticker_paths = Vec::new(); + while let Some(sticker_entry) = stickers.next_entry().await? { + if !sticker_entry.file_type().await?.is_file() { + continue; + } + let sticker_name = sticker_entry.file_name().into_string().unwrap_or_default(); + if sticker_name.ends_with(".png") || sticker_name.ends_with(".webp") { + sticker_paths.push( + sticker_entry + .path() + .to_str() + .map(|s| s.to_owned()) + .context("path conversion to string failed")?, + ); + } + } + if !sticker_paths.is_empty() { + result.insert(pack_name, sticker_paths); } } - if !sticker_paths.is_empty() { - result.insert(pack_name, sticker_paths); - } + Ok(result) } - - Ok(result) + .bind_context_id(account_id) + .await } /// Returns the messageid of the sent message @@ -1841,11 +2024,16 @@ impl CommandApi { ) -> Result { let ctx = self.get_context(account_id).await?; - let mut msg = Message::new(Viewtype::Text); - msg.set_text(Some(text)); + async { + let mut msg = Message::new(Viewtype::Text); + msg.set_text(Some(text)); - let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?; - Ok(message_id.to_u32()) + let message_id = + deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?; + Ok(message_id.to_u32()) + } + .bind_context_id(account_id) + .await } // mimics the old desktop call, will get replaced with something better in the composer rewrite, @@ -1860,37 +2048,41 @@ impl CommandApi { quoted_message_id: Option, ) -> Result<(u32, MessageObject)> { let ctx = self.get_context(account_id).await?; - let mut message = Message::new(if file.is_some() { - Viewtype::File - } else { - Viewtype::Text - }); - if text.is_some() { - message.set_text(text); - } - if let Some(file) = file { - message.set_file(file, None); - } - if let Some((latitude, longitude)) = location { - message.set_location(latitude, longitude); - } - if let Some(id) = quoted_message_id { - message - .set_quote( - &ctx, - Some( - &Message::load_from_db(&ctx, MsgId::new(id)) - .await - .context("message to quote could not be loaded")?, - ), - ) - .await?; + async { + let mut message = Message::new(if file.is_some() { + Viewtype::File + } else { + Viewtype::Text + }); + if text.is_some() { + message.set_text(text); + } + if let Some(file) = file { + message.set_file(file, None); + } + if let Some((latitude, longitude)) = location { + message.set_location(latitude, longitude); + } + if let Some(id) = quoted_message_id { + message + .set_quote( + &ctx, + Some( + &Message::load_from_db(&ctx, MsgId::new(id)) + .await + .context("message to quote could not be loaded")?, + ), + ) + .await?; + } + let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message) + .await? + .to_u32(); + let message = MessageObject::from_message_id(&ctx, msg_id).await?; + Ok((msg_id, message)) } - let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message) - .await? - .to_u32(); - let message = MessageObject::from_message_id(&ctx, msg_id).await?; - Ok((msg_id, message)) + .bind_context_id(account_id) + .await } // mimics the old desktop call, will get replaced with something better in the composer rewrite, @@ -1906,31 +2098,35 @@ impl CommandApi { quoted_message_id: Option, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - let mut draft = Message::new(if file.is_some() { - Viewtype::File - } else { - Viewtype::Text - }); - if text.is_some() { - draft.set_text(text); - } - if let Some(file) = file { - draft.set_file(file, None); - } - if let Some(id) = quoted_message_id { - draft - .set_quote( - &ctx, - Some( - &Message::load_from_db(&ctx, MsgId::new(id)) - .await - .context("message to quote could not be loaded")?, - ), - ) - .await?; - } + async { + let mut draft = Message::new(if file.is_some() { + Viewtype::File + } else { + Viewtype::Text + }); + if text.is_some() { + draft.set_text(text); + } + if let Some(file) = file { + draft.set_file(file, None); + } + if let Some(id) = quoted_message_id { + draft + .set_quote( + &ctx, + Some( + &Message::load_from_db(&ctx, MsgId::new(id)) + .await + .context("message to quote could not be loaded")?, + ), + ) + .await?; + } - ChatId::new(chat_id).set_draft(&ctx, Some(&mut draft)).await + ChatId::new(chat_id).set_draft(&ctx, Some(&mut draft)).await + } + .bind_context_id(account_id) + .await } } diff --git a/deltachat-repl/Cargo.toml b/deltachat-repl/Cargo.toml index d069622494..a5028b91df 100644 --- a/deltachat-repl/Cargo.toml +++ b/deltachat-repl/Cargo.toml @@ -14,6 +14,9 @@ pretty_env_logger = "0.4" rusqlite = "0.28" rustyline = "11" tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", default-features=false, features = ["std", "env-filter", "registry", "fmt"] } +tracing-log = "0.1.3" [features] default = ["vendored"] diff --git a/deltachat-repl/src/cmdline.rs b/deltachat-repl/src/cmdline.rs index 9fdc8e91e9..349414dfcd 100644 --- a/deltachat-repl/src/cmdline.rs +++ b/deltachat-repl/src/cmdline.rs @@ -465,7 +465,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu continue_key_transfer(&context, MsgId::new(arg1.parse()?), arg2).await?; } "has-backup" => { - has_backup(&context, blobdir).await?; + has_backup(blobdir).await?; } "export-backup" => { let dir = dirs::home_dir().unwrap_or_default(); @@ -508,7 +508,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu imex(&context, ImexMode::ImportSelfKeys, arg1.as_ref(), None).await?; } "export-setup" => { - let setup_code = create_setup_code(&context); + let setup_code = create_setup_code(); let file_name = blobdir.join("autocrypt-setup-message.html"); let file_content = render_setup_file(&context, &setup_code).await?; fs::write(&file_name, file_content).await?; @@ -1244,7 +1244,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu let socks5_enabled = context .get_config_bool(config::Config::Socks5Enabled) .await?; - match provider::get_provider_info(&context, arg1, socks5_enabled).await { + match provider::get_provider_info(arg1, socks5_enabled).await { Some(info) => { println!("Information for provider belonging to {arg1}:"); println!("status: {}", info.status as u32); diff --git a/deltachat-repl/src/main.rs b/deltachat-repl/src/main.rs index 2670b311f1..4c5776c7f5 100644 --- a/deltachat-repl/src/main.rs +++ b/deltachat-repl/src/main.rs @@ -22,7 +22,6 @@ use deltachat::qr_code_generator::get_securejoin_qr_svg; use deltachat::securejoin::*; use deltachat::stock_str::StockStrings; use deltachat::{EventType, Events}; -use log::{error, info, warn}; use rustyline::completion::{Completer, FilenameCompleter, Pair}; use rustyline::error::ReadlineError; use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; @@ -33,6 +32,9 @@ use rustyline::{ }; use tokio::fs; use tokio::runtime::Handle; +use tracing::{error, info, warn}; +use tracing_log::LogTracer; +use tracing_subscriber::{fmt, EnvFilter}; mod cmdline; use self::cmdline::*; @@ -481,7 +483,12 @@ async fn handle_cmd( #[tokio::main] async fn main() -> Result<(), Error> { - let _ = pretty_env_logger::try_init(); + // Convert `log` records into `tracing` events. + LogTracer::init()?; + + // Setup `tracing` subscriber according to `RUST_LOG` environment variable. + let filter = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?; + fmt().with_env_filter(filter).with_writer(io::stderr).init(); let args = std::env::args().collect(); start(args).await?; diff --git a/deltachat-rpc-server/Cargo.toml b/deltachat-rpc-server/Cargo.toml index 74d8a17daf..2da04d4c44 100644 --- a/deltachat-rpc-server/Cargo.toml +++ b/deltachat-rpc-server/Cargo.toml @@ -14,12 +14,12 @@ deltachat-jsonrpc = { path = "../deltachat-jsonrpc", default-features = false } deltachat = { path = "..", default-features = false } anyhow = "1" -env_logger = { version = "0.10.0" } futures-lite = "1.12.0" -log = "0.4" serde_json = "1.0.91" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.25.0", features = ["io-std"] } +tracing-subscriber = { version = "0.3", default-features=false, features = ["std", "env-filter", "fmt"] } +tracing = { version = "0.1" } yerpc = { version = "0.4.0", features = ["anyhow_expose"] } [features] diff --git a/deltachat-rpc-server/src/main.rs b/deltachat-rpc-server/src/main.rs index 1e957a38bd..a532cc50f2 100644 --- a/deltachat-rpc-server/src/main.rs +++ b/deltachat-rpc-server/src/main.rs @@ -11,6 +11,8 @@ use deltachat_jsonrpc::api::{Accounts, CommandApi}; use futures_lite::stream::StreamExt; use tokio::io::{self, AsyncBufReadExt, BufReader}; use tokio::task::JoinHandle; +use tracing::{info, trace}; +use tracing_subscriber::{fmt, EnvFilter}; use yerpc::{RpcClient, RpcSession}; #[tokio::main(flavor = "multi_thread")] @@ -32,14 +34,18 @@ async fn main() -> Result<()> { return Err(anyhow!("Unrecognized argument {:?}", arg)); } - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + let filter = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?; + fmt() + .with_env_filter(filter) + .with_writer(std::io::stderr) + .init(); let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "accounts".to_string()); - log::info!("Starting with accounts directory `{}`.", path); + info!("Starting with accounts directory `{}`.", path); let accounts = Accounts::new(PathBuf::from(&path)).await?; let events = accounts.get_event_emitter(); - log::info!("Creating JSON-RPC API."); + info!("Creating JSON-RPC API."); let state = CommandApi::new(accounts); let (client, mut out_receiver) = RpcClient::new(); @@ -58,7 +64,7 @@ async fn main() -> Result<()> { let send_task: JoinHandle> = tokio::spawn(async move { while let Some(message) = out_receiver.next().await { let message = serde_json::to_string(&message)?; - log::trace!("RPC send {}", message); + trace!("RPC send {message}"); println!("{message}"); } Ok(()) @@ -69,13 +75,13 @@ async fn main() -> Result<()> { let stdin = io::stdin(); let mut lines = BufReader::new(stdin).lines(); while let Some(message) = lines.next_line().await? { - log::trace!("RPC recv {}", message); + trace!("RPC recv {}", message); let session = session.clone(); tokio::spawn(async move { session.handle_incoming(&message).await; }); } - log::info!("EOF reached on stdin"); + info!("EOF reached on stdin"); Ok(()) }); diff --git a/examples/simple.rs b/examples/simple.rs index cca50bb279..9bf3efde94 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,3 +1,5 @@ +use tempfile::tempdir; + use deltachat::chat::{self, ChatId}; use deltachat::chatlist::*; use deltachat::config; @@ -6,24 +8,25 @@ use deltachat::context::*; use deltachat::message::Message; use deltachat::stock_str::StockStrings; use deltachat::{EventType, Events}; -use tempfile::tempdir; +use tracing::{error, info, warn}; +use tracing_subscriber::{fmt, EnvFilter}; fn cb(event: EventType) { match event { EventType::ConfigureProgress { progress, .. } => { - log::info!("progress: {}", progress); + info!("progress: {progress}"); } EventType::Info(msg) => { - log::info!("{}", msg); + info!("{msg}"); } EventType::Warning(msg) => { - log::warn!("{}", msg); + warn!("{msg}"); } EventType::Error(msg) => { - log::error!("{}", msg); + error!("{msg}"); } event => { - log::info!("{:?}", event); + info!("{event:?}"); } } } @@ -31,16 +34,22 @@ fn cb(event: EventType) { /// Run with `RUST_LOG=simple=info cargo run --release --example simple -- email pw`. #[tokio::main] async fn main() { - pretty_env_logger::try_init_timed().ok(); + let filter = EnvFilter::try_from_default_env() + .or_else(|_| EnvFilter::try_new("info")) + .unwrap(); + fmt() + .with_env_filter(filter) + .with_writer(std::io::stderr) + .init(); let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); - log::info!("creating database {:?}", dbfile); + info!("creating database {:?}", dbfile); let ctx = Context::new(&dbfile, 0, Events::new(), StockStrings::new()) .await .expect("Failed to create context"); let info = ctx.get_info().await; - log::info!("info: {:#?}", info); + info!("info: {:#?}", info); let events = ctx.get_event_emitter(); let events_spawn = tokio::task::spawn(async move { @@ -49,7 +58,7 @@ async fn main() { } }); - log::info!("configuring"); + info!("configuring"); let args = std::env::args().collect::>(); assert_eq!(args.len(), 3, "requires email password"); let email = args[1].clone(); @@ -63,9 +72,9 @@ async fn main() { ctx.configure().await.unwrap(); - log::info!("------ RUN ------"); + info!("------ RUN ------"); ctx.start_io().await; - log::info!("--- SENDING A MESSAGE ---"); + info!("--- SENDING A MESSAGE ---"); let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com") .await @@ -73,7 +82,7 @@ async fn main() { let chat_id = ChatId::create_for_contact(&ctx, contact_id).await.unwrap(); for i in 0..1 { - log::info!("sending message {}", i); + info!("sending message {}", i); chat::send_text_msg(&ctx, chat_id, format!("Hi, here is my {i}nth message!")) .await .unwrap(); @@ -82,19 +91,19 @@ async fn main() { // wait for the message to be sent out tokio::time::sleep(std::time::Duration::from_secs(1)).await; - log::info!("fetching chats.."); + info!("fetching chats.."); let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap(); for i in 0..chats.len() { let msg = Message::load_from_db(&ctx, chats.get_msg_id(i).unwrap().unwrap()) .await .unwrap(); - log::info!("[{}] msg: {:?}", i, msg); + info!("[{i}] msg: {msg:?}"); } - log::info!("stopping"); + info!("stopping"); ctx.stop_io().await; - log::info!("closing"); + info!("closing"); drop(ctx); events_spawn.await.unwrap(); } diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 9d80e35f74..8f89cad853 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -95,6 +95,7 @@ def __init__(self, db_path, os_name=None, logging=True, closed=False) -> None: ptr, lib.dc_context_unref, ) + self._id = lib.dc_get_id(self._dc_context) self._shutdown_event = Event() self._event_thread = EventThread(self) @@ -139,6 +140,10 @@ def get_info(self) -> Dict[str, str]: """return dictionary of built config parameters.""" return get_dc_info_as_dict(self._dc_context) + def get_id(self) -> int: + """Return account ID.""" + return self._id + def dump_account_info(self, logfile): def log(*args, **kwargs): kwargs["file"] = logfile diff --git a/python/src/deltachat/events.py b/python/src/deltachat/events.py index 0ae86a9adb..0d042da635 100644 --- a/python/src/deltachat/events.py +++ b/python/src/deltachat/events.py @@ -263,6 +263,7 @@ def run(self) -> None: self._process_event(event) def _process_event(self, event) -> None: + account_id = lib.dc_event_get_account_id(event) evt = lib.dc_event_get_id(event) data1 = lib.dc_event_get_data1_int(event) # the following code relates to the deltachat/_build.py's helper @@ -272,6 +273,7 @@ def _process_event(self, event) -> None: data2 = from_optional_dc_charpointer(lib.dc_event_get_data2_str(event)) else: data2 = lib.dc_event_get_data2_int(event) + assert account_id == self.account.get_id(), f"data2={data2}" lib.dc_event_unref(event) ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2) diff --git a/src/accounts.rs b/src/accounts.rs index f81c9a7ca7..976882085d 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -262,7 +262,7 @@ impl Accounts { pub async fn stop_io(&self) { // Sending an event here wakes up event loop even // if there are no accounts. - info!(self, "Stopping IO for all accounts."); + self.emit_event(EventType::Info("Stopping IO for all accounts.".to_string())); for account in self.accounts.values() { account.stop_io().await; } @@ -291,6 +291,11 @@ impl Accounts { pub fn get_event_emitter(&self) -> EventEmitter { self.events.get_emitter() } + + /// Returns event channel. + pub fn events(&self) -> Events { + self.events.clone() + } } /// Configuration file name. diff --git a/src/blob.rs b/src/blob.rs index 93bfbb3c54..640e0b3848 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -14,6 +14,7 @@ use num_traits::FromPrimitive; use tokio::io::AsyncWriteExt; use tokio::{fs, io}; use tokio_stream::wrappers::ReadDirStream; +use tracing::{error, info, warn}; use crate::config::Config; use crate::constants::{ @@ -329,7 +330,7 @@ impl<'a> BlobObject<'a> { // max_bytes is 20_000 bytes: Outlook servers don't allow headers larger than 32k. // 32 / 4 * 3 = 24k if you account for base64 encoding. To be safe, we reduced this to 20k. - if let Some(new_name) = self.recode_to_size(context, blob_abs, img_wh, Some(20_000))? { + if let Some(new_name) = self.recode_to_size(blob_abs, img_wh, Some(20_000))? { self.name = new_name; } Ok(()) @@ -351,10 +352,7 @@ impl<'a> BlobObject<'a> { MediaQuality::Worse => WORSE_IMAGE_SIZE, }; - if self - .recode_to_size(context, blob_abs, img_wh, None)? - .is_some() - { + if self.recode_to_size(blob_abs, img_wh, None)?.is_some() { return Err(format_err!( "Internal error: recode_to_size(..., None) shouldn't change the name of the image" )); @@ -364,21 +362,20 @@ impl<'a> BlobObject<'a> { fn recode_to_size( &self, - context: &Context, mut blob_abs: PathBuf, mut img_wh: u32, max_bytes: Option, ) -> Result> { 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 orientation = self.get_exif_orientation(); let mut encoded = Vec::new(); let mut changed_name = None; 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)?; + exceeds_width || encoded_img_exceeds_bytes(&img, max_bytes, &mut encoded)?; let do_rotate = matches!(orientation, Ok(90) | Ok(180) | Ok(270)); if do_scale || do_rotate { @@ -401,7 +398,7 @@ impl<'a> BlobObject<'a> { loop { let new_img = img.thumbnail(img_wh, img_wh); - if encoded_img_exceeds_bytes(context, &new_img, max_bytes, &mut encoded)? { + if encoded_img_exceeds_bytes(&new_img, max_bytes, &mut encoded)? { if img_wh < 20 { return Err(format_err!( "Failed to scale image to below {}B", @@ -416,7 +413,6 @@ impl<'a> BlobObject<'a> { } info!( - context, "Final scaled-down image size: {}B ({}px).", encoded.len(), img_wh @@ -446,7 +442,7 @@ impl<'a> BlobObject<'a> { }) } - pub fn get_exif_orientation(&self, context: &Context) -> Result { + pub fn get_exif_orientation(&self) -> Result { let file = std::fs::File::open(self.to_abs_path())?; let mut bufreader = std::io::BufReader::new(&file); let exifreader = exif::Reader::new(); @@ -458,7 +454,7 @@ impl<'a> BlobObject<'a> { Some(3) => return Ok(180), Some(6) => return Ok(90), Some(8) => return Ok(270), - other => warn!(context, "Exif orientation value ignored: {other:?}."), + other => warn!("Exif orientation value ignored: {other:?}."), } } Ok(0) @@ -490,7 +486,7 @@ impl<'a> BlobDirContents<'a> { match entry { Ok(entry) => Some(entry), Err(err) => { - error!(context, "Failed to read blob file: {err}."); + error!("Failed to read blob file: {err:#}."); None } } @@ -500,7 +496,6 @@ impl<'a> BlobDirContents<'a> { true => Some(entry.path()), false => { warn!( - context, "Export: Found blob dir entry {} that is not a file, ignoring.", entry.path().display() ); @@ -543,7 +538,7 @@ impl<'a> Iterator for BlobDirIter<'a> { // silently skipping them is fine. match BlobObject::from_path(self.context, path) { Ok(blob) => return Some(blob), - Err(err) => warn!(self.context, "{err}"), + Err(err) => warn!("{err:#}"), } } None @@ -559,7 +554,6 @@ fn encode_img(img: &DynamicImage, encoded: &mut Vec) -> anyhow::Result<()> { Ok(()) } fn encoded_img_exceeds_bytes( - context: &Context, img: &DynamicImage, max_bytes: Option, encoded: &mut Vec, @@ -568,7 +562,6 @@ fn encoded_img_exceeds_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(), @@ -823,7 +816,7 @@ mod tests { let blob = BlobObject::new_from_path(&t, &avatar_blob).await.unwrap(); - blob.recode_to_size(&t, blob.to_abs_path(), 1000, Some(3000)) + blob.recode_to_size(blob.to_abs_path(), 1000, Some(3000)) .unwrap(); assert!(file_size(&avatar_blob).await <= 3000); assert!(file_size(&avatar_blob).await > 2000); @@ -1007,7 +1000,7 @@ mod tests { 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); + assert_eq!(blob.get_exif_orientation().unwrap_or(0), orientation); let mut msg = Message::new(Viewtype::Image); msg.set_file(file.to_str().unwrap(), None); @@ -1028,7 +1021,7 @@ mod tests { let file = bob_msg.get_file(&bob).unwrap(); let blob = BlobObject::new_from_path(&bob, &file).await?; - assert_eq!(blob.get_exif_orientation(&bob).unwrap_or(0), 0); + assert_eq!(blob.get_exif_orientation().unwrap_or(0), 0); let img = check_image_size(file, compressed_width, compressed_height); Ok(img) diff --git a/src/chat.rs b/src/chat.rs index eaffb476ff..b4e4ddc31b 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -10,6 +10,7 @@ use std::time::{Duration, SystemTime}; use anyhow::{bail, ensure, Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use serde::{Deserialize, Serialize}; +use tracing::{error, info, warn}; use crate::aheader::EncryptPreference; use crate::blob::BlobObject; @@ -245,10 +246,7 @@ impl ChatId { Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat).await?; chat_id } else { - warn!( - context, - "Cannot create chat, contact {contact_id} does not exist." - ); + warn!("Cannot create chat, contact {contact_id} does not exist."); bail!("Can not create chat for non-existing contact"); } } @@ -284,12 +282,7 @@ impl ChatId { let chat_id = ChatId::new(u32::try_from(row_id)?); info!( - context, - "Created group/mailinglist '{}' grpid={} as {}, blocked={}.", - grpname, - grpid, - chat_id, - create_blocked, + "Created group/mailinglist '{grpname}' grpid={grpid} as {chat_id}, blocked={create_blocked}." ); Ok(chat_id) @@ -336,16 +329,13 @@ impl ChatId { Chattype::Single => { for contact_id in get_chat_contacts(context, self).await? { if contact_id != ContactId::SELF { - info!( - context, - "Blocking the contact {contact_id} to block 1:1 chat." - ); + info!("Blocking the contact {contact_id} to block 1:1 chat."); Contact::block(context, contact_id).await?; } } } Chattype::Group => { - info!(context, "Can't block groups yet, deleting the chat."); + info!("Can't block groups yet, deleting the chat."); self.delete(context).await?; } Chattype::Mailinglist => { @@ -410,7 +400,7 @@ impl ChatId { let chat = Chat::load_from_db(context, self).await?; if protect == chat.protected { - info!(context, "Protection status unchanged for {}.", self); + info!("Protection status unchanged for {self}."); return Ok(()); } @@ -500,7 +490,7 @@ impl ChatId { let chat = Chat::load_from_db(context, self).await?; if let Err(e) = self.inner_set_protection(context, protect).await { - error!(context, "Cannot set protection: {e:#}."); // make error user-visible + error!("Cannot set protection: {e:#}."); // make error user-visible return Err(e); } @@ -1068,10 +1058,7 @@ impl ChatId { !self.is_special(), "can not set gossiped timestamp for special chats" ); - info!( - context, - "Set gossiped_timestamp for chat {} to {}.", self, timestamp, - ); + info!("Set gossiped_timestamp for chat {self} to {timestamp}."); context .sql @@ -1209,10 +1196,7 @@ impl Chat { } } Err(err) => { - error!( - context, - "Failed to load contacts for {}: {:#}.", chat.id, err - ); + error!("Failed to load contacts for {}: {:#}.", chat.id, err); } } chat.name = chat_name; @@ -1475,10 +1459,7 @@ impl Chat { { to_id = id; } else { - error!( - context, - "Cannot send message, contact for {} not found.", self.id, - ); + error!("Cannot send message, contact for {} not found.", self.id,); bail!("Cannot set message, contact for {} not found.", self.id); } } else if self.typ == Chattype::Group @@ -2033,10 +2014,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { if msg.viewtype == Viewtype::Image { if let Err(err) = blob.recode_to_image_size(context).await { - warn!( - context, - "Cannot recode image, using original data: {err:#}." - ); + warn!("Cannot recode image, using original data: {err:#}."); } } msg.param.set(Param::File, blob.as_name()); @@ -2076,7 +2054,6 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { } info!( - context, "Attaching \"{}\" for message type #{}.", blob.to_abs_path().display(), msg.viewtype @@ -2259,7 +2236,7 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result attach_selfavatar, Err(err) => { - warn!(context, "SMTP job cannot get selfavatar-state: {err:#}."); + warn!("SMTP job cannot get selfavatar-state: {err:#}."); false } }; @@ -2284,10 +2261,7 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result Result Result<()> { - info!(self, "Configure ..."); + info!("Configure ..."); let mut param = LoginParam::load_candidate_params(self).await?; let old_addr = self.get_config(Config::ConfiguredAddr).await?; @@ -125,13 +126,10 @@ async fn on_configure_completed( if let Some(config_defaults) = &provider.config_defaults { for def in config_defaults.iter() { if !context.config_exists(def.key).await? { - info!(context, "apply config_defaults {}={}", def.key, def.value); + info!("apply config_defaults {}={}", def.key, def.value); context.set_config(def.key, Some(def.value)).await?; } else { - info!( - context, - "skip already set config_defaults {}={}", def.key, def.value - ); + info!("skip already set config_defaults {}={}", def.key, def.value); } } } @@ -143,7 +141,7 @@ async fn on_configure_completed( .await .is_err() { - warn!(context, "cannot add after_login_hint as core-provider-info"); + warn!("cannot add after_login_hint as core-provider-info"); } } } @@ -186,7 +184,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { .await? .and_then(|e| e.parse().ok()) { - info!(ctx, "Authorized address is {}", oauth2_addr); + info!("Authorized address is {oauth2_addr}"); param.addr = oauth2_addr; ctx.sql .set_raw_config("addr", Some(param.addr.as_str())) @@ -215,22 +213,17 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { { // no advanced parameters entered by the user: query provider-database or do Autoconfig - info!( - ctx, - "checking internal provider-info for offline autoconfig" - ); + info!("checking internal provider-info for offline autoconfig"); - if let Some(provider) = - provider::get_provider_info(ctx, ¶m_domain, socks5_enabled).await - { + if let Some(provider) = provider::get_provider_info(¶m_domain, socks5_enabled).await { param.provider = Some(provider); match provider.status { provider::Status::Ok | provider::Status::Preparation => { if provider.server.is_empty() { - info!(ctx, "offline autoconfig found, but no servers defined"); + info!("offline autoconfig found, but no servers defined"); param_autoconfig = None; } else { - info!(ctx, "offline autoconfig found"); + info!("offline autoconfig found"); let servers = provider .server .iter() @@ -257,17 +250,17 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { } } provider::Status::Broken => { - info!(ctx, "offline autoconfig found, provider is broken"); + info!("offline autoconfig found, provider is broken"); param_autoconfig = None; } } } else { // Try receiving autoconfig - info!(ctx, "no offline autoconfig found"); + info!("no offline autoconfig found"); param_autoconfig = if socks5_enabled { // Currently we can't do http requests through socks5, to not leak // the ip, just don't do online autoconfig - info!(ctx, "socks5 enabled, skipping autoconfig"); + info!("socks5 enabled, skipping autoconfig"); None } else { get_autoconfig(ctx, param, ¶m_domain, ¶m_addr_urlencoded).await @@ -465,7 +458,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { progress!(ctx, 920); e2ee::ensure_secret_key_exists(ctx).await?; - info!(ctx, "key generation completed"); + info!("key generation completed"); ctx.set_config_bool(Config::FetchedExistingMsgs, false) .await?; @@ -578,13 +571,13 @@ async fn try_imap_one_param( "None".to_string() } ); - info!(context, "Trying: {}", inf); + info!("Trying: {}", inf); let (_s, r) = async_channel::bounded(1); let mut imap = match Imap::new(param, socks5_config.clone(), addr, provider_strict_tls, r) { Err(err) => { - info!(context, "failure: {:#}", err); + info!("failure: {:#}", err); return Err(ConfigurationError { config: inf, msg: format!("{err:#}"), @@ -595,14 +588,14 @@ async fn try_imap_one_param( match imap.connect(context).await { Err(err) => { - info!(context, "failure: {:#}", err); + info!("failure: {:#}", err); Err(ConfigurationError { config: inf, msg: format!("{err:#}"), }) } Ok(()) => { - info!(context, "success: {}", inf); + info!("success: {}", inf); Ok(imap) } } @@ -630,19 +623,19 @@ async fn try_smtp_one_param( "None".to_string() } ); - info!(context, "Trying: {}", inf); + info!("Trying: {inf}"); if let Err(err) = smtp .connect(context, param, socks5_config, addr, provider_strict_tls) .await { - info!(context, "failure: {}", err); + info!("failure: {err:#}"); Err(ConfigurationError { config: inf, msg: format!("{err:#}"), }) } else { - info!(context, "success: {}", inf); + info!("success: {inf}"); smtp.disconnect().await; Ok(()) } diff --git a/src/configure/auto_mozilla.rs b/src/configure/auto_mozilla.rs index d53b5a8c5c..755d052b91 100644 --- a/src/configure/auto_mozilla.rs +++ b/src/configure/auto_mozilla.rs @@ -1,10 +1,10 @@ //! # Thunderbird's Autoconfiguration implementation //! //! Documentation: +use quick_xml::events::{BytesStart, Event}; use std::io::BufRead; use std::str::FromStr; - -use quick_xml::events::{BytesStart, Event}; +use tracing::warn; use super::read_url::read_url; use super::{Error, ServerParams}; @@ -263,10 +263,7 @@ pub(crate) async fn moz_autoconfigure( let res = parse_serverparams(¶m_in.addr, &xml_raw); if let Err(err) = &res { - warn!( - context, - "Failed to parse Thunderbird autoconfiguration XML: {}", err - ); + warn!("Failed to parse Thunderbird autoconfiguration XML: {err:#}"); } res } diff --git a/src/configure/auto_outlook.rs b/src/configure/auto_outlook.rs index 8d42fd353b..17713cc5f1 100644 --- a/src/configure/auto_outlook.rs +++ b/src/configure/auto_outlook.rs @@ -6,6 +6,7 @@ use std::io::BufRead; use quick_xml::events::Event; +use tracing::warn; use super::read_url::read_url; use super::{Error, ServerParams}; @@ -202,7 +203,7 @@ pub(crate) async fn outlk_autodiscover( let xml_raw = read_url(context, &url).await?; let res = parse_xml(&xml_raw); if let Err(err) = &res { - warn!(context, "{}", err); + warn!("{err:#}"); } match res? { ParsingResult::RedirectUrl(redirect_url) => url = redirect_url, diff --git a/src/configure/read_url.rs b/src/configure/read_url.rs index d164a70073..19c6478432 100644 --- a/src/configure/read_url.rs +++ b/src/configure/read_url.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, format_err}; +use tracing::info; use crate::context::Context; use crate::socks::Socks5Config; @@ -6,12 +7,12 @@ use crate::socks::Socks5Config; pub async fn read_url(context: &Context, url: &str) -> anyhow::Result { match read_url_inner(context, url).await { Ok(s) => { - info!(context, "Successfully read url {}", url); + info!("Successfully read url {url}"); Ok(s) } Err(e) => { - info!(context, "Can't read URL {}: {:#}", url, e); - Err(format_err!("Can't read URL {}: {:#}", url, e)) + info!("Can't read URL {url}: {e:#}"); + Err(format_err!("Can't read URL {url}: {e:#}")) } } } @@ -32,7 +33,7 @@ pub async fn read_url_inner(context: &Context, url: &str) -> anyhow::Result 50 && addr.contains('+')) { - info!(context, "hiding contact {}", addr); + info!("hiding contact {addr}"); origin = Origin::Hidden; // For these kind of email addresses, sender and address often don't belong together // (like hocuri ). In this example, hocuri shouldn't @@ -699,7 +703,7 @@ impl Contact { sth_modified = Modifier::Created; row_id = u32::try_from(transaction.last_insert_rowid())?; - info!(context, "added contact id={} addr={}", row_id, &addr); + info!("added contact id={} addr={}", row_id, &addr); } Ok(row_id) }).await?; @@ -739,15 +743,12 @@ impl Contact { } } Err(err) => { - warn!( - context, - "Failed to add address {} from address book: {}", addr, err - ); + warn!("Failed to add address {addr} from address book: {err:#}"); } } } Err(err) => { - warn!(context, "{:#}.", err); + warn!("{err:#}."); } } } @@ -1055,7 +1056,7 @@ impl Contact { } /// Updates `param` column in the database. - pub async fn update_param(&self, context: &Context) -> Result<()> { + pub(crate) async fn update_param(&self, context: &Context) -> Result<()> { context .sql .execute( @@ -1067,7 +1068,7 @@ impl Contact { } /// Updates `status` column in the database. - pub async fn update_status(&self, context: &Context) -> Result<()> { + pub(crate) async fn update_status(&self, context: &Context) -> Result<()> { context .sql .execute( @@ -1388,7 +1389,7 @@ pub(crate) async fn set_profile_image( .set_config(Config::Selfavatar, Some(profile_image)) .await?; } else { - info!(context, "Do not use unencrypted selfavatar."); + info!("Do not use unencrypted selfavatar."); } } else { contact.param.set(Param::ProfileImage, profile_image); @@ -1400,7 +1401,7 @@ pub(crate) async fn set_profile_image( if was_encrypted { context.set_config(Config::Selfavatar, None).await?; } else { - info!(context, "Do not use unencrypted selfavatar deletion."); + info!("Do not use unencrypted selfavatar deletion."); } } else { contact.param.remove(Param::ProfileImage); @@ -1555,7 +1556,12 @@ impl RecentlySeenLoop { pub(crate) fn new(context: Context) -> Self { let (interrupt_send, interrupt_recv) = channel::bounded(1); - let handle = task::spawn(Self::run(context, interrupt_recv)); + let handle = task::spawn( + Self::run(context, interrupt_recv) + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ); Self { handle, interrupt_send, @@ -1607,7 +1613,6 @@ impl RecentlySeenLoop { if let Ok(duration) = until.duration_since(now) { info!( - context, "Recently seen loop waiting for {} or interrupt", duration_to_str(duration) ); @@ -1621,10 +1626,7 @@ impl RecentlySeenLoop { } } Ok(Err(err)) => { - warn!( - context, - "Error receiving an interruption in recently seen loop: {}", err - ); + warn!("Error receiving an interruption in recently seen loop: {err:#}."); // Maybe the sender side is closed. // Terminate the loop to avoid looping indefinitely. return; @@ -1638,10 +1640,7 @@ impl RecentlySeenLoop { } } } else { - info!( - context, - "Recently seen loop is not waiting, event is already due." - ); + info!("Recently seen loop is not waiting, event is already due."); // Event is already in the past. if let Some(contact_id) = contact_id { diff --git a/src/context.rs b/src/context.rs index a9bbd01ad1..fbb0f47d5a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -13,13 +13,14 @@ use async_channel::{self as channel, Receiver, Sender}; use ratelimit::Ratelimit; use tokio::sync::{Mutex, RwLock}; use tokio::task; +use tracing::{info, warn}; use crate::chat::{get_chat_cnt, ChatId}; use crate::config::Config; use crate::constants::DC_VERSION_STR; use crate::contact::Contact; use crate::debug_logging::DebugEventLogData; -use crate::events::{Event, EventEmitter, EventType, Events}; +use crate::events::{Event, EventEmitter, EventLayer, EventType, Events}; use crate::key::{DcKey, SignedPublicKey}; use crate::login_param::LoginParam; use crate::message::{self, MessageState, MsgId}; @@ -30,6 +31,8 @@ use crate::stock_str::StockStrings; use crate::timesmearing::SmearedTimestamp; use crate::tools::{duration_to_str, time}; +pub mod future; + /// Builder for the [`Context`]. /// /// Many arguments to the [`Context`] are kind of optional and only needed to handle @@ -233,12 +236,6 @@ pub struct InnerContext { creation_time: SystemTime, - /// 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: std::sync::RwLock, - - /// If debug logging is enabled, this contains all necessary information pub(crate) debug_logging: RwLock>, } @@ -296,7 +293,7 @@ impl Context { events: Events, stock_strings: StockStrings, ) -> Result { - let context = Self::new_closed(dbfile, id, events, stock_strings).await?; + let context = Self::new_closed(dbfile, id, events.clone(), stock_strings).await?; // Open the database if is not encrypted. if context.check_passphrase("".to_string()).await? { @@ -305,6 +302,11 @@ impl Context { Ok(context) } + /// Returns `tracing` subscriber layer. + pub fn to_layer(&self) -> EventLayer { + self.events.to_layer() + } + /// Creates new context without opening the database. pub async fn new_closed( dbfile: &Path, @@ -382,7 +384,6 @@ impl Context { server_id: RwLock::new(None), creation_time: std::time::SystemTime::now(), last_full_folder_scan: Mutex::new(None), - last_error: std::sync::RwLock::new("".to_string()), debug_logging: RwLock::new(None), }; @@ -396,7 +397,7 @@ impl Context { /// Starts the IO scheduler. pub async fn start_io(&self) { if let Ok(false) = self.is_configured().await { - warn!(self, "can not start io on a context that is not configured"); + warn!("can not start io on a context that is not configured"); return; } self.scheduler.start(self.clone()).await; @@ -539,13 +540,13 @@ impl Context { match &*s { RunningState::Running { cancel_sender } => { if let Err(err) = cancel_sender.send(()).await { - warn!(self, "could not cancel ongoing: {:#}", err); + warn!("could not cancel ongoing: {:#}", err); } - info!(self, "Signaling the ongoing process to stop ASAP.",); + info!("Signaling the ongoing process to stop ASAP.",); *s = RunningState::ShallStop; } RunningState::ShallStop | RunningState::Stopped => { - info!(self, "No ongoing process to stop.",); + info!("No ongoing process to stop.",); } } } diff --git a/src/context/future.rs b/src/context/future.rs new file mode 100644 index 0000000000..47764c5086 --- /dev/null +++ b/src/context/future.rs @@ -0,0 +1,83 @@ +//! Futures extensions to track current context ID. + +use pin_project_lite::pin_project; +use std::cell::RefCell; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::thread_local; + +thread_local! { + static THREAD_CONTEXT_ID: RefCell = RefCell::new(0); +} + +pub(crate) struct ContextIdGuard { + previous: u32, +} + +pub(crate) fn current_context_id() -> u32 { + THREAD_CONTEXT_ID.with(|context_id| *context_id.borrow()) +} + +impl ContextIdGuard { + fn new(context_id: u32) -> Self { + let previous = THREAD_CONTEXT_ID.with(|prev_context_id| { + let ret = *prev_context_id.borrow(); + *prev_context_id.borrow_mut() = context_id; + ret + }); + Self { previous } + } +} + +impl Drop for ContextIdGuard { + fn drop(&mut self) { + THREAD_CONTEXT_ID.with(|context_id| { + *context_id.borrow_mut() = self.previous; + }) + } +} + +pin_project! { + /// A future with attached context ID. + #[derive(Debug, Clone)] + pub struct ContextIdFuture { + context_id: u32, + #[pin] + future: F, + } +} + +impl ContextIdFuture { + /// Wraps a future. + pub fn new(context_id: u32, future: F) -> Self { + Self { context_id, future } + } +} + +impl Future for ContextIdFuture { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let context_id = self.context_id; + + let this = self.project(); + let _guard = ContextIdGuard::new(context_id); + this.future.poll(cx) + } +} + +/// Future extension to bind context ID. +pub trait ContextIdFutureExt: Sized { + /// Binds context ID to the future. + fn bind_context_id(self, context_id: u32) -> ContextIdFuture { + ContextIdFuture::new(context_id, self) + } + + /// Binds current context ID to the future. + fn bind_current_context_id(self) -> ContextIdFuture { + self.bind_context_id(current_context_id()) + } +} + +impl ContextIdFutureExt for F where F: Future {} diff --git a/src/debug_logging.rs b/src/debug_logging.rs index d7c93f0e4c..fe614b4ecb 100644 --- a/src/debug_logging.rs +++ b/src/debug_logging.rs @@ -12,6 +12,7 @@ use async_channel::{self as channel, Receiver}; use serde_json::json; use std::path::PathBuf; use tokio::task; +use tracing::info; /// Store all information needed to log an event to a webxdc. pub struct DebugEventLogData { @@ -132,7 +133,7 @@ pub(crate) async fn set_debug_logging_xdc(ctx: &Context, id: Option) -> a }); } } - info!(ctx, "replacing logging webxdc"); + info!("replacing logging webxdc"); } // Delete current debug logging None => { @@ -140,7 +141,7 @@ pub(crate) async fn set_debug_logging_xdc(ctx: &Context, id: Option) -> a .set_raw_config(Config::DebugLogging.as_ref(), None) .await?; *ctx.debug_logging.write().await = None; - info!(ctx, "removing logging webxdc"); + info!("removing logging webxdc"); } } Ok(()) diff --git a/src/decrypt.rs b/src/decrypt.rs index 1165e3dd7e..122000e258 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use anyhow::Result; use mailparse::ParsedMail; +use tracing::{info, warn}; use crate::aheader::Aheader; use crate::authres::handle_authres; @@ -24,7 +25,6 @@ use crate::pgp; /// /// If the message is wrongly signed, HashSet will be empty. pub fn try_decrypt( - context: &Context, mail: &ParsedMail<'_>, private_keyring: &Keyring, public_keyring_for_validate: &Keyring, @@ -36,7 +36,7 @@ pub fn try_decrypt( None => return Ok(None), Some(res) => res, }; - info!(context, "Detected Autocrypt-mime message"); + info!("Detected Autocrypt-mime message"); decrypt_part( encrypted_data_part, @@ -54,7 +54,6 @@ pub(crate) async fn prepare_decryption( if mail.headers.get_header(HeaderDef::ListPost).is_some() { if mail.headers.get_header(HeaderDef::Autocrypt).is_some() { info!( - context, "Ignoring autocrypt header since this is a mailing list message. \ NOTE: For privacy reasons, the mailing list software should remove Autocrypt headers." ); @@ -78,13 +77,13 @@ pub(crate) async fn prepare_decryption( Ok(header) if addr_cmp(&header.addr, from) => Some(header), Ok(header) => { warn!( - context, - "Autocrypt header address {:?} is not {:?}.", header.addr, from + "Autocrypt header address {:?} is not {:?}.", + header.addr, from ); None } Err(err) => { - warn!(context, "Failed to parse Autocrypt header: {:#}.", err); + warn!("Failed to parse Autocrypt header: {:#}.", err); None } } @@ -319,8 +318,8 @@ pub(crate) async fn get_autocrypt_peerstate( peerstate.save_to_db(&context.sql).await?; } else { info!( - context, - "Refusing to update existing peerstate of {}", &peerstate.addr + "Refusing to update existing peerstate of {}", + &peerstate.addr ); } } diff --git a/src/download.rs b/src/download.rs index 89c99f0111..fed2c99ea3 100644 --- a/src/download.rs +++ b/src/download.rs @@ -6,6 +6,7 @@ use std::collections::BTreeMap; use anyhow::{anyhow, Result}; use deltachat_derive::{FromSql, ToSql}; use serde::{Deserialize, Serialize}; +use tracing::{info, warn}; use crate::config::Config; use crate::context::Context; @@ -124,7 +125,7 @@ impl Job { /// Called in response to `Action::DownloadMsg`. pub(crate) async fn download_msg(&self, context: &Context, imap: &mut Imap) -> Status { if let Err(err) = imap.prepare(context).await { - warn!(context, "download: could not connect: {:#}", err); + warn!("download: could not connect: {:#}", err); return Status::RetryNow; } @@ -195,7 +196,7 @@ impl Imap { } // we are connected, and the folder is selected - info!(context, "Downloading message {}/{} fully...", folder, uid); + info!("Downloading message {}/{} fully...", folder, uid); let mut uid_message_ids: BTreeMap = BTreeMap::new(); uid_message_ids.insert(uid, rfc724_mid); @@ -240,7 +241,7 @@ impl MimeMessage { text += format!(" [{until}]").as_str(); }; - info!(context, "Partial download: {}", text); + info!("Partial download: {}", text); self.parts.push(Part { typ: Viewtype::Text, diff --git a/src/e2ee.rs b/src/e2ee.rs index b870f89fef..50ce12d619 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -2,6 +2,7 @@ use anyhow::{format_err, Context as _, Result}; use num_traits::FromPrimitive; +use tracing::info; use crate::aheader::{Aheader, EncryptPreference}; use crate::config::Config; @@ -51,7 +52,6 @@ impl EncryptHelper { /// Returns an error if `e2ee_guaranteed` is true, but one or more keys are missing. pub fn should_encrypt( &self, - context: &Context, e2ee_guaranteed: bool, peerstates: &[(Option, &str)], ) -> Result { @@ -63,10 +63,7 @@ impl EncryptHelper { for (peerstate, addr) in peerstates { match peerstate { Some(peerstate) => { - info!( - context, - "peerstate for {:?} is {}", addr, peerstate.prefer_encrypt - ); + info!("peerstate for {:?} is {}", addr, peerstate.prefer_encrypt); match peerstate.prefer_encrypt { EncryptPreference::NoPreference => {} EncryptPreference::Mutual => prefer_encrypt_count += 1, @@ -82,7 +79,7 @@ impl EncryptHelper { if e2ee_guaranteed { return Err(format_err!("{}", msg)); } else { - info!(context, "{}", msg); + info!("{}", msg); return Ok(false); } } @@ -322,22 +319,22 @@ Sent with my Delta Chat Messenger: https://delta.chat"; // test with EncryptPreference::NoPreference: // if e2ee_eguaranteed is unset, there is no encryption as not more than half of peers want encryption let ps = new_peerstates(EncryptPreference::NoPreference); - assert!(encrypt_helper.should_encrypt(&t, true, &ps).unwrap()); - assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); + assert!(encrypt_helper.should_encrypt(true, &ps).unwrap()); + assert!(!encrypt_helper.should_encrypt(false, &ps).unwrap()); // test with EncryptPreference::Reset let ps = new_peerstates(EncryptPreference::Reset); - assert!(encrypt_helper.should_encrypt(&t, true, &ps).unwrap()); - assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); + assert!(encrypt_helper.should_encrypt(true, &ps).unwrap()); + assert!(!encrypt_helper.should_encrypt(false, &ps).unwrap()); // test with EncryptPreference::Mutual (self is also Mutual) let ps = new_peerstates(EncryptPreference::Mutual); - assert!(encrypt_helper.should_encrypt(&t, true, &ps).unwrap()); - assert!(encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); + assert!(encrypt_helper.should_encrypt(true, &ps).unwrap()); + assert!(encrypt_helper.should_encrypt(false, &ps).unwrap()); // test with missing peerstate let ps = vec![(None, "bob@foo.bar")]; - assert!(encrypt_helper.should_encrypt(&t, true, &ps).is_err()); - assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); + assert!(encrypt_helper.should_encrypt(true, &ps).is_err()); + assert!(!encrypt_helper.should_encrypt(false, &ps).unwrap()); } } diff --git a/src/ephemeral.rs b/src/ephemeral.rs index 75fb1c1272..455bfa687c 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -72,6 +72,7 @@ use anyhow::{ensure, Result}; use async_channel::Receiver; use serde::{Deserialize, Serialize}; use tokio::time::timeout; +use tracing::{error, info, warn}; use crate::chat::{send_msg, ChatId}; use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH}; @@ -225,8 +226,8 @@ impl ChatId { msg.param.set_cmd(SystemMessage::EphemeralTimerChanged); if let Err(err) = send_msg(context, self, &mut msg).await { error!( - context, - "Failed to send a message about ephemeral message timer change: {:?}", err + "Failed to send a message about ephemeral message timer change: {:?}", + err ); } } @@ -433,7 +434,7 @@ pub(crate) async fn delete_expired_messages(context: &Context, now: i64) -> Resu let rows = select_expired_messages(context, now).await?; if !rows.is_empty() { - info!(context, "Attempting to delete {} messages.", rows.len()); + info!("Attempting to delete {} messages.", rows.len()); let (msgs_changed, webxdc_deleted) = context .sql @@ -523,7 +524,7 @@ async fn next_expiration_timestamp(context: &Context) -> Option { .await { Err(err) => { - warn!(context, "Can't calculate next ephemeral timeout: {}", err); + warn!("Can't calculate next ephemeral timeout: {}", err); None } Ok(ephemeral_timestamp) => ephemeral_timestamp, @@ -533,8 +534,8 @@ async fn next_expiration_timestamp(context: &Context) -> Option { match next_delete_device_after_timestamp(context).await { Err(err) => { warn!( - context, - "Can't calculate timestamp of the next message expiration: {}", err + "Can't calculate timestamp of the next message expiration: {}", + err ); None } @@ -563,7 +564,6 @@ pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiv if let Ok(duration) = until.duration_since(now) { info!( - context, "Ephemeral loop waiting for deletion in {} or interrupt", duration_to_str(duration) ); diff --git a/src/events.rs b/src/events.rs index ebb1d299f7..dab5135178 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,10 +1,17 @@ +#![allow(missing_docs)] //! # Events specification. +use std::fmt::Write; use std::path::PathBuf; +use std::sync::{Arc, RwLock}; use async_channel::{self as channel, Receiver, Sender, TrySendError}; use serde::Serialize; +use tracing::Level; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + use crate::chat::ChatId; use crate::contact::ContactId; use crate::ephemeral::Timer as EphemeralTimer; @@ -16,6 +23,11 @@ use crate::webxdc::StatusUpdateSerial; pub struct Events { receiver: Receiver, sender: Sender, + + /// 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: Arc>, } impl Default for Events { @@ -29,11 +41,26 @@ impl Events { pub fn new() -> Self { let (sender, receiver) = channel::bounded(1_000); - Self { receiver, sender } + Self { + receiver, + sender, + last_error: Arc::new(RwLock::new("".to_string())), + } + } + + /// 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) { + let error = error.to_string(); + let mut last_error = self.last_error.write().unwrap(); + *last_error = error; } - /// Emits an event. + /// Emits an event into event channel. pub fn emit(&self, event: Event) { + if let EventType::Error(formatted) = &event.typ { + self.set_last_error(formatted); + } match self.sender.try_send(event) { Ok(()) => {} Err(TrySendError::Full(event)) => { @@ -53,6 +80,89 @@ impl Events { pub fn get_emitter(&self) -> EventEmitter { EventEmitter(self.receiver.clone()) } + + /// Returns `tracing` subscriber layer. + pub fn to_layer(&self) -> EventLayer { + EventLayer::new(self.sender.clone(), self.last_error.clone()) + } +} + +#[derive(Debug)] +struct MessageStorage<'a>(&'a mut String); + +impl tracing::field::Visit for MessageStorage<'_> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + if field.name() == "message" { + write!(self.0, "{value:?}").ok(); + } + } +} + +/// Tracing subscriber layer converting logs into Delta Chat events. +#[derive(Debug)] +pub struct EventLayer { + /// Event channel for event submission. + sender: Sender, + + last_error: Arc>, +} + +impl EventLayer { + pub(crate) fn new(sender: Sender, last_error: Arc>) -> Self { + Self { sender, last_error } + } + + fn set_last_error(&self, error: &str) { + let error = error.to_string(); + let mut last_error = self.last_error.write().unwrap(); + *last_error = error; + } +} + +impl Layer for EventLayer +where + S: tracing::Subscriber + for<'a> LookupSpan<'a>, +{ + fn on_event( + &self, + event: &tracing::Event<'_>, + _ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + let context_id = crate::context::future::current_context_id(); + let &level = event.metadata().level(); + let mut message = "".to_string(); + let mut visitor = MessageStorage(&mut message); + event.record(&mut visitor); + + match level { + Level::ERROR => { + self.set_last_error(&message); + self.sender + .try_send(Event { + id: context_id, + typ: EventType::Error(message), + }) + .ok(); + } + Level::WARN => { + self.sender + .try_send(Event { + id: context_id, + typ: EventType::Warning(message), + }) + .ok(); + } + Level::INFO => { + self.sender + .try_send(Event { + id: context_id, + typ: EventType::Info(message), + }) + .ok(); + } + Level::TRACE | Level::DEBUG => {} + } + } } /// A receiver of events from a [`Context`]. diff --git a/src/html.rs b/src/html.rs index a76214bc68..27776fa168 100644 --- a/src/html.rs +++ b/src/html.rs @@ -16,6 +16,7 @@ use futures::future::FutureExt; use lettre_email::mime::{self, Mime}; use lettre_email::PartBuilder; use mailparse::ParsedContentType; +use tracing::warn; use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::message::{Message, MsgId}; @@ -87,7 +88,7 @@ impl HtmlMsgParser { /// Function takes a raw mime-message string, /// searches for the main-text part /// and returns that as parser.html - pub async fn from_bytes(context: &Context, rawmime: &[u8]) -> Result { + pub async fn from_bytes(rawmime: &[u8]) -> Result { let mut parser = HtmlMsgParser { html: "".to_string(), plain: None, @@ -102,7 +103,7 @@ impl HtmlMsgParser { parser.html = plain.to_html(); } } else { - parser.cid_to_data_recursive(context, &parsedmail).await?; + parser.cid_to_data_recursive(&parsedmail).await?; } Ok(parser) @@ -173,7 +174,6 @@ impl HtmlMsgParser { /// This allows the final html-file to be self-contained. fn cid_to_data_recursive<'a>( &'a mut self, - context: &'a Context, mail: &'a mailparse::ParsedMail<'a>, ) -> Pin> + 'a + Send>> { // Boxed future to deal with recursion @@ -181,7 +181,7 @@ impl HtmlMsgParser { match get_mime_multipart_type(&mail.ctype) { MimeMultipartType::Multiple => { for cur_data in &mail.subparts { - self.cid_to_data_recursive(context, cur_data).await?; + self.cid_to_data_recursive(cur_data).await?; } Ok(()) } @@ -191,7 +191,7 @@ impl HtmlMsgParser { return Ok(()); } let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; - self.cid_to_data_recursive(context, &mail).await + self.cid_to_data_recursive(&mail).await } MimeMultipartType::Single => { let mimetype = mail.ctype.mimetype.parse::()?; @@ -214,10 +214,8 @@ impl HtmlMsgParser { .to_string() } Err(e) => warn!( - context, "Cannot create regex for cid: {} throws {}", - re_string, - e + re_string, e ), } } @@ -249,15 +247,15 @@ impl MsgId { let rawmime = message::get_mime_headers(context, self).await?; if !rawmime.is_empty() { - match HtmlMsgParser::from_bytes(context, &rawmime).await { + match HtmlMsgParser::from_bytes(&rawmime).await { Err(err) => { - warn!(context, "get_html: parser error: {:#}", err); + warn!("get_html: parser error: {:#}", err); Ok(None) } Ok(parser) => Ok(Some(parser.html)), } } else { - warn!(context, "get_html: no mime for {}", self); + warn!("get_html: no mime for {}", self); Ok(None) } } @@ -286,9 +284,8 @@ mod tests { #[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert_eq!( parser.html, r##" @@ -305,9 +302,8 @@ This message does not have Content-Type nor Subject.
#[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert_eq!( parser.html, r##" @@ -324,9 +320,8 @@ message with a non-UTF-8 encoding: äöüßÄÖÜ
#[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert!(parser.plain.unwrap().flowed); assert_eq!( parser.html, @@ -347,9 +342,8 @@ and will be wrapped as usual.
#[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert_eq!( parser.html, r##" @@ -369,9 +363,8 @@ test some special html-characters as < > and & but also " and &#x #[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); // on windows, `\r\n` linends are returned from mimeparser, // however, rust multiline-strings use just `\n`; @@ -387,9 +380,8 @@ test some special html-characters as < > and & but also " and &#x #[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert_eq!( parser.html.replace('\r', ""), // see comment in test_htmlparse_html() r##" @@ -402,9 +394,8 @@ test some special html-characters as < > and & but also " and &#x #[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"); - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert_eq!( parser.html.replace('\r', ""), // see comment in test_htmlparse_html() r##" @@ -421,7 +412,6 @@ test some special html-characters as < > and & but also " and &#x 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. - let t = TestContext::new().await; let raw = include_bytes!("../test-data/message/apple_cid_jpg.eml"); let test = String::from_utf8_lossy(raw); assert!(test.contains("Content-Id: <8AE052EF-BC90-486F-BB78-58D3590308EC@fritz.box>")); @@ -429,7 +419,7 @@ test some special html-characters as < > and & but also " and &#x assert!(test.find("data:").is_none()); // parsing converts cid: to data: - let parser = HtmlMsgParser::from_bytes(&t.ctx, raw).await.unwrap(); + let parser = HtmlMsgParser::from_bytes(raw).await.unwrap(); assert!(parser.html.contains("")); assert!(!parser.html.contains("Content-Id:")); assert!(parser.html.contains("data:image/jpeg;base64,/9j/4AAQ")); diff --git a/src/imap.rs b/src/imap.rs index 6e3437fc0d..2946f9b949 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -16,6 +16,7 @@ use async_channel::Receiver; use async_imap::types::{Fetch, Flag, Name, NameAttribute, UnsolicitedResponse}; use futures::StreamExt; use num_traits::FromPrimitive; +use tracing::{error, info, warn}; use crate::chat::{self, ChatId, ChatIdBlocked}; use crate::config::Config; @@ -306,7 +307,7 @@ impl Imap { let oauth2 = self.config.lp.oauth2; - info!(context, "Connecting to IMAP server"); + info!("Connecting to IMAP server"); let connection_res: Result = if self.config.lp.security == Socket::Starttls || self.config.lp.security == Socket::Plain { @@ -363,7 +364,7 @@ impl Imap { let imap_pw: &str = config.lp.password.as_ref(); let login_res = if oauth2 { - info!(context, "Logging into IMAP server with OAuth 2"); + info!("Logging into IMAP server with OAuth 2"); let addr: &str = config.addr.as_ref(); let token = get_oauth2_access_token(context, addr, imap_pw, true) @@ -375,7 +376,7 @@ impl Imap { }; client.authenticate("XOAUTH2", auth).await } else { - info!(context, "Logging into IMAP server with LOGIN"); + info!("Logging into IMAP server with LOGIN"); client.login(imap_user, imap_pw).await }; @@ -391,7 +392,7 @@ impl Imap { "IMAP-LOGIN as {}", self.config.lp.user ))); - info!(context, "Successfully logged into IMAP server"); + info!("Successfully logged into IMAP server"); Ok(()) } @@ -399,7 +400,7 @@ impl Imap { let imap_user = self.config.lp.user.to_owned(); let message = stock_str::cannot_login(context, &imap_user).await; - warn!(context, "{} ({:#})", message, err); + warn!("{} ({:#})", message, err); let lock = context.wrong_pw_warning_mutex.lock().await; if self.login_failed_once @@ -407,7 +408,7 @@ impl Imap { && context.get_config_bool(Config::NotifyAboutWrongPw).await? { if let Err(e) = context.set_config(Config::NotifyAboutWrongPw, None).await { - warn!(context, "{:#}", e); + warn!("{:#}", e); } drop(lock); @@ -417,7 +418,7 @@ impl Imap { chat::add_device_msg_with_importance(context, None, Some(&mut msg), true) .await { - warn!(context, "{:#}", e); + warn!("{:#}", e); } } else { self.login_failed_once = true; @@ -445,8 +446,8 @@ impl Imap { /// Drops the session without disconnecting properly. /// Useful in case of an IMAP error, when it's unclear if it's in a correct state and it's /// easier to setup a new connection. - pub fn trigger_reconnect(&mut self, context: &Context) { - info!(context, "Dropping an IMAP connection."); + pub fn trigger_reconnect(&mut self) { + info!("Dropping an IMAP connection."); self.session = None; } @@ -510,7 +511,7 @@ impl Imap { .as_mut() .context("IMAP No connection established")?; - session.select_folder(context, Some(folder)).await?; + session.select_folder(Some(folder)).await?; let mut list = session .uid_fetch("1:*", RFC724MID_UID) @@ -521,7 +522,7 @@ impl Imap { let headers = match get_fetch_headers(&fetch) { Ok(headers) => headers, Err(err) => { - warn!(context, "Failed to parse FETCH headers: {}", err); + warn!("Failed to parse FETCH headers: {}", err); continue; } }; @@ -539,7 +540,6 @@ impl Imap { } info!( - context, "Resync: collected {} message IDs in folder {}", msgs.len(), folder, @@ -582,7 +582,7 @@ impl Imap { ) -> Result { let session = self.session.as_mut().context("no session")?; let newly_selected = session - .select_or_create_folder(context, folder) + .select_or_create_folder(folder) .await .with_context(|| format!("failed to select or create folder {folder}"))?; let mailbox = session @@ -611,7 +611,6 @@ impl Imap { } else if let Some(uid_next) = mailbox.uid_next { if uid_next < old_uid_next { warn!( - context, "The server illegally decreased the uid_next of folder {} from {} to {} without changing validity ({}), resyncing UIDs...", folder, old_uid_next, uid_next, new_uid_validity, ); @@ -629,7 +628,7 @@ impl Imap { set_modseq(context, folder, 0).await?; if mailbox.exists == 0 { - info!(context, "Folder \"{}\" is empty.", folder); + info!("Folder \"{}\" is empty.", folder); // set uid_next=1 for empty folders. // If we do not do this here, we'll miss the first message @@ -645,10 +644,7 @@ impl Imap { let new_uid_next = match mailbox.uid_next { Some(uid_next) => uid_next, None => { - warn!( - context, - "IMAP folder has no uid_next, fall back to fetching" - ); + warn!("IMAP folder has no uid_next, fall back to fetching"); // note that we use fetch by sequence number // and thus we only need to get exactly the // last-index message. @@ -685,13 +681,8 @@ impl Imap { job::schedule_resync(context).await?; } info!( - context, "uid/validity change folder {}: new {}/{} previous {}/{}", - folder, - new_uid_next, - new_uid_validity, - old_uid_next, - old_uid_validity, + folder, new_uid_next, new_uid_validity, old_uid_next, old_uid_validity, ); Ok(false) } @@ -707,7 +698,7 @@ impl Imap { fetch_existing_msgs: bool, ) -> Result { if should_ignore_folder(context, folder, folder_meaning).await? { - info!(context, "Not fetching from {}", folder); + info!("Not fetching from {}", folder); return Ok(false); } @@ -717,7 +708,7 @@ impl Imap { .with_context(|| format!("failed to select folder {folder}"))?; if !new_emails && !fetch_existing_msgs { - info!(context, "No new emails in folder {}", folder); + info!("No new emails in folder {}", folder); return Ok(false); } @@ -743,7 +734,7 @@ impl Imap { let headers = match get_fetch_headers(fetch_response) { Ok(headers) => headers, Err(err) => { - warn!(context, "Failed to parse FETCH headers: {}", err); + warn!("Failed to parse FETCH headers: {}", err); continue; } }; @@ -843,7 +834,7 @@ impl Imap { set_uid_next(context, folder, new_uid_next).await?; } - info!(context, "{} mails read from \"{}\".", read_cnt, folder); + info!("{} mails read from \"{}\".", read_cnt, folder); let msg_ids: Vec = received_msgs .iter() @@ -890,10 +881,7 @@ impl Imap { None => continue, }; if let Some(folder) = context.get_config(config).await? { - info!( - context, - "Fetching existing messages from folder \"{}\"", folder - ); + info!("Fetching existing messages from folder \"{}\"", folder); self.fetch_new_messages(context, &folder, meaning, true) .await .context("could not fetch existing messages")?; @@ -901,7 +889,7 @@ impl Imap { } } - info!(context, "Done fetching existing messages."); + info!("Done fetching existing messages."); Ok(()) } @@ -986,21 +974,15 @@ impl Session { Err(err) => { if context.should_delete_to_trash().await? { error!( - context, "Cannot move messages {} to {}, no fallback to COPY/DELETE because \ delete_to_trash is set. Error: {:#}", - set, - target, - err, + set, target, err, ); return Err(err.into()); } warn!( - context, "Cannot move messages, fallback to COPY/DELETE {} to {}: {}", - set, - target, - err + set, target, err ); } } @@ -1011,14 +993,14 @@ impl Session { let copy = !context.is_trash(target).await?; if copy { info!( - context, - "Server does not support MOVE, fallback to COPY/DELETE {} to {}", set, target + "Server does not support MOVE, fallback to COPY/DELETE {} to {}", + set, target ); self.uid_copy(&set, &target).await?; } else { error!( - context, - "Server does not support MOVE, fallback to DELETE {} to {}", set, target, + "Server does not support MOVE, fallback to DELETE {} to {}", + set, target, ); } context @@ -1067,7 +1049,7 @@ impl Session { // MOVE/DELETE operations. This does not result in multiple SELECT commands // being sent because `select_folder()` does nothing if the folder is already // selected. - self.select_folder(context, Some(folder)).await?; + self.select_folder(Some(folder)).await?; // Empty target folder name means messages should be deleted. if target.is_empty() { @@ -1088,8 +1070,8 @@ impl Session { // Expunge folder if needed, e.g. if some jobs have // deleted messages on the server. - if let Err(err) = self.maybe_close_folder(context).await { - warn!(context, "failed to close folder: {:?}", err); + if let Err(err) = self.maybe_close_folder().await { + warn!("failed to close folder: {:?}", err); } Ok(()) @@ -1115,23 +1097,17 @@ impl Session { .await?; for (folder, rowid_set, uid_set) in UidGrouper::from(rows) { - self.select_folder(context, Some(&folder)) + self.select_folder(Some(&folder)) .await .context("failed to select folder")?; if let Err(err) = self.add_flag_finalized_with_set(&uid_set, "\\Seen").await { warn!( - context, "Cannot mark messages {} in folder {} as seen, will retry later: {}.", - uid_set, - folder, - err + uid_set, folder, err ); } else { - info!( - context, - "Marked messages {} in folder {} as seen.", uid_set, folder - ); + info!("Marked messages {} in folder {} as seen.", uid_set, folder); context .sql .execute( @@ -1159,15 +1135,12 @@ impl Imap { .with_context(|| format!("No IMAP connection established, folder: {folder}"))?; if !session.can_condstore() { - info!( - context, - "Server does not support CONDSTORE, skipping flag synchronization." - ); + info!("Server does not support CONDSTORE, skipping flag synchronization."); return Ok(()); } session - .select_folder(context, Some(folder)) + .select_folder(Some(folder)) .await .context("failed to select folder")?; @@ -1180,8 +1153,8 @@ impl Imap { // We are not interested in actual value of HIGHESTMODSEQ. if mailbox.highest_modseq.is_none() { info!( - context, - "Mailbox {} does not support mod-sequences, skipping flag synchronization.", folder + "Mailbox {} does not support mod-sequences, skipping flag synchronization.", + folder ); return Ok(()); } @@ -1203,7 +1176,7 @@ impl Imap { let uid = if let Some(uid) = fetch.uid { uid } else { - info!(context, "FETCH result contains no UID, skipping"); + info!("FETCH result contains no UID, skipping"); continue; }; let is_seen = fetch.flags().any(|flag| flag == Flag::Seen); @@ -1223,7 +1196,7 @@ impl Imap { highest_modseq = modseq; } } else { - warn!(context, "FETCH result contains no MODSEQ"); + warn!("FETCH result contains no MODSEQ"); } } @@ -1269,7 +1242,7 @@ impl Imap { } } Err(err) => { - warn!(context, "{}", err); + warn!("{}", err); continue; } }; @@ -1366,7 +1339,6 @@ impl Imap { let session = self.session.as_mut().context("no IMAP session")?; for (request_uids, set) in build_sequence_sets(&request_uids)? { info!( - context, "Starting a {} FETCH of message set \"{}\".", if fetch_partially { "partial" } else { "full" }, set @@ -1407,7 +1379,7 @@ impl Imap { let next_fetch_response = match next_fetch_response { Ok(next_fetch_response) => next_fetch_response, Err(err) => { - warn!(context, "Failed to process IMAP FETCH result: {}.", err); + warn!("Failed to process IMAP FETCH result: {}.", err); continue; } }; @@ -1423,24 +1395,21 @@ impl Imap { // another client changes \Seen flag on a message after we do a prefetch but // before fetch. It's not an error if we receive such unsolicited response. info!( - context, - "Skipping not requested FETCH response for UID {}.", next_uid + "Skipping not requested FETCH response for UID {}.", + next_uid ); } else if uid_msgs.insert(next_uid, next_fetch_response).is_some() { - warn!(context, "Got duplicated UID {}.", next_uid); + warn!("Got duplicated UID {}.", next_uid); } } else { - info!(context, "Skipping FETCH response without UID."); + info!("Skipping FETCH response without UID."); } } let fetch_response = match fetch_response { Some(fetch) => fetch, None => { - warn!( - context, - "Missed UID {} in the server response.", request_uid - ); + warn!("Missed UID {} in the server response.", request_uid); continue; } }; @@ -1454,7 +1423,7 @@ impl Imap { }; if is_deleted { - info!(context, "Not processing deleted msg {}.", request_uid); + info!("Not processing deleted msg {}.", request_uid); last_uid = Some(request_uid); continue; } @@ -1462,10 +1431,7 @@ impl Imap { let body = if let Some(body) = body { body } else { - info!( - context, - "Not processing message {} without a BODY.", request_uid - ); + info!("Not processing message {} without a BODY.", request_uid); last_uid = Some(request_uid); continue; }; @@ -1476,17 +1442,13 @@ impl Imap { rfc724_mid } else { error!( - context, "No Message-ID corresponding to UID {} passed in uid_messsage_ids.", request_uid ); continue; }; - info!( - context, - "Passing message UID {} to receive_imf().", request_uid - ); + info!("Passing message UID {} to receive_imf().", request_uid); match receive_imf_inner( context, rfc724_mid, @@ -1503,7 +1465,7 @@ impl Imap { } } Err(err) => { - warn!(context, "receive_imf error: {:#}.", err); + warn!("receive_imf error: {:#}.", err); } }; last_uid = Some(request_uid) @@ -1515,18 +1477,13 @@ impl Imap { if count != request_uids.len() { warn!( - context, "Failed to fetch all UIDs: got {}, requested {}, we requested the UIDs {:?}.", count, request_uids.len(), request_uids, ); } else { - info!( - context, - "Successfully received {} UIDs.", - request_uids.len() - ); + info!("Successfully received {} UIDs.", request_uids.len()); } } @@ -1567,7 +1524,7 @@ impl Imap { return Some(ImapActionResult::RetryLater); } if let Err(err) = self.prepare(context).await { - warn!(context, "prepare_imap_op failed: {}", err); + warn!("prepare_imap_op failed: {}", err); return Some(ImapActionResult::RetryLater); } @@ -1577,24 +1534,24 @@ impl Imap { .context("no IMAP connection established") { Err(err) => { - error!(context, "Failed to prepare IMAP operation: {:#}", err); + error!("Failed to prepare IMAP operation: {:#}", err); return Some(ImapActionResult::Failed); } Ok(session) => session, }; - match session.select_folder(context, Some(folder)).await { + match session.select_folder(Some(folder)).await { Ok(_) => None, Err(select_folder::Error::ConnectionLost) => { - warn!(context, "Lost imap connection"); + warn!("Lost imap connection"); Some(ImapActionResult::RetryLater) } Err(select_folder::Error::BadFolderName(folder_name)) => { - warn!(context, "invalid folder name: {:?}", folder_name); + warn!("invalid folder name: {:?}", folder_name); Some(ImapActionResult::Failed) } Err(err) => { - warn!(context, "failed to select folder {:?}: {:#}", folder, err); + warn!("failed to select folder {:?}: {:#}", folder, err); Some(ImapActionResult::RetryLater) } } @@ -1623,7 +1580,6 @@ impl Imap { /// Returns first found or created folder name. async fn configure_mvbox<'a>( &mut self, - context: &Context, folders: &[&'a str], create_mvbox: bool, ) -> Result> { @@ -1634,15 +1590,15 @@ impl Imap { // Close currently selected folder if needed. // We are going to select folders using low-level EXAMINE operations below. - session.select_folder(context, None).await?; + session.select_folder(None).await?; for folder in folders { - info!(context, "Looking for MVBOX-folder \"{}\"...", &folder); + info!("Looking for MVBOX-folder \"{}\"...", &folder); let res = session.examine(&folder).await; if res.is_ok() { info!( - context, - "MVBOX-folder {:?} successfully selected, using it.", &folder + "MVBOX-folder {:?} successfully selected, using it.", + &folder ); session.close().await?; return Ok(Some(folder)); @@ -1653,11 +1609,11 @@ impl Imap { for folder in folders { match session.create(&folder).await { Ok(_) => { - info!(context, "MVBOX-folder {} created.", &folder); + info!("MVBOX-folder {} created.", &folder); return Ok(Some(folder)); } Err(err) => { - warn!(context, "Cannot create MVBOX-folder {:?}: {}", &folder, err); + warn!("Cannot create MVBOX-folder {:?}: {}", &folder, err); } } } @@ -1682,7 +1638,7 @@ impl Imap { while let Some(folder) = folders.next().await { let folder = folder?; - info!(context, "Scanning folder: {:?}", folder); + info!("Scanning folder: {:?}", folder); // Update the delimiter iff there is a different one, but only once. if let Some(d) = folder.delimiter() { @@ -1706,11 +1662,11 @@ impl Imap { } drop(folders); - info!(context, "Using \"{}\" as folder-delimiter.", delimiter); + info!("Using \"{}\" as folder-delimiter.", delimiter); let fallback_folder = format!("INBOX{delimiter}DeltaChat"); let mvbox_folder = self - .configure_mvbox(context, &["DeltaChat", &fallback_folder], create_mvbox) + .configure_mvbox(&["DeltaChat", &fallback_folder], create_mvbox) .await .context("failed to configure mvbox")?; @@ -1718,7 +1674,7 @@ impl Imap { .set_config(Config::ConfiguredInboxFolder, Some("INBOX")) .await?; if let Some(mvbox_folder) = mvbox_folder { - info!(context, "Setting MVBOX FOLDER TO {}", &mvbox_folder); + info!("Setting MVBOX FOLDER TO {}", &mvbox_folder); context .set_config(Config::ConfiguredMvboxFolder, Some(mvbox_folder)) .await?; @@ -1731,7 +1687,7 @@ impl Imap { .set_raw_config_int("folders_configured", DC_FOLDERS_CONFIGURED_VERSION) .await?; - info!(context, "FINISHED configuring IMAP-folders."); + info!("FINISHED configuring IMAP-folders."); Ok(()) } } @@ -1741,18 +1697,15 @@ impl Session { /// Drains all responses from `session.unsolicited_responses` in the process. /// If this returns `true`, this means that new emails arrived and you should /// fetch again, even if you just fetched. - fn server_sent_unsolicited_exists(&self, context: &Context) -> Result { + fn server_sent_unsolicited_exists(&self) -> Result { let mut unsolicited_exists = false; while let Ok(response) = self.unsolicited_responses.try_recv() { match response { UnsolicitedResponse::Exists(_) => { - info!( - context, - "Need to fetch again, got unsolicited EXISTS {:?}", response - ); + info!("Need to fetch again, got unsolicited EXISTS {:?}", response); unsolicited_exists = true; } - _ => info!(context, "ignoring unsolicited response {:?}", response), + _ => info!("ignoring unsolicited response {:?}", response), } } Ok(unsolicited_exists) @@ -1796,8 +1749,8 @@ async fn should_move_out_of_spam( Some(res) => res, None => { warn!( - context, - "Contact with From address {:?} cannot exist, not moving out of spam", from + "Contact with From address {:?} cannot exist, not moving out of spam", + from ); return Ok(false); } @@ -2120,7 +2073,7 @@ pub(crate) async fn prefetch_should_download( // (prevent_rename is the last argument of from_field_to_contact_id()) if flags.any(|f| f == Flag::Draft) { - info!(context, "Ignoring draft message"); + info!("Ignoring draft message"); return Ok(false); } @@ -2425,8 +2378,8 @@ async fn add_all_recipients_as_contacts( m } else { info!( - context, - "Folder {} is not configured, skipping fetching contacts from it.", folder + "Folder {} is not configured, skipping fetching contacts from it.", + folder ); return Ok(()); }; @@ -2450,10 +2403,8 @@ async fn add_all_recipients_as_contacts( let recipient_addr = match ContactAddress::new(&recipient.addr) { Err(err) => { warn!( - context, "Could not add contact for recipient with address {:?}: {:#}", - recipient.addr, - err + recipient.addr, err ); continue; } diff --git a/src/imap/idle.rs b/src/imap/idle.rs index fbce499b76..fc9f3bd1ed 100644 --- a/src/imap/idle.rs +++ b/src/imap/idle.rs @@ -4,6 +4,7 @@ use anyhow::{bail, Context as _, Result}; use async_channel::Receiver; use async_imap::extensions::idle::IdleResponse; use futures_lite::FutureExt; +use tracing::{error, info, warn}; use super::session::Session; use super::Imap; @@ -15,7 +16,6 @@ const IDLE_TIMEOUT: Duration = Duration::from_secs(23 * 60); impl Session { pub async fn idle( mut self, - context: &Context, idle_interrupt_receiver: Receiver, watch_folder: Option, ) -> Result<(Self, InterruptInfo)> { @@ -27,14 +27,14 @@ impl Session { let mut info = Default::default(); - self.select_folder(context, watch_folder.as_deref()).await?; + self.select_folder(watch_folder.as_deref()).await?; - if self.server_sent_unsolicited_exists(context)? { + if self.server_sent_unsolicited_exists()? { return Ok((self, info)); } if let Ok(info) = idle_interrupt_receiver.try_recv() { - info!(context, "skip idle, got interrupt {:?}", info); + info!("skip idle, got interrupt {:?}", info); return Ok((self, info)); } @@ -56,10 +56,7 @@ impl Session { } let folder_name = watch_folder.as_deref().unwrap_or("None"); - info!( - context, - "{}: Idle entering wait-on-remote state", folder_name - ); + info!("{}: Idle entering wait-on-remote state", folder_name); let fut = idle_wait.map(|ev| ev.map(Event::IdleResponse)).race(async { let info = idle_interrupt_receiver.recv().await; @@ -71,29 +68,20 @@ impl Session { match fut.await { Ok(Event::IdleResponse(IdleResponse::NewData(x))) => { - info!(context, "{}: Idle has NewData {:?}", folder_name, x); + info!("{}: Idle has NewData {:?}", folder_name, x); } Ok(Event::IdleResponse(IdleResponse::Timeout)) => { - info!( - context, - "{}: Idle-wait timeout or interruption", folder_name - ); + info!("{}: Idle-wait timeout or interruption", folder_name); } Ok(Event::IdleResponse(IdleResponse::ManualInterrupt)) => { - info!( - context, - "{}: Idle wait was interrupted manually", folder_name - ); + info!("{}: Idle wait was interrupted manually", folder_name); } Ok(Event::Interrupt(i)) => { - info!( - context, - "{}: Idle wait was interrupted: {:?}", folder_name, &i - ); + info!("{}: Idle wait was interrupted: {:?}", folder_name, &i); info = i; } Err(err) => { - warn!(context, "{}: Idle wait errored: {:?}", folder_name, err); + warn!("{}: Idle wait errored: {:?}", folder_name, err); } } @@ -124,14 +112,14 @@ impl Imap { let watch_folder = if let Some(watch_folder) = watch_folder { watch_folder } else { - info!(context, "IMAP-fake-IDLE: no folder, waiting for interrupt"); + info!("IMAP-fake-IDLE: no folder, waiting for interrupt"); return self .idle_interrupt_receiver .recv() .await .unwrap_or_default(); }; - info!(context, "IMAP-fake-IDLEing folder={:?}", watch_folder); + info!("IMAP-fake-IDLEing folder={:?}", watch_folder); // check every minute if there are new messages // TODO: grow sleep durations / make them more flexible @@ -159,7 +147,7 @@ impl Imap { // (setup_handle_if_needed might not know about them if we // never successfully connected) if let Err(err) = self.prepare(context).await { - warn!(context, "fake_idle: could not connect: {}", err); + warn!("fake_idle: could not connect: {}", err); continue; } if let Some(session) = &self.session { @@ -168,7 +156,7 @@ impl Imap { break InterruptInfo::new(false); } } - info!(context, "fake_idle is connected"); + info!("fake_idle is connected"); // we are connected, let's see if fetching messages results // in anything. If so, we behave as if IDLE had data but // will have already fetched the messages so perform_*_fetch @@ -178,27 +166,26 @@ impl Imap { .await { Ok(res) => { - info!(context, "fetch_new_messages returned {:?}", res); + info!("fetch_new_messages returned {:?}", res); if res { break InterruptInfo::new(false); } } Err(err) => { - error!(context, "could not fetch from folder: {:#}", err); - self.trigger_reconnect(context); + error!("could not fetch from folder: {:#}", err); + self.trigger_reconnect(); } } } Event::Interrupt(info) => { // Interrupt - info!(context, "Fake IDLE interrupted"); + info!("Fake IDLE interrupted"); break info; } } }; info!( - context, "IMAP-fake-IDLE done after {:.4}s", SystemTime::now() .duration_since(fake_idle_start_time) diff --git a/src/imap/scan_folders.rs b/src/imap/scan_folders.rs index 29051e7b9a..afb381b614 100644 --- a/src/imap/scan_folders.rs +++ b/src/imap/scan_folders.rs @@ -2,6 +2,7 @@ use std::{collections::BTreeMap, time::Instant}; use anyhow::{Context as _, Result}; use futures::stream::StreamExt; +use tracing::info; use super::{get_folder_meaning_by_attrs, get_folder_meaning_by_name}; use crate::config::Config; @@ -24,7 +25,7 @@ impl Imap { return Ok(false); } } - info!(context, "Starting full folder scan"); + info!("Starting full folder scan"); self.prepare(context).await?; let folders = self.list_folders(context).await?; @@ -65,7 +66,7 @@ impl Imap { { let session = self.session.as_mut().context("no session")?; // Drain leftover unsolicited EXISTS messages - session.server_sent_unsolicited_exists(context)?; + session.server_sent_unsolicited_exists()?; loop { self.fetch_move_delete(context, folder.name(), folder_meaning) @@ -74,7 +75,7 @@ impl Imap { let session = self.session.as_mut().context("no session")?; // If the server sent an unsocicited EXISTS during the fetch, we need to fetch again - if !session.server_sent_unsolicited_exists(context)? { + if !session.server_sent_unsolicited_exists()? { break; } } diff --git a/src/imap/select_folder.rs b/src/imap/select_folder.rs index 2d6dfa9a63..15fb78c2fe 100644 --- a/src/imap/select_folder.rs +++ b/src/imap/select_folder.rs @@ -1,9 +1,9 @@ //! # IMAP folder selection module. use anyhow::Context as _; +use tracing::info; use super::session::Session as ImapSession; -use crate::context::Context; type Result = std::result::Result; @@ -34,13 +34,13 @@ impl ImapSession { /// /// CLOSE is considerably faster than an EXPUNGE, see /// - pub(super) async fn maybe_close_folder(&mut self, context: &Context) -> anyhow::Result<()> { + pub(super) async fn maybe_close_folder(&mut self) -> anyhow::Result<()> { if let Some(folder) = &self.selected_folder { if self.selected_folder_needs_expunge { - info!(context, "Expunge messages in \"{}\".", folder); + info!("Expunge messages in \"{}\".", folder); self.close().await.context("IMAP close/expunge failed")?; - info!(context, "close/expunge succeeded"); + info!("close/expunge succeeded"); self.selected_folder = None; self.selected_folder_needs_expunge = false; } @@ -51,11 +51,7 @@ impl ImapSession { /// Selects a folder, possibly updating uid_validity and, if needed, /// expunging the folder to remove delete-marked messages. /// Returns whether a new folder was selected. - pub(super) async fn select_folder( - &mut self, - context: &Context, - folder: Option<&str>, - ) -> Result { + pub(super) async fn select_folder(&mut self, folder: Option<&str>) -> Result { // if there is a new folder and the new folder is equal to the selected one, there's nothing to do. // if there is _no_ new folder, we continue as we might want to expunge below. if let Some(folder) = folder { @@ -67,7 +63,7 @@ impl ImapSession { } // deselect existing folder, if needed (it's also done implicitly by SELECT, however, without EXPUNGE then) - self.maybe_close_folder(context).await?; + self.maybe_close_folder().await?; // select new folder if let Some(folder) = folder { @@ -104,19 +100,18 @@ impl ImapSession { /// Selects a folder. Tries to create it once and select again if the folder does not exist. pub(super) async fn select_or_create_folder( &mut self, - context: &Context, folder: &str, ) -> anyhow::Result { - match self.select_folder(context, Some(folder)).await { + match self.select_folder( Some(folder)).await { Ok(newly_selected) => Ok(newly_selected), Err(err) => match err { Error::NoFolder(..) => { - info!(context, "Failed to select folder {} because it does not exist, trying to create it.", folder); + info!( "Failed to select folder {} because it does not exist, trying to create it.", folder); self.create(folder).await.with_context(|| { format!("Couldn't select folder ('{err}'), then create() failed") })?; - Ok(self.select_folder(context, Some(folder)).await.with_context(|| format!("failed to select newely created folder {folder}"))?) + Ok(self.select_folder(Some(folder)).await.with_context(|| format!("failed to select newely created folder {folder}"))?) } _ => Err(err).with_context(|| format!("failed to select folder {folder} with error other than NO, not trying to create it")), }, diff --git a/src/imex.rs b/src/imex.rs index 885187cae9..e1853d9e9f 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -11,6 +11,7 @@ use futures_lite::FutureExt; use rand::{thread_rng, Rng}; use tokio::fs::{self, File}; use tokio_tar::Archive; +use tracing::{error, info, warn}; use crate::blob::{BlobDirContents, BlobObject}; use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId}; @@ -103,10 +104,10 @@ pub async fn imex( if let Err(err) = res.as_ref() { // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: - error!(context, "IMEX failed to complete: {:#}", err); + error!("IMEX failed to complete: {:#}", err); context.emit_event(EventType::ImexProgress(0)); } else { - info!(context, "IMEX successfully completed"); + info!("IMEX successfully completed"); context.emit_event(EventType::ImexProgress(1000)); } @@ -114,7 +115,7 @@ 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 { +pub async fn has_backup(dir_name: &Path) -> Result { let mut dir_iter = tokio::fs::read_dir(dir_name).await?; let mut newest_backup_name = "".to_string(); let mut newest_backup_path: Option = None; @@ -144,7 +145,7 @@ pub async fn has_backup(_context: &Context, dir_name: &Path) -> Result { /// /// Returns setup code. pub async fn initiate_key_transfer(context: &Context) -> Result { - let setup_code = create_setup_code(context); + let setup_code = create_setup_code(); /* this may require a keypair to be created. this may take a second ... */ let setup_file_content = render_setup_file(context, &setup_code).await?; /* encrypting may also take a while ... */ @@ -226,7 +227,7 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result String { +pub fn create_setup_code() -> String { let mut random_val: u16; let mut rng = thread_rng(); let mut ret = String::new(); @@ -343,7 +344,7 @@ async fn set_self_key( ) .await?; - info!(context, "stored self key: {:?}", keypair.secret.key_id()); + info!("stored self key: {:?}", keypair.secret.key_id()); Ok(()) } @@ -376,7 +377,7 @@ async fn imex_inner( path: &Path, passphrase: Option, ) -> Result<()> { - info!(context, "Import/export dir: {}", path.display()); + info!("Import/export dir: {}", path.display()); ensure!(context.sql.is_open().await, "Database not opened."); context.emit_event(EventType::ImexProgress(10)); @@ -426,7 +427,6 @@ async fn import_backup( let backup_file = File::open(backup_to_import).await?; let file_size = backup_file.metadata().await?.len(); info!( - context, "Import \"{}\" ({} bytes) to \"{}\".", backup_to_import.display(), file_size, @@ -468,7 +468,7 @@ async fn import_backup( if let Some(name) = from_path.file_name() { fs::rename(&from_path, context.get_blobdir().join(name)).await?; } else { - warn!(context, "No file name"); + warn!("No file name."); } } } @@ -527,7 +527,6 @@ async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Res .context("could not export database")?; info!( - context, "Backup '{}' to '{}'.", context.get_dbfile().display(), dest_path.display(), @@ -541,7 +540,7 @@ async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Res context.emit_event(EventType::ImexFileWritten(dest_path)); } Err(e) => { - error!(context, "backup failed: {}", e); + error!("backup failed: {}", e); } } @@ -613,7 +612,7 @@ async fn import_self_keys(context: &Context, dir: &Path) -> Result<()> { continue; } set_default = if name_f.contains("legacy") { - info!(context, "found legacy key '{}'", path_plus_name.display()); + info!("found legacy key '{}'", path_plus_name.display()); false } else { true @@ -623,17 +622,13 @@ async fn import_self_keys(context: &Context, dir: &Path) -> Result<()> { continue; } } - info!( - context, - "considering key file: {}", - path_plus_name.display() - ); + info!("considering key file: {}", path_plus_name.display()); match read_file(context, &path_plus_name).await { Ok(buf) => { let armored = std::string::String::from_utf8_lossy(&buf); if let Err(err) = set_self_key(context, &armored, set_default, false).await { - info!(context, "set_self_key: {}", err); + info!("set_self_key: {}", err); continue; } } @@ -678,7 +673,7 @@ async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> { let id = Some(id).filter(|_| is_default != 0); if let Ok(key) = public_key { if let Err(err) = export_key_to_asc_file(context, dir, id, &key).await { - error!(context, "Failed to export public key: {:#}.", err); + error!("Failed to export public key: {:#}.", err); export_errors += 1; } } else { @@ -686,7 +681,7 @@ async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> { } if let Ok(key) = private_key { if let Err(err) = export_key_to_asc_file(context, dir, id, &key).await { - error!(context, "Failed to export private key: {:#}.", err); + error!("Failed to export private key: {:#}.", err); export_errors += 1; } } else { @@ -723,7 +718,6 @@ where dir.join(format!("{}-key-{}.asc", kind, &id)) }; info!( - context, "Exporting key {:?} to {}", key.key_id(), file_name.display() @@ -764,7 +758,7 @@ async fn export_database(context: &Context, dest: &Path, passphrase: String) -> .sql .call_write(|conn| { conn.execute("VACUUM;", params![]) - .map_err(|err| warn!(context, "Vacuum failed, exporting anyway {err}")) + .map_err(|err| warn!("Vacuum failed, exporting anyway {err:#}.")) .ok(); conn.execute( "ATTACH DATABASE ? AS backup KEY ?", @@ -824,8 +818,7 @@ mod tests { #[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); + let setupcode = create_setup_code(); assert_eq!(setupcode.len(), 44); assert_eq!(setupcode.chars().nth(4).unwrap(), '-'); assert_eq!(setupcode.chars().nth(9).unwrap(), '-'); @@ -890,7 +883,7 @@ mod tests { let context2 = TestContext::new().await; assert!(!context2.is_configured().await?); - assert!(has_backup(&context2, backup_dir.path()).await.is_err()); + assert!(has_backup(backup_dir.path()).await.is_err()); // export from context1 assert!( @@ -904,7 +897,7 @@ mod tests { .await; // import to context2 - let backup = has_backup(&context2, backup_dir.path()).await?; + let backup = has_backup(backup_dir.path()).await?; // Import of unencrypted backup with incorrect "foobar" backup passphrase fails. assert!(imex( @@ -949,7 +942,7 @@ mod tests { imex(&context1, ImexMode::ExportBackup, backup_dir.path(), None).await?; // import to context2 - let backup = has_backup(&context2, backup_dir.path()).await?; + let backup = has_backup(backup_dir.path()).await?; let context2_cloned = context2.clone(); let handle = task::spawn(async move { imex( diff --git a/src/imex/transfer.rs b/src/imex/transfer.rs index d93cf4f6d6..3ae2969010 100644 --- a/src/imex/transfer.rs +++ b/src/imex/transfer.rs @@ -44,6 +44,7 @@ use tokio::sync::broadcast::error::RecvError; use tokio::sync::{broadcast, Mutex}; use tokio::task::{JoinHandle, JoinSet}; use tokio_stream::wrappers::ReadDirStream; +use tracing::{error, info, warn}; use crate::blob::BlobDirContents; use crate::chat::delete_and_reset_all_device_msgs; @@ -100,7 +101,7 @@ impl BackupProvider { let dbfile = context_dir.join(DBFILE_BACKUP_NAME); if fs::metadata(&dbfile).await.is_ok() { fs::remove_file(&dbfile).await?; - warn!(context, "Previous database export deleted"); + warn!("Previous database export deleted."); } let dbfile = TempPathGuard::new(dbfile); let res = tokio::select! { @@ -109,7 +110,7 @@ impl BackupProvider { match res { Ok(slf) => Ok(slf), Err(err) => { - error!(context, "Failed to set up second device setup: {:#}", err); + error!("Failed to set up second device setup: {err:#}."); Err(err) }, } @@ -170,7 +171,7 @@ impl BackupProvider { .auth_token(token) .spawn()?; context.emit_event(SendProgress::ProviderListening.into()); - info!(context, "Waiting for remote to connect"); + info!("Waiting for remote to connect."); let ticket = provider.ticket(hash); Ok((provider, ticket)) } @@ -257,7 +258,7 @@ impl BackupProvider { match &res { Ok(_) => context.emit_event(SendProgress::Completed.into()), Err(err) => { - error!(context, "Backup transfer failure: {err:#}"); + error!("Backup transfer failure: {err:#}."); context.emit_event(SendProgress::Failed.into()) } } @@ -414,7 +415,7 @@ async fn get_backup_inner(context: &Context, qr: Qr) -> Result<()> { peer_id: Some(ticket.peer), keylog: false, }; - info!(context, "attempting to contact {}", addr); + info!("Attempting to contact {addr}."); match transfer_from_provider(context, &ticket, opts).await { Ok(_) => { delete_and_reset_all_device_msgs(context).await?; @@ -422,7 +423,7 @@ async fn get_backup_inner(context: &Context, qr: Qr) -> Result<()> { return Ok(()); } Err(TransferError::ConnectionError(err)) => { - warn!(context, "Connection error: {err:#}."); + warn!("Connection error: {err:#}."); continue; } Err(TransferError::Other(err)) => { @@ -492,7 +493,6 @@ async fn transfer_from_provider( match res { Ok(stats) => { info!( - context, "Backup transfer finished, transfer rate is {} Mbps.", stats.mbits() ); @@ -527,7 +527,7 @@ async fn on_blob( let dbfile = context_dir.join(DBFILE_BACKUP_NAME); if fs::metadata(&dbfile).await.is_ok() { fs::remove_file(&dbfile).await?; - warn!(context, "Previous database export deleted"); + warn!("Previous database export deleted."); } dbfile } else { @@ -547,12 +547,11 @@ async fn on_blob( let token = ticket.token.to_string(); jobs.lock().await.spawn(async move { if let Err(err) = context.sql.import(&path, token).await { - error!(context, "cannot import database: {:#?}", err); + error!("Cannot import database: {err:#?}."); } if let Err(err) = fs::remove_file(&path).await { error!( - context, - "failed to delete database import file '{}': {:#?}", + "Failed to delete database import file '{}': {:#?}.", path.display(), err, ); diff --git a/src/job.rs b/src/job.rs index f6fc198090..15b1fbe20c 100644 --- a/src/job.rs +++ b/src/job.rs @@ -11,6 +11,7 @@ use std::sync::atomic::Ordering; use anyhow::{Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use rand::{thread_rng, Rng}; +use tracing::{error, info, warn}; use crate::context::Context; use crate::imap::Imap; @@ -110,7 +111,7 @@ impl Job { /// /// The Job is consumed by this method. pub(crate) async fn save(self, context: &Context) -> Result<()> { - info!(context, "saving job {:?}", self); + info!("saving job {:?}", self); if self.job_id != 0 { context @@ -153,7 +154,7 @@ impl<'a> Connection<'a> { } pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_>, mut job: Job) { - info!(context, "Job {} started...", &job); + info!("Job {} started...", &job); let try_res = match perform_job_action(context, &mut job, &mut connection, 0).await { Status::RetryNow => perform_job_action(context, &mut job, &mut connection, 1).await, @@ -165,39 +166,33 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_ let tries = job.tries + 1; if tries < JOB_RETRIES { - info!(context, "Increase job {job} tries to {tries}."); + info!("Increase job {job} tries to {tries}."); job.tries = tries; let time_offset = get_backoff_time_offset(tries); job.desired_timestamp = time() + time_offset; info!( - context, "job #{} not succeeded on try #{}, retry in {} seconds.", - job.job_id, - tries, - time_offset + job.job_id, tries, time_offset ); job.save(context).await.unwrap_or_else(|err| { - error!(context, "Failed to save job: {err:#}."); + error!("Failed to save job: {err:#}."); }); } else { - info!( - context, - "Remove job {job} as it exhausted {JOB_RETRIES} retries." - ); + info!("Remove job {job} as it exhausted {JOB_RETRIES} retries."); job.delete(context).await.unwrap_or_else(|err| { - error!(context, "Failed to delete job: {err:#}."); + error!("Failed to delete job: {err:#}."); }); } } Status::Finished(res) => { if let Err(err) = res { - warn!(context, "Remove job {job} as it failed with error {err:#}."); + warn!("Remove job {job} as it failed with error {err:#}."); } else { - info!(context, "Remove job {job} as it succeeded."); + info!("Remove job {job} as it succeeded."); } job.delete(context).await.unwrap_or_else(|err| { - error!(context, "failed to delete job: {:#}", err); + error!("failed to delete job: {:#}", err); }); } } @@ -209,13 +204,13 @@ async fn perform_job_action( connection: &mut Connection<'_>, tries: u32, ) -> Status { - info!(context, "Begin immediate try {tries} of job {job}."); + info!("Begin immediate try {tries} of job {job}."); let try_res = match job.action { Action::DownloadMsg => job.download_msg(context, connection.inbox()).await, }; - info!(context, "Finished immediate try {tries} of job {job}."); + info!("Finished immediate try {tries} of job {job}."); try_res } @@ -247,7 +242,7 @@ pub(crate) async fn schedule_resync(context: &Context) -> Result<()> { pub async fn add(context: &Context, job: Job) -> Result<()> { job.save(context).await.context("failed to save job")?; - info!(context, "Interrupt: IMAP."); + info!("Interrupt: IMAP."); context .scheduler .interrupt_inbox(InterruptInfo::new(false)) @@ -261,7 +256,7 @@ pub async fn add(context: &Context, job: Job) -> Result<()> { /// jobs, this is tricky and probably wrong currently. Look at the /// SQL queries for details. pub(crate) async fn load_next(context: &Context, info: &InterruptInfo) -> Result> { - info!(context, "Loading job."); + info!("Loading job."); let query; let params; @@ -313,7 +308,7 @@ LIMIT 1; Ok(job) => return Ok(job), Err(err) => { // Remove invalid job from the DB - info!(context, "Cleaning up job, because of {err:#}."); + info!("Cleaning up job, because of {err:#}."); // TODO: improve by only doing a single query let id = context diff --git a/src/key.rs b/src/key.rs index 74fb997cf5..85877852fa 100644 --- a/src/key.rs +++ b/src/key.rs @@ -14,6 +14,7 @@ pub use pgp::composed::{SignedPublicKey, SignedSecretKey}; use pgp::ser::Serialize; use pgp::types::{KeyTrait, SecretKeyTrait}; use tokio::runtime::Handle; +use tracing::info; use crate::config::Config; use crate::constants::KeyGenType; @@ -211,14 +212,13 @@ async fn generate_keypair(context: &Context) -> Result { let start = std::time::SystemTime::now(); let keytype = KeyGenType::from_i32(context.get_config_int(Config::KeyGenType).await?) .unwrap_or_default(); - info!(context, "Generating keypair with type {}", keytype); + info!("Generating keypair with type {}", keytype); let keypair = Handle::current() .spawn_blocking(move || crate::pgp::create_keypair(addr, keytype)) .await??; store_self_keypair(context, &keypair, KeyPairUse::Default).await?; info!( - context, "Keypair generated in {:.3}s.", start.elapsed().unwrap_or_default().as_secs() ); diff --git a/src/lib.rs b/src/lib.rs index 51a72a1d57..38433633a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,6 @@ pub trait ToSql: rusqlite::ToSql + Send + Sync {} impl ToSql for T {} -#[macro_use] pub mod log; #[cfg(feature = "internals")] diff --git a/src/location.rs b/src/location.rs index 375c40662c..de85c2fe46 100644 --- a/src/location.rs +++ b/src/location.rs @@ -8,6 +8,7 @@ use async_channel::Receiver; use bitflags::bitflags; use quick_xml::events::{BytesEnd, BytesStart, BytesText}; use tokio::time::timeout; +use tracing::{info, warn}; use crate::chat::{self, ChatId}; use crate::contact::ContactId; @@ -337,9 +338,9 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64 ContactId::SELF, ] ).await { - warn!(context, "failed to store location {:#}", err); + warn!( "failed to store location {:#}", err); } else { - info!(context, "stored location for chat {}", chat_id); + info!("stored location for chat {}", chat_id); continue_streaming = true; } } @@ -639,7 +640,7 @@ pub(crate) async fn location_loop(context: &Context, interrupt_receiver: Receive loop { let next_event = match maybe_send_locations(context).await { Err(err) => { - warn!(context, "maybe_send_locations failed: {:#}", err); + warn!("maybe_send_locations failed: {:#}", err); Some(60) // Retry one minute later. } Ok(next_event) => next_event, @@ -652,7 +653,6 @@ pub(crate) async fn location_loop(context: &Context, interrupt_receiver: Receive }; info!( - context, "Location loop is waiting for {} or interrupt", duration_to_str(duration) ); @@ -719,10 +719,7 @@ async fn maybe_send_locations(context: &Context) -> Result> { // Send location-only message. // Pending locations are attached automatically to every message, // so also to this empty text message. - info!( - context, - "Chat {} has pending locations, sending them.", chat_id - ); + info!("Chat {} has pending locations, sending them.", chat_id); let mut msg = Message::new(Viewtype::Text); msg.hidden = true; msg.param.set_cmd(SystemMessage::LocationOnly); @@ -730,8 +727,8 @@ async fn maybe_send_locations(context: &Context) -> Result> { } else { // Wait until pending locations can be sent. info!( - context, - "Chat {} has pending locations, but they can't be sent yet.", chat_id + "Chat {} has pending locations, but they can't be sent yet.", + chat_id ); next_event = next_event .into_iter() @@ -740,17 +737,14 @@ async fn maybe_send_locations(context: &Context) -> Result> { } } else { info!( - context, - "Chat {} has location streaming enabled, but no pending locations.", chat_id + "Chat {} has location streaming enabled, but no pending locations.", + chat_id ); } } else { // Location streaming was either explicitly disabled (locations_send_begin = 0) or // locations_send_until is in the past. - info!( - context, - "Disabling location streaming for chat {}.", chat_id - ); + info!("Disabling location streaming for chat {}.", chat_id); context .sql .execute( diff --git a/src/log.rs b/src/log.rs index c755484e61..396c1c1b3f 100644 --- a/src/log.rs +++ b/src/log.rs @@ -4,36 +4,6 @@ use crate::context::Context; -#[macro_export] -macro_rules! info { - ($ctx:expr, $msg:expr) => { - info!($ctx, $msg,) - }; - ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{ - let formatted = format!($msg, $($args),*); - let full = format!("{file}:{line}: {msg}", - file = file!(), - line = line!(), - msg = &formatted); - $ctx.emit_event($crate::EventType::Info(full)); - }}; -} - -#[macro_export] -macro_rules! warn { - ($ctx:expr, $msg:expr) => { - warn!($ctx, $msg,) - }; - ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{ - let formatted = format!($msg, $($args),*); - let full = format!("{file}:{line}: {msg}", - file = file!(), - line = line!(), - msg = &formatted); - $ctx.emit_event($crate::EventType::Warning(full)); - }}; -} - #[macro_export] macro_rules! error { ($ctx:expr, $msg:expr) => { @@ -41,7 +11,6 @@ macro_rules! error { }; ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{ let formatted = format!($msg, $($args),*); - $ctx.set_last_error(&formatted); $ctx.emit_event($crate::EventType::Error(formatted)); }}; } @@ -50,13 +19,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) { - let mut last_error = self.last_error.write().unwrap(); + let mut last_error = self.events.last_error.write().unwrap(); *last_error = error.to_string(); } /// Get last error string. pub fn get_last_error(&self) -> String { - let last_error = &*self.last_error.read().unwrap(); + let last_error = &*self.events.last_error.read().unwrap(); last_error.clone() } } @@ -168,12 +137,6 @@ mod tests { error!(t, "foo-error"); assert_eq!(t.get_last_error(), "foo-error"); - warn!(t, "foo-warning"); - assert_eq!(t.get_last_error(), "foo-error"); - - info!(t, "foo-info"); - assert_eq!(t.get_last_error(), "foo-error"); - error!(t, "bar-error"); error!(t, "baz-error"); assert_eq!(t.get_last_error(), "baz-error"); diff --git a/src/message.rs b/src/message.rs index 415618f08f..0a617c96f2 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,6 +7,7 @@ use anyhow::{ensure, format_err, Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use rusqlite::types::ValueRef; use serde::{Deserialize, Serialize}; +use tracing::{error, info, warn}; use crate::chat::{self, Chat, ChatId}; use crate::config::Config; @@ -337,7 +338,6 @@ impl Message { Ok(t) => t, Err(_) => { warn!( - context, concat!( "dc_msg_load_from_db: could not get ", "text column as non-lossy utf8 id {}" @@ -1511,7 +1511,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> && (curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed) { update_msg_state(context, id, MessageState::InSeen).await?; - info!(context, "Seen message {}.", id); + info!("Seen message {}.", id); markseen_on_imap_table(context, &curr_rfc724_mid).await?; @@ -1595,11 +1595,11 @@ pub(crate) async fn set_msg_failed(context: &Context, msg_id: MsgId, error: &str if let Ok(mut msg) = Message::load_from_db(context, msg_id).await { if msg.state.can_fail() { msg.state = MessageState::OutFailed; - warn!(context, "{} failed: {}", msg_id, error); + warn!("{} failed: {}", msg_id, error); } else { warn!( - context, - "{} seems to have failed ({}), but state is {}", msg_id, error, msg.state + "{} seems to have failed ({}), but state is {}", + msg_id, error, msg.state ) } @@ -1616,7 +1616,7 @@ pub(crate) async fn set_msg_failed(context: &Context, msg_id: MsgId, error: &str msg_id, }), Err(e) => { - warn!(context, "{:?}", e); + warn!("{:?}", e); } } } @@ -1630,10 +1630,7 @@ pub async fn handle_mdn( timestamp_sent: i64, ) -> Result> { if from_id == ContactId::SELF { - warn!( - context, - "ignoring MDN sent to self, this is a bug on the sender device" - ); + warn!("ignoring MDN sent to self, this is a bug on the sender device"); // This is not an error on our side, // we successfully ignored an invalid MDN and return `Ok`. @@ -1667,7 +1664,6 @@ pub async fn handle_mdn( res } else { info!( - context, "handle_mdn found no message with Message-ID {:?} sent by us in the database", rfc724_mid ); @@ -1785,7 +1781,7 @@ async fn ndn_maybe_add_info_msg( Chattype::Mailinglist => { // ndn_maybe_add_info_msg() is about the case when delivery to the group failed. // If we get an NDN for the mailing list, just issue a warning. - warn!(context, "ignoring NDN for mailing list."); + warn!("ignoring NDN for mailing list."); } Chattype::Single | Chattype::Undefined => {} } @@ -1806,7 +1802,7 @@ pub async fn get_unblocked_msg_cnt(context: &Context) -> usize { { Ok(res) => res, Err(err) => { - error!(context, "get_unblocked_msg_cnt() failed. {:#}", err); + error!("get_unblocked_msg_cnt() failed. {:#}", err); 0 } } @@ -1826,7 +1822,7 @@ pub async fn get_request_msg_cnt(context: &Context) -> usize { { Ok(res) => res, Err(err) => { - error!(context, "get_request_msg_cnt() failed. {:#}", err); + error!("get_request_msg_cnt() failed. {:#}", err); 0 } } @@ -1897,7 +1893,7 @@ pub(crate) async fn rfc724_mid_exists( ) -> Result> { let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>'); if rfc724_mid.is_empty() { - warn!(context, "Empty rfc724_mid passed to rfc724_mid_exists"); + warn!("Empty rfc724_mid passed to rfc724_mid_exists"); return Ok(None); } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 6d291ae23f..276a2066cd 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -8,6 +8,7 @@ use chrono::TimeZone; use format_flowed::{format_flowed, format_flowed_quote}; use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder}; use tokio::fs; +use tracing::{info, warn}; use crate::blob::BlobObject; use crate::chat::Chat; @@ -651,8 +652,7 @@ impl<'a> MimeFactory<'a> { }; let peerstates = self.peerstates_for_recipients(context).await?; - let should_encrypt = - encrypt_helper.should_encrypt(context, e2ee_guaranteed, &peerstates)?; + let should_encrypt = encrypt_helper.should_encrypt(e2ee_guaranteed, &peerstates)?; let is_encrypted = should_encrypt && !force_plaintext; let message = if parts.is_empty() { @@ -730,7 +730,6 @@ impl<'a> MimeFactory<'a> { if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { info!( - context, "mimefactory: unencrypted message mime-body:\n{}", message.clone().build().as_string(), ); @@ -819,7 +818,6 @@ impl<'a> MimeFactory<'a> { if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { info!( - context, "mimefactory: outgoing message mime-body:\n{}", outer_message.clone().build().as_string(), ); @@ -940,7 +938,6 @@ impl<'a> MimeFactory<'a> { & DC_FROM_HANDSHAKE { info!( - context, "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", "vg-member-added", ); @@ -1025,8 +1022,8 @@ impl<'a> MimeFactory<'a> { let step = msg.param.get(Param::Arg).unwrap_or_default(); if !step.is_empty() { info!( - context, - "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", step, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + step, ); headers .protected @@ -1074,7 +1071,7 @@ impl<'a> MimeFactory<'a> { } if let Some(grpimage) = grpimage { - info!(context, "setting group image '{}'", grpimage); + info!("setting group image '{}'", grpimage); let mut meta = Message { viewtype: Viewtype::Image, ..Default::default() @@ -1234,7 +1231,7 @@ impl<'a> MimeFactory<'a> { match self.get_location_kml_part(context).await { Ok(part) => parts.push(part), Err(err) => { - warn!(context, "mimefactory: could not send location: {}", err); + warn!("mimefactory: could not send location: {}", err); } } } @@ -1265,7 +1262,7 @@ impl<'a> MimeFactory<'a> { "Chat-User-Avatar".into(), format!("base64:{avatar}"), )), - Err(err) => warn!(context, "mimefactory: cannot attach selfavatar: {}", err), + Err(err) => warn!("mimefactory: cannot attach selfavatar: {}", err), }, None => headers .protected diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 6d9b242c87..ff9c2555ec 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -12,6 +12,7 @@ use format_flowed::unformat_flowed; use lettre_email::mime::{self, Mime}; use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo}; use once_cell::sync::Lazy; +use tracing::{error, info, warn}; use crate::aheader::{Aheader, EncryptPreference}; use crate::blob::BlobObject; @@ -213,7 +214,6 @@ impl MimeMessage { // Parse IMF headers. MimeMessage::merge_headers( - context, &mut headers, &mut recipients, &mut from, @@ -231,7 +231,6 @@ impl MimeMessage { // messages are shown as unencrypted anyway. MimeMessage::merge_headers( - context, &mut headers, &mut recipients, &mut from, @@ -281,14 +280,13 @@ impl MimeMessage { let public_keyring = keyring_from_peerstate(decryption_info.peerstate.as_ref()); let (mail, mut signatures, encrypted) = match tokio::task::block_in_place(|| { - try_decrypt(context, &mail, &private_keyring, &public_keyring) + try_decrypt(&mail, &private_keyring, &public_keyring) }) { Ok(Some((raw, signatures))) => { mail_raw = raw; let decrypted_mail = mailparse::parse_mail(&mail_raw)?; if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { info!( - context, "decrypted message mime-body:\n{}", String::from_utf8_lossy(&mail_raw), ); @@ -297,7 +295,7 @@ impl MimeMessage { } Ok(None) => (Ok(mail), HashSet::new(), false), Err(err) => { - warn!(context, "decryption failed: {:#}", err); + warn!("decryption failed: {:#}", err); (Err(err), HashSet::new(), false) } }; @@ -336,7 +334,6 @@ impl MimeMessage { headers.remove("subject"); MimeMessage::merge_headers( - context, &mut headers, &mut recipients, &mut signed_from, @@ -355,10 +352,7 @@ impl MimeMessage { // Or it's because someone is doing some replay attack // - OTOH, I can't come up with an attack scenario // where this would be useful. - warn!( - context, - "From header in signed part doesn't match the outer one", - ); + warn!("From header in signed part doesn't match the outer one",); } } } @@ -468,7 +462,7 @@ impl MimeMessage { } /// Parses system messages. - fn parse_system_message_headers(&mut self, context: &Context) { + fn parse_system_message_headers(&mut self) { if self.get_header(HeaderDef::AutocryptSetupMessage).is_some() { self.parts.retain(|part| { part.mimetype.is_none() @@ -478,7 +472,7 @@ impl MimeMessage { if self.parts.len() == 1 { self.is_system_message = SystemMessage::AutocryptSetupMessage; } else { - warn!(context, "could not determine ASM mime-part"); + warn!("could not determine ASM mime-part"); } } else if let Some(value) = self.get_header(HeaderDef::ChatContent) { if value == "location-streaming-enabled" { @@ -602,7 +596,7 @@ impl MimeMessage { } async fn parse_headers(&mut self, context: &Context) -> Result<()> { - self.parse_system_message_headers(context); + self.parse_system_message_headers(); self.parse_avatar_headers(context).await; self.parse_videochat_headers(); if self.delivery_report.is_none() { @@ -659,8 +653,8 @@ impl MimeMessage { } } else { warn!( - context, - "{} requested a read receipt to {}, ignoring", from, dn_to.addr + "{} requested a read receipt to {}, ignoring", + from, dn_to.addr ); } } @@ -724,10 +718,7 @@ impl MimeMessage { { Ok(blob) => Some(AvatarAction::Change(blob.as_name().to_string())), Err(err) => { - warn!( - context, - "Could not save decoded avatar to blob file: {:#}", err - ); + warn!("Could not save decoded avatar to blob file: {:#}", err); None } } @@ -795,13 +786,12 @@ impl MimeMessage { if mail.ctype.params.get("protected-headers").is_some() { if mail.ctype.mimetype == "text/rfc822-headers" { warn!( - context, "Protected headers found in text/rfc822-headers attachment: Will be ignored.", ); return Ok(false); } - warn!(context, "Ignoring nested protected headers"); + warn!( "Ignoring nested protected headers"); } enum MimeS { @@ -920,7 +910,7 @@ impl MimeMessage { if mail.subparts.len() >= 2 { match mail.ctype.params.get("report-type").map(|s| s as &str) { Some("disposition-notification") => { - if let Some(report) = self.process_report(context, mail)? { + if let Some(report) = self.process_report(mail)? { self.mdn_reports.push(report); } @@ -938,7 +928,7 @@ impl MimeMessage { } // Some providers, e.g. Tiscali, forget to set the report-type. So, if it's None, assume that it might be delivery-status Some("delivery-status") | None => { - if let Some(report) = self.process_delivery_status(context, mail)? { + if let Some(report) = self.process_delivery_status(mail)? { self.delivery_report = Some(report); } @@ -1002,7 +992,7 @@ impl MimeMessage { let (mime_type, msg_type) = get_mime_type(mail)?; let raw_mime = mail.ctype.mimetype.to_lowercase(); - let filename = get_attachment_filename(context, mail)?; + let filename = get_attachment_filename(mail)?; let old_part_count = self.parts.len(); @@ -1022,7 +1012,7 @@ impl MimeMessage { None => { match mime_type.type_() { mime::IMAGE | mime::AUDIO | mime::VIDEO | mime::APPLICATION => { - warn!(context, "Missing attachment"); + warn!("Missing attachment"); return Ok(false); } mime::TEXT @@ -1033,7 +1023,7 @@ impl MimeMessage { let decoded_data = match mail.get_body() { Ok(decoded_data) => decoded_data, Err(err) => { - warn!(context, "Invalid body parsed {:#}", err); + warn!("Invalid body parsed {:#}", err); // Note that it's not always an error - might be no data return Ok(false); } @@ -1053,7 +1043,7 @@ impl MimeMessage { let decoded_data = match mail.get_body() { Ok(decoded_data) => decoded_data, Err(err) => { - warn!(context, "Invalid body parsed {:#}", err); + warn!("Invalid body parsed {:#}", err); // Note that it's not always an error - might be no data return Ok(false); } @@ -1185,7 +1175,7 @@ impl MimeMessage { if filename.starts_with("location") || filename.starts_with("message") { let parsed = location::Kml::parse(decoded_data) .map_err(|err| { - warn!(context, "failed to parse kml part: {:#}", err); + warn!("failed to parse kml part: {:#}", err); }) .ok(); if filename.starts_with("location") { @@ -1203,7 +1193,7 @@ impl MimeMessage { self.sync_items = context .parse_sync_items(serialized) .map_err(|err| { - warn!(context, "failed to parse sync data: {:#}", err); + warn!("failed to parse sync data: {:#}", err); }) .ok(); return Ok(()); @@ -1224,13 +1214,13 @@ impl MimeMessage { Ok(blob) => blob, Err(err) => { error!( - context, - "Could not add blob for mime part {}, error {:#}", filename, err + "Could not add blob for mime part {}, error {:#}", + filename, err ); return Ok(()); } }; - info!(context, "added blobfile: {:?}", blob.as_name()); + info!("added blobfile: {:?}", blob.as_name()); /* create and register Mime part referencing the new Blob object */ let mut part = Part::default(); @@ -1261,23 +1251,20 @@ impl MimeMessage { ) -> Result { let key = match str::from_utf8(decoded_data) { Err(err) => { - warn!(context, "PGP key attachment is not a UTF-8 file: {}", err); + warn!("PGP key attachment is not a UTF-8 file: {}", err); return Ok(false); } Ok(key) => key, }; let key = match SignedPublicKey::from_asc(key) { Err(err) => { - warn!( - context, - "PGP key attachment is not an ASCII-armored file: {:#}", err - ); + warn!("PGP key attachment is not an ASCII-armored file: {:#}", err); return Ok(false); } Ok((key, _)) => key, }; if let Err(err) = key.verify() { - warn!(context, "attached PGP key verification failed: {}", err); + warn!("attached PGP key verification failed: {}", err); return Ok(false); } if !key.details.users.iter().any(|user| { @@ -1293,19 +1280,17 @@ impl MimeMessage { // user have an Autocrypt-capable MUA and also attaches a key, but if that's the // case, let 'em first disable Autocrypt and then change the key by attaching it. warn!( - context, "not using attached PGP key for peer '{}' because another one is already set \ with prefer-encrypt={}", - peerstate.addr, - peerstate.prefer_encrypt, + peerstate.addr, peerstate.prefer_encrypt, ); return Ok(false); } } peerstate.public_key = Some(key); info!( - context, - "using attached PGP key for peer '{}' with prefer-encrypt=mutual", peerstate.addr, + "using attached PGP key for peer '{}' with prefer-encrypt=mutual", + peerstate.addr, ); peerstate.prefer_encrypt = EncryptPreference::Mutual; peerstate.save_to_db(&context.sql).await?; @@ -1357,7 +1342,6 @@ impl MimeMessage { } fn merge_headers( - context: &Context, headers: &mut HashMap, recipients: &mut Vec, from: &mut Option, @@ -1376,7 +1360,7 @@ impl MimeMessage { Ok(addrlist) => { *chat_disposition_notification_to = addrlist.extract_single_info(); } - Err(e) => warn!(context, "Could not read {} address: {}", key, e), + Err(e) => warn!("Could not read {} address: {}", key, e), } } else { let value = field.get_value(); @@ -1398,11 +1382,7 @@ impl MimeMessage { } } - fn process_report( - &self, - context: &Context, - report: &mailparse::ParsedMail<'_>, - ) -> Result> { + fn process_report(&self, report: &mailparse::ParsedMail<'_>) -> Result> { // parse as mailheaders let report_body = if let Some(subpart) = report.subparts.get(1) { subpart.get_body_raw()? @@ -1433,7 +1413,6 @@ impl MimeMessage { })); } warn!( - context, "ignoring unknown disposition-notification, Message-Id: {:?}", report_fields.get_header_value(HeaderDef::MessageId) ); @@ -1443,7 +1422,6 @@ impl MimeMessage { fn process_delivery_status( &self, - context: &Context, report: &mailparse::ParsedMail<'_>, ) -> Result> { // Assume failure. @@ -1455,7 +1433,7 @@ impl MimeMessage { if status_part.ctype.mimetype != "message/delivery-status" && status_part.ctype.mimetype != "message/global-delivery-status" { - warn!(context, "Second part of Delivery Status Notification is not message/delivery-status or message/global-delivery-status, ignoring"); + warn!( "Second part of Delivery Status Notification is not message/delivery-status or message/global-delivery-status, ignoring"); return Ok(None); } @@ -1469,14 +1447,14 @@ impl MimeMessage { let (status_fields, _) = mailparse::parse_headers(status_body)?; if let Some(action) = status_fields.get_first_value("action") { if action != "failed" { - info!(context, "DSN with {:?} action", action); + info!("DSN with {:?} action", action); failure = false; } } else { - warn!(context, "DSN without action"); + warn!("DSN without action"); } } else { - warn!(context, "DSN without per-recipient fields"); + warn!("DSN without per-recipient fields"); } } else { // No message/delivery-status part. @@ -1513,7 +1491,6 @@ impl MimeMessage { } warn!( - context, "ignoring unknown ndn-notification, Message-Id: {:?}", report_fields.get_header_value(HeaderDef::MessageId) ); @@ -1638,7 +1615,7 @@ impl MimeMessage { } Ok(None) => {} Err(err) => { - warn!(context, "failed to handle_mdn: {:#}", err); + warn!("failed to handle_mdn: {:#}", err); } } } @@ -1651,7 +1628,7 @@ impl MimeMessage { .find(|p| p.typ == Viewtype::Text) .map(|p| p.msg.clone()); if let Err(e) = message::handle_ndn(context, delivery_report, error).await { - warn!(context, "Could not handle ndn: {}", e); + warn!("Could not handle ndn: {}", e); } } } @@ -1699,7 +1676,7 @@ async fn update_gossip_peerstates( let header = match value.parse::() { Ok(header) => header, Err(err) => { - warn!(context, "Failed parsing Autocrypt-Gossip header: {}", err); + warn!("Failed parsing Autocrypt-Gossip header: {}", err); continue; } }; @@ -1709,16 +1686,16 @@ async fn update_gossip_peerstates( .any(|info| addr_cmp(&info.addr, &header.addr)) { warn!( - context, - "Ignoring gossiped \"{}\" as the address is not in To/Cc list.", &header.addr, + "Ignoring gossiped \"{}\" as the address is not in To/Cc list.", + &header.addr, ); continue; } if addr_cmp(from, &header.addr) { // Non-standard, but anyway we can't update the cached peerstate here. warn!( - context, - "Ignoring gossiped \"{}\" as it equals the From address", &header.addr, + "Ignoring gossiped \"{}\" as it equals the From address", + &header.addr, ); continue; } @@ -1909,10 +1886,7 @@ fn is_attachment_disposition(mail: &mailparse::ParsedMail<'_>) -> bool { /// returned. If Content-Disposition is "attachment" but filename is /// not specified, filename is guessed. If Content-Disposition cannot /// be parsed, returns an error. -fn get_attachment_filename( - context: &Context, - mail: &mailparse::ParsedMail, -) -> Result> { +fn get_attachment_filename(mail: &mailparse::ParsedMail) -> Result> { let ct = mail.get_content_disposition(); // try to get file name as "encoded-words" from @@ -1924,7 +1898,7 @@ fn get_attachment_filename( // be graceful and just use the original name. // some MUA, including Delta Chat up to core1.50, // use `filename*` mistakenly for simple encoded-words without following rfc2231 - warn!(context, "apostrophed encoding invalid: {}", name); + warn!("apostrophed encoding invalid: {}", name); desired_filename = Some(name); } } @@ -2154,147 +2128,123 @@ mod tests { assert!(is_attachment_disposition(&mail.subparts[1])); } - fn load_mail_with_attachment<'a>(t: &'a TestContext, raw: &'a [u8]) -> ParsedMail<'a> { + fn load_mail_with_attachment(raw: &[u8]) -> ParsedMail<'_> { let mail = mailparse::parse_mail(raw).unwrap(); - assert!(get_attachment_filename(t, &mail).unwrap().is_none()); - assert!(get_attachment_filename(t, &mail.subparts[0]) + assert!(get_attachment_filename(&mail).unwrap().is_none()); + assert!(get_attachment_filename(&mail.subparts[0]) .unwrap() .is_none()); mail } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_simple.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_simple.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("test.html".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_encoded_words.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_encoded_words() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_encoded_words.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Maßnahmen Okt. 2020.html".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_encoded_words_binary.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_encoded_words_binary() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_encoded_words_binary.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some(" § 165 Abs".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_encoded_words_windows1251.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_encoded_words_windows1251() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_encoded_words_windows1251.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("file Что нового 2020.pdf".to_string())) } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_get_attachment_filename_encoded_words_cont() { + #[test] + fn test_get_attachment_filename_encoded_words_cont() { // test continued encoded-words and also test apostropes work that way - let t = TestContext::new().await; - let mail = load_mail_with_attachment( - &t, - include_bytes!("../test-data/message/attach_filename_encoded_words_cont.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_encoded_words_cont.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Maßn'ah'men Okt. 2020.html".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_encoded_words_bad_delimiter.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_encoded_words_bad_delimiter() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_encoded_words_bad_delimiter.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); // not decoded as a space is missing after encoded-words part assert_eq!(filename, Some("=?utf-8?q?foo?=.bar".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_apostrophed.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_apostrophed() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_apostrophed.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Maßnahmen Okt. 2021.html".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_apostrophed_cont.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_apostrophed_cont() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_apostrophed_cont.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Maßnahmen März 2022.html".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_apostrophed_windows1251.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_apostrophed_windows1251() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_apostrophed_windows1251.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("программирование.HTM".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_apostrophed_cp1252.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_apostrophed_cp1252() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_apostrophed_cp1252.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Auftragsbestätigung.pdf".to_string())) } - #[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( - &t, - include_bytes!("../test-data/message/attach_filename_apostrophed_invalid.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + #[test] + fn test_get_attachment_filename_apostrophed_invalid() { + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_apostrophed_invalid.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("somedäüta.html.zip".to_string())) } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_get_attachment_filename_combined() { + #[test] + 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; - let mail = load_mail_with_attachment( - &t, - include_bytes!("../test-data/message/attach_filename_combined.eml"), - ); - let filename = get_attachment_filename(&t, &mail.subparts[1]).unwrap(); + let mail = load_mail_with_attachment(include_bytes!( + "../test-data/message/attach_filename_combined.eml" + )); + let filename = get_attachment_filename(&mail.subparts[1]).unwrap(); assert_eq!(filename, Some("Maßnahmen Okt. 2020.html".to_string())) } diff --git a/src/net.rs b/src/net.rs index a0f3a33132..c341d1c847 100644 --- a/src/net.rs +++ b/src/net.rs @@ -8,6 +8,7 @@ use anyhow::{Context as _, Error, Result}; use tokio::net::{lookup_host, TcpStream}; use tokio::time::timeout; use tokio_io_timeout::TimeoutStream; +use tracing::{info, warn}; use crate::context::Context; use crate::tools::time; @@ -50,8 +51,8 @@ async fn lookup_host_with_cache( Ok(res) => res, Err(err) => { warn!( - context, - "DNS resolution for {}:{} failed: {:#}.", hostname, port, err + "DNS resolution for {}:{} failed: {:#}.", + hostname, port, err ); Vec::new() } @@ -64,7 +65,7 @@ async fn lookup_host_with_cache( continue; } - info!(context, "Resolved {}:{} into {}.", hostname, port, &addr); + info!("Resolved {}:{} into {}.", hostname, port, &addr); // Update the cache. context @@ -110,8 +111,8 @@ async fn lookup_host_with_cache( } Err(err) => { warn!( - context, - "Failed to parse cached address {:?}: {:#}.", cached_address, err + "Failed to parse cached address {:?}: {:#}.", + cached_address, err ); } } @@ -163,10 +164,7 @@ pub(crate) async fn connect_tcp( break; } Err(err) => { - warn!( - context, - "Failed to connect to {}: {:#}.", resolved_addr, err - ); + warn!("Failed to connect to {}: {:#}.", resolved_addr, err); last_error = Some(err); } } diff --git a/src/oauth2.rs b/src/oauth2.rs index 2fe8795b4d..01823c2267 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use anyhow::Result; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use serde::Deserialize; +use tracing::{info, warn}; use crate::config::Config; use crate::context::Context; @@ -62,7 +63,7 @@ pub async fn get_oauth2_url( redirect_uri: &str, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - if let Some(oauth2) = Oauth2::from_address(context, addr, socks5_enabled).await { + if let Some(oauth2) = Oauth2::from_address(addr, socks5_enabled).await { context .sql .set_raw_config("oauth2_pending_redirect_uri", Some(redirect_uri)) @@ -83,7 +84,7 @@ pub(crate) async fn get_oauth2_access_token( regenerate: bool, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - if let Some(oauth2) = Oauth2::from_address(context, addr, socks5_enabled).await { + if let Some(oauth2) = Oauth2::from_address(addr, socks5_enabled).await { let lock = context.oauth2_mutex.lock().await; // read generated token @@ -105,7 +106,7 @@ pub(crate) async fn get_oauth2_access_token( let (redirect_uri, token_url, update_redirect_uri_on_success) = if refresh_token.is_none() || refresh_token_for != code { - info!(context, "Generate OAuth2 refresh_token and access_token...",); + info!("Generate OAuth2 refresh_token and access_token...",); ( context .sql @@ -116,10 +117,7 @@ pub(crate) async fn get_oauth2_access_token( true, ) } else { - info!( - context, - "Regenerate OAuth2 access_token by refresh_token...", - ); + info!("Regenerate OAuth2 access_token by refresh_token...",); ( context .sql @@ -167,14 +165,14 @@ pub(crate) async fn get_oauth2_access_token( Ok(response) => response, Err(err) => { warn!( - context, - "Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err + "Failed to parse OAuth2 JSON response from {}: error: {}", + token_url, err ); return Ok(None); } }, Err(err) => { - warn!(context, "Error calling OAuth2 at {}: {:?}", token_url, err); + warn!("Error calling OAuth2 at {}: {:?}", token_url, err); return Ok(None); } }; @@ -215,14 +213,14 @@ pub(crate) async fn get_oauth2_access_token( .await?; } } else { - warn!(context, "Failed to find OAuth2 access token"); + warn!("Failed to find OAuth2 access token"); } drop(lock); Ok(response.access_token) } else { - warn!(context, "Internal OAuth2 error: 2"); + warn!("Internal OAuth2 error: 2"); Ok(None) } @@ -234,7 +232,7 @@ pub(crate) async fn get_oauth2_addr( code: &str, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - let oauth2 = match Oauth2::from_address(context, addr, socks5_enabled).await { + let oauth2 = match Oauth2::from_address(addr, socks5_enabled).await { Some(o) => o, None => return Ok(None), }; @@ -260,13 +258,13 @@ pub(crate) async fn get_oauth2_addr( } impl Oauth2 { - async fn from_address(context: &Context, addr: &str, skip_mx: bool) -> Option { + async fn from_address(addr: &str, skip_mx: bool) -> Option { let addr_normalized = normalize_addr(addr); if let Some(domain) = addr_normalized .find('@') .map(|index| addr_normalized.split_at(index + 1).1) { - if let Some(oauth2_authorizer) = provider::get_provider_info(context, domain, skip_mx) + if let Some(oauth2_authorizer) = provider::get_provider_info(domain, skip_mx) .await .and_then(|provider| provider.oauth2_authorizer.as_ref()) { @@ -294,14 +292,14 @@ impl Oauth2 { let client = match crate::http::get_client(socks5_config) { Ok(cl) => cl, Err(err) => { - warn!(context, "failed to get HTTP client: {}", err); + warn!("failed to get HTTP client: {}", err); return None; } }; let response = match client.get(userinfo_url).send().await { Ok(response) => response, Err(err) => { - warn!(context, "failed to get userinfo: {}", err); + warn!("failed to get userinfo: {}", err); return None; } }; @@ -309,7 +307,7 @@ impl Oauth2 { let parsed = match response { Ok(parsed) => parsed, Err(err) => { - warn!(context, "Error getting userinfo: {}", err); + warn!("Error getting userinfo: {}", err); return None; } }; @@ -319,11 +317,11 @@ impl Oauth2 { if let Some(s) = addr.as_str() { Some(s.to_string()) } else { - warn!(context, "E-mail in userinfo is not a string: {}", addr); + warn!("E-mail in userinfo is not a string: {}", addr); None } } else { - warn!(context, "E-mail missing in userinfo."); + warn!("E-mail missing in userinfo."); None } } @@ -377,39 +375,34 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_oauth_from_address() { - let t = TestContext::new().await; assert_eq!( - Oauth2::from_address(&t, "hello@gmail.com", false).await, + Oauth2::from_address("hello@gmail.com", false).await, Some(OAUTH2_GMAIL) ); assert_eq!( - Oauth2::from_address(&t, "hello@googlemail.com", false).await, + Oauth2::from_address("hello@googlemail.com", false).await, Some(OAUTH2_GMAIL) ); assert_eq!( - Oauth2::from_address(&t, "hello@yandex.com", false).await, + Oauth2::from_address("hello@yandex.com", false).await, Some(OAUTH2_YANDEX) ); assert_eq!( - Oauth2::from_address(&t, "hello@yandex.ru", false).await, + Oauth2::from_address("hello@yandex.ru", false).await, Some(OAUTH2_YANDEX) ); - assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None); + assert_eq!(Oauth2::from_address("hello@web.de", false).await, None); } #[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; assert_eq!( - Oauth2::from_address(&t, "hello@youtube.com", false).await, + Oauth2::from_address("hello@youtube.com", false).await, Some(OAUTH2_GMAIL) ); // without MX lookup, we would not know as youtube.com is not in our provider-db - assert_eq!( - Oauth2::from_address(&t, "hello@youtube.com", true).await, - None - ); + assert_eq!(Oauth2::from_address("hello@youtube.com", true).await, None); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] diff --git a/src/peerstate.rs b/src/peerstate.rs index 16378c9188..114884344a 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -4,6 +4,7 @@ use std::collections::HashSet; use anyhow::{Context as _, Error, Result}; use num_traits::FromPrimitive; +use tracing::warn; use crate::aheader::{Aheader, EncryptPreference}; use crate::chat::{self, Chat}; @@ -594,10 +595,8 @@ impl Peerstate { } Err(err) => { warn!( - context, "New address {:?} is not valid, not doing AEAP: {:#}.", - new_addr, - err + new_addr, err ) } } diff --git a/src/provider.rs b/src/provider.rs index 2d37b13474..416d38cac8 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -4,10 +4,10 @@ mod data; use anyhow::Result; use chrono::{NaiveDateTime, NaiveTime}; +use tracing::warn; use trust_dns_resolver::{config, AsyncResolver, TokioAsyncResolver}; use crate::config::Config; -use crate::context::Context; use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED}; /// Provider status according to manual testing. @@ -184,11 +184,7 @@ fn get_resolver() -> Result { /// /// For compatibility, email address can be passed to this function /// instead of the domain. -pub async fn get_provider_info( - context: &Context, - domain: &str, - skip_mx: bool, -) -> Option<&'static Provider> { +pub async fn get_provider_info(domain: &str, skip_mx: bool) -> Option<&'static Provider> { let domain = domain.rsplit('@').next()?; if let Some(provider) = get_provider_by_domain(domain) { @@ -196,7 +192,7 @@ pub async fn get_provider_info( } if !skip_mx { - if let Some(provider) = get_provider_by_mx(context, domain).await { + if let Some(provider) = get_provider_by_mx(domain).await { return Some(provider); } } @@ -216,7 +212,7 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> { /// Finds a provider based on MX record for the given domain. /// /// For security reasons, only Gmail can be configured this way. -pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'static Provider> { +pub async fn get_provider_by_mx(domain: &str) -> Option<&'static Provider> { if let Ok(resolver) = get_resolver() { let mut fqdn: String = domain.to_string(); if !fqdn.ends_with('.') { @@ -243,7 +239,7 @@ pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'sta } } } else { - warn!(context, "cannot get a resolver to check MX records."); + warn!("cannot get a resolver to check MX records."); } None @@ -272,7 +268,6 @@ mod tests { use chrono::NaiveDate; use super::*; - use crate::test_utils::TestContext; use crate::tools::time; #[test] @@ -322,13 +317,12 @@ mod tests { #[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()); - assert!(get_provider_info(&t, "google.com", false).await.unwrap().id == "gmail"); + assert!(get_provider_info("", false).await.is_none()); + assert!(get_provider_info("google.com", false).await.unwrap().id == "gmail"); // get_provider_info() accepts email addresses for backwards compatibility assert!( - get_provider_info(&t, "example@google.com", false) + get_provider_info("example@google.com", false) .await .unwrap() .id diff --git a/src/qr.rs b/src/qr.rs index 920776b5eb..1a32ffd6f2 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -8,6 +8,7 @@ pub use dclogin_scheme::LoginOptions; use once_cell::sync::Lazy; use percent_encoding::percent_decode_str; use serde::Deserialize; +use tracing::info; use self::dclogin_scheme::configure_from_login_qr; use crate::chat::{self, get_chat_id_by_grpid, ChatIdBlocked}; @@ -247,7 +248,7 @@ fn starts_with_ignore_case(string: &str, pattern: &str) -> bool { /// The function should be called after a QR code is scanned. /// The function takes the raw text scanned and checks what can be done with it. pub async fn check_qr(context: &Context, qr: &str) -> Result { - info!(context, "Scanned QR code: {}", qr); + info!("Scanned QR code: {}", qr); let qrcode = if starts_with_ignore_case(qr, OPENPGP4FPR_SCHEME) { decode_openpgp(context, qr) diff --git a/src/quota.rs b/src/quota.rs index f192fd1d79..58e56abc11 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -5,6 +5,7 @@ use std::sync::atomic::Ordering; use anyhow::{anyhow, Context as _, Result}; use async_imap::types::{Quota, QuotaResource}; +use tracing::warn; use crate::chat::add_device_msg_with_importance; use crate::config::Config; @@ -134,7 +135,7 @@ impl Context { /// Called in response to `Action::UpdateRecentQuota`. pub(crate) async fn update_recent_quota(&self, imap: &mut Imap) -> Result<()> { if let Err(err) = imap.prepare(self).await { - warn!(self, "could not connect: {:#}", err); + warn!("could not connect: {err:#}"); return Ok(()); } @@ -162,7 +163,7 @@ impl Context { self.set_config(Config::QuotaExceeding, None).await?; } } - Err(err) => warn!(self, "cannot get highest quota usage: {:#}", err), + Err(err) => warn!("cannot get highest quota usage: {:#}", err), } } diff --git a/src/reaction.rs b/src/reaction.rs index 832304b87f..35d014b22d 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -18,6 +18,7 @@ use std::collections::BTreeMap; use std::fmt; use anyhow::Result; +use tracing::info; use crate::chat::{send_msg, ChatId}; use crate::contact::ContactId; @@ -232,8 +233,8 @@ pub(crate) async fn set_msg_reaction( set_msg_id_reaction(context, msg_id, chat_id, contact_id, reaction).await } else { info!( - context, - "Can't assign reaction to unknown message with Message-ID {}", in_reply_to + "Can't assign reaction to unknown message with Message-ID {}", + in_reply_to ); Ok(()) } diff --git a/src/receive_imf.rs b/src/receive_imf.rs index f10d145496..f1c46c5712 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -9,6 +9,7 @@ use mailparse::{parse_mail, SingleInfo}; use num_traits::FromPrimitive; use once_cell::sync::Lazy; use regex::Regex; +use tracing::{info, warn}; use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ProtectionStatus}; use crate::config::Config; @@ -96,11 +97,10 @@ pub(crate) async fn receive_imf_inner( is_partial_download: Option, fetching_existing_messages: bool, ) -> Result> { - info!(context, "Receiving message, seen={seen}..."); + info!("Receiving message, seen={seen}..."); if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { info!( - context, "receive_imf: incoming message mime-body:\n{}", String::from_utf8_lossy(imf_raw), ); @@ -109,7 +109,7 @@ pub(crate) async fn receive_imf_inner( let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, is_partial_download).await { Err(err) => { - warn!(context, "receive_imf: can't parse MIME: {err:#}."); + warn!("receive_imf: can't parse MIME: {err:#}."); let msg_ids; if !rfc724_mid.starts_with(GENERATED_PREFIX) { let row_id = context @@ -138,11 +138,11 @@ pub(crate) async fn receive_imf_inner( // we can not add even an empty record if we have no info whatsoever if !mime_parser.has_headers() { - warn!(context, "receive_imf: no headers found."); + warn!("receive_imf: no headers found."); return Ok(None); } - info!(context, "Received message has Message-Id: {rfc724_mid}"); + info!("Received message has Message-Id: {rfc724_mid}"); // check, if the mail is already in our database. // make sure, this check is done eg. before securejoin-processing. @@ -151,14 +151,11 @@ pub(crate) async fn receive_imf_inner( let msg = Message::load_from_db(context, old_msg_id).await?; if msg.download_state() != DownloadState::Done && is_partial_download.is_none() { // the message was partially downloaded before and is fully downloaded now. - info!( - context, - "Message already partly in DB, replacing by full message." - ); + info!("Message already partly in DB, replacing by full message."); Some(old_msg_id) } else { // the message was probably moved around. - info!(context, "Message already in DB, doing nothing."); + info!("Message already in DB, doing nothing."); return Ok(None); } } else { @@ -180,10 +177,7 @@ pub(crate) async fn receive_imf_inner( match from_field_to_contact_id(context, &mime_parser.from, prevent_rename).await? { Some(contact_id_res) => contact_id_res, None => { - warn!( - context, - "receive_imf: From field does not contain an acceptable address." - ); + warn!("receive_imf: From field does not contain an acceptable address."); return Ok(None); } }; @@ -246,10 +240,7 @@ pub(crate) async fn receive_imf_inner( .iter() .all(|recipient| mime_parser.gossiped_addr.contains(&recipient.addr)) { - info!( - context, - "Received message contains Autocrypt-Gossip for all members, updating timestamp." - ); + info!("Received message contains Autocrypt-Gossip for all members, updating timestamp."); if chat_id.get_gossiped_timestamp(context).await? < sent_timestamp { chat_id .set_gossiped_timestamp(context, sent_timestamp) @@ -269,13 +260,13 @@ pub(crate) async fn receive_imf_inner( if from_id == ContactId::SELF { if mime_parser.was_encrypted() { if let Err(err) = context.execute_sync_items(sync_items).await { - warn!(context, "receive_imf cannot execute sync items: {err:#}."); + warn!("receive_imf cannot execute sync items: {err:#}."); } } else { - warn!(context, "Sync items are not encrypted."); + warn!("Sync items are not encrypted."); } } else { - warn!(context, "Sync items not sent by self."); + warn!("Sync items not sent by self."); } } @@ -284,7 +275,7 @@ pub(crate) async fn receive_imf_inner( .receive_status_update(from_id, insert_msg_id, status_update) .await { - warn!(context, "receive_imf cannot update status: {err:#}."); + warn!("receive_imf cannot update status: {err:#}."); } } @@ -302,7 +293,7 @@ pub(crate) async fn receive_imf_inner( ) .await { - warn!(context, "receive_imf cannot update profile image: {err:#}."); + warn!("receive_imf cannot update profile image: {err:#}."); }; } } @@ -328,7 +319,7 @@ pub(crate) async fn receive_imf_inner( ) .await { - warn!(context, "Cannot update contact status: {err:#}."); + warn!("Cannot update contact status: {err:#}."); } } @@ -389,10 +380,7 @@ pub async fn from_field_to_contact_id( let from_addr = match ContactAddress::new(&from.addr) { Ok(from_addr) => from_addr, Err(err) => { - warn!( - context, - "Cannot create a contact for the given From field: {err:#}." - ); + warn!("Cannot create a contact for the given From field: {err:#}."); return Ok(None); } }; @@ -473,7 +461,7 @@ async fn add_parts( // this message is a classic email not a chat-message nor a reply to one match show_emails { ShowEmails::Off => { - info!(context, "Classical email not shown (TRASH)."); + info!("Classical email not shown (TRASH)."); chat_id = Some(DC_CHAT_ID_TRASH); allow_creation = false; } @@ -516,7 +504,7 @@ async fn add_parts( securejoin_seen = false; } Err(err) => { - warn!(context, "Error in Secure-Join message handling: {err:#}."); + warn!("Error in Secure-Join message handling: {err:#}."); chat_id = Some(DC_CHAT_ID_TRASH); securejoin_seen = true; } @@ -533,7 +521,7 @@ async fn add_parts( if chat_id.is_none() && mime_parser.delivery_report.is_some() { chat_id = Some(DC_CHAT_ID_TRASH); - info!(context, "Message is a DSN (TRASH).",); + info!("Message is a DSN (TRASH).",); } if chat_id.is_none() { @@ -707,10 +695,7 @@ async fn add_parts( // the contact requests will pop up and this should be just fine. Contact::scaleup_origin_by_id(context, from_id, Origin::IncomingReplyTo) .await?; - info!( - context, - "Message is a reply to a known message, mark sender as known.", - ); + info!("Message is a reply to a known message, mark sender as known.",); } } } @@ -751,7 +736,7 @@ async fn add_parts( chat_id = None; } Err(err) => { - warn!(context, "Error in Secure-Join watching: {err:#}."); + warn!("Error in Secure-Join watching: {err:#}."); chat_id = Some(DC_CHAT_ID_TRASH); } } @@ -768,7 +753,7 @@ async fn add_parts( if is_draft { // Most mailboxes have a "Drafts" folder where constantly new emails appear but we don't actually want to show them - info!(context, "Email is probably just a draft (TRASH)."); + info!("Email is probably just a draft (TRASH)."); chat_id = Some(DC_CHAT_ID_TRASH); } @@ -860,14 +845,14 @@ async fn add_parts( if fetching_existing_messages && mime_parser.decrypting_failed { chat_id = Some(DC_CHAT_ID_TRASH); // We are only gathering old messages on first start. We do not want to add loads of non-decryptable messages to the chats. - info!(context, "Existing non-decipherable message (TRASH)."); + info!("Existing non-decipherable message (TRASH)."); } if mime_parser.webxdc_status_update.is_some() && mime_parser.parts.len() == 1 { if let Some(part) = mime_parser.parts.first() { if part.typ == Viewtype::Text && part.msg.is_empty() { chat_id = Some(DC_CHAT_ID_TRASH); - info!(context, "Message is a status update only (TRASH)."); + info!("Message is a status update only (TRASH)."); } } } @@ -877,7 +862,7 @@ async fn add_parts( DC_CHAT_ID_TRASH } else { chat_id.unwrap_or_else(|| { - info!(context, "No chat id for message (TRASH)."); + info!("No chat id for message (TRASH)."); DC_CHAT_ID_TRASH }) }; @@ -889,7 +874,7 @@ async fn add_parts( match value.parse::() { Ok(timer) => timer, Err(err) => { - warn!(context, "Can't parse ephemeral timer \"{value}\": {err:#}."); + warn!("Can't parse ephemeral timer \"{value}\": {err:#}."); EphemeralTimer::Disabled } } @@ -909,7 +894,7 @@ async fn add_parts( && !mime_parser.parts.is_empty() && chat_id.get_ephemeral_timer(context).await? != ephemeral_timer { - info!(context, "Received new ephemeral timer value {ephemeral_timer:?} for chat {chat_id}, checking if it should be applied."); + info!("Received new ephemeral timer value {ephemeral_timer:?} for chat {chat_id}, checking if it should be applied."); if is_dc_message == MessengerMessage::Yes && get_previous_message(context, mime_parser) .await? @@ -924,7 +909,6 @@ async fn add_parts( // value is different, it means the sender has not received some timer update that we // have seen or sent ourselves, so we ignore incoming timer to prevent a rollback. warn!( - context, "Ignoring ephemeral timer change to {ephemeral_timer:?} for chat {chat_id} to avoid rollback.", ); } else if chat_id @@ -935,15 +919,9 @@ async fn add_parts( .inner_set_ephemeral_timer(context, ephemeral_timer) .await { - warn!( - context, - "Failed to modify timer for chat {chat_id}: {err:#}." - ); + warn!("Failed to modify timer for chat {chat_id}: {err:#}."); } else { - info!( - context, - "Updated ephemeral timer to {ephemeral_timer:?} for chat {chat_id}." - ); + info!("Updated ephemeral timer to {ephemeral_timer:?} for chat {chat_id}."); if mime_parser.is_system_message != SystemMessage::EphemeralTimerChanged { chat::add_info_msg( context, @@ -955,10 +933,7 @@ async fn add_parts( } } } else { - warn!( - context, - "Ignoring ephemeral timer change to {ephemeral_timer:?} because it is outdated." - ); + warn!("Ignoring ephemeral timer change to {ephemeral_timer:?} because it is outdated."); } } @@ -986,7 +961,7 @@ async fn add_parts( if chat.is_protected() || new_status.is_some() { if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await { - warn!(context, "Verification problem: {err:#}."); + warn!("Verification problem: {err:#}."); let s = format!("{err}. See 'Info' for more details"); mime_parser.repl_msg_by_error(&s); } else { @@ -1243,10 +1218,7 @@ SET rfc724_mid=excluded.rfc724_mid, chat_id=excluded.chat_id, chat_id.unarchive_if_not_muted(context, state).await?; - info!( - context, - "Message has {icnt} parts and is assigned to chat #{chat_id}." - ); + info!("Message has {icnt} parts and is assigned to chat #{chat_id}."); // new outgoing message from another device marks the chat as noticed. if !incoming && !chat_id.is_special() { @@ -1327,7 +1299,6 @@ async fn save_locations( } } else { warn!( - context, "Address in location.kml {:?} is not the same as the sender address {:?}.", addr, contact.get_addr() @@ -1415,8 +1386,8 @@ async fn lookup_chat_by_reply( } info!( - context, - "Assigning message to {} as it's a reply to {}.", parent_chat.id, parent.rfc724_mid + "Assigning message to {} as it's a reply to {}.", + parent_chat.id, parent.rfc724_mid ); return Ok(Some((parent_chat.id, parent_chat.blocked))); } @@ -1486,7 +1457,7 @@ async fn create_or_lookup_group( .map(|chat_id| (chat_id, create_blocked)); return Ok(res); } else { - info!(context, "Creating ad-hoc group prevented from caller."); + info!("Creating ad-hoc group prevented from caller."); return Ok(None); }; @@ -1512,7 +1483,7 @@ async fn create_or_lookup_group( let create_protected = if mime_parser.get_header(HeaderDef::ChatVerified).is_some() { if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await { - warn!(context, "Verification problem: {err:#}."); + warn!("Verification problem: {err:#}."); let s = format!("{err}. See 'Info' for more details"); mime_parser.repl_msg_by_error(&s); } @@ -1544,7 +1515,7 @@ async fn create_or_lookup_group( { // Group does not exist but should be created. if !allow_creation { - info!(context, "Creating group forbidden by caller."); + info!("Creating group forbidden by caller."); return Ok(None); } @@ -1589,6 +1560,7 @@ async fn create_or_lookup_group( // .await?; //} + info!("Chat {} is created.", new_chat_id); context.emit_event(EventType::ChatModified(new_chat_id)); } @@ -1605,7 +1577,7 @@ async fn create_or_lookup_group( } else { // The message was decrypted successfully, but contains a late "quit" or otherwise // unwanted message. - info!(context, "Message belongs to unwanted group (TRASH)."); + info!("Message belongs to unwanted group (TRASH)."); Ok(Some((DC_CHAT_ID_TRASH, Blocked::Not))) } } @@ -1645,7 +1617,7 @@ async fn apply_group_changes( Some(stock_str::msg_del_member(context, &removed_addr, from_id).await) }; } - None => warn!(context, "Removed {removed_addr:?} has no contact_id."), + None => warn!("Removed {removed_addr:?} has no contact_id."), } } else { removed_id = None; @@ -1670,7 +1642,7 @@ async fn apply_group_changes( .update_timestamp(context, Param::GroupNameTimestamp, sent_timestamp) .await? { - info!(context, "Updating grpname for chat {chat_id}."); + info!("Updating grpname for chat {chat_id}."); context .sql .execute( @@ -1710,7 +1682,7 @@ async fn apply_group_changes( if mime_parser.get_header(HeaderDef::ChatVerified).is_some() { if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await { - warn!(context, "Verification problem: {err:#}."); + warn!("Verification problem: {err:#}."); let s = format!("{err}. See 'Info' for more details"); mime_parser.repl_msg_by_error(&s); } @@ -1729,7 +1701,6 @@ async fn apply_group_changes( && !chat::is_contact_in_chat(context, chat_id, from_id).await? { warn!( - context, "Contact {from_id} attempts to modify group chat {chat_id} member list without being a member." ); } else if chat_id @@ -1763,7 +1734,7 @@ async fn apply_group_changes( } members_to_add.dedup(); - info!(context, "Adding {members_to_add:?} to chat id={chat_id}."); + info!("Adding {members_to_add:?} to chat id={chat_id}."); chat::add_to_chat_contacts_table(context, chat_id, &members_to_add).await?; send_event_chat_modified = true; } @@ -1771,17 +1742,13 @@ async fn apply_group_changes( if let Some(avatar_action) = &mime_parser.group_avatar { if !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await? { - warn!( - context, - "Received group avatar update for group chat {chat_id} we are not a member of." - ); + warn!("Received group avatar update for group chat {chat_id} we are not a member of."); } else if !chat::is_contact_in_chat(context, chat_id, from_id).await? { warn!( - context, "Contact {from_id} attempts to modify group chat {chat_id} avatar without being a member.", ); } else { - info!(context, "Group-avatar change for {chat_id}."); + info!("Group-avatar change for {chat_id}."); if chat .param .update_timestamp(Param::AvatarTimestamp, sent_timestamp)? @@ -1923,7 +1890,7 @@ async fn create_or_lookup_mailinglist( chat::add_to_chat_contacts_table(context, chat_id, &[ContactId::SELF]).await?; Ok(Some((chat_id, blocked))) } else { - info!(context, "Creating list forbidden by caller."); + info!("Creating list forbidden by caller."); Ok(None) } } @@ -1946,7 +1913,7 @@ async fn apply_mailinglist_changes( let list_post = match ContactAddress::new(list_post) { Ok(list_post) => list_post, Err(err) => { - warn!(context, "Invalid List-Post: {:#}.", err); + warn!("Invalid List-Post: {:#}.", err); return Ok(()); } }; @@ -2013,10 +1980,7 @@ async fn create_adhoc_group( member_ids: &[ContactId], ) -> Result> { if mime_parser.is_mailinglist_message() { - info!( - context, - "Not creating ad-hoc group for mailing list message." - ); + info!("Not creating ad-hoc group for mailing list message."); return Ok(None); } @@ -2030,15 +1994,12 @@ async fn create_adhoc_group( // Chat-Group-ID and incompatible Message-ID format. // // Instead, assign the message to 1:1 chat with the sender. - warn!( - context, - "Not creating ad-hoc group for message that cannot be decrypted." - ); + warn!("Not creating ad-hoc group for message that cannot be decrypted."); return Ok(None); } if member_ids.len() < 3 { - info!(context, "Not creating ad-hoc group: too few contacts."); + info!("Not creating ad-hoc group: too few contacts."); return Ok(None); } @@ -2078,11 +2039,7 @@ async fn check_verified_properties( // we do not fail here currently, this would exclude (a) non-deltas // and (b) deltas with different protection views across multiple devices. // for group creation or protection enabled/disabled, however, Chat-Verified is respected. - warn!( - context, - "{} did not mark message as protected.", - contact.get_addr() - ); + warn!("{} did not mark message as protected.", contact.get_addr()); } // ensure, the contact is verified @@ -2144,7 +2101,6 @@ async fn check_verified_properties( for (to_addr, mut is_verified) in rows { info!( - context, "check_verified_properties: {:?} self={:?}.", to_addr, context.is_self_addr(&to_addr).await @@ -2163,7 +2119,7 @@ async fn check_verified_properties( || peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint && peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint { - info!(context, "{} has verified {}.", contact.get_addr(), to_addr); + info!("{} has verified {}.", contact.get_addr(), to_addr); let fp = peerstate.gossip_key_fingerprint.clone(); if let Some(fp) = fp { peerstate.set_verified( @@ -2295,7 +2251,7 @@ async fn add_or_lookup_contacts_by_address_list( add_or_lookup_contact_by_addr(context, display_name, addr, origin).await?; contact_ids.insert(contact_id); } else { - warn!(context, "Contact with address {:?} cannot exist.", addr); + warn!("Contact with address {:?} cannot exist.", addr); } } diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs index ed3791fe78..c6883b2cbe 100644 --- a/src/receive_imf/tests.rs +++ b/src/receive_imf/tests.rs @@ -1,4 +1,6 @@ use tokio::fs; +use tracing_futures::WithSubscriber; +use tracing_subscriber::{prelude::*, registry::Registry}; use super::*; use crate::aheader::EncryptPreference; @@ -1340,8 +1342,12 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() { let chat = Chat::load_from_db(&t, chat_id).await.unwrap(); assert!(chat.can_send(&t).await.unwrap()); + let subscriber = Registry::default().with(t.to_layer()); let imf_raw = format!("In-Reply-To: 3333@example.org\n{GH_MAILINGLIST2}"); - receive_imf(&t, imf_raw.as_bytes(), false).await.unwrap(); + receive_imf(&t, imf_raw.as_bytes(), false) + .with_subscriber(subscriber) + .await + .unwrap(); assert_eq!( t.get_last_msg().await.in_reply_to.unwrap(), diff --git a/src/scheduler.rs b/src/scheduler.rs index 8d8118dd8c..d69bfa9809 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -7,10 +7,14 @@ use futures::future::try_join_all; use futures_lite::FutureExt; use tokio::sync::{oneshot, RwLock, RwLockWriteGuard}; use tokio::task; +use tracing::instrument::Instrument; +use tracing::{error, info, warn}; +use tracing_futures::WithSubscriber; use self::connectivity::ConnectivityStore; use crate::config::Config; use crate::contact::{ContactId, RecentlySeenLoop}; +use crate::context::future::ContextIdFutureExt; use crate::context::Context; use crate::ephemeral::{self, delete_expired_imap_messages}; use crate::events::EventType; @@ -57,11 +61,10 @@ impl SchedulerState { /// Starts the scheduler if it is not yet started. async fn do_start(mut inner: RwLockWriteGuard<'_, InnerSchedulerState>, context: Context) { - info!(context, "starting IO"); - let ctx = context.clone(); + info!("Starting IO."); match Scheduler::start(context).await { Ok(scheduler) => inner.scheduler = Some(scheduler), - Err(err) => error!(&ctx, "Failed to start IO: {:#}", err), + Err(err) => error!("Failed to start IO: {err:#}."), } } @@ -79,7 +82,7 @@ impl SchedulerState { // For this, the caller needs to instruct the event poller // to terminate on receiving the next event and then call stop_io() // which will emit the below event(s) - info!(context, "stopping IO"); + context.emit_event(EventType::Info("Stopping IO.".to_string())); if let Some(debug_logging) = context.debug_logging.read().await.as_ref() { debug_logging.loop_handle.abort(); } @@ -116,7 +119,7 @@ impl SchedulerState { /// Restarts the scheduler, only if it is running. pub(crate) async fn restart(&self, context: &Context) { - info!(context, "restarting IO"); + info!("Restarting IO."); if self.is_running().await { self.stop(context).await; self.start(context.clone()).await; @@ -249,7 +252,7 @@ pub(crate) struct Scheduler { async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConnectionHandlers) { use futures::future::FutureExt; - info!(ctx, "starting inbox loop"); + info!("starting inbox loop"); let ImapConnectionHandlers { mut connection, stop_receiver, @@ -259,7 +262,7 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne let fut = async move { let ctx = ctx1; if let Err(err) = started.send(()).await { - warn!(ctx, "inbox loop, missing started receiver: {}", err); + warn!("Inbox loop, missing started receiver: {err:#}."); return; }; @@ -267,7 +270,7 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne loop { let job = match job::load_next(&ctx, &info).await { Err(err) => { - error!(ctx, "Failed loading job from the database: {:#}.", err); + error!("Failed loading job from the database: {err:#}."); None } Ok(job) => job, @@ -282,14 +285,14 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne let quota_requested = ctx.quota_update_request.swap(false, Ordering::Relaxed); if quota_requested { if let Err(err) = ctx.update_recent_quota(&mut connection).await { - warn!(ctx, "Failed to update quota: {:#}.", err); + warn!("Failed to update quota: {err:#}."); } } let resync_requested = ctx.resync_request.swap(false, Ordering::Relaxed); if resync_requested { if let Err(err) = connection.resync_folders(&ctx).await { - warn!(ctx, "Failed to resync folders: {:#}.", err); + warn!("Failed to resync folders: {err:#}."); ctx.resync_request.store(true, Ordering::Relaxed); } } @@ -305,7 +308,7 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne } } Err(err) => { - warn!(ctx, "Failed to get last housekeeping time: {}", err); + warn!("Failed to get last housekeeping time: {err:#}."); } }; @@ -319,17 +322,17 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne if let Err(err) = ctx.set_config_bool(Config::FetchedExistingMsgs, true).await { - warn!(ctx, "Can't set Config::FetchedExistingMsgs: {:#}", err); + warn!("Can't set Config::FetchedExistingMsgs: {:#}", err); } if let Err(err) = connection.fetch_existing_msgs(&ctx).await { - warn!(ctx, "Failed to fetch existing messages: {:#}", err); - connection.trigger_reconnect(&ctx); + warn!("Failed to fetch existing messages: {:#}", err); + connection.trigger_reconnect(); } } } Err(err) => { - warn!(ctx, "Can't get Config::FetchedExistingMsgs: {:#}", err); + warn!("Can't get Config::FetchedExistingMsgs: {:#}", err); } } @@ -342,7 +345,7 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne stop_receiver .recv() .map(|_| { - info!(ctx, "shutting down inbox loop"); + info!("shutting down inbox loop"); }) .race(fut) .await; @@ -362,7 +365,7 @@ async fn fetch_idle( let folder_config = match folder_meaning.to_config() { Some(c) => c, None => { - error!(ctx, "Bad folder meaning: {}", folder_meaning); + error!("Bad folder meaning: {}", folder_meaning); return connection .fake_idle(ctx, None, FolderMeaning::Unknown) .await; @@ -372,8 +375,8 @@ async fn fetch_idle( Ok(folder) => folder, Err(err) => { warn!( - ctx, - "Can not watch {} folder, failed to retrieve config: {:#}", folder_config, err + "Can not watch {} folder, failed to retrieve config: {:#}", + folder_config, err ); return connection .fake_idle(ctx, None, FolderMeaning::Unknown) @@ -385,7 +388,7 @@ async fn fetch_idle( watch_folder } else { connection.connectivity.set_not_configured(ctx).await; - info!(ctx, "Can not watch {} folder, not set", folder_config); + info!("Can not watch {folder_config} folder, not set"); return connection .fake_idle(ctx, None, FolderMeaning::Unknown) .await; @@ -397,8 +400,8 @@ async fn fetch_idle( .await .context("prepare IMAP connection") { - warn!(ctx, "{:#}", err); - connection.trigger_reconnect(ctx); + warn!("{err:#}"); + connection.trigger_reconnect(); return connection .fake_idle(ctx, Some(watch_folder), folder_meaning) .await; @@ -412,7 +415,7 @@ async fn fetch_idle( .context("store_seen_flags_on_imap") .ok_or_log(ctx); } else { - warn!(ctx, "No session even though we just prepared it"); + warn!("No session even though we just prepared it"); } } @@ -422,8 +425,8 @@ async fn fetch_idle( .await .context("fetch_move_delete") { - connection.trigger_reconnect(ctx); - warn!(ctx, "{:#}", err); + connection.trigger_reconnect(); + warn!("{:#}", err); return InterruptInfo::new(false); } @@ -446,7 +449,7 @@ async fn fetch_idle( Err(err) => { // Don't reconnect, if there is a problem with the connection we will realize this when IDLEing // but maybe just one folder can't be selected or something - warn!(ctx, "{:#}", err); + warn!("{:#}", err); } Ok(true) => { // Fetch the watched folder again in case scanning other folder moved messages @@ -460,8 +463,8 @@ async fn fetch_idle( .await .context("fetch_move_delete after scan_folders") { - connection.trigger_reconnect(ctx); - warn!(ctx, "{:#}", err); + connection.trigger_reconnect(); + warn!("{:#}", err); return InterruptInfo::new(false); } } @@ -481,19 +484,15 @@ async fn fetch_idle( ctx.emit_event(EventType::ImapInboxIdle); if let Some(session) = connection.session.take() { if !session.can_idle() { - info!( - ctx, - "IMAP session does not support IDLE, going to fake idle." - ); + info!("IMAP session does not support IDLE, going to fake idle."); return connection .fake_idle(ctx, Some(watch_folder), folder_meaning) .await; } - info!(ctx, "IMAP session supports IDLE, using it."); + info!("IMAP session supports IDLE, using it."); match session .idle( - ctx, connection.idle_interrupt_receiver.clone(), Some(watch_folder), ) @@ -505,13 +504,13 @@ async fn fetch_idle( info } Err(err) => { - connection.trigger_reconnect(ctx); - warn!(ctx, "{:#}", err); + connection.trigger_reconnect(); + warn!("{:#}", err); InterruptInfo::new(false) } } } else { - warn!(ctx, "No IMAP session, going to fake idle."); + warn!("No IMAP session, going to fake idle."); connection .fake_idle(ctx, Some(watch_folder), folder_meaning) .await @@ -526,7 +525,7 @@ async fn simple_imap_loop( ) { use futures::future::FutureExt; - info!(ctx, "starting simple loop for {}", folder_meaning); + info!("starting simple loop for {folder_meaning}"); let ImapConnectionHandlers { mut connection, stop_receiver, @@ -537,7 +536,7 @@ async fn simple_imap_loop( let fut = async move { let ctx = ctx1; if let Err(err) = started.send(()).await { - warn!(&ctx, "simple imap loop, missing started receiver: {}", err); + warn!("simple imap loop, missing started receiver: {}", err); return; } @@ -549,7 +548,7 @@ async fn simple_imap_loop( stop_receiver .recv() .map(|_| { - info!(ctx, "shutting down simple loop"); + info!("shutting down simple loop"); }) .race(fut) .await; @@ -558,7 +557,7 @@ async fn simple_imap_loop( async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnectionHandlers) { use futures::future::FutureExt; - info!(ctx, "starting smtp loop"); + info!("starting smtp loop"); let SmtpConnectionHandlers { mut connection, stop_receiver, @@ -569,20 +568,19 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect let fut = async move { let ctx = ctx1; if let Err(err) = started.send(()).await { - warn!(&ctx, "smtp loop, missing started receiver: {}", err); + warn!("smtp loop, missing started receiver: {}", err); return; } let mut timeout = None; loop { if let Err(err) = send_smtp_messages(&ctx, &mut connection).await { - warn!(ctx, "send_smtp_messages failed: {:#}", err); + warn!("send_smtp_messages failed: {:#}", err); timeout = Some(timeout.map_or(30, |timeout: u64| timeout.saturating_mul(3))) } else { let duration_until_can_send = ctx.ratelimit.read().await.until_can_send(); if !duration_until_can_send.is_zero() { info!( - ctx, "smtp got rate limited, waiting for {} until can send again", duration_to_str(duration_until_can_send) ); @@ -597,7 +595,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect } // Fake Idle - info!(ctx, "smtp fake idle - started"); + info!("smtp fake idle - started"); match &connection.last_send_error { None => connection.connectivity.set_connected(&ctx).await, Some(err) => connection.connectivity.set_err(&ctx, err).await, @@ -609,8 +607,8 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect // unnecessary retries. if let Some(timeout) = timeout { info!( - ctx, - "smtp has messages to retry, planning to retry {} seconds later", timeout + "smtp has messages to retry, planning to retry {} seconds later", + timeout ); let duration = std::time::Duration::from_secs(timeout); tokio::time::timeout(duration, async { @@ -619,18 +617,18 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect .await .unwrap_or_default(); } else { - info!(ctx, "smtp has no messages to retry, waiting for interrupt"); + info!("smtp has no messages to retry, waiting for interrupt"); idle_interrupt_receiver.recv().await.unwrap_or_default(); }; - info!(ctx, "smtp fake idle - interrupted") + info!("smtp fake idle - interrupted") } }; stop_receiver .recv() .map(|_| { - info!(ctx, "shutting down smtp loop"); + info!("shutting down smtp loop"); }) .race(fut) .await; @@ -652,7 +650,12 @@ impl Scheduler { let (inbox_start_send, inbox_start_recv) = channel::bounded(1); let handle = { let ctx = ctx.clone(); - task::spawn(inbox_loop(ctx, inbox_start_send, inbox_handlers)) + task::spawn( + inbox_loop(ctx, inbox_start_send, inbox_handlers) + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ) }; let inbox = SchedBox { meaning: FolderMeaning::Inbox, @@ -672,7 +675,12 @@ impl Scheduler { let (conn_state, handlers) = ImapConnectionState::new(&ctx).await?; let (start_send, start_recv) = channel::bounded(1); let ctx = ctx.clone(); - let handle = task::spawn(simple_imap_loop(ctx, start_send, handlers, meaning)); + let handle = task::spawn( + simple_imap_loop(ctx, start_send, handlers, meaning) + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ); oboxes.push(SchedBox { meaning, conn_state, @@ -684,22 +692,37 @@ impl Scheduler { let smtp_handle = { let ctx = ctx.clone(); - task::spawn(smtp_loop(ctx, smtp_start_send, smtp_handlers)) + task::spawn( + smtp_loop(ctx, smtp_start_send, smtp_handlers) + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ) }; start_recvs.push(smtp_start_recv); let ephemeral_handle = { let ctx = ctx.clone(); - task::spawn(async move { - ephemeral::ephemeral_loop(&ctx, ephemeral_interrupt_recv).await; - }) + task::spawn( + async move { + ephemeral::ephemeral_loop(&ctx, ephemeral_interrupt_recv).await; + } + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ) }; let location_handle = { let ctx = ctx.clone(); - task::spawn(async move { - location::location_loop(&ctx, location_interrupt_recv).await; - }) + task::spawn( + async move { + location::location_loop(&ctx, location_interrupt_recv).await; + } + .with_current_subscriber() + .bind_current_context_id() + .in_current_span(), + ) }; let recently_seen_loop = RecentlySeenLoop::new(ctx.clone()); @@ -721,7 +744,7 @@ impl Scheduler { bail!("failed to start scheduler: {}", err); } - info!(ctx, "scheduler is running"); + info!("scheduler is running"); Ok(res) } diff --git a/src/scheduler/connectivity.rs b/src/scheduler/connectivity.rs index 91edcad614..5ba9d722d1 100644 --- a/src/scheduler/connectivity.rs +++ b/src/scheduler/connectivity.rs @@ -4,6 +4,7 @@ use std::{iter::once, ops::Deref, sync::Arc}; use anyhow::{anyhow, Result}; use humansize::{format_size, BINARY}; use tokio::sync::Mutex; +use tracing::info; use crate::events::EventType; use crate::imap::{scan_folders::get_watched_folder_configs, FolderMeaning}; @@ -408,7 +409,7 @@ impl Context { ret += &format!("{}: ", &*escaper::encode_minimal(root_name)); } else { - info!(self, "connectivity: root name hidden: \"{}\"", root_name); + info!("connectivity: root name hidden: \"{}\"", root_name); } let messages = stock_str::messages(self).await; diff --git a/src/securejoin.rs b/src/securejoin.rs index f854b7d980..a009026b3d 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -4,6 +4,7 @@ use std::convert::TryFrom; use anyhow::{bail, Context as _, Error, Result}; use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC}; +use tracing::{error, info, warn}; use crate::aheader::EncryptPreference; use crate::chat::{self, Chat, ChatId, ChatIdBlocked}; @@ -124,7 +125,7 @@ pub async fn get_securejoin_qr(context: &Context, group: Option) -> Resu ) }; - info!(context, "Generated QR code: {}", qr); + info!("Generated QR code: {}", qr); Ok(qr) } @@ -133,7 +134,7 @@ async fn get_self_fingerprint(context: &Context) -> Option { match SignedPublicKey::load_self(context).await { Ok(key) => Some(key.fingerprint()), Err(_) => { - warn!(context, "get_self_fingerprint(): failed to load key"); + warn!("get_self_fingerprint(): failed to load key"); None } } @@ -147,9 +148,9 @@ async fn get_self_fingerprint(context: &Context) -> Option { /// The function returns immediately and the handshake will run in background. pub async fn join_securejoin(context: &Context, qr: &str) -> Result { securejoin(context, qr).await.map_err(|err| { - warn!(context, "Fatal joiner error: {:#}", err); + warn!("Fatal joiner error: {:#}", err); // The user just scanned this QR code so has context on what failed. - error!(context, "QR process failed"); + error!("QR process failed"); err }) } @@ -160,7 +161,7 @@ async fn securejoin(context: &Context, qr: &str) -> Result { ==== Step 2 in "Setup verified contact" protocol ===== ========================================================*/ - info!(context, "Requesting secure-join ...",); + info!("Requesting secure-join ...",); let qr_scan = check_qr(context, qr).await?; let invite = QrInvite::try_from(qr_scan)?; @@ -215,7 +216,6 @@ async fn fingerprint_equals_sender( Ok(peerstate) => peerstate, Err(err) => { warn!( - context, "Failed to sender peerstate for {}: {}", contact.get_addr(), err @@ -288,8 +288,8 @@ pub(crate) async fn handle_securejoin_handshake( .context("Not a Secure-Join message")?; info!( - context, - ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step, + ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", + step, ); let join_vg = step.starts_with("vg-"); @@ -308,15 +308,15 @@ pub(crate) async fn handle_securejoin_handshake( let invitenumber = match mime_message.get_header(HeaderDef::SecureJoinInvitenumber) { Some(n) => n, None => { - warn!(context, "Secure-join denied (invitenumber missing)"); + warn!("Secure-join denied (invitenumber missing)"); return Ok(HandshakeMessage::Ignore); } }; if !token::exists(context, token::Namespace::InviteNumber, invitenumber).await { - warn!(context, "Secure-join denied (bad invitenumber)."); + warn!("Secure-join denied (bad invitenumber)."); return Ok(HandshakeMessage::Ignore); } - info!(context, "Secure-join requested.",); + info!("Secure-join requested.",); inviter_progress!(context, contact_id, 300); @@ -366,7 +366,7 @@ pub(crate) async fn handle_securejoin_handshake( return Ok(HandshakeMessage::Ignore); } }; - if !encrypted_and_signed(context, mime_message, Some(&fingerprint)) { + if !encrypted_and_signed(mime_message, Some(&fingerprint)) { could_not_establish_secure_connection( context, contact_id, @@ -386,7 +386,7 @@ pub(crate) async fn handle_securejoin_handshake( .await?; return Ok(HandshakeMessage::Ignore); } - info!(context, "Fingerprint verified.",); + info!("Fingerprint verified.",); // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code let auth_0 = match mime_message.get_header(HeaderDef::SecureJoinAuth) { Some(auth) => auth, @@ -429,7 +429,7 @@ pub(crate) async fn handle_securejoin_handshake( return Ok(HandshakeMessage::Ignore); } Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited).await?; - info!(context, "Auth verified.",); + info!("Auth verified.",); context.emit_event(EventType::ContactsChanged(Some(contact_id))); inviter_progress!(context, contact_id, 600); if join_vg { @@ -439,7 +439,7 @@ pub(crate) async fn handle_securejoin_handshake( let field_grpid = match mime_message.get_header(HeaderDef::SecureJoinGroup) { Some(s) => s.as_str(), None => { - warn!(context, "Missing Secure-Join-Group header"); + warn!("Missing Secure-Join-Group header"); return Ok(HandshakeMessage::Ignore); } }; @@ -450,7 +450,7 @@ pub(crate) async fn handle_securejoin_handshake( chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true) .await { - error!(context, "failed to add contact: {}", err); + error!("failed to add contact: {}", err); } } None => bail!("Chat {} not found", &field_grpid), @@ -501,7 +501,7 @@ pub(crate) async fn handle_securejoin_handshake( if let Ok(contact) = Contact::get_by_id(context, contact_id).await { if contact.is_verified(context).await? == VerifiedStatus::Unverified { - warn!(context, "{} invalid.", step); + warn!("{} invalid.", step); return Ok(HandshakeMessage::Ignore); } if join_vg { @@ -510,7 +510,7 @@ pub(crate) async fn handle_securejoin_handshake( .map(|s| s.as_str()) .unwrap_or_else(|| ""); if let Err(err) = chat::get_chat_id_by_grpid(context, field_grpid).await { - warn!(context, "Failed to lookup chat_id from grpid: {}", err); + warn!("Failed to lookup chat_id from grpid: {}", err); return Err( err.context(format!("Chat for group {} not found", &field_grpid)) ); @@ -518,12 +518,12 @@ pub(crate) async fn handle_securejoin_handshake( } Ok(HandshakeMessage::Ignore) // "Done" deletes the message and breaks multi-device } else { - warn!(context, "{} invalid.", step); + warn!("{} invalid.", step); Ok(HandshakeMessage::Ignore) } } _ => { - warn!(context, "invalid step: {}", step); + warn!("invalid step: {}", step); Ok(HandshakeMessage::Ignore) } } @@ -557,7 +557,7 @@ pub(crate) async fn observe_securejoin_on_other_device( let step = mime_message .get_header(HeaderDef::SecureJoin) .context("Not a Secure-Join message")?; - info!(context, "observing secure-join message \'{}\'", step); + info!("observing secure-join message \'{}\'", step); match step.as_str() { "vg-request-with-auth" @@ -566,11 +566,7 @@ pub(crate) async fn observe_securejoin_on_other_device( | "vc-contact-confirm" | "vg-member-added-received" | "vc-contact-confirm-received" => { - if !encrypted_and_signed( - context, - mime_message, - get_self_fingerprint(context).await.as_ref(), - ) { + if !encrypted_and_signed(mime_message, get_self_fingerprint(context).await.as_ref()) { could_not_establish_secure_connection( context, contact_id, @@ -715,8 +711,8 @@ async fn could_not_establish_secure_connection( let msg = stock_str::contact_not_verified(context, &contact).await; chat::add_info_msg(context, chat_id, &msg, time()).await?; warn!( - context, - "StockMessage::ContactNotVerified posted to 1:1 chat ({})", details + "StockMessage::ContactNotVerified posted to 1:1 chat ({})", + details ); Ok(()) } @@ -733,7 +729,7 @@ async fn mark_peer_as_verified( PeerstateVerifiedStatus::BidirectVerified, verifier, ) { - error!(context, "Could not mark peer as verified: {}", err); + error!("Could not mark peer as verified: {}", err); return Err(err); } peerstate.prefer_encrypt = EncryptPreference::Mutual; @@ -749,25 +745,24 @@ async fn mark_peer_as_verified( ******************************************************************************/ fn encrypted_and_signed( - context: &Context, mimeparser: &MimeMessage, expected_fingerprint: Option<&Fingerprint>, ) -> bool { if !mimeparser.was_encrypted() { - warn!(context, "Message not encrypted.",); + warn!("Message not encrypted.",); false } else if let Some(expected_fingerprint) = expected_fingerprint { if !mimeparser.signatures.contains(expected_fingerprint) { warn!( - context, - "Message does not match expected fingerprint {}.", expected_fingerprint, + "Message does not match expected fingerprint {}.", + expected_fingerprint, ); false } else { true } } else { - warn!(context, "Fingerprint for comparison missing."); + warn!("Fingerprint for comparison missing."); false } } diff --git a/src/securejoin/bob.rs b/src/securejoin/bob.rs index 674fa94f76..10e526ac8d 100644 --- a/src/securejoin/bob.rs +++ b/src/securejoin/bob.rs @@ -4,6 +4,7 @@ //! the required user interactions. use anyhow::{Context as _, Result}; +use tracing::{error, warn}; use super::bobstate::{BobHandshakeStage, BobState}; use super::qrinvite::QrInvite; @@ -45,7 +46,7 @@ pub(super) async fn start_protocol(context: &Context, invite: QrInvite) -> Resul let (state, stage, aborted_states) = BobState::start_protocol(context, invite.clone(), chat_id).await?; for state in aborted_states { - error!(context, "Aborting previously unfinished QR Join process."); + error!("Aborting previously unfinished QR Join process."); state.notify_aborted(context, "new QR scanned").await?; state.emit_progress(context, JoinerProgress::Error); } @@ -133,10 +134,7 @@ pub(super) async fn handle_contact_confirm( Ok(retval) } Some(_) => { - warn!( - context, - "Impossible state returned from handling handshake message" - ); + warn!("Impossible state returned from handling handshake message"); Ok(retval) } None => Ok(retval), @@ -208,8 +206,8 @@ impl BobState { let chat_id = self.joining_chat_id(context).await?; chat::add_info_msg(context, chat_id, &msg, time()).await?; warn!( - context, - "StockMessage::ContactNotVerified posted to joining chat ({})", why + "StockMessage::ContactNotVerified posted to joining chat ({})", + why ); Ok(()) } diff --git a/src/securejoin/bobstate.rs b/src/securejoin/bobstate.rs index b6584f6e1e..6ebd5ac2e6 100644 --- a/src/securejoin/bobstate.rs +++ b/src/securejoin/bobstate.rs @@ -9,6 +9,7 @@ use anyhow::{Error, Result}; use rusqlite::Connection; +use tracing::{info, warn}; use super::qrinvite::QrInvite; use super::{encrypted_and_signed, fingerprint_equals_sender, mark_peer_as_verified}; @@ -96,7 +97,7 @@ impl BobState { if fingerprint_equals_sender(context, invite.fingerprint(), invite.contact_id()).await? { // The scanned fingerprint matches Alice's key, we can proceed to step 4b. - info!(context, "Taking securejoin protocol shortcut"); + info!("Taking securejoin protocol shortcut"); send_handshake_message(context, &invite, chat_id, BobHandshakeMsg::RequestWithAuth) .await?; ( @@ -245,15 +246,14 @@ impl BobState { Some(step) => step, None => { warn!( - context, "Message has no Secure-Join header: {}", mime_message.get_rfc724_mid().unwrap_or_default() ); return Ok(None); } }; - if !self.is_msg_expected(context, step.as_str()) { - info!(context, "{} message out of sync for BobState", step); + if !self.is_msg_expected(step.as_str()) { + info!("{} message out of sync for BobState", step); return Ok(None); } match step.as_str() { @@ -264,19 +264,19 @@ impl BobState { self.step_contact_confirm(context, mime_message).await } _ => { - warn!(context, "Invalid step for BobState: {}", step); + warn!("Invalid step for BobState: {}", step); Ok(None) } } } /// Returns `true` if the message is expected according to the protocol. - fn is_msg_expected(&self, context: &Context, step: &str) -> bool { + fn is_msg_expected(&self, step: &str) -> bool { let variant_matches = match self.invite { QrInvite::Contact { .. } => step.starts_with("vc-"), QrInvite::Group { .. } => step.starts_with("vg-"), }; - let step_matches = self.next.matches(context, step); + let step_matches = self.next.matches(step); variant_matches && step_matches } @@ -289,11 +289,8 @@ impl BobState { context: &Context, mime_message: &MimeMessage, ) -> Result> { - info!( - context, - "Bob Step 4 - handling vc-auth-require/vg-auth-required message" - ); - if !encrypted_and_signed(context, mime_message, Some(self.invite.fingerprint())) { + info!("Bob Step 4 - handling vc-auth-require/vg-auth-required message"); + if !encrypted_and_signed(mime_message, Some(self.invite.fingerprint())) { let reason = if mime_message.was_encrypted() { "Valid signature missing" } else { @@ -310,7 +307,7 @@ impl BobState { .await?; return Ok(Some(BobHandshakeStage::Terminated("Fingerprint mismatch"))); } - info!(context, "Fingerprint verified.",); + info!("Fingerprint verified.",); self.update_next(&context.sql, SecureJoinStep::ContactConfirm) .await?; self.send_handshake_message(context, BobHandshakeMsg::RequestWithAuth) @@ -331,10 +328,7 @@ impl BobState { context: &Context, mime_message: &MimeMessage, ) -> Result> { - info!( - context, - "Bob Step 7 - handling vc-contact-confirm/vg-member-added message" - ); + info!("Bob Step 7 - handling vc-contact-confirm/vg-member-added message"); let vg_expect_encrypted = match self.invite { QrInvite::Contact { .. } => { // setup-contact is always encrypted @@ -357,7 +351,7 @@ impl BobState { } }; if vg_expect_encrypted - && !encrypted_and_signed(context, mime_message, Some(self.invite.fingerprint())) + && !encrypted_and_signed(mime_message, Some(self.invite.fingerprint())) { self.update_next(&context.sql, SecureJoinStep::Terminated) .await?; @@ -381,7 +375,7 @@ impl BobState { .map(|s| s.as_str()) .ok_or_else(|| Error::msg("Missing Chat-Group-Member-Added header"))?; if !context.is_self_addr(member_added).await? { - info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."); + info!( "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."); return Ok(None); } } @@ -389,10 +383,7 @@ impl BobState { self.send_handshake_message(context, BobHandshakeMsg::ContactConfirmReceived) .await .map_err(|_| { - warn!( - context, - "Failed to send vc-contact-confirm-received/vg-member-added-received" - ); + warn!("Failed to send vc-contact-confirm-received/vg-member-added-received"); }) // This is not an error affecting the protocol outcome. .ok(); @@ -530,16 +521,16 @@ pub enum SecureJoinStep { impl SecureJoinStep { /// Compares the legacy string representation of a step to a [`SecureJoinStep`] variant. - fn matches(&self, context: &Context, step: &str) -> bool { + fn matches(&self, step: &str) -> bool { match self { Self::AuthRequired => step == "vc-auth-required" || step == "vg-auth-required", Self::ContactConfirm => step == "vc-contact-confirm" || step == "vg-member-added", SecureJoinStep::Terminated => { - warn!(context, "Terminated state for next securejoin step"); + warn!("Terminated state for next securejoin step"); false } SecureJoinStep::Completed => { - warn!(context, "Completed state for next securejoin step"); + warn!("Completed state for next securejoin step"); false } } diff --git a/src/smtp.rs b/src/smtp.rs index 26402b3256..01333e978a 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -9,6 +9,7 @@ use async_smtp::response::{Category, Code, Detail}; use async_smtp::{self as smtp, EmailAddress, SmtpTransport}; use tokio::io::BufStream; use tokio::task; +use tracing::{error, info, warn}; use crate::config::Config; use crate::contact::{Contact, ContactId}; @@ -87,7 +88,7 @@ impl Smtp { /// Connect using configured parameters. pub async fn connect_configured(&mut self, context: &Context) -> Result<()> { if self.has_maybe_stale_connection() { - info!(context, "Closing stale connection"); + info!("Closing stale connection"); self.disconnect().await; } @@ -234,7 +235,7 @@ impl Smtp { provider_strict_tls: bool, ) -> Result<()> { if self.is_connected() { - warn!(context, "SMTP already connected."); + warn!("SMTP already connected."); return Ok(()); } @@ -364,7 +365,7 @@ pub(crate) async fn smtp_send( msg_id: MsgId, ) -> SendResult { if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { - info!(context, "smtp-sending out mime message:"); + info!("smtp-sending out mime message:"); println!("{message}"); } @@ -385,7 +386,7 @@ pub(crate) async fn smtp_send( let status = match send_result { Err(crate::smtp::send::Error::SmtpSend(err)) => { // Remote error, retry later. - info!(context, "SMTP failed to send: {:?}", &err); + info!("SMTP failed to send: {:?}", &err); let res = match err { async_smtp::error::Error::Permanent(ref response) => { @@ -412,10 +413,10 @@ pub(crate) async fn smtp_send( }; if maybe_transient { - info!(context, "Permanent error that is likely to actually be transient, postponing retry for later"); + info!( "Permanent error that is likely to actually be transient, postponing retry for later"); SendResult::Retry } else { - info!(context, "Permanent error, message sending failed"); + info!("Permanent error, message sending failed"); // If we do not retry, add an info message to the chat. // Yandex error "554 5.7.1 [2] Message rejected under suspicion of SPAM; https://ya.cc/..." // should definitely go here, because user has to open the link to @@ -436,27 +437,22 @@ pub(crate) async fn smtp_send( // Any extended smtp status codes like x.1.1, x.1.2 or x.1.3 that we // receive as a transient error are misconfigurations of the smtp server. // See - info!(context, "Received extended status code {} for a transient error. This looks like a misconfigured SMTP server, let's fail immediately", first_word); + info!("Received extended status code {} for a transient error. This looks like a misconfigured SMTP server, let's fail immediately", first_word); SendResult::Failure(format_err!("Permanent SMTP error: {}", err)) } else { info!( - context, "Transient error with status code {}, postponing retry for later", first_word ); SendResult::Retry } } else { - info!( - context, - "Transient error without status code, postponing retry for later" - ); + info!("Transient error without status code, postponing retry for later"); SendResult::Retry } } _ => { info!( - context, "Message sending failed without error returned by the server, retry later" ); SendResult::Retry @@ -464,7 +460,7 @@ pub(crate) async fn smtp_send( }; // this clears last_success info - info!(context, "Failed to send message over SMTP, disconnecting"); + info!("Failed to send message over SMTP, disconnecting"); smtp.disconnect().await; res @@ -472,19 +468,19 @@ pub(crate) async fn smtp_send( Err(crate::smtp::send::Error::Envelope(err)) => { // Local error, job is invalid, do not retry. smtp.disconnect().await; - warn!(context, "SMTP job is invalid: {}", err); + warn!("SMTP job is invalid: {}", err); SendResult::Failure(err) } Err(crate::smtp::send::Error::NoTransport) => { // Should never happen. // It does not even make sense to disconnect here. - error!(context, "SMTP job failed because SMTP has no transport"); + error!("SMTP job failed because SMTP has no transport"); SendResult::Failure(format_err!("SMTP has not transport")) } Err(crate::smtp::send::Error::Other(err)) => { // Local error, job is invalid, do not retry. smtp.disconnect().await; - warn!(context, "unable to load job: {}", err); + warn!("unable to load job: {}", err); SendResult::Failure(err) } Ok(()) => SendResult::Success, @@ -551,8 +547,8 @@ pub(crate) async fn send_msg_to_smtp( bail!("Number of retries exceeded the limit"); } info!( - context, - "Try number {} to send message {} over SMTP", retries, msg_id + "Try number {} to send message {} over SMTP", + retries, msg_id ); let recipients_list = recipients @@ -561,7 +557,7 @@ pub(crate) async fn send_msg_to_smtp( |addr| match async_smtp::EmailAddress::new(addr.to_string()) { Ok(addr) => Some(addr), Err(err) => { - warn!(context, "invalid recipient: {} {:?}", addr, err); + warn!("invalid recipient: {} {:?}", addr, err); None } }, @@ -574,10 +570,7 @@ pub(crate) async fn send_msg_to_smtp( .await .with_context(|| format!("failed to check message {msg_id} existence"))? { - info!( - context, - "Sending of message {} was cancelled by the user.", msg_id - ); + info!("Sending of message {} was cancelled by the user.", msg_id); return Ok(()); } @@ -607,7 +600,7 @@ pub(crate) async fn send_msg_to_smtp( async fn send_mdns(context: &Context, connection: &mut Smtp) -> Result<()> { loop { if !context.ratelimit.read().await.can_send() { - info!(context, "Ratelimiter does not allow sending MDNs now"); + info!("Ratelimiter does not allow sending MDNs now"); return Ok(()); } @@ -715,7 +708,7 @@ async fn send_mdn_msg_id( match smtp_send(context, &recipients, &body, smtp, msg_id).await { SendResult::Success => { - info!(context, "Successfully sent MDN for {}", msg_id); + info!("Successfully sent MDN for {}", msg_id); context .sql .execute("DELETE FROM smtp_mdns WHERE msg_id = ?", paramsv![msg_id]) @@ -733,10 +726,7 @@ async fn send_mdn_msg_id( Ok(()) } SendResult::Retry => { - info!( - context, - "Temporary SMTP failure while sending an MDN for {}", msg_id - ); + info!("Temporary SMTP failure while sending an MDN for {}", msg_id); Ok(()) } SendResult::Failure(err) => Err(err), @@ -751,7 +741,7 @@ async fn send_mdn(context: &Context, smtp: &mut Smtp) -> Result { context.sql.execute("DELETE FROM smtp_mdns", []).await?; return Ok(false); } - info!(context, "Sending MDNs"); + info!("Sending MDNs"); context .sql diff --git a/src/smtp/send.rs b/src/smtp/send.rs index b1b25b7a7e..e1d22a79a7 100644 --- a/src/smtp/send.rs +++ b/src/smtp/send.rs @@ -1,6 +1,7 @@ //! # SMTP message sending use async_smtp::{EmailAddress, Envelope, SendableEmail}; +use tracing::warn; use super::Smtp; use crate::config::Config; @@ -70,8 +71,8 @@ impl Smtp { self.last_success = Some(std::time::SystemTime::now()); } else { warn!( - context, - "uh? SMTP has no transport, failed to send to {}", recipients_display + "uh? SMTP has no transport, failed to send to {}", + recipients_display ); return Err(Error::NoTransport); } diff --git a/src/sql.rs b/src/sql.rs index ff149f98cb..3d4e997321 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -22,6 +22,7 @@ use crate::param::{Param, Params}; use crate::peerstate::{deduplicate_peerstates, Peerstate}; use crate::stock_str; use crate::tools::{delete_file, time}; +use tracing::{error, info, warn}; #[allow(missing_docs)] #[macro_export] @@ -216,7 +217,7 @@ impl Sql { // the structure is complete now and all objects are usable if recalc_fingerprints { - info!(context, "[migration] recalc fingerprints"); + info!("[migration] recalc fingerprints"); let addrs = self .query_map( "SELECT addr FROM acpeerstates;", @@ -265,7 +266,7 @@ impl Sql { .await? } Err(e) => { - warn!(context, "Migrations can't recode avatar, removing. {:#}", e); + warn!("Migrations can't recode avatar, removing. {:#}", e); context.set_config(Config::Selfavatar, None).await? } } @@ -280,8 +281,8 @@ impl Sql { pub async fn open(&self, context: &Context, passphrase: String) -> Result<()> { if self.is_open().await { error!( - context, - "Cannot open, database \"{:?}\" already opened.", self.dbfile, + "Cannot open, database \"{:?}\" already opened.", + self.dbfile, ); bail!("SQL database is already opened."); } @@ -291,7 +292,7 @@ impl Sql { self.close().await; Err(err) } else { - info!(context, "Opened database {:?}.", self.dbfile); + info!("Opened database {:?}.", self.dbfile); *self.is_encrypted.write().await = Some(passphrase_nonempty); // setup debug logging if there is an entry containing its id @@ -697,28 +698,19 @@ fn new_connection(path: &Path, passphrase: &str) -> Result { /// Cleanup the account to restore some storage and optimize the database. pub async fn housekeeping(context: &Context) -> Result<()> { if let Err(err) = remove_unused_files(context).await { - warn!( - context, - "Housekeeping: cannot remove unused files: {:#}.", err - ); + warn!("Housekeeping: cannot remove unused files: {err:#}."); } if let Err(err) = start_ephemeral_timers(context).await { - warn!( - context, - "Housekeeping: cannot start ephemeral timers: {:#}.", err - ); + warn!("Housekeeping: cannot start ephemeral timers: {err:#}."); } if let Err(err) = prune_tombstones(&context.sql).await { - warn!( - context, - "Housekeeping: Cannot prune message tombstones: {:#}.", err - ); + warn!("Housekeeping: Cannot prune message tombstones: {err:#}."); } if let Err(err) = deduplicate_peerstates(&context.sql).await { - warn!(context, "Failed to deduplicate peerstates: {:#}.", err) + warn!("Failed to deduplicate peerstates: {err:#}.") } context.schedule_quota_update().await?; @@ -731,11 +723,11 @@ pub async fn housekeeping(context: &Context) -> Result<()> { .await { Err(err) => { - warn!(context, "Failed to run incremental vacuum: {err:#}."); + warn!("Failed to run incremental vacuum: {err:#}."); } Ok(Some(())) => { // Incremental vacuum returns a zero-column result if it did anything. - info!(context, "Successfully ran incremental vacuum."); + info!("Successfully ran incremental vacuum."); } Ok(None) => { // Incremental vacuum returned `SQLITE_DONE` immediately, @@ -747,7 +739,7 @@ pub async fn housekeeping(context: &Context) -> Result<()> { .set_config(Config::LastHousekeeping, Some(&time().to_string())) .await { - warn!(context, "Can't set config: {e:#}."); + warn!("Can't set config: {e:#}."); } context @@ -759,7 +751,7 @@ pub async fn housekeeping(context: &Context) -> Result<()> { .await .ok_or_log_msg(context, "failed to remove old MDNs"); - info!(context, "Housekeeping done."); + info!("Housekeeping done."); Ok(()) } @@ -768,7 +760,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { let mut files_in_use = HashSet::new(); let mut unreferenced_count = 0; - info!(context, "Start housekeeping..."); + info!("Start housekeeping..."); maybe_add_from_param( &context.sql, &mut files_in_use, @@ -814,7 +806,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { .await .context("housekeeping: failed to SELECT value FROM config")?; - info!(context, "{} files in use.", files_in_use.len()); + info!("{} files in use.", files_in_use.len()); /* go through directories and delete unused files */ let blobdir = context.get_blobdir(); for p in [&blobdir.join(BLOBS_BACKUP_NAME), blobdir] { @@ -845,7 +837,6 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { // The dir could be created not by a user, but by a desktop // environment f.e. So, no warning. info!( - context, "Housekeeping: Cannot rmdir {}: {:#}.", entry.path().display(), e @@ -867,7 +858,6 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { && (recently_created || recently_modified || recently_accessed) { info!( - context, "Housekeeping: Keeping new unreferenced file #{}: {:?}.", unreferenced_count, entry.file_name(), @@ -878,7 +868,6 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { unreferenced_count += 1; } info!( - context, "Housekeeping: Deleting unreferenced file #{}: {:?}.", unreferenced_count, entry.file_name() @@ -886,7 +875,6 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { let path = entry.path(); if let Err(err) = delete_file(context, &path).await { error!( - context, "Failed to delete unused file {}: {:#}.", path.display(), err @@ -895,12 +883,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { } } Err(err) => { - warn!( - context, - "Housekeeping: Cannot read dir {}: {:#}.", - p.display(), - err - ); + warn!("Housekeeping: Cannot read dir {}: {:#}.", p.display(), err); } } } @@ -1162,7 +1145,6 @@ mod tests { #[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; // as migrations::run() was already executed on context creation, // another call should not result in any action needed. @@ -1174,7 +1156,9 @@ mod tests { assert!(!disable_server_delete); assert!(!recode_avatar); - info!(&t, "test_migration_flags: XXX END MARKER"); + t.emit_event(crate::EventType::Info( + "test_migration_flags: XXX END MARKER".to_string(), + )); loop { let evt = t diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index 046db4fb25..9bd0e153f4 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -1,6 +1,7 @@ //! Migrations module. use anyhow::{Context as _, Result}; +use tracing::{info, warn}; use crate::config::Config; use crate::constants::ShowEmails; @@ -372,7 +373,7 @@ UPDATE chats SET protected=1, type=120 WHERE type=130;"#, ) .await?; } else { - warn!(context, "Can't parse configured address: {:?}", addr); + warn!("Can't parse configured address: {:?}", addr); } } @@ -715,10 +716,7 @@ CREATE INDEX smtp_messageid ON imap(rfc724_mid); } else { "Created new database; " }; - info!( - context, - "{}[migration] v{}-v{}", created_db, dbversion, new_version - ); + info!("{}[migration] v{}-v{}", created_db, dbversion, new_version); } Ok(( diff --git a/src/sync.rs b/src/sync.rs index ea10cbce95..5ce22b0243 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -4,6 +4,7 @@ use anyhow::Result; use lettre_email::mime::{self}; use lettre_email::PartBuilder; use serde::{Deserialize, Serialize}; +use tracing::{info, warn}; use crate::chat::{Chat, ChatId}; use crate::config::Config; @@ -90,8 +91,8 @@ impl Context { let chat = Chat::load_from_db(self, chat_id).await?; if !chat.is_promoted() { info!( - self, - "group '{}' not yet promoted, do not sync tokens yet.", chat.grpid + "group '{}' not yet promoted, do not sync tokens yet.", + chat.grpid ); return Ok(()); } @@ -226,7 +227,7 @@ impl Context { /// If eg. just an item cannot be deleted, /// that should not hold off the other items to be executed. pub(crate) async fn execute_sync_items(&self, items: &SyncItems) -> Result<()> { - info!(self, "executing {} sync item(s)", items.items.len()); + info!("executing {} sync item(s)", items.items.len()); for item in &items.items { match &item.data { AddQrToken(token) => { @@ -236,10 +237,7 @@ impl Context { { Some(chat_id) } else { - warn!( - self, - "Ignoring token for nonexistent/deleted group '{}'.", grpid - ); + warn!("Ignoring token for nonexistent/deleted group '{}'.", grpid); continue; } } else { diff --git a/src/test_utils.rs b/src/test_utils.rs index 07fbef649c..37cb64a888 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -17,6 +17,7 @@ use tempfile::{tempdir, TempDir}; use tokio::runtime::Handle; use tokio::sync::RwLock; use tokio::task; +use tracing::warn; use crate::chat::{self, Chat, ChatId, MessageListOptions}; use crate::chatlist::Chatlist; @@ -536,8 +537,8 @@ impl TestContext { .expect("add_or_lookup"); match modified { Modifier::None => (), - Modifier::Modified => warn!(&self.ctx, "Contact {} modified by TestContext", &addr), - Modifier::Created => warn!(&self.ctx, "Contact {} created by TestContext", &addr), + Modifier::Modified => warn!("Contact {} modified by TestContext", &addr), + Modifier::Created => warn!("Contact {} created by TestContext", &addr), } Contact::load_from_db(&self.ctx, contact_id).await.unwrap() } diff --git a/src/tools.rs b/src/tools.rs index 17de3e0ef7..546bc96cff 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -19,6 +19,7 @@ use mailparse::headers::Headers; use mailparse::MailHeaderMap; use rand::{thread_rng, Rng}; use tokio::{fs, io}; +use tracing::warn; use crate::chat::{add_device_msg, add_device_msg_with_importance}; use crate::constants::{DC_ELLIPSIS, DC_OUTDATED_WARNING_DAYS}; @@ -200,7 +201,7 @@ async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestam .await .ok(); } else { - warn!(context, "Can't convert current timestamp"); + warn!("Can't convert current timestamp"); } return true; } @@ -336,7 +337,7 @@ pub(crate) async fn delete_file(context: &Context, path: impl AsRef) -> Re bail!("path {} does not exist", path_abs.display()); } if !path_abs.is_file() { - warn!(context, "refusing to delete non-file {}.", path.display()); + warn!("refusing to delete non-file \"{}\".", path.display()); bail!("not a file: \"{}\"", path.display()); } @@ -358,7 +359,7 @@ pub async fn delete_files_in_dir(context: &Context, path: impl AsRef) -> R Ok(file) => { delete_file(context, file.file_name()).await?; } - Err(e) => warn!(context, "Could not read file to delete: {}", e), + Err(e) => warn!("Could not read file to delete: {}", e), } } Ok(()) @@ -374,7 +375,6 @@ pub(crate) async fn create_folder( Ok(_) => Ok(()), Err(err) => { warn!( - context, "Cannot create directory \"{}\": {}", path.as_ref().display(), err @@ -396,7 +396,6 @@ pub(crate) async fn write_file( let path_abs = get_abs_path(context, &path); fs::write(&path_abs, buf).await.map_err(|err| { warn!( - context, "Cannot write {} bytes to \"{}\": {}", buf.len(), path.as_ref().display(), @@ -414,7 +413,6 @@ pub async fn read_file(context: &Context, path: impl AsRef) -> Result Ok(bytes), Err(err) => { warn!( - context, "Cannot read \"{}\" or file is empty: {}", path.as_ref().display(), err @@ -431,7 +429,6 @@ pub async fn open_file(context: &Context, path: impl AsRef) -> Result Ok(bytes), Err(err) => { warn!( - context, "Cannot read \"{}\" or file is empty: {}", path.as_ref().display(), err @@ -452,7 +449,6 @@ pub fn open_file_std>( Ok(bytes) => Ok(bytes), Err(err) => { warn!( - context, "Cannot read \"{}\" or file is empty: {}", path.as_ref().display(), err diff --git a/src/webxdc.rs b/src/webxdc.rs index 07d7da54ad..de11dee625 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -10,6 +10,7 @@ use lettre_email::PartBuilder; use serde::{Deserialize, Serialize}; use serde_json::Value; use tokio::io::AsyncReadExt; +use tracing::{info, warn}; use crate::chat::Chat; use crate::contact::ContactId; @@ -187,8 +188,8 @@ impl Context { if file.len() as u64 > WEBXDC_RECEIVING_LIMIT { info!( - self, - "{} exceeds receiving limit of {} bytes", &filename, WEBXDC_RECEIVING_LIMIT + "{} exceeds receiving limit of {} bytes", + &filename, WEBXDC_RECEIVING_LIMIT ); return Ok(false); } @@ -196,13 +197,13 @@ impl Context { let archive = match async_zip::read::mem::ZipFileReader::new(file.to_vec()).await { Ok(archive) => archive, Err(_) => { - info!(self, "{} cannot be opened as zip-file", &filename); + info!("{} cannot be opened as zip-file", &filename); return Ok(false); } }; if find_zip_entry(archive.file(), "index.html").is_none() { - info!(self, "{} misses index.html", &filename); + info!("{} misses index.html", &filename); return Ok(false); } @@ -229,14 +230,14 @@ impl Context { let valid = match async_zip::read::fs::ZipFileReader::new(path).await { Ok(archive) => { if find_zip_entry(archive.file(), "index.html").is_none() { - info!(self, "{} misses index.html", filename); + info!("{} misses index.html", filename); false } else { true } } Err(_) => { - info!(self, "{} cannot be opened as zip-file", filename); + info!("{} cannot be opened as zip-file", filename); false } }; @@ -751,7 +752,7 @@ impl Message { if let Some(ref name) = manifest.name { let name = name.trim(); if name.is_empty() { - warn!(context, "empty name given in manifest"); + warn!("empty name given in manifest"); manifest.name = None; } }