diff --git a/examples/simple.rs b/examples/simple.rs index 38b143fc5..2b2c6e2a4 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -36,10 +36,11 @@ async fn main() { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); println!("creating database {:?}", dbfile); - let ctx = - Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context"); + let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile) + .await + .expect("Failed to create context"); let running = Arc::new(RwLock::new(true)); - let info = ctx.get_info(); + let info = ctx.get_info().await; let duration = time::Duration::from_millis(4000); println!("info: {:#?}", info); @@ -54,18 +55,19 @@ async fn main() { perform_inbox_fetch(&ctx1).await; if *r1.read().await { - // perform_inbox_idle(&ctx1).await; + perform_inbox_idle(&ctx1).await; } } } }); let r1 = running.clone(); + let ctx1 = ctx.clone(); let t2 = async_std::task::spawn(async move { while *r1.read().await { - // perform_smtp_jobs(&ctx1).await; + perform_smtp_jobs(&ctx1).await; if *r1.read().await { - // perform_smtp_idle(&ctx1).await; + perform_smtp_idle(&ctx1).await; } } }); @@ -85,17 +87,19 @@ async fn main() { async_std::task::sleep(duration).await; println!("sending a message"); - let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap(); - let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap(); + let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com") + .await + .unwrap(); + let chat_id = chat::create_by_contact_id(&ctx, contact_id).await.unwrap(); chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()) .await .unwrap(); println!("fetching chats.."); - let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap(); + let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap(); for i in 0..chats.len() { - let summary = chats.get_summary(&ctx, 0, None); + let summary = chats.get_summary(&ctx, 0, None).await; let text1 = summary.get_text1(); let text2 = summary.get_text2(); println!("chat: {} - {:?} - {:?}", i, text1, text2,); diff --git a/src/blob.rs b/src/blob.rs index 70d5ff7a0..c43cff569 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -486,9 +486,9 @@ mod tests { use crate::test_utils::*; - #[test] - fn test_create() { - let t = dummy_context(); + #[async_std::test] + async fn test_create() { + let t = dummy_context().await; let blob = BlobObject::create(&t.ctx, "foo", b"hello").unwrap(); let fname = t.ctx.get_blobdir().join("foo"); let data = fs::read(fname).unwrap(); @@ -497,39 +497,39 @@ mod tests { assert_eq!(blob.to_abs_path(), t.ctx.get_blobdir().join("foo")); } - #[test] - fn test_lowercase_ext() { - let t = dummy_context(); + #[async_std::test] + async fn test_lowercase_ext() { + let t = dummy_context().await; let blob = BlobObject::create(&t.ctx, "foo.TXT", b"hello").unwrap(); assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt"); } - #[test] - fn test_as_file_name() { - let t = dummy_context(); + #[async_std::test] + async fn test_as_file_name() { + let t = dummy_context().await; let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); assert_eq!(blob.as_file_name(), "foo.txt"); } - #[test] - fn test_as_rel_path() { - let t = dummy_context(); + #[async_std::test] + async fn test_as_rel_path() { + let t = dummy_context().await; let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); assert_eq!(blob.as_rel_path(), Path::new("foo.txt")); } - #[test] - fn test_suffix() { - let t = dummy_context(); + #[async_std::test] + async fn test_suffix() { + let t = dummy_context().await; let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); assert_eq!(blob.suffix(), Some("txt")); let blob = BlobObject::create(&t.ctx, "bar", b"world").unwrap(); assert_eq!(blob.suffix(), None); } - #[test] - fn test_create_dup() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_dup() { + let t = dummy_context().await; BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap(); let foo_path = t.ctx.get_blobdir().join("foo.txt"); assert!(foo_path.exists()); @@ -546,9 +546,9 @@ mod tests { } } - #[test] - fn test_double_ext_preserved() { - let t = dummy_context(); + #[async_std::test] + async fn test_double_ext_preserved() { + let t = dummy_context().await; BlobObject::create(&t.ctx, "foo.tar.gz", b"hello").unwrap(); let foo_path = t.ctx.get_blobdir().join("foo.tar.gz"); assert!(foo_path.exists()); @@ -566,18 +566,18 @@ mod tests { } } - #[test] - fn test_create_long_names() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_long_names() { + let t = dummy_context().await; let s = "1".repeat(150); let blob = BlobObject::create(&t.ctx, &s, b"data").unwrap(); let blobname = blob.as_name().split('/').last().unwrap(); assert!(blobname.len() < 128); } - #[test] - fn test_create_and_copy() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_and_copy() { + let t = dummy_context().await; let src = t.dir.path().join("src"); fs::write(&src, b"boo").unwrap(); let blob = BlobObject::create_and_copy(&t.ctx, &src).unwrap(); @@ -591,9 +591,9 @@ mod tests { assert!(!whoops.exists()); } - #[test] - fn test_create_from_path() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_from_path() { + let t = dummy_context().await; let src_ext = t.dir.path().join("external"); fs::write(&src_ext, b"boo").unwrap(); @@ -609,9 +609,9 @@ mod tests { let data = fs::read(blob.to_abs_path()).unwrap(); assert_eq!(data, b"boo"); } - #[test] - fn test_create_from_name_long() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_from_name_long() { + let t = dummy_context().await; let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html"); fs::write(&src_ext, b"boo").unwrap(); let blob = BlobObject::new_from_path(&t.ctx, &src_ext).unwrap(); diff --git a/src/chat.rs b/src/chat.rs index e81589fcc..af4ef43d4 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -119,7 +119,7 @@ impl ChatId { "UPDATE contacts SET selfavatar_sent=? WHERE id IN(SELECT contact_id FROM chats_contacts WHERE chat_id=?);", - params![timestamp, self], + paramsv![timestamp, self], ) .await?; Ok(()) @@ -134,14 +134,14 @@ impl ChatId { .sql .execute( "UPDATE chats SET blocked=? WHERE id=?;", - params![new_blocked, self], + paramsv![new_blocked, self], ) .await .is_ok() } - pub fn unblock(self, context: &Context) { - self.set_blocked(context, Blocked::Not); + pub async fn unblock(self, context: &Context) { + self.set_blocked(context, Blocked::Not).await; } /// Archives or unarchives a chat. @@ -161,7 +161,7 @@ impl ChatId { .sql .execute( "UPDATE msgs SET state=? WHERE chat_id=? AND state=?;", - params![MessageState::InNoticed, self, MessageState::InFresh], + paramsv![MessageState::InNoticed, self, MessageState::InFresh], ) .await?; } @@ -170,7 +170,7 @@ impl ChatId { .sql .execute( "UPDATE chats SET archived=? WHERE id=?;", - params![visibility, self], + paramsv![visibility, self], ) .await?; @@ -189,7 +189,7 @@ impl ChatId { .sql .execute( "UPDATE chats SET archived=0 WHERE id=? and archived=1", - params![self], + paramsv![self], ) .await?; Ok(()) @@ -209,23 +209,26 @@ impl ChatId { .sql .execute( "DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);", - params![self], + paramsv![self], ) .await?; context .sql - .execute("DELETE FROM msgs WHERE chat_id=?;", params![self]) + .execute("DELETE FROM msgs WHERE chat_id=?;", paramsv![self]) .await?; context .sql - .execute("DELETE FROM chats_contacts WHERE chat_id=?;", params![self]) + .execute( + "DELETE FROM chats_contacts WHERE chat_id=?;", + paramsv![self], + ) .await?; context .sql - .execute("DELETE FROM chats WHERE id=?;", params![self]) + .execute("DELETE FROM chats WHERE id=?;", paramsv![self]) .await?; context.call_cb(Event::MsgsChanged { @@ -272,10 +275,10 @@ impl ChatId { async fn get_draft_msg_id(self, context: &Context) -> Option { context .sql - .query_get_value::<_, MsgId>( + .query_get_value::( context, "SELECT id FROM msgs WHERE chat_id=? AND state=?;", - params![self, MessageState::OutDraft], + paramsv![self, MessageState::OutDraft], ) .await } @@ -299,7 +302,7 @@ impl ChatId { async fn maybe_delete_draft(self, context: &Context) -> bool { match self.get_draft_msg_id(context).await { Some(msg_id) => { - Message::delete_from_db(context, msg_id); + Message::delete_from_db(context, msg_id).await; true } None => false, @@ -333,13 +336,13 @@ impl ChatId { .execute( "INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden) VALUES (?,?,?, ?,?,?,?,?);", - params![ + paramsv![ self, DC_CONTACT_ID_SELF, time(), msg.viewtype, MessageState::OutDraft, - msg.text.as_ref().map(String::as_str).unwrap_or(""), + msg.text.as_ref().cloned().unwrap_or_default(), msg.param.to_string(), 1, ], @@ -353,10 +356,10 @@ impl ChatId { pub async fn get_msg_cnt(self, context: &Context) -> usize { context .sql - .query_get_value::<_, i32>( + .query_get_value::( context, "SELECT COUNT(*) FROM msgs WHERE chat_id=?;", - params![self], + paramsv![self], ) .await .unwrap_or_default() as usize @@ -365,14 +368,14 @@ impl ChatId { pub async fn get_fresh_msg_cnt(self, context: &Context) -> usize { context .sql - .query_get_value::<_, i32>( + .query_get_value::( context, "SELECT COUNT(*) FROM msgs WHERE state=10 AND hidden=0 AND chat_id=?;", - params![self], + paramsv![self], ) .await .unwrap_or_default() as usize @@ -459,7 +462,7 @@ impl Chat { c.blocked, c.locations_send_until, c.muted_until FROM chats c WHERE c.id=?;", - params![chat_id], + paramsv![chat_id], |row| { let c = Chat { id: chat_id, @@ -490,13 +493,13 @@ impl Chat { } Ok(mut chat) => { if chat.id.is_deaddrop() { - chat.name = context.stock_str(StockMessage::DeadDrop).into(); + chat.name = context.stock_str(StockMessage::DeadDrop).await.into(); } else if chat.id.is_archived_link() { - let tempname = context.stock_str(StockMessage::ArchivedChats); + let tempname = context.stock_str(StockMessage::ArchivedChats).await; let cnt = dc_get_archived_cnt(context).await; chat.name = format!("{} ({})", tempname, cnt); } else if chat.id.is_starred() { - chat.name = context.stock_str(StockMessage::StarredMsgs).into(); + chat.name = context.stock_str(StockMessage::StarredMsgs).await.into(); } else { if chat.typ == Chattype::Single { let contacts = get_chat_contacts(context, chat.id).await; @@ -509,9 +512,9 @@ impl Chat { chat.name = chat_name; } if chat.param.exists(Param::Selftalk) { - chat.name = context.stock_str(StockMessage::SavedMessages).into(); + chat.name = context.stock_str(StockMessage::SavedMessages).await.into(); } else if chat.param.exists(Param::Devicetalk) { - chat.name = context.stock_str(StockMessage::DeviceMessages).into(); + chat.name = context.stock_str(StockMessage::DeviceMessages).await.into(); } } Ok(chat) @@ -538,7 +541,7 @@ impl Chat { .sql .execute( "UPDATE chats SET param=? WHERE id=?", - params![self.param.to_string(), self.id], + paramsv![self.param.to_string(), self.id], ) .await?; Ok(()) @@ -563,7 +566,10 @@ impl Chat { // returns either the address or the number of chat members if self.typ == Chattype::Single && self.param.exists(Param::Selftalk) { - return context.stock_str(StockMessage::SelfTalkSubTitle).into(); + return context + .stock_str(StockMessage::SelfTalkSubTitle) + .await + .into(); } if self.typ == Chattype::Single { @@ -575,7 +581,7 @@ impl Chat { FROM chats_contacts cc LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=?;", - params![self.id], + paramsv![self.id], ) .await .unwrap_or_else(|| "Err".into()); @@ -583,10 +589,12 @@ impl Chat { if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { if self.id.is_deaddrop() { - return context.stock_str(StockMessage::DeadDrop).into(); + return context.stock_str(StockMessage::DeadDrop).await.into(); } let cnt = get_chat_contact_cnt(context, self.id).await; - return context.stock_string_repl_int(StockMessage::Member, cnt as i32); + return context + .stock_string_repl_int(StockMessage::Member, cnt as i32) + .await; } "Err".to_string() @@ -606,7 +614,7 @@ impl Chat { async fn get_parent_mime_headers(&self, context: &Context) -> Option<(String, String, String)> { let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)); - let params = params![self.id]; + let params = paramsv![self.id]; let sql = &context.sql; let query = Self::parent_query("rfc724_mid, mime_in_reply_to, mime_references"); @@ -616,7 +624,7 @@ impl Chat { async fn parent_is_encrypted(&self, context: &Context) -> Result { let sql = &context.sql; - let params = params![self.id]; + let params = paramsv![self.id]; let query = Self::parent_query("param"); let packed: Option = sql.query_get_value_result(&query, params).await?; @@ -771,7 +779,7 @@ impl Chat { .query_get_value( context, "SELECT contact_id FROM chats_contacts WHERE chat_id=?;", - params![self.id], + paramsv![self.id], ) .await { @@ -809,7 +817,7 @@ impl Chat { LEFT JOIN contacts c ON cc.contact_id=c.id \ LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ WHERE cc.chat_id=? AND cc.contact_id>9;", - params![self.id], + paramsv![self.id], |row| { let addr: String = row.get(1)?; @@ -897,7 +905,7 @@ impl Chat { "INSERT INTO locations \ (timestamp,from_id,chat_id, latitude,longitude,independent)\ VALUES (?,?,?, ?,?,1);", // 1=DC_CONTACT_ID_SELF - params![ + paramsv![ timestamp, DC_CONTACT_ID_SELF, self.id, @@ -925,7 +933,7 @@ impl Chat { if context.sql.execute( "INSERT INTO msgs (rfc724_mid, chat_id, from_id, to_id, timestamp, type, state, txt, param, hidden, mime_in_reply_to, mime_references, location_id) VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?);", - params![ + paramsv![ new_rfc724_mid, self.id, DC_CONTACT_ID_SELF, @@ -933,7 +941,7 @@ impl Chat { timestamp, msg.viewtype, msg.state, - msg.text.as_ref().map_or("", String::as_str), + msg.text.as_ref().cloned().unwrap_or_default(), msg.param.to_string(), msg.hidden, new_in_reply_to, @@ -1093,7 +1101,7 @@ pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result Result Result< Ok((chat_id, chat_blocked)) => { if chat_blocked != Blocked::Not { // unblock chat (typically move it from the deaddrop to view - chat_id.unblock(context); + chat_id.unblock(context).await; } chat_id } @@ -1132,7 +1140,7 @@ pub async fn create_by_contact_id(context: &Context, contact_id: u32) -> Result< } else { let (chat_id, _) = create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).await?; - Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat); + Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat).await; chat_id } } @@ -1184,13 +1192,13 @@ async fn update_special_chat_name( stock_id: StockMessage, ) -> Result<(), Error> { if let Ok((chat_id, _)) = lookup_by_contact_id(context, contact_id).await { - let name: String = context.stock_str(stock_id).into(); + let name: String = context.stock_str(stock_id).await.into(); // the `!= name` condition avoids unneeded writes context .sql .execute( "UPDATE chats SET name=? WHERE id=? AND name!=?;", - params![name, chat_id, name], + paramsv![name, chat_id, name], ) .await?; } @@ -1274,7 +1282,7 @@ pub(crate) async fn lookup_by_contact_id( WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", - params![contact_id as i32], + paramsv![contact_id as i32], |row| { Ok(( row.get::<_, ChatId>(0)?, @@ -1390,7 +1398,7 @@ async fn prepare_msg_common( } msg.id = chat - .prepare_msg_raw(context, msg, dc_create_smeared_timestamp(context)) + .prepare_msg_raw(context, msg, dc_create_smeared_timestamp(context).await) .await?; msg.chat_id = chat_id; @@ -1408,7 +1416,7 @@ pub async fn is_contact_in_chat(context: &Context, chat_id: ChatId, contact_id: .sql .exists( "SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id=?;", - params![chat_id, contact_id as i32], + paramsv![chat_id, contact_id as i32], ) .await .unwrap_or_default() @@ -1443,7 +1451,7 @@ pub async fn send_msg( } } msg.param.remove(Param::PrepForwards); - msg.save_param_to_disk(context); + msg.save_param_to_disk(context).await; } return send_msg_inner(context, chat_id, msg).await; } @@ -1469,7 +1477,7 @@ async fn send_msg_inner( chat_id.is_unset() || chat_id == msg.chat_id, "Inconsistent chat ID" ); - message::update_msg_state(context, msg.id, MessageState::OutPending); + message::update_msg_state(context, msg.id, MessageState::OutPending).await; } job::send_msg(context, msg.id).await?; @@ -1551,7 +1559,7 @@ pub async fn get_chat_msgs( AND contacts.blocked=0 AND m.msgrmsg>=? ORDER BY m.timestamp,m.id;", - params![if show_emails == ShowEmails::All { 0 } else { 1 }], + paramsv![if show_emails == ShowEmails::All { 0 } else { 1 }], process_row, process_rows, ) @@ -1568,7 +1576,7 @@ pub async fn get_chat_msgs( AND m.hidden=0 AND ct.blocked=0 ORDER BY m.timestamp,m.id;", - params![], + paramsv![], process_row, process_rows, ) @@ -1582,7 +1590,7 @@ pub async fn get_chat_msgs( WHERE m.chat_id=? AND m.hidden=0 ORDER BY m.timestamp, m.id;", - params![chat_id], + paramsv![chat_id], process_row, process_rows, ) @@ -1602,7 +1610,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<(), .sql .exists( "SELECT id FROM msgs WHERE chat_id=? AND state=?;", - params![chat_id, MessageState::InFresh], + paramsv![chat_id, MessageState::InFresh], ) .await? { @@ -1616,7 +1624,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<(), SET state=13 WHERE chat_id=? AND state=10;", - params![chat_id], + paramsv![chat_id], ) .await?; @@ -1635,7 +1643,7 @@ pub async fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { "SELECT id FROM msgs WHERE state=10;", - params![], + paramsv![], ) .await? { @@ -1648,7 +1656,7 @@ pub async fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { "UPDATE msgs SET state=13 WHERE state=10;", - params![], + paramsv![], ) .await?; @@ -1676,7 +1684,7 @@ pub async fn get_chat_media( WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;", - params![ + paramsv![ chat_id, msg_type, if msg_type2 != Viewtype::Unknown { @@ -1777,7 +1785,7 @@ pub async fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Vec { ON c.id=cc.contact_id WHERE cc.chat_id=? ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;", - params![chat_id], + paramsv![chat_id], |row| row.get::<_, u32>(0), |ids| ids.collect::, _>>().map_err(Into::into), ) @@ -1792,18 +1800,20 @@ pub async fn create_group_chat( ) -> Result { ensure!(!chat_name.as_ref().is_empty(), "Invalid chat name"); - let draft_txt = context.stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name); + let draft_txt = context + .stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name) + .await; let grpid = dc_create_id(); context.sql.execute( "INSERT INTO chats (type, name, grpid, param, created_timestamp) VALUES(?, ?, ?, \'U=1\', ?);", - params![ + paramsv![ if verified != VerifiedStatus::Unverified { Chattype::VerifiedGroup } else { Chattype::Group }, - chat_name.as_ref(), + chat_name.as_ref().to_string(), grpid, time(), ], @@ -1818,7 +1828,7 @@ pub async fn create_group_chat( if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await { let mut draft_msg = Message::new(Viewtype::Text); draft_msg.set_text(Some(draft_txt)); - chat_id.set_draft_raw(context, &mut draft_msg); + chat_id.set_draft_raw(context, &mut draft_msg).await; } context.call_cb(Event::MsgsChanged { @@ -1841,7 +1851,7 @@ pub(crate) async fn add_to_chat_contacts_table( .sql .execute( "INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)", - params![chat_id, contact_id as i32], + paramsv![chat_id, contact_id as i32], ) .await .is_ok() @@ -1858,7 +1868,7 @@ pub(crate) async fn remove_from_chat_contacts_table( .sql .execute( "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?", - params![chat_id, contact_id as i32], + paramsv![chat_id, contact_id as i32], ) .await .is_ok() @@ -1933,7 +1943,7 @@ pub(crate) async fn add_contact_to_chat_ex( } else { // else continue and send status mail if chat.typ == Chattype::VerifiedGroup - && contact.is_verified(context) != VerifiedStatus::BidirectVerified + && contact.is_verified(context).await != VerifiedStatus::BidirectVerified { error!( context, @@ -1947,12 +1957,16 @@ pub(crate) async fn add_contact_to_chat_ex( } if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { msg.viewtype = Viewtype::Text; - msg.text = Some(context.stock_system_msg( - StockMessage::MsgAddMember, - contact.get_addr(), - "", - DC_CONTACT_ID_SELF as u32, - )); + msg.text = Some( + context + .stock_system_msg( + StockMessage::MsgAddMember, + contact.get_addr(), + "", + DC_CONTACT_ID_SELF as u32, + ) + .await, + ); msg.param.set_cmd(SystemMessage::MemberAddedToGroup); msg.param.set(Param::Arg, contact.get_addr()); msg.param.set_int(Param::Arg2, from_handshake.into()); @@ -1980,7 +1994,7 @@ async fn real_group_exists(context: &Context, chat_id: ChatId) -> bool { .sql .exists( "SELECT id FROM chats WHERE id=? AND (type=120 OR type=130);", - params![chat_id], + paramsv![chat_id], ) .await .unwrap_or_default() @@ -1998,10 +2012,10 @@ pub(crate) async fn reset_gossiped_timestamp( pub async fn get_gossiped_timestamp(context: &Context, chat_id: ChatId) -> i64 { context .sql - .query_get_value::<_, i64>( + .query_get_value::( context, "SELECT gossiped_timestamp FROM chats WHERE id=?;", - params![chat_id], + paramsv![chat_id], ) .await .unwrap_or_default() @@ -2022,7 +2036,7 @@ pub(crate) async fn set_gossiped_timestamp( .sql .execute( "UPDATE chats SET gossiped_timestamp=? WHERE id=?;", - params![timestamp, chat_id], + paramsv![timestamp, chat_id], ) .await?; @@ -2052,7 +2066,7 @@ pub(crate) async fn shall_attach_selfavatar( FROM chats_contacts cc LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=? AND cc.contact_id!=?;", - params![chat_id, DC_CONTACT_ID_SELF], + paramsv![chat_id, DC_CONTACT_ID_SELF], |row| Ok(row.get::<_, i64>(0)), |rows| { let mut needs_attach = false; @@ -2124,7 +2138,7 @@ pub async fn set_muted( .sql .execute( "UPDATE chats SET muted_until=? WHERE id=?;", - params![duration, chat_id], + paramsv![duration, chat_id], ) .await .is_ok() @@ -2172,19 +2186,27 @@ pub async fn remove_contact_from_chat( msg.viewtype = Viewtype::Text; if contact.id == DC_CONTACT_ID_SELF { set_group_explicitly_left(context, chat.grpid).await?; - msg.text = Some(context.stock_system_msg( - StockMessage::MsgGroupLeft, - "", - "", - DC_CONTACT_ID_SELF, - )); + msg.text = Some( + context + .stock_system_msg( + StockMessage::MsgGroupLeft, + "", + "", + DC_CONTACT_ID_SELF, + ) + .await, + ); } else { - msg.text = Some(context.stock_system_msg( - StockMessage::MsgDelMember, - contact.get_addr(), - "", - DC_CONTACT_ID_SELF, - )); + msg.text = Some( + context + .stock_system_msg( + StockMessage::MsgDelMember, + contact.get_addr(), + "", + DC_CONTACT_ID_SELF, + ) + .await, + ); } msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup); msg.param.set(Param::Arg, contact.get_addr()); @@ -2216,7 +2238,7 @@ async fn set_group_explicitly_left(context: &Context, grpid: impl AsRef) -> .sql .execute( "INSERT INTO leftgrps (grpid) VALUES(?);", - params![grpid.as_ref()], + paramsv![grpid.as_ref().to_string()], ) .await?; } @@ -2232,7 +2254,7 @@ pub(crate) async fn is_group_explicitly_left( .sql .exists( "SELECT id FROM leftgrps WHERE grpid=?;", - params![grpid.as_ref()], + paramsv![grpid.as_ref()], ) .await .map_err(Into::into) @@ -2266,19 +2288,23 @@ pub async fn set_chat_name( .sql .execute( "UPDATE chats SET name=? WHERE id=?;", - params![new_name.as_ref(), chat_id], + paramsv![new_name.as_ref().to_string(), chat_id], ) .await .is_ok() { if chat.is_promoted() { msg.viewtype = Viewtype::Text; - msg.text = Some(context.stock_system_msg( - StockMessage::MsgGrpName, - &chat.name, - new_name.as_ref(), - DC_CONTACT_ID_SELF, - )); + msg.text = Some( + context + .stock_system_msg( + StockMessage::MsgGrpName, + &chat.name, + new_name.as_ref(), + DC_CONTACT_ID_SELF, + ) + .await, + ); msg.param.set_cmd(SystemMessage::GroupNameChanged); if !chat.name.is_empty() { msg.param.set(Param::Arg, &chat.name); @@ -2332,12 +2358,11 @@ pub async fn set_chat_profile_image( if new_image.as_ref().is_empty() { chat.param.remove(Param::ProfileImage); msg.param.remove(Param::Arg); - msg.text = Some(context.stock_system_msg( - StockMessage::MsgGrpImgDeleted, - "", - "", - DC_CONTACT_ID_SELF, - )); + msg.text = Some( + context + .stock_system_msg(StockMessage::MsgGrpImgDeleted, "", "", DC_CONTACT_ID_SELF) + .await, + ); } else { let image_blob = BlobObject::from_path(context, Path::new(new_image.as_ref())).or_else( |err| match err { @@ -2350,12 +2375,11 @@ pub async fn set_chat_profile_image( image_blob.recode_to_avatar_size(context)?; chat.param.set(Param::ProfileImage, image_blob.as_name()); msg.param.set(Param::Arg, image_blob.as_name()); - msg.text = Some(context.stock_system_msg( - StockMessage::MsgGrpImgChanged, - "", - "", - DC_CONTACT_ID_SELF, - )); + msg.text = Some( + context + .stock_system_msg(StockMessage::MsgGrpImgChanged, "", "", DC_CONTACT_ID_SELF) + .await, + ); } chat.update_param(context).await?; if chat.is_promoted() { @@ -2387,7 +2411,7 @@ pub async fn forward_msgs( chat_id.unarchive(context).await?; if let Ok(mut chat) = Chat::load_from_db(context, chat_id).await { ensure!(chat.can_send(), "cannot send to {}", chat_id); - curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len()); + curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len()).await; let ids = context .sql .query_map( @@ -2395,7 +2419,7 @@ pub async fn forward_msgs( "SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id", msg_ids.iter().map(|_| "?").join(",") ), - msg_ids, + msg_ids.iter().map(|v| v as &dyn crate::ToSql).collect(), |row| row.get::<_, MsgId>(0), |ids| ids.collect::, _>>().map_err(Into::into), ) @@ -2436,7 +2460,7 @@ pub async fn forward_msgs( .set(Param::PrepForwards, new_msg_id.to_u32().to_string()); } - msg.save_param_to_disk(context); + msg.save_param_to_disk(context).await; msg.param = save_param; } else { msg.state = MessageState::OutPending; @@ -2461,10 +2485,10 @@ pub async fn forward_msgs( pub(crate) async fn get_chat_contact_cnt(context: &Context, chat_id: ChatId) -> usize { context .sql - .query_get_value::<_, isize>( + .query_get_value::( context, "SELECT COUNT(*) FROM chats_contacts WHERE chat_id=?;", - params![chat_id], + paramsv![chat_id], ) .await .unwrap_or_default() as usize @@ -2475,10 +2499,10 @@ pub(crate) async fn get_chat_cnt(context: &Context) -> usize { /* no database, no chats - this is no error (needed eg. for information) */ context .sql - .query_get_value::<_, isize>( + .query_get_value::( context, "SELECT COUNT(*) FROM chats WHERE id>9 AND blocked=0;", - params![], + paramsv![], ) .await .unwrap_or_default() as usize @@ -2495,7 +2519,7 @@ pub(crate) async fn get_chat_id_by_grpid( .sql .query_row( "SELECT id, blocked, type FROM chats WHERE grpid=?;", - params![grpid.as_ref()], + paramsv![grpid.as_ref()], |row| { let chat_id = row.get::<_, ChatId>(0)?; @@ -2535,21 +2559,21 @@ pub async fn add_device_msg( .0; let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device"); - msg.try_calc_and_set_dimensions(context).ok(); + msg.try_calc_and_set_dimensions(context).await.ok(); prepare_msg_blob(context, msg)?; chat_id.unarchive(context).await?; context.sql.execute( "INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,param,rfc724_mid) \ VALUES (?,?,?, ?,?,?, ?,?,?);", - params![ + paramsv![ chat_id, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF, - dc_create_smeared_timestamp(context), + dc_create_smeared_timestamp(context).await, msg.viewtype, MessageState::InFresh, - msg.text.as_ref().map_or("", String::as_str), + msg.text.as_ref().cloned().unwrap_or_default(), msg.param.to_string(), rfc724_mid, ], @@ -2567,7 +2591,7 @@ pub async fn add_device_msg( .sql .execute( "INSERT INTO devmsglabels (label) VALUES (?);", - params![label], + paramsv![label.to_string()], ) .await?; } @@ -2585,7 +2609,7 @@ pub async fn was_device_msg_ever_added(context: &Context, label: &str) -> Result .sql .query_row( "SELECT label FROM devmsglabels WHERE label=?", - params![label], + paramsv![label], |_| Ok(()), ) .await @@ -2606,12 +2630,12 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul .sql .execute( "DELETE FROM msgs WHERE from_id=?;", - params![DC_CONTACT_ID_DEVICE], + paramsv![DC_CONTACT_ID_DEVICE], ) .await?; context .sql - .execute("DELETE FROM devmsglabels;", params![]) + .execute("DELETE FROM devmsglabels;", paramsv![]) .await?; Ok(()) } @@ -2624,14 +2648,14 @@ pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl if context.sql.execute( "INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);", - params![ + paramsv![ chat_id, DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO, - dc_create_smeared_timestamp(context), + dc_create_smeared_timestamp(context).await, Viewtype::Text, MessageState::InNoticed, - text.as_ref(), + text.as_ref().to_string(), rfc724_mid, ] ).await.is_err() { @@ -2658,7 +2682,7 @@ mod tests { #[async_std::test] async fn test_chat_info() { - let t = dummy_context(); + let t = dummy_context().await; let bob = Contact::create(&t.ctx, "bob", "bob@example.com") .await .unwrap(); @@ -2693,7 +2717,7 @@ mod tests { #[async_std::test] async fn test_get_draft_no_draft() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF) .await .unwrap(); @@ -2703,7 +2727,7 @@ mod tests { #[async_std::test] async fn test_get_draft_special_chat_id() { - let t = dummy_context(); + let t = dummy_context().await; let draft = ChatId::new(DC_CHAT_ID_LAST_SPECIAL) .get_draft(&t.ctx) .await @@ -2715,20 +2739,20 @@ mod tests { async fn test_get_draft_no_chat() { // This is a weird case, maybe this should be an error but we // do not get this info from the database currently. - let t = dummy_context(); + let t = dummy_context().await; let draft = ChatId::new(42).get_draft(&t.ctx).await.unwrap(); assert!(draft.is_none()); } #[async_std::test] async fn test_get_draft() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF) .await .unwrap(); let mut msg = Message::new(Viewtype::Text); msg.set_text(Some("hello".to_string())); - chat_id.set_draft(&t.ctx, Some(&mut msg)); + chat_id.set_draft(&t.ctx, Some(&mut msg)).await; let draft = chat_id.get_draft(&t.ctx).await.unwrap().unwrap(); let msg_text = msg.get_text(); let draft_text = draft.get_text(); @@ -2738,7 +2762,7 @@ mod tests { #[async_std::test] async fn test_add_contact_to_chat_ex_add_self() { // Adding self to a contact should succeed, even though it's pointless. - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo") .await .unwrap(); @@ -2750,7 +2774,7 @@ mod tests { #[async_std::test] async fn test_self_talk() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF) .await .unwrap(); @@ -2762,13 +2786,16 @@ mod tests { assert!(chat.visibility == ChatVisibility::Normal); assert!(!chat.is_device_talk()); assert!(chat.can_send()); - assert_eq!(chat.name, t.ctx.stock_str(StockMessage::SavedMessages)); + assert_eq!( + chat.name, + t.ctx.stock_str(StockMessage::SavedMessages).await + ); assert!(chat.get_profile_image(&t.ctx).await.is_some()); } #[async_std::test] async fn test_deaddrop_chat() { - let t = dummy_context(); + let t = dummy_context().await; let chat = Chat::load_from_db(&t.ctx, ChatId::new(DC_CHAT_ID_DEADDROP)) .await .unwrap(); @@ -2778,12 +2805,12 @@ mod tests { assert!(chat.visibility == ChatVisibility::Normal); assert!(!chat.is_device_talk()); assert!(!chat.can_send()); - assert_eq!(chat.name, t.ctx.stock_str(StockMessage::DeadDrop)); + assert_eq!(chat.name, t.ctx.stock_str(StockMessage::DeadDrop).await); } #[async_std::test] async fn test_add_device_msg_unlabelled() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; // add two device-messages let mut msg1 = Message::new(Viewtype::Text); @@ -2818,7 +2845,7 @@ mod tests { #[async_std::test] async fn test_add_device_msg_labelled() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; // add two device-messages with the same label (second attempt is not added) let mut msg1 = Message::new(Viewtype::Text); @@ -2855,7 +2882,10 @@ mod tests { assert!(chat.is_device_talk()); assert!(!chat.is_self_talk()); assert!(!chat.can_send()); - assert_eq!(chat.name, t.ctx.stock_str(StockMessage::DeviceMessages)); + assert_eq!( + chat.name, + t.ctx.stock_str(StockMessage::DeviceMessages).await + ); assert!(chat.get_profile_image(&t.ctx).await.is_some()); // delete device message, make sure it is not added again @@ -2869,7 +2899,7 @@ mod tests { #[async_std::test] async fn test_add_device_msg_label_only() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; let res = add_device_msg(&t.ctx, Some(""), None).await; assert!(res.is_err()); let res = add_device_msg(&t.ctx, Some("some-label"), None).await; @@ -2889,7 +2919,7 @@ mod tests { #[async_std::test] async fn test_was_device_msg_ever_added() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; add_device_msg(&t.ctx, Some("some-label"), None).await.ok(); assert!(was_device_msg_ever_added(&t.ctx, "some-label") .await @@ -2913,7 +2943,7 @@ mod tests { #[async_std::test] async fn test_delete_device_chat() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; let mut msg = Message::new(Viewtype::Text); msg.text = Some("message text".to_string()); @@ -2933,8 +2963,8 @@ mod tests { #[async_std::test] async fn test_device_chat_cannot_sent() { - let t = test_context(Some(Box::new(logging_cb))); - t.ctx.update_device_chats().unwrap(); + let t = test_context(Some(Box::new(logging_cb))).await; + t.ctx.update_device_chats().await.unwrap(); let (device_chat_id, _) = create_or_lookup_by_contact_id(&t.ctx, DC_CONTACT_ID_DEVICE, Blocked::Not) .await @@ -2953,7 +2983,7 @@ mod tests { #[async_std::test] async fn test_delete_and_reset_all_device_msgs() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; let mut msg = Message::new(Viewtype::Text); msg.text = Some("message text".to_string()); let msg_id1 = add_device_msg(&t.ctx, Some("some-label"), Some(&mut msg)) @@ -2990,7 +3020,7 @@ mod tests { #[async_std::test] async fn test_archive() { // create two chats - let t = dummy_context(); + let t = dummy_context().await; let mut msg = Message::new(Viewtype::Text); msg.text = Some("foo".to_string()); let msg_id = add_device_msg(&t.ctx, None, Some(&mut msg)).await.unwrap(); @@ -3104,7 +3134,7 @@ mod tests { #[async_std::test] async fn test_pinned() { - let t = dummy_context(); + let t = dummy_context().await; // create 3 chats, wait 1 second in between to get a reliable order (we order by time) let mut msg = Message::new(Viewtype::Text); @@ -3163,7 +3193,7 @@ mod tests { #[async_std::test] async fn test_set_chat_name() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo") .await .unwrap(); @@ -3187,7 +3217,7 @@ mod tests { #[async_std::test] async fn test_create_same_chat_twice() { - let context = dummy_context(); + let context = dummy_context().await; let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de") .await .unwrap(); @@ -3206,7 +3236,7 @@ mod tests { #[async_std::test] async fn test_shall_attach_selfavatar() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo") .await .unwrap(); @@ -3230,7 +3260,7 @@ mod tests { #[async_std::test] async fn test_set_mute_duration() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo") .await .unwrap(); diff --git a/src/chatlist.rs b/src/chatlist.rs index fa8544654..3fe730181 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -136,7 +136,7 @@ impl Chatlist { AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2) GROUP BY c.id ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - params![MessageState::OutDraft, query_contact_id as i32, ChatVisibility::Pinned], + paramsv![MessageState::OutDraft, query_contact_id as i32, ChatVisibility::Pinned], process_row, process_rows, ).await? @@ -159,7 +159,7 @@ impl Chatlist { AND c.archived=1 GROUP BY c.id ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - params![MessageState::OutDraft], + paramsv![MessageState::OutDraft], process_row, process_rows, ) @@ -192,7 +192,7 @@ impl Chatlist { AND c.name LIKE ? GROUP BY c.id ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - params![MessageState::OutDraft, str_like_cmd], + paramsv![MessageState::OutDraft, str_like_cmd], process_row, process_rows, ) @@ -221,7 +221,7 @@ impl Chatlist { AND NOT c.archived=?2 GROUP BY c.id ORDER BY c.id=?3 DESC, c.archived=?4 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - params![MessageState::OutDraft, ChatVisibility::Archived, sort_id_up, ChatVisibility::Pinned], + paramsv![MessageState::OutDraft, ChatVisibility::Archived, sort_id_up, ChatVisibility::Pinned], process_row, process_rows, ).await?; @@ -333,9 +333,15 @@ impl Chatlist { ret.text2 = None; } else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED { - ret.text2 = Some(context.stock_str(StockMessage::NoMessages).to_string()); + ret.text2 = Some( + context + .stock_str(StockMessage::NoMessages) + .await + .to_string(), + ); } else { - ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context); + ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context) + .await; } ret @@ -353,7 +359,7 @@ pub async fn dc_get_archived_cnt(context: &Context) -> u32 { .query_get_value( context, "SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;", - params![], + paramsv![], ) .await .unwrap_or_default() @@ -376,7 +382,7 @@ async fn get_last_deaddrop_fresh_msg(context: &Context) -> Option { " AND c.blocked=2", " ORDER BY m.timestamp DESC, m.id DESC;" ), - params![], + paramsv![], ) .await } @@ -389,7 +395,7 @@ mod tests { #[async_std::test] async fn test_try_load() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat") .await .unwrap(); @@ -410,7 +416,7 @@ mod tests { // drafts are sorted to the top let mut msg = Message::new(Viewtype::Text); msg.set_text(Some("hello".to_string())); - chat_id2.set_draft(&t.ctx, Some(&mut msg)); + chat_id2.set_draft(&t.ctx, Some(&mut msg)).await; let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); assert_eq!(chats.get_chat_id(0), chat_id2); @@ -453,8 +459,8 @@ mod tests { #[async_std::test] async fn test_search_special_chat_names() { - let t = dummy_context(); - t.ctx.update_device_chats().unwrap(); + let t = dummy_context().await; + t.ctx.update_device_chats().await.unwrap(); let chats = Chatlist::try_load(&t.ctx, 0, Some("t-1234-s"), None) .await @@ -467,6 +473,7 @@ mod tests { t.ctx .set_stock_translation(StockMessage::SavedMessages, "test-1234-save".to_string()) + .await .unwrap(); let chats = Chatlist::try_load(&t.ctx, 0, Some("t-1234-s"), None) .await @@ -475,6 +482,7 @@ mod tests { t.ctx .set_stock_translation(StockMessage::DeviceMessages, "test-5678-babbel".to_string()) + .await .unwrap(); let chats = Chatlist::try_load(&t.ctx, 0, Some("t-5678-b"), None) .await @@ -484,14 +492,14 @@ mod tests { #[async_std::test] async fn test_get_summary_unwrap() { - let t = dummy_context(); + let t = dummy_context().await; let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat") .await .unwrap(); let mut msg = Message::new(Viewtype::Text); msg.set_text(Some("foo:\nbar \r\n test".to_string())); - chat_id1.set_draft(&t.ctx, Some(&mut msg)); + chat_id1.set_draft(&t.ctx, Some(&mut msg)).await; let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); let summary = chats.get_summary(&t.ctx, 0, None).await; diff --git a/src/config.rs b/src/config.rs index 62c02e510..187d26735 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,7 +10,6 @@ use crate::dc_tools::*; use crate::job::*; use crate::mimefactory::RECOMMENDED_FILE_SIZE; use crate::stock::StockMessage; -use rusqlite::NO_PARAMS; /// The available configuration keys. #[derive( @@ -113,7 +112,7 @@ impl Context { // Default values match key { - Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()), + Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).await.into_owned()), _ => key.get_str("default").map(|s| s.to_string()), } } @@ -135,7 +134,7 @@ impl Context { match key { Config::Selfavatar => { self.sql - .execute("UPDATE contacts SET selfavatar_sent=0;", NO_PARAMS) + .execute("UPDATE contacts SET selfavatar_sent=0;", paramsv![]) .await?; self.sql .set_raw_config_bool(self, "attach_selfavatar", true) @@ -167,7 +166,7 @@ impl Context { ret } Config::Selfstatus => { - let def = self.stock_str(StockMessage::StatusLine); + let def = self.stock_str(StockMessage::StatusLine).await; let val = if value.is_none() || value.unwrap() == def { None } else { @@ -224,7 +223,7 @@ mod tests { #[async_std::test] async fn test_selfavatar_outside_blobdir() { - let t = dummy_context(); + let t = dummy_context().await; let avatar_src = t.dir.path().join("avatar.jpg"); let avatar_bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg"); File::create(&avatar_src) @@ -253,7 +252,7 @@ mod tests { #[async_std::test] async fn test_selfavatar_in_blobdir() { - let t = dummy_context(); + let t = dummy_context().await; let avatar_src = t.ctx.get_blobdir().join("avatar.png"); let avatar_bytes = include_bytes!("../test-data/image/avatar900x900.png"); File::create(&avatar_src) diff --git a/src/configure/mod.rs b/src/configure/mod.rs index d069132d2..b73aff7f5 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -33,7 +33,7 @@ macro_rules! progress { impl Context { /// Starts a configuration job. pub async fn configure(&self) { - if self.has_ongoing() { + if self.has_ongoing().await { warn!(self, "There is already another ongoing process running.",); return; } @@ -57,7 +57,7 @@ pub(crate) async fn job_configure_imap(context: &Context) -> job::Status { progress!(context, 0); return job::Status::Finished(Err(format_err!("Database not opened"))); } - if !context.alloc_ongoing() { + if !context.alloc_ongoing().await { progress!(context, 0); return job::Status::Finished(Err(format_err!("Cannot allocated ongoing process"))); } @@ -87,7 +87,7 @@ pub(crate) async fn job_configure_imap(context: &Context) -> job::Status { const STEP_13_AFTER_AUTOCONFIG: u8 = 13; let mut step_counter: u8 = 0; - while !context.shall_stop_ongoing() { + while !context.shall_stop_ongoing().await { step_counter += 1; let success = match step_counter { @@ -108,6 +108,7 @@ pub(crate) async fn job_configure_imap(context: &Context) -> job::Status { progress!(context, 10); if let Some(oauth2_addr) = dc_get_oauth2_addr(context, ¶m.addr, ¶m.mail_pw) + .await .and_then(|e| e.parse().ok()) { info!(context, "Authorized address is {}", oauth2_addr); @@ -444,7 +445,7 @@ pub(crate) async fn job_configure_imap(context: &Context) -> job::Status { } } - context.free_ongoing(); + context.free_ongoing().await; progress!(context, if success { 1000 } else { 0 }); job::Status::Finished(Ok(())) } @@ -574,7 +575,7 @@ async fn try_imap_one_param(context: &Context, param: &LoginParam) -> Option Option { - if context.shall_stop_ongoing() { + if context.shall_stop_ongoing().await { Some(false) } else { warn!(context, "could not connect: {}", err); @@ -642,7 +643,7 @@ mod tests { #[async_std::test] async fn test_no_panic_on_bad_credentials() { - let t = dummy_context(); + let t = dummy_context().await; t.ctx .set_config(Config::Addr, Some("probably@unexistant.addr")) .await @@ -654,9 +655,9 @@ mod tests { job_configure_imap(&t.ctx).await; } - #[test] - fn test_get_offline_autoconfig() { - let context = dummy_context().ctx; + #[async_std::test] + async fn test_get_offline_autoconfig() { + let context = dummy_context().await.ctx; let mut params = LoginParam::new(); params.addr = "someone123@example.org".to_string(); diff --git a/src/contact.rs b/src/contact.rs index 29a9bab9a..2453c1f91 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -170,7 +170,7 @@ impl Contact { "SELECT c.name, c.addr, c.origin, c.blocked, c.authname, c.param FROM contacts c WHERE c.id=?;", - params![contact_id as i32], + paramsv![contact_id as i32], |row| { let contact = Self { id: contact_id, @@ -186,13 +186,16 @@ impl Contact { ) .await?; if contact_id == DC_CONTACT_ID_SELF { - res.name = context.stock_str(StockMessage::SelfMsg).to_string(); + res.name = context.stock_str(StockMessage::SelfMsg).await.to_string(); res.addr = context .get_config(Config::ConfiguredAddr) .await .unwrap_or_default(); } else if contact_id == DC_CONTACT_ID_DEVICE { - res.name = context.stock_str(StockMessage::DeviceMessages).to_string(); + res.name = context + .stock_str(StockMessage::DeviceMessages) + .await + .to_string(); res.addr = DC_CONTACT_ID_DEVICE_ADDR.to_string(); } Ok(res) @@ -251,7 +254,7 @@ impl Contact { }, )); if blocked { - Contact::unblock(context, contact_id); + Contact::unblock(context, contact_id).await; } Ok(contact_id) @@ -266,7 +269,7 @@ impl Contact { .sql .execute( "UPDATE msgs SET state=? WHERE from_id=? AND state=?;", - params![MessageState::InNoticed, id as i32, MessageState::InFresh], + paramsv![MessageState::InNoticed, id as i32, MessageState::InFresh], ) .await .is_ok() @@ -301,7 +304,7 @@ impl Contact { context.sql.query_get_value( context, "SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;", - params![ + paramsv![ addr_normalized, DC_CONTACT_ID_LAST_SPECIAL as i32, DC_ORIGIN_MIN_CONTACT_LIST, @@ -348,13 +351,13 @@ impl Contact { ); ensure!(origin != Origin::Unknown, "Missing valid origin"); - let addr = addr_normalize(addr.as_ref()); + let addr = addr_normalize(addr.as_ref()).to_string(); let addr_self = context .get_config(Config::ConfiguredAddr) .await .unwrap_or_default(); - if addr_cmp(addr, addr_self) { + if addr_cmp(&addr, addr_self) { return Ok((DC_CONTACT_ID_SELF, sth_modified)); } @@ -379,7 +382,7 @@ impl Contact { if let Ok((id, row_name, row_addr, row_origin, row_authname)) = context.sql.query_row( "SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;", - params![addr], + paramsv![addr.to_string()], |row| { let row_id = row.get(0)?; let row_name: String = row.get(1)?; @@ -416,30 +419,30 @@ impl Contact { if update_name || update_authname || update_addr || origin > row_origin { let new_name = if update_name { if !name.as_ref().is_empty() { - name.as_ref() + name.as_ref().to_string() } else { - &row_authname + row_authname.clone() } } else { - &row_name + row_name }; context .sql .execute( "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;", - params![ + paramsv![ new_name, - if update_addr { addr } else { &row_addr }, + if update_addr { addr.to_string() } else { row_addr }, if origin > row_origin { origin } else { row_origin }, if update_authname { - name.as_ref() + name.as_ref().to_string() } else { - &row_authname + row_authname }, row_id ], @@ -452,7 +455,7 @@ impl Contact { // This is one of the few duplicated data, however, getting the chat list is easier this way. context.sql.execute( "UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);", - params![new_name, Chattype::Single, row_id] + paramsv![new_name, Chattype::Single, row_id] ).await.ok(); } sth_modified = Modifier::Modified; @@ -466,11 +469,11 @@ impl Contact { .sql .execute( "INSERT INTO contacts (name, addr, origin, authname) VALUES(?, ?, ?, ?);", - params![ - name.as_ref(), + paramsv![ + name.as_ref().to_string(), addr, origin, - if update_authname { name.as_ref() } else { "" } + if update_authname { name.as_ref().to_string() } else { "".to_string() } ], ) .await @@ -478,10 +481,10 @@ impl Contact { { row_id = context .sql - .get_rowid(context, "contacts", "addr", addr) + .get_rowid(context, "contacts", "addr", &addr) .await?; sth_modified = Modifier::Created; - info!(context, "added contact id={} addr={}", row_id, addr); + info!(context, "added contact id={} addr={}", row_id, &addr); } else { error!(context, "Cannot add contact."); } @@ -577,13 +580,13 @@ impl Contact { AND (c.name LIKE ?4 OR c.addr LIKE ?5) \ AND (1=?6 OR LENGTH(ps.verified_key_fingerprint)!=0) \ ORDER BY LOWER(c.name||c.addr),c.id;", - params![ + paramsv![ self_addr, DC_CONTACT_ID_LAST_SPECIAL as i32, Origin::IncomingReplyTo, - &s3str_like_cmd, - &s3str_like_cmd, - if flag_verified_only { 0 } else { 1 }, + s3str_like_cmd, + s3str_like_cmd, + if flag_verified_only { 0i32 } else { 1i32 }, ], |row| row.get::<_, i32>(0), |ids| { @@ -604,7 +607,7 @@ impl Contact { if let Some(query) = query { if self_addr.contains(query.as_ref()) || self_name.contains(query.as_ref()) - || self_name2.contains(query.as_ref()) + || self_name2.await.contains(query.as_ref()) { add_self = true; } @@ -616,7 +619,7 @@ impl Contact { context.sql.query_map( "SELECT id FROM contacts WHERE addr!=?1 AND id>?2 AND origin>=?3 AND blocked=0 ORDER BY LOWER(name||addr),id;", - params![self_addr, DC_CONTACT_ID_LAST_SPECIAL as i32, 0x100], + paramsv![self_addr, DC_CONTACT_ID_LAST_SPECIAL as i32, 0x100], |row| row.get::<_, i32>(0), |ids| { for id in ids { @@ -637,10 +640,10 @@ impl Contact { pub async fn get_blocked_cnt(context: &Context) -> usize { context .sql - .query_get_value::<_, isize>( + .query_get_value::( context, "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0", - params![DC_CONTACT_ID_LAST_SPECIAL as i32], + paramsv![DC_CONTACT_ID_LAST_SPECIAL as i32], ) .await .unwrap_or_default() as usize @@ -652,7 +655,7 @@ impl Contact { .sql .query_map( "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", - params![DC_CONTACT_ID_LAST_SPECIAL as i32], + paramsv![DC_CONTACT_ID_LAST_SPECIAL as i32], |row| row.get::<_, u32>(0), |ids| { ids.collect::, _>>() @@ -672,7 +675,7 @@ impl Contact { let mut ret = String::new(); if let Ok(contact) = Contact::load_from_db(context, contact_id).await { - let peerstate = Peerstate::from_addr(context, &context.sql, &contact.addr); + let peerstate = Peerstate::from_addr(context, &context.sql, &contact.addr).await; let loginparam = LoginParam::from_database(context, "configured_").await; let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql).await; @@ -684,18 +687,19 @@ impl Contact { .is_some() { let peerstate = peerstate.as_ref().unwrap(); - let p = - context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual { + let p = context + .stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual { StockMessage::E2ePreferred } else { StockMessage::E2eAvailable - }); + }) + .await; ret += &p; if self_key.is_none() { e2ee::ensure_secret_key_exists(context).await?; self_key = Key::from_self_public(context, &loginparam.addr, &context.sql).await; } - let p = context.stock_str(StockMessage::FingerPrints); + let p = context.stock_str(StockMessage::FingerPrints).await; ret += &format!(" {}:", p); let fingerprint_self = self_key @@ -729,9 +733,9 @@ impl Contact { } else if 0 == loginparam.server_flags & DC_LP_IMAP_SOCKET_PLAIN as i32 && 0 == loginparam.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 { - ret += &context.stock_str(StockMessage::EncrTransp); + ret += &context.stock_str(StockMessage::EncrTransp).await; } else { - ret += &context.stock_str(StockMessage::EncrNone); + ret += &context.stock_str(StockMessage::EncrNone).await; } } @@ -753,7 +757,7 @@ impl Contact { .query_get_value( context, "SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;", - params![contact_id as i32], + paramsv![contact_id as i32], ) .await .unwrap_or_default(); @@ -764,7 +768,7 @@ impl Contact { .query_get_value( context, "SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;", - params![contact_id as i32, contact_id as i32], + paramsv![contact_id as i32, contact_id as i32], ) .await .unwrap_or_default() @@ -777,7 +781,7 @@ impl Contact { .sql .execute( "DELETE FROM contacts WHERE id=?;", - params![contact_id as i32], + paramsv![contact_id as i32], ) .await { @@ -815,7 +819,7 @@ impl Contact { .sql .execute( "UPDATE contacts SET param=? WHERE id=?", - params![self.param.to_string(), self.id as i32], + paramsv![self.param.to_string(), self.id as i32], ) .await?; Ok(()) @@ -913,17 +917,17 @@ impl Contact { /// /// The UI may draw a checkbox or something like that beside verified contacts. /// - pub fn is_verified(&self, context: &Context) -> VerifiedStatus { - self.is_verified_ex(context, None) + pub async fn is_verified(&self, context: &Context) -> VerifiedStatus { + self.is_verified_ex(context, None).await } /// Same as `Contact::is_verified` but allows speeding up things /// by adding the peerstate belonging to the contact. /// If you do not have the peerstate available, it is loaded automatically. - pub fn is_verified_ex( + pub async fn is_verified_ex( &self, context: &Context, - peerstate: Option<&Peerstate>, + peerstate: Option<&Peerstate<'_>>, ) -> VerifiedStatus { // We're always sort of secured-verified as we could verify the key on this device any time with the key // on this device @@ -937,7 +941,7 @@ impl Contact { } } - let peerstate = Peerstate::from_addr(context, &context.sql, &self.addr); + let peerstate = Peerstate::from_addr(context, &context.sql, &self.addr).await; if let Some(ps) = peerstate { if ps.verified_key.is_some() { return VerifiedStatus::BidirectVerified; @@ -975,10 +979,10 @@ impl Contact { context .sql - .query_get_value::<_, isize>( + .query_get_value::( context, "SELECT COUNT(*) FROM contacts WHERE id>?;", - params![DC_CONTACT_ID_LAST_SPECIAL as i32], + paramsv![DC_CONTACT_ID_LAST_SPECIAL as i32], ) .await .unwrap_or_default() as usize @@ -993,7 +997,7 @@ impl Contact { .sql .exists( "SELECT id FROM contacts WHERE id=?;", - params![contact_id as i32], + paramsv![contact_id as i32], ) .await .unwrap_or_default() @@ -1004,7 +1008,7 @@ impl Contact { .sql .execute( "UPDATE contacts SET origin=? WHERE id=? AND origin Result<()> { - let t = test_context(None); + let t = test_context(None).await; assert!(t.ctx.is_self_addr("me@me.org").await.is_err()); let addr = configure_alice_keypair(&t.ctx).await; @@ -1268,10 +1280,10 @@ mod tests { Ok(()) } - #[test] - fn test_add_or_lookup() { + #[async_std::test] + async fn test_add_or_lookup() { // add some contacts, this also tests add_address_book() - let t = dummy_context(); + let t = dummy_context().await; let book = concat!( " Name one \n one@eins.org \n", "Name two\ntwo@deux.net\n", @@ -1279,15 +1291,16 @@ mod tests { "\nthree@drei.sam\n", "Name two\ntwo@deux.net\n" // should not be added again ); - assert_eq!(Contact::add_address_book(&t.ctx, book).unwrap(), 3); + assert_eq!(Contact::add_address_book(&t.ctx, book).await.unwrap(), 3); // check first added contact, this does not modify because of lower origin let (contact_id, sth_modified) = Contact::add_or_lookup(&t.ctx, "bla foo", "one@eins.org", Origin::IncomingUnknownTo) + .await .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); assert_eq!(sth_modified, Modifier::None); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_id(), contact_id); assert_eq!(contact.get_name(), "Name one"); assert_eq!(contact.get_display_name(), "Name one"); @@ -1301,10 +1314,11 @@ mod tests { " one@eins.org ", Origin::ManuallyCreated, ) + .await .unwrap(); assert_eq!(contact_id, contact_id_test); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_name(), "Real one"); assert_eq!(contact.get_addr(), "one@eins.org"); assert!(!contact.is_blocked()); @@ -1312,10 +1326,11 @@ mod tests { // check third added contact (contact without name) let (contact_id, sth_modified) = Contact::add_or_lookup(&t.ctx, "", "three@drei.sam", Origin::IncomingUnknownTo) + .await .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); assert_eq!(sth_modified, Modifier::None); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_name(), ""); assert_eq!(contact.get_display_name(), "three@drei.sam"); assert_eq!(contact.get_addr(), "three@drei.sam"); @@ -1328,10 +1343,11 @@ mod tests { "three@drei.sam", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert_eq!(contact_id, contact_id_test); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_name_n_addr(), "m. serious (three@drei.sam)"); assert!(!contact.is_blocked()); @@ -1342,25 +1358,31 @@ mod tests { "three@drei.sam", Origin::ManuallyCreated, ) + .await .unwrap(); assert_eq!(contact_id, contact_id_test); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "m. serious"); assert_eq!(contact.get_name_n_addr(), "schnucki (three@drei.sam)"); assert!(!contact.is_blocked()); // check SELF - let contact = Contact::load_from_db(&t.ctx, DC_CONTACT_ID_SELF).unwrap(); + let contact = Contact::load_from_db(&t.ctx, DC_CONTACT_ID_SELF) + .await + .unwrap(); assert_eq!(DC_CONTACT_ID_SELF, 1); - assert_eq!(contact.get_name(), t.ctx.stock_str(StockMessage::SelfMsg)); + assert_eq!( + contact.get_name(), + t.ctx.stock_str(StockMessage::SelfMsg).await + ); assert_eq!(contact.get_addr(), ""); // we're not configured assert!(!contact.is_blocked()); } - #[test] - fn test_remote_authnames() { - let t = dummy_context(); + #[async_std::test] + async fn test_remote_authnames() { + let t = dummy_context().await; // incoming mail `From: bob1 ` - this should init authname and name let (contact_id, sth_modified) = Contact::add_or_lookup( @@ -1369,10 +1391,11 @@ mod tests { "bob@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); assert_eq!(sth_modified, Modifier::Created); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "bob1"); assert_eq!(contact.get_name(), "bob1"); assert_eq!(contact.get_display_name(), "bob1"); @@ -1384,18 +1407,21 @@ mod tests { "bob@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "bob2"); assert_eq!(contact.get_name(), "bob2"); assert_eq!(contact.get_display_name(), "bob2"); // manually edit name to "bob3" - authname should be still be "bob2" a given in `From:` above - let contact_id = Contact::create(&t.ctx, "bob3", "bob@example.org").unwrap(); + let contact_id = Contact::create(&t.ctx, "bob3", "bob@example.org") + .await + .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "bob2"); assert_eq!(contact.get_name(), "bob3"); assert_eq!(contact.get_display_name(), "bob3"); @@ -1407,23 +1433,26 @@ mod tests { "bob@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "bob4"); assert_eq!(contact.get_name(), "bob3"); assert_eq!(contact.get_display_name(), "bob3"); } - #[test] - fn test_remote_authnames_create_empty() { - let t = dummy_context(); + #[async_std::test] + async fn test_remote_authnames_create_empty() { + let t = dummy_context().await; // manually create "claire@example.org" without a given name - let contact_id = Contact::create(&t.ctx, "", "claire@example.org").unwrap(); + let contact_id = Contact::create(&t.ctx, "", "claire@example.org") + .await + .unwrap(); assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), ""); assert_eq!(contact.get_name(), ""); assert_eq!(contact.get_display_name(), "claire@example.org"); @@ -1435,10 +1464,11 @@ mod tests { "claire@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert_eq!(contact_id, contact_id_same); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "claire1"); assert_eq!(contact.get_name(), "claire1"); assert_eq!(contact.get_display_name(), "claire1"); @@ -1450,22 +1480,25 @@ mod tests { "claire@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); assert_eq!(contact_id, contact_id_same); assert_eq!(sth_modified, Modifier::Modified); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "claire2"); assert_eq!(contact.get_name(), "claire2"); assert_eq!(contact.get_display_name(), "claire2"); } - #[test] - fn test_remote_authnames_edit_empty() { - let t = dummy_context(); + #[async_std::test] + async fn test_remote_authnames_edit_empty() { + let t = dummy_context().await; // manually create "dave@example.org" - let contact_id = Contact::create(&t.ctx, "dave1", "dave@example.org").unwrap(); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact_id = Contact::create(&t.ctx, "dave1", "dave@example.org") + .await + .unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), ""); assert_eq!(contact.get_name(), "dave1"); assert_eq!(contact.get_display_name(), "dave1"); @@ -1477,15 +1510,18 @@ mod tests { "dave@example.org", Origin::IncomingUnknownFrom, ) + .await .unwrap(); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "dave2"); assert_eq!(contact.get_name(), "dave1"); assert_eq!(contact.get_display_name(), "dave1"); // manually clear the name - Contact::create(&t.ctx, "", "dave@example.org").unwrap(); - let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap(); + Contact::create(&t.ctx, "", "dave@example.org") + .await + .unwrap(); + let contact = Contact::load_from_db(&t.ctx, contact_id).await.unwrap(); assert_eq!(contact.get_authname(), "dave2"); assert_eq!(contact.get_name(), "dave2"); assert_eq!(contact.get_display_name(), "dave2"); diff --git a/src/context.rs b/src/context.rs index e9790079a..7a7408f63 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,7 +3,9 @@ use std::collections::HashMap; use std::ffi::OsString; use std::path::{Path, PathBuf}; -use std::sync::{atomic::AtomicBool, Arc, Mutex, RwLock}; +use std::sync::atomic::AtomicBool; + +use async_std::sync::{Arc, Mutex, RwLock}; use crate::chat::*; use crate::config::Config; @@ -156,14 +158,14 @@ impl Context { * Ongoing process allocation/free/check ******************************************************************************/ - pub fn alloc_ongoing(&self) -> bool { - if self.has_ongoing() { + pub async fn alloc_ongoing(&self) -> bool { + if self.has_ongoing().await { warn!(self, "There is already another ongoing process running.",); false } else { let s_a = self.running_state.clone(); - let mut s = s_a.write().unwrap(); + let mut s = s_a.write().await; s.ongoing_running = true; s.shall_stop_ongoing = false; @@ -172,25 +174,25 @@ impl Context { } } - pub fn free_ongoing(&self) { + pub async fn free_ongoing(&self) { let s_a = self.running_state.clone(); - let mut s = s_a.write().unwrap(); + let mut s = s_a.write().await; s.ongoing_running = false; s.shall_stop_ongoing = true; } - pub fn has_ongoing(&self) -> bool { + pub async fn has_ongoing(&self) -> bool { let s_a = self.running_state.clone(); - let s = s_a.read().unwrap(); + let s = s_a.read().await; s.ongoing_running || !s.shall_stop_ongoing } /// Signal an ongoing process to stop. - pub fn stop_ongoing(&self) { + pub async fn stop_ongoing(&self) { let s_a = self.running_state.clone(); - let mut s = s_a.write().unwrap(); + let mut s = s_a.write().await; if s.ongoing_running && !s.shall_stop_ongoing { info!(self, "Signaling the ongoing process to stop ASAP.",); @@ -200,12 +202,8 @@ impl Context { }; } - pub fn shall_stop_ongoing(&self) -> bool { - self.running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing + pub async fn shall_stop_ongoing(&self) -> bool { + self.running_state.clone().read().await.shall_stop_ongoing } /******************************************************************************* @@ -218,8 +216,8 @@ impl Context { let l2 = LoginParam::from_database(self, "configured_").await; let displayname = self.get_config(Config::Displayname).await; let chats = get_chat_cnt(self).await as usize; - let real_msgs = message::get_real_msg_cnt(self) as usize; - let deaddrop_msgs = message::get_deaddrop_msg_cnt(self) as usize; + let real_msgs = message::get_real_msg_cnt(self).await as usize; + let deaddrop_msgs = message::get_deaddrop_msg_cnt(self).await as usize; let contacts = Contact::get_real_cnt(self).await as usize; let is_configured = self.get_config_int(Config::Configured).await; let dbversion = self @@ -234,16 +232,12 @@ impl Context { let prv_key_cnt: Option = self .sql - .query_get_value(self, "SELECT COUNT(*) FROM keypairs;", rusqlite::NO_PARAMS) + .query_get_value(self, "SELECT COUNT(*) FROM keypairs;", paramsv![]) .await; let pub_key_cnt: Option = self .sql - .query_get_value( - self, - "SELECT COUNT(*) FROM acpeerstates;", - rusqlite::NO_PARAMS, - ) + .query_get_value(self, "SELECT COUNT(*) FROM acpeerstates;", paramsv![]) .await; let fingerprint_str = @@ -316,7 +310,7 @@ impl Context { } pub async fn get_fresh_msgs(&self) -> Vec { - let show_deaddrop = 0; + let show_deaddrop: i32 = 0; self.sql .query_map( concat!( @@ -333,7 +327,7 @@ impl Context { " AND (c.blocked=0 OR c.blocked=?)", " ORDER BY m.timestamp DESC,m.id DESC;" ), - &[10, 9, if 0 != show_deaddrop { 2 } else { 0 }], + paramsv![10, 9, if 0 != show_deaddrop { 2 } else { 0 }], |row| row.get::<_, MsgId>(0), |rows| { let mut ret = Vec::new(); @@ -388,7 +382,7 @@ impl Context { self.sql .query_map( query, - params![chat_id, &strLikeInText, &strLikeBeg], + paramsv![chat_id, strLikeInText, strLikeBeg], |row| row.get::<_, MsgId>("id"), |rows| { let mut ret = Vec::new(); @@ -475,7 +469,7 @@ impl Drop for Context { info!(self, "disconnecting SMTP"); self.smtp.disconnect().await; - self.sql.close(self); + self.sql.close(self).await; }); } } @@ -519,79 +513,85 @@ mod tests { use crate::test_utils::*; - #[test] - fn test_wrong_db() { + #[async_std::test] + async fn test_wrong_db() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); std::fs::write(&dbfile, b"123").unwrap(); - let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile); + let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile).await; assert!(res.is_err()); } - #[test] - fn test_get_fresh_msgs() { - let t = dummy_context(); - let fresh = t.ctx.get_fresh_msgs(); + #[async_std::test] + async fn test_get_fresh_msgs() { + let t = dummy_context().await; + let fresh = t.ctx.get_fresh_msgs().await; assert!(fresh.is_empty()) } - #[test] - fn test_blobdir_exists() { + #[async_std::test] + async fn test_blobdir_exists() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); - Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap(); + Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile) + .await + .unwrap(); let blobdir = tmp.path().join("db.sqlite-blobs"); assert!(blobdir.is_dir()); } - #[test] - fn test_wrong_blogdir() { + #[async_std::test] + async fn test_wrong_blogdir() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("db.sqlite-blobs"); std::fs::write(&blobdir, b"123").unwrap(); - let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile); + let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).await; assert!(res.is_err()); } - #[test] - fn test_sqlite_parent_not_exists() { + #[async_std::test] + async fn test_sqlite_parent_not_exists() { let tmp = tempfile::tempdir().unwrap(); let subdir = tmp.path().join("subdir"); let dbfile = subdir.join("db.sqlite"); let dbfile2 = dbfile.clone(); - Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap(); + Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile) + .await + .unwrap(); assert!(subdir.is_dir()); assert!(dbfile2.is_file()); } - #[test] - fn test_with_empty_blobdir() { + #[async_std::test] + async fn test_with_empty_blobdir() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = PathBuf::new(); - let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir); + let res = + Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir).await; assert!(res.is_err()); } - #[test] - fn test_with_blobdir_not_exists() { + #[async_std::test] + async fn test_with_blobdir_not_exists() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("blobs"); - let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir); + let res = + Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir).await; assert!(res.is_err()); } - #[test] - fn no_crashes_on_context_deref() { - let t = dummy_context(); + #[async_std::test] + async fn no_crashes_on_context_deref() { + let t = dummy_context().await; std::mem::drop(t.ctx); } #[async_std::test] async fn test_get_info() { - let t = dummy_context(); + let t = dummy_context().await; let info = t.ctx.get_info().await; assert!(info.get("database_dir").is_some()); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index d8c7b3cfd..91c4b3f23 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -96,7 +96,7 @@ pub async fn dc_receive_imf( // https://github.com/deltachat/deltachat-core/issues/150) let (from_id, from_id_blocked, incoming_origin) = if let Some(field_from) = mime_parser.get(HeaderDef::From_) { - from_field_to_contact_id(context, field_from)? + from_field_to_contact_id(context, field_from).await? } else { (0, false, Origin::Unknown) }; @@ -105,17 +105,20 @@ pub async fn dc_receive_imf( let mut to_ids = ContactIds::new(); for header_def in &[HeaderDef::To, HeaderDef::Cc] { if let Some(field) = mime_parser.get(header_def.clone()) { - to_ids.extend(&dc_add_or_lookup_contacts_by_address_list( - context, - &field, - if !incoming { - Origin::OutgoingTo - } else if incoming_origin.is_known() { - Origin::IncomingTo - } else { - Origin::IncomingUnknownTo - }, - )?); + to_ids.extend( + &dc_add_or_lookup_contacts_by_address_list( + context, + &field, + if !incoming { + Origin::OutgoingTo + } else if incoming_origin.is_known() { + Origin::IncomingTo + } else { + Origin::IncomingUnknownTo + }, + ) + .await?, + ); } } @@ -178,7 +181,8 @@ pub async fn dc_receive_imf( from_id, insert_msg_id, hidden, - ); + ) + .await; } if let Some(avatar_action) = &mime_parser.user_avatar { @@ -225,7 +229,7 @@ pub async fn dc_receive_imf( /// Converts "From" field to contact id. /// /// Also returns whether it is blocked or not and its origin. -pub fn from_field_to_contact_id( +pub async fn from_field_to_contact_id( context: &Context, field_from: &str, ) -> Result<(u32, bool, Origin)> { @@ -233,7 +237,8 @@ pub fn from_field_to_contact_id( context, &field_from, Origin::IncomingUnknownFrom, - )?; + ) + .await?; if from_ids.contains(&DC_CONTACT_ID_SELF) { Ok((DC_CONTACT_ID_SELF, false, Origin::OutgoingBcc)) @@ -248,7 +253,7 @@ pub fn from_field_to_contact_id( let mut from_id_blocked = false; let mut incoming_origin = Origin::Unknown; - if let Ok(contact) = Contact::load_from_db(context, from_id) { + if let Ok(contact) = Contact::load_from_db(context, from_id).await { from_id_blocked = contact.blocked; incoming_origin = contact.origin; } @@ -297,10 +302,11 @@ async fn add_parts( // (if the mail was moved around) and finish. (we may get a mail twice eg. if it is // moved between folders. make sure, this check is done eg. before securejoin-processing) */ if let Ok((old_server_folder, old_server_uid, _)) = - message::rfc724_mid_exists(context, &rfc724_mid) + message::rfc724_mid_exists(context, &rfc724_mid).await { if old_server_folder != server_folder.as_ref() || old_server_uid != server_uid { - message::update_server_uid(context, &rfc724_mid, server_folder.as_ref(), server_uid); + message::update_server_uid(context, &rfc724_mid, server_folder.as_ref(), server_uid) + .await; } bail!("Message already in DB"); @@ -308,7 +314,7 @@ async fn add_parts( let mut msgrmsg = if mime_parser.has_chat_version() { MessengerMessage::Yes - } else if is_reply_to_messenger_message(context, mime_parser) { + } else if is_reply_to_messenger_message(context, mime_parser).await { MessengerMessage::Reply } else { MessengerMessage::No @@ -368,8 +374,8 @@ async fn add_parts( } Err(err) => { *hidden = true; - context.bob.write().unwrap().status = 0; // secure-join failed - context.stop_ongoing(); + context.bob.write().await.status = 0; // secure-join failed + context.stop_ongoing().await; error!(context, "Error in Secure-Join message handling: {}", err); } } @@ -408,7 +414,7 @@ async fn add_parts( && chat_id_blocked != Blocked::Not && create_blocked == Blocked::Not { - new_chat_id.unblock(context); + new_chat_id.unblock(context).await; chat_id_blocked = Blocked::Not; } } @@ -442,12 +448,12 @@ async fn add_parts( } if !chat_id.is_unset() && Blocked::Not != chat_id_blocked { if Blocked::Not == create_blocked { - chat_id.unblock(context); + chat_id.unblock(context).await; chat_id_blocked = Blocked::Not; - } else if is_reply_to_known_message(context, mime_parser) { + } else if is_reply_to_known_message(context, mime_parser).await { // we do not want any chat to be created implicitly. Because of the origin-scale-up, // the contact requests will pop up and this should be just fine. - Contact::scaleup_origin_by_id(context, from_id, Origin::IncomingReplyTo); + 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.", @@ -496,7 +502,7 @@ async fn add_parts( chat_id_blocked = new_chat_id_blocked; // automatically unblock chat when the user sends a message if !chat_id.is_unset() && chat_id_blocked != Blocked::Not { - new_chat_id.unblock(context); + new_chat_id.unblock(context).await; chat_id_blocked = Blocked::Not; } } @@ -518,7 +524,7 @@ async fn add_parts( && Blocked::Not != chat_id_blocked && Blocked::Not == create_blocked { - chat_id.unblock(context); + chat_id.unblock(context).await; chat_id_blocked = Blocked::Not; } } @@ -538,7 +544,7 @@ async fn add_parts( chat_id_blocked = bl; if !chat_id.is_unset() && Blocked::Not != chat_id_blocked { - chat_id.unblock(context); + chat_id.unblock(context).await; chat_id_blocked = Blocked::Not; } } @@ -557,7 +563,8 @@ async fn add_parts( &mut sort_timestamp, sent_timestamp, &mut rcvd_timestamp, - ); + ) + .await; // unarchive chat chat_id.unarchive(context).await?; @@ -613,9 +620,9 @@ async fn add_parts( .set_int(Param::Cmd, mime_parser.is_system_message as i32); } - stmt.execute(params![ + stmt.execute(paramsv![ rfc724_mid, - server_folder.as_ref(), + server_folder.as_ref().to_string(), server_uid as i32, *chat_id, from_id as i32, @@ -626,7 +633,7 @@ async fn add_parts( part.typ, state, msgrmsg, - &part.msg, + part.msg, // txt_raw might contain invalid utf8 txt_raw.unwrap_or_default(), part.param.to_string(), @@ -675,7 +682,7 @@ async fn add_parts( Ok(()) } -fn save_locations( +async fn save_locations( context: &Context, mime_parser: &MimeMessage, chat_id: ChatId, @@ -691,11 +698,14 @@ fn save_locations( if mime_parser.message_kml.is_some() { let locations = &mime_parser.message_kml.as_ref().unwrap().locations; - let newest_location_id = - location::save(context, chat_id, from_id, locations, true).unwrap_or_default(); + let newest_location_id = location::save(context, chat_id, from_id, locations, true) + .await + .unwrap_or_default(); if 0 != newest_location_id && !hidden - && location::set_msg_location_id(context, insert_msg_id, newest_location_id).is_ok() + && location::set_msg_location_id(context, insert_msg_id, newest_location_id) + .await + .is_ok() { location_id_written = true; send_event = true; @@ -704,18 +714,21 @@ fn save_locations( if mime_parser.location_kml.is_some() { if let Some(ref addr) = mime_parser.location_kml.as_ref().unwrap().addr { - if let Ok(contact) = Contact::get_by_id(context, from_id) { + if let Ok(contact) = Contact::get_by_id(context, from_id).await { if contact.get_addr().to_lowercase() == addr.to_lowercase() { let locations = &mime_parser.location_kml.as_ref().unwrap().locations; let newest_location_id = location::save(context, chat_id, from_id, locations, false) + .await .unwrap_or_default(); if newest_location_id != 0 && !hidden && !location_id_written { if let Err(err) = location::set_msg_location_id( context, insert_msg_id, newest_location_id, - ) { + ) + .await + { error!(context, "Failed to set msg_location_id: {:?}", err); } } @@ -730,7 +743,7 @@ fn save_locations( } #[allow(clippy::too_many_arguments)] -fn calc_timestamps( +async fn calc_timestamps( context: &Context, chat_id: ChatId, from_id: u32, @@ -747,19 +760,22 @@ fn calc_timestamps( } *sort_timestamp = message_timestamp; if is_fresh_msg { - let last_msg_time: Option = context.sql.query_get_value( - context, - "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?", - params![chat_id, from_id as i32, *sort_timestamp], - ); + let last_msg_time: Option = context + .sql + .query_get_value( + context, + "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?", + paramsv![chat_id, from_id as i32, *sort_timestamp], + ) + .await; if let Some(last_msg_time) = last_msg_time { if last_msg_time > 0 && *sort_timestamp <= last_msg_time { *sort_timestamp = last_msg_time + 1; } } } - if *sort_timestamp >= dc_smeared_time(context) { - *sort_timestamp = dc_create_smeared_timestamp(context); + if *sort_timestamp >= dc_smeared_time(context).await { + *sort_timestamp = dc_create_smeared_timestamp(context).await; } } @@ -792,8 +808,9 @@ async fn create_or_lookup_group( let mut better_msg: String = From::from(""); if mime_parser.is_system_message == SystemMessage::LocationStreamingEnabled { - better_msg = - context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32); + better_msg = context + .stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32) + .await; set_better_msg(mime_parser, &better_msg); } @@ -823,6 +840,7 @@ async fn create_or_lookup_group( from_id, to_ids, ) + .await .map_err(|err| { info!(context, "could not create adhoc-group: {:?}", err); err @@ -841,41 +859,47 @@ async fn create_or_lookup_group( let left_group = Contact::lookup_id_by_addr(context, X_MrRemoveFromGrp.as_ref().unwrap()) .await == from_id as u32; - better_msg = context.stock_system_msg( - if left_group { - StockMessage::MsgGroupLeft - } else { - StockMessage::MsgDelMember - }, - X_MrRemoveFromGrp.as_ref().unwrap(), - "", - from_id as u32, - ) + better_msg = context + .stock_system_msg( + if left_group { + StockMessage::MsgGroupLeft + } else { + StockMessage::MsgDelMember + }, + X_MrRemoveFromGrp.as_ref().unwrap(), + "", + from_id as u32, + ) + .await } else { let field = mime_parser.get(HeaderDef::ChatGroupMemberAdded).cloned(); if let Some(optional_field) = field { mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; - better_msg = context.stock_system_msg( - StockMessage::MsgAddMember, - &optional_field, - "", - from_id as u32, - ); + better_msg = context + .stock_system_msg( + StockMessage::MsgAddMember, + &optional_field, + "", + from_id as u32, + ) + .await; X_MrAddToGrp = Some(optional_field); } else { let field = mime_parser.get(HeaderDef::ChatGroupNameChanged); if let Some(field) = field { X_MrGrpNameChanged = true; - better_msg = context.stock_system_msg( - StockMessage::MsgGrpName, - field, - if let Some(ref name) = grpname { - name - } else { - "" - }, - from_id as u32, - ); + better_msg = context + .stock_system_msg( + StockMessage::MsgGrpName, + field, + if let Some(ref name) = grpname { + name + } else { + "" + }, + from_id as u32, + ) + .await; mime_parser.is_system_message = SystemMessage::GroupNameChanged; } else if let Some(value) = mime_parser.get(HeaderDef::ChatContent) { @@ -884,15 +908,17 @@ async fn create_or_lookup_group( // this is just an explicit message containing the group-avatar, // apart from that, the group-avatar is send along with various other messages mime_parser.is_system_message = SystemMessage::GroupImageChanged; - better_msg = context.stock_system_msg( - match avatar_action { - AvatarAction::Delete => StockMessage::MsgGrpImgDeleted, - AvatarAction::Change(_) => StockMessage::MsgGrpImgChanged, - }, - "", - "", - from_id as u32, - ) + better_msg = context + .stock_system_msg( + match avatar_action { + AvatarAction::Delete => StockMessage::MsgGrpImgDeleted, + AvatarAction::Change(_) => StockMessage::MsgGrpImgChanged, + }, + "", + "", + from_id as u32, + ) + .await } } } @@ -907,7 +933,7 @@ async fn create_or_lookup_group( if !chat_id.is_error() { if chat_id_verified { if let Err(err) = - check_verified_properties(context, mime_parser, from_id as u32, to_ids) + check_verified_properties(context, mime_parser, from_id as u32, to_ids).await { warn!(context, "verification problem: {}", err); let s = format!("{}. See 'Info' for more details", err); @@ -921,7 +947,7 @@ async fn create_or_lookup_group( // but still show the message as part of the chat. // After all, the sender has a reference/in-reply-to that // points to this chat. - let s = context.stock_str(StockMessage::UnknownSenderForChat); + let s = context.stock_str(StockMessage::UnknownSenderForChat).await; mime_parser.repl_msg_by_error(s.to_string()); } } @@ -948,7 +974,7 @@ async fn create_or_lookup_group( // group does not exist but should be created let create_verified = if mime_parser.get(HeaderDef::ChatVerified).is_some() { if let Err(err) = - check_verified_properties(context, mime_parser, from_id as u32, to_ids) + check_verified_properties(context, mime_parser, from_id as u32, to_ids).await { warn!(context, "verification problem: {}", err); let s = format!("{}. See 'Info' for more details", err); @@ -989,6 +1015,7 @@ async fn create_or_lookup_group( from_id, to_ids, ) + .await .map_err(|err| { warn!(context, "failed to create ad-hoc group: {:?}", err); err @@ -1019,7 +1046,7 @@ async fn create_or_lookup_group( .sql .execute( "UPDATE chats SET name=? WHERE id=?;", - params![grpname, chat_id], + paramsv![grpname.to_string(), chat_id], ) .await .is_ok() @@ -1061,7 +1088,7 @@ async fn create_or_lookup_group( if !Contact::addr_equals_contact(context, &self_addr, to_id).await && !chat::is_contact_in_chat(context, chat_id, to_id).await { - chat::add_to_chat_contacts_table(context, chat_id, to_id); + chat::add_to_chat_contacts_table(context, chat_id, to_id).await; } } send_EVENT_CHAT_MODIFIED = true; @@ -1069,7 +1096,7 @@ async fn create_or_lookup_group( let contact_id = Contact::lookup_id_by_addr(context, removed_addr).await; if contact_id != 0 { info!(context, "remove {:?} from chat id={}", contact_id, chat_id); - chat::remove_from_chat_contacts_table(context, chat_id, contact_id); + chat::remove_from_chat_contacts_table(context, chat_id, contact_id).await; } send_EVENT_CHAT_MODIFIED = true; } @@ -1091,7 +1118,7 @@ fn extract_grpid(mime_parser: &MimeMessage, headerdef: HeaderDef) -> Option<&str } /// Handle groups for received messages, return chat_id/Blocked status on success -fn create_or_lookup_adhoc_group( +async fn create_or_lookup_adhoc_group( context: &Context, mime_parser: &MimeMessage, allow_creation: bool, @@ -1126,12 +1153,14 @@ fn create_or_lookup_adhoc_group( return Ok((ChatId::new(0), Blocked::Not)); } - let chat_ids = search_chat_ids_by_contact_ids(context, &member_ids)?; + let chat_ids = search_chat_ids_by_contact_ids(context, &member_ids).await?; if !chat_ids.is_empty() { let chat_ids_str = join(chat_ids.iter().map(|x| x.to_string()), ","); - let res = context.sql.query_row( - format!( - "SELECT c.id, + let res = context + .sql + .query_row( + format!( + "SELECT c.id, c.blocked FROM chats c LEFT JOIN msgs m @@ -1140,16 +1169,17 @@ fn create_or_lookup_adhoc_group( ORDER BY m.timestamp DESC, m.id DESC LIMIT 1;", - chat_ids_str - ), - params![], - |row| { - Ok(( - row.get::<_, ChatId>(0)?, - row.get::<_, Option>(1)?.unwrap_or_default(), - )) - }, - ); + chat_ids_str + ), + paramsv![], + |row| { + Ok(( + row.get::<_, ChatId>(0)?, + row.get::<_, Option>(1)?.unwrap_or_default(), + )) + }, + ) + .await; if let Ok((id, id_blocked)) = res { /* success, chat found */ @@ -1167,7 +1197,7 @@ fn create_or_lookup_adhoc_group( // create a new ad-hoc group // - there is no need to check if this group exists; otherwise we would have caught it above - let grpid = create_adhoc_grp_id(context, &member_ids); + let grpid = create_adhoc_grp_id(context, &member_ids).await; if grpid.is_empty() { warn!( context, @@ -1176,9 +1206,10 @@ fn create_or_lookup_adhoc_group( return Ok((ChatId::new(0), Blocked::Not)); } // use subject as initial chat name - let grpname = mime_parser.get_subject().unwrap_or_else(|| { - context.stock_string_repl_int(StockMessage::Member, member_ids.len() as i32) - }); + let default_name = context + .stock_string_repl_int(StockMessage::Member, member_ids.len() as i32) + .await; + let grpname = mime_parser.get_subject().unwrap_or_else(|| default_name); // create group record let new_chat_id: ChatId = create_group_record( @@ -1187,9 +1218,10 @@ fn create_or_lookup_adhoc_group( grpname, create_blocked, VerifiedStatus::Unverified, - ); + ) + .await; for &member_id in &member_ids { - chat::add_to_chat_contacts_table(context, new_chat_id, member_id); + chat::add_to_chat_contacts_table(context, new_chat_id, member_id).await; } context.call_cb(Event::ChatModified(new_chat_id)); @@ -1206,7 +1238,7 @@ async fn create_group_record( ) -> ChatId { if context.sql.execute( "INSERT INTO chats (type, name, grpid, blocked, created_timestamp) VALUES(?, ?, ?, ?, ?);", - params![ + paramsv![ if VerifiedStatus::Unverified != create_verified { Chattype::VerifiedGroup } else { @@ -1245,7 +1277,7 @@ async fn create_group_record( chat_id } -fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { +async fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { /* algorithm: - sort normalized, lowercased, e-mail addresses alphabetically - put all e-mail addresses into a single string, separate the address by a single comma @@ -1255,6 +1287,7 @@ fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { let member_ids_str = join(member_ids.iter().map(|x| x.to_string()), ","); let member_cs = context .get_config(Config::ConfiguredAddr) + .await .unwrap_or_else(|| "no-self".to_string()) .to_lowercase(); @@ -1265,7 +1298,7 @@ fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { "SELECT addr FROM contacts WHERE id IN({}) AND id!=1", // 1=DC_CONTACT_ID_SELF member_ids_str ), - params![], + paramsv![], |row| row.get::<_, String>(0), |rows| { let mut addrs = rows.collect::, _>>()?; @@ -1278,6 +1311,7 @@ fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { Ok(acc) }, ) + .await .unwrap_or_else(|_| member_cs); hex_hash(&members) @@ -1289,7 +1323,7 @@ fn hex_hash(s: impl AsRef) -> String { hex::encode(&result[..8]) } -fn search_chat_ids_by_contact_ids( +async fn search_chat_ids_by_contact_ids( context: &Context, unsorted_contact_ids: &[u32], ) -> Result> { @@ -1318,7 +1352,7 @@ fn search_chat_ids_by_contact_ids( ORDER BY cc.chat_id, cc.contact_id;", // 1=DC_CONTACT_ID_SELF contact_ids_str ), - params![], + paramsv![], |row| Ok((row.get::<_, ChatId>(0)?, row.get::<_, u32>(1)?)), |rows| { let mut last_chat_id = ChatId::new(0); @@ -1347,20 +1381,20 @@ fn search_chat_ids_by_contact_ids( } Ok(()) } - )?; + ).await?; } } Ok(chat_ids) } -fn check_verified_properties( +async fn check_verified_properties( context: &Context, mimeparser: &MimeMessage, from_id: u32, to_ids: &ContactIds, ) -> Result<()> { - let contact = Contact::load_from_db(context, from_id)?; + let contact = Contact::load_from_db(context, from_id).await?; ensure!(mimeparser.was_encrypted(), "This message is not encrypted."); @@ -1369,10 +1403,10 @@ fn check_verified_properties( // this check is skipped for SELF as there is no proper SELF-peerstate // and results in group-splits otherwise. if from_id != DC_CONTACT_ID_SELF { - let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr()); + let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr()).await; if peerstate.is_none() - || contact.is_verified_ex(context, peerstate.as_ref()) + || contact.is_verified_ex(context, peerstate.as_ref()).await != VerifiedStatus::BidirectVerified { bail!( @@ -1398,29 +1432,32 @@ fn check_verified_properties( } let to_ids_str = join(to_ids.iter().map(|x| x.to_string()), ","); - let rows = context.sql.query_map( - format!( - "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ + let rows = context + .sql + .query_map( + format!( + "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ", - to_ids_str - ), - params![], - |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1).unwrap_or(0))), - |rows| { - rows.collect::, _>>() - .map_err(Into::into) - }, - )?; + to_ids_str + ), + paramsv![], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1).unwrap_or(0))), + |rows| { + rows.collect::, _>>() + .map_err(Into::into) + }, + ) + .await?; for (to_addr, _is_verified) in rows.into_iter() { info!( context, "check_verified_properties: {:?} self={:?}", to_addr, - context.is_self_addr(&to_addr) + context.is_self_addr(&to_addr).await ); let mut is_verified = _is_verified != 0; - let peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); + let peerstate = Peerstate::from_addr(context, &context.sql, &to_addr).await; // mark gossiped keys (if any) as verified if mimeparser.gossipped_addr.contains(&to_addr) { @@ -1442,7 +1479,7 @@ fn check_verified_properties( &fp, PeerstateVerifiedStatus::BidirectVerified, ); - peerstate.save_to_db(&context.sql, false)?; + peerstate.save_to_db(&context.sql, false).await?; is_verified = true; } } @@ -1468,18 +1505,18 @@ fn set_better_msg(mime_parser: &mut MimeMessage, better_msg: impl AsRef) { } } -fn is_reply_to_known_message(context: &Context, mime_parser: &MimeMessage) -> bool { +async fn is_reply_to_known_message(context: &Context, mime_parser: &MimeMessage) -> bool { /* check if the message is a reply to a known message; the replies are identified by the Message-ID from `In-Reply-To`/`References:` (to support non-Delta-Clients) */ if let Some(field) = mime_parser.get(HeaderDef::InReplyTo) { - if is_known_rfc724_mid_in_list(context, &field) { + if is_known_rfc724_mid_in_list(context, &field).await { return true; } } if let Some(field) = mime_parser.get(HeaderDef::References) { - if is_known_rfc724_mid_in_list(context, &field) { + if is_known_rfc724_mid_in_list(context, &field).await { return true; } } @@ -1487,14 +1524,14 @@ fn is_reply_to_known_message(context: &Context, mime_parser: &MimeMessage) -> bo false } -fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool { +async fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool { if mid_list.is_empty() { return false; } if let Ok(ids) = mailparse::msgidparse(mid_list) { for id in ids.iter() { - if is_known_rfc724_mid(context, id) { + if is_known_rfc724_mid(context, id).await { return true; } } @@ -1504,7 +1541,7 @@ fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool { } /// Check if a message is a reply to a known message (messenger or non-messenger). -fn is_known_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { +async fn is_known_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { context .sql .exists( @@ -1512,8 +1549,9 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { LEFT JOIN chats c ON m.chat_id=c.id \ WHERE m.rfc724_mid=? \ AND m.chat_id>9 AND c.blocked=0;", - params![rfc724_mid], + paramsv![rfc724_mid], ) + .await .unwrap_or_default() } @@ -1522,15 +1560,15 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { /// - checks also if any of the referenced IDs are send by a messenger /// - it is okay, if the referenced messages are moved to trash here /// - no check for the Chat-* headers (function is only called if it is no messenger message itself) -fn is_reply_to_messenger_message(context: &Context, mime_parser: &MimeMessage) -> bool { +async fn is_reply_to_messenger_message(context: &Context, mime_parser: &MimeMessage) -> bool { if let Some(value) = mime_parser.get(HeaderDef::InReplyTo) { - if is_msgrmsg_rfc724_mid_in_list(context, &value) { + if is_msgrmsg_rfc724_mid_in_list(context, &value).await { return true; } } if let Some(value) = mime_parser.get(HeaderDef::References) { - if is_msgrmsg_rfc724_mid_in_list(context, &value) { + if is_msgrmsg_rfc724_mid_in_list(context, &value).await { return true; } } @@ -1538,10 +1576,10 @@ fn is_reply_to_messenger_message(context: &Context, mime_parser: &MimeMessage) - false } -pub(crate) fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool { +pub(crate) async fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool { if let Ok(ids) = mailparse::msgidparse(mid_list) { for id in ids.iter() { - if is_msgrmsg_rfc724_mid(context, id) { + if is_msgrmsg_rfc724_mid(context, id).await { return true; } } @@ -1550,17 +1588,18 @@ pub(crate) fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: &str) - } /// Check if a message is a reply to any messenger message. -fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { +async fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool { context .sql .exists( "SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;", - params![rfc724_mid], + paramsv![rfc724_mid], ) + .await .unwrap_or_default() } -fn dc_add_or_lookup_contacts_by_address_list( +async fn dc_add_or_lookup_contacts_by_address_list( context: &Context, addr_list_raw: &str, origin: Origin, @@ -1576,21 +1615,22 @@ fn dc_add_or_lookup_contacts_by_address_list( for addr in addrs.iter() { match addr { mailparse::MailAddr::Single(info) => { - contact_ids.insert(add_or_lookup_contact_by_addr( - context, - &info.display_name, - &info.addr, - origin, - )?); + contact_ids.insert( + add_or_lookup_contact_by_addr(context, &info.display_name, &info.addr, origin) + .await?, + ); } mailparse::MailAddr::Group(infos) => { for info in &infos.addrs { - contact_ids.insert(add_or_lookup_contact_by_addr( - context, - &info.display_name, - &info.addr, - origin, - )?); + contact_ids.insert( + add_or_lookup_contact_by_addr( + context, + &info.display_name, + &info.addr, + origin, + ) + .await?, + ); } } } @@ -1600,13 +1640,13 @@ fn dc_add_or_lookup_contacts_by_address_list( } /// Add contacts to database on receiving messages. -fn add_or_lookup_contact_by_addr( +async fn add_or_lookup_contact_by_addr( context: &Context, display_name: &Option, addr: &str, origin: Origin, ) -> Result { - if context.is_self_addr(addr)? { + if context.is_self_addr(addr).await? { return Ok(DC_CONTACT_ID_SELF); } let display_name_normalized = display_name @@ -1615,7 +1655,7 @@ fn add_or_lookup_contact_by_addr( .unwrap_or_default(); let (row_id, _modified) = - Contact::add_or_lookup(context, display_name_normalized, addr, origin)?; + Contact::add_or_lookup(context, display_name_normalized, addr, origin).await?; ensure!(row_id > 0, "could not add contact: {:?}", addr); Ok(row_id) @@ -1650,31 +1690,35 @@ mod tests { assert_eq!(res, "b94d27b9934d3e08"); } - #[test] - fn test_grpid_simple() { - let context = dummy_context(); + #[async_std::test] + async fn test_grpid_simple() { + let context = dummy_context().await; let raw = b"From: hello\n\ Subject: outer-subject\n\ In-Reply-To: \n\ References: \n\ \n\ hello\x00"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), None); let grpid = Some("HcxyMARjyJy"); assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid); } - #[test] - fn test_grpid_from_multiple() { - let context = dummy_context(); + #[async_std::test] + async fn test_grpid_from_multiple() { + let context = dummy_context().await; let raw = b"From: hello\n\ Subject: outer-subject\n\ In-Reply-To: \n\ References: , \n\ \n\ hello\x00"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); let grpid = Some("HcxyMARjyJy"); assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), grpid); assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid); diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 0aa7bbeeb..1144a2b57 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -99,9 +99,9 @@ const MAX_SECONDS_TO_LEND_FROM_FUTURE: i64 = 5; // returns the currently smeared timestamp, // may be used to check if call to dc_create_smeared_timestamp() is needed or not. // the returned timestamp MUST NOT be used to be sent out or saved in the database! -pub(crate) fn dc_smeared_time(context: &Context) -> i64 { +pub(crate) async fn dc_smeared_time(context: &Context) -> i64 { let mut now = time(); - let ts = *context.last_smeared_timestamp.read().unwrap(); + let ts = *context.last_smeared_timestamp.read().await; if ts >= now { now = ts + 1; } @@ -110,11 +110,11 @@ pub(crate) fn dc_smeared_time(context: &Context) -> i64 { } // returns a timestamp that is guaranteed to be unique. -pub(crate) fn dc_create_smeared_timestamp(context: &Context) -> i64 { +pub(crate) async fn dc_create_smeared_timestamp(context: &Context) -> i64 { let now = time(); let mut ret = now; - let mut last_smeared_timestamp = context.last_smeared_timestamp.write().unwrap(); + let mut last_smeared_timestamp = context.last_smeared_timestamp.write().await; if ret <= *last_smeared_timestamp { ret = *last_smeared_timestamp + 1; if ret - now > MAX_SECONDS_TO_LEND_FROM_FUTURE { @@ -129,12 +129,12 @@ pub(crate) fn dc_create_smeared_timestamp(context: &Context) -> i64 { // creates `count` timestamps that are guaranteed to be unique. // the frist created timestamps is returned directly, // get the other timestamps just by adding 1..count-1 -pub(crate) fn dc_create_smeared_timestamps(context: &Context, count: usize) -> i64 { +pub(crate) async fn dc_create_smeared_timestamps(context: &Context, count: usize) -> i64 { let now = time(); let count = count as i64; let mut start = now + min(count, MAX_SECONDS_TO_LEND_FROM_FUTURE) - count; - let mut last_smeared_timestamp = context.last_smeared_timestamp.write().unwrap(); + let mut last_smeared_timestamp = context.last_smeared_timestamp.write().await; start = max(*last_smeared_timestamp + 1, start); *last_smeared_timestamp = start + count - 1; @@ -711,9 +711,9 @@ mod tests { } } - #[test] - fn test_file_handling() { - let t = dummy_context(); + #[async_std::test] + async fn test_file_handling() { + let t = dummy_context().await; let context = &t.ctx; let dc_file_exist = |ctx: &Context, fname: &str| { ctx.get_blobdir() @@ -784,15 +784,15 @@ mod tests { assert!(!listflags_has(listflags, DC_GCL_ADD_SELF)); } - #[test] - fn test_create_smeared_timestamp() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_smeared_timestamp() { + let t = dummy_context().await; assert_ne!( - dc_create_smeared_timestamp(&t.ctx), - dc_create_smeared_timestamp(&t.ctx) + dc_create_smeared_timestamp(&t.ctx).await, + dc_create_smeared_timestamp(&t.ctx).await ); assert!( - dc_create_smeared_timestamp(&t.ctx) + dc_create_smeared_timestamp(&t.ctx).await >= SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() @@ -800,17 +800,17 @@ mod tests { ); } - #[test] - fn test_create_smeared_timestamps() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_smeared_timestamps() { + let t = dummy_context().await; let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1; - let start = dc_create_smeared_timestamps(&t.ctx, count as usize); - let next = dc_smeared_time(&t.ctx); + let start = dc_create_smeared_timestamps(&t.ctx, count as usize).await; + let next = dc_smeared_time(&t.ctx).await; assert!((start + count - 1) < next); let count = MAX_SECONDS_TO_LEND_FROM_FUTURE + 30; - let start = dc_create_smeared_timestamps(&t.ctx, count as usize); - let next = dc_smeared_time(&t.ctx); + let start = dc_create_smeared_timestamps(&t.ctx, count as usize).await; + let next = dc_smeared_time(&t.ctx).await; assert!((start + count - 1) < next); } } diff --git a/src/e2ee.rs b/src/e2ee.rs index 317d1a542..549ee4b72 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -137,7 +137,7 @@ pub async fn try_decrypt( let autocryptheader = Aheader::from_headers(context, &from, &mail.headers); if message_time > 0 { - peerstate = Peerstate::from_addr(context, &context.sql, &from); + peerstate = Peerstate::from_addr(context, &context.sql, &from).await; if let Some(ref mut peerstate) = peerstate { if let Some(ref header) = autocryptheader { @@ -167,7 +167,7 @@ pub async fn try_decrypt( .await { if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 { - peerstate = Peerstate::from_addr(&context, &context.sql, &from); + peerstate = Peerstate::from_addr(&context, &context.sql, &from).await; } if let Some(ref peerstate) = peerstate { if peerstate.degrade_event.is_some() { @@ -207,7 +207,7 @@ async fn load_or_generate_self_public_key( return SignedPublicKey::try_from(key) .map_err(|_| Error::Message("Not a public key".into())); } - let _guard = context.generating_key_mutex.lock().unwrap(); + let _guard = context.generating_key_mutex.lock().await; // Check again in case the key was generated while we were waiting for the lock. if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql).await { @@ -377,15 +377,15 @@ mod tests { #[async_std::test] async fn test_prexisting() { - let t = dummy_context(); + let t = dummy_context().await; let test_addr = configure_alice_keypair(&t.ctx).await; assert_eq!(ensure_secret_key_exists(&t.ctx).await.unwrap(), test_addr); } - #[test] - fn test_not_configured() { - let t = dummy_context(); - assert!(ensure_secret_key_exists(&t.ctx).is_err()); + #[async_std::test] + async fn test_not_configured() { + let t = dummy_context().await; + assert!(ensure_secret_key_exists(&t.ctx).await.is_err()); } } @@ -418,40 +418,42 @@ Sent with my Delta Chat Messenger: https://delta.chat"; #[async_std::test] async fn test_existing() { - let t = dummy_context(); + let t = dummy_context().await; let addr = configure_alice_keypair(&t.ctx).await; let key = load_or_generate_self_public_key(&t.ctx, addr).await; assert!(key.is_ok()); } - #[test] + #[async_std::test] #[ignore] // generating keys is expensive - fn test_generate() { - let t = dummy_context(); + async fn test_generate() { + let t = dummy_context().await; let addr = "alice@example.org"; - let key0 = load_or_generate_self_public_key(&t.ctx, addr); + let key0 = load_or_generate_self_public_key(&t.ctx, addr).await; assert!(key0.is_ok()); - let key1 = load_or_generate_self_public_key(&t.ctx, addr); + let key1 = load_or_generate_self_public_key(&t.ctx, addr).await; assert!(key1.is_ok()); assert_eq!(key0.unwrap(), key1.unwrap()); } - #[test] + #[async_std::test] #[ignore] - fn test_generate_concurrent() { + async fn test_generate_concurrent() { use std::sync::Arc; - use std::thread; - let t = dummy_context(); + let t = dummy_context().await; let ctx = Arc::new(t.ctx); let ctx0 = Arc::clone(&ctx); - let thr0 = - thread::spawn(move || load_or_generate_self_public_key(&ctx0, "alice@example.org")); + let thr0 = async_std::task::spawn(async move { + load_or_generate_self_public_key(&ctx0, "alice@example.org").await + }); let ctx1 = Arc::clone(&ctx); - let thr1 = - thread::spawn(move || load_or_generate_self_public_key(&ctx1, "alice@example.org")); - let res0 = thr0.join().unwrap(); - let res1 = thr1.join().unwrap(); + let thr1 = async_std::task::spawn(async move { + load_or_generate_self_public_key(&ctx1, "alice@example.org").await + }); + + let res0 = thr0.await; + let res1 = thr1.await; assert_eq!(res0.unwrap(), res1.unwrap()); } } diff --git a/src/imap/mod.rs b/src/imap/mod.rs index 9cb7018f8..cc802318a 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -280,7 +280,9 @@ impl Imap { if (server_flags & DC_LP_AUTH_OAUTH2) != 0 { let addr: &str = config.addr.as_ref(); - if let Some(token) = dc_get_oauth2_access_token(context, addr, imap_pw, true) { + if let Some(token) = + dc_get_oauth2_access_token(context, addr, imap_pw, true).await + { let auth = OAuth2 { user: imap_user.into(), access_token: token, @@ -298,11 +300,13 @@ impl Imap { let config = self.config.read().await; let imap_server: &str = config.imap_server.as_ref(); let imap_port = config.imap_port; - context.stock_string_repl_str2( - StockMessage::ServerResponse, - format!("{}:{}", imap_server, imap_port), - err.to_string(), - ) + context + .stock_string_repl_str2( + StockMessage::ServerResponse, + format!("{}:{}", imap_server, imap_port), + err.to_string(), + ) + .await }; // IMAP connection failures are reported to users emit_event!(context, Event::ErrorNetwork(message)); @@ -319,7 +323,9 @@ impl Imap { } Err((err, _)) => { let imap_user = self.config.read().await.imap_user.to_owned(); - let message = context.stock_string_repl_str(StockMessage::CannotLogin, &imap_user); + let message = context + .stock_string_repl_str(StockMessage::CannotLogin, &imap_user) + .await; emit_event!( context, @@ -539,7 +545,8 @@ impl Imap { // id we do not do this here, we'll miss the first message // as we will get in here again and fetch from lastseenuid+1 then - self.set_config_last_seen_uid(context, &folder, new_uid_validity, 0); + self.set_config_last_seen_uid(context, &folder, new_uid_validity, 0) + .await; return Ok((new_uid_validity, 0)); } @@ -577,7 +584,8 @@ impl Imap { } }; - self.set_config_last_seen_uid(context, &folder, new_uid_validity, new_last_seen_uid); + self.set_config_last_seen_uid(context, &folder, new_uid_validity, new_last_seen_uid) + .await; info!( context, "uid/validity change: new {}/{} current {}/{}", @@ -651,6 +659,7 @@ impl Imap { ); } else { let show = prefetch_should_download(context, &headers, show_emails) + .await .map_err(|err| { warn!(context, "prefetch_should_download error: {}", err); err @@ -687,7 +696,8 @@ impl Imap { }; if new_last_seen_uid > last_seen_uid { - self.set_config_last_seen_uid(context, &folder, uid_validity, new_last_seen_uid); + self.set_config_last_seen_uid(context, &folder, uid_validity, new_last_seen_uid) + .await; } if read_errors > 0 { @@ -1322,7 +1332,7 @@ async fn precheck_imf( server_uid: u32, ) -> bool { if let Ok((old_server_folder, old_server_uid, msg_id)) = - message::rfc724_mid_exists(context, &rfc724_mid) + message::rfc724_mid_exists(context, &rfc724_mid).await { if old_server_folder.is_empty() && old_server_uid == 0 { info!(context, "[move] detected bcc-self {}", rfc724_mid,); @@ -1342,7 +1352,7 @@ async fn precheck_imf( } if old_server_folder != server_folder || old_server_uid != server_uid { - update_server_uid(context, &rfc724_mid, server_folder, server_uid); + update_server_uid(context, &rfc724_mid, server_folder, server_uid).await; } true } else { @@ -1367,18 +1377,18 @@ fn prefetch_get_message_id(headers: &[mailparse::MailHeader]) -> Result } } -fn prefetch_is_reply_to_chat_message( +async fn prefetch_is_reply_to_chat_message( context: &Context, - headers: &[mailparse::MailHeader], + headers: &[mailparse::MailHeader<'_>], ) -> Result { if let Some(value) = headers.get_header_value(HeaderDef::InReplyTo)? { - if is_msgrmsg_rfc724_mid_in_list(context, &value) { + if is_msgrmsg_rfc724_mid_in_list(context, &value).await { return Ok(true); } } if let Some(value) = headers.get_header_value(HeaderDef::References)? { - if is_msgrmsg_rfc724_mid_in_list(context, &value) { + if is_msgrmsg_rfc724_mid_in_list(context, &value).await { return Ok(true); } } @@ -1386,13 +1396,13 @@ fn prefetch_is_reply_to_chat_message( Ok(false) } -fn prefetch_should_download( +async fn prefetch_should_download( context: &Context, - headers: &[mailparse::MailHeader], + headers: &[mailparse::MailHeader<'_>], show_emails: ShowEmails, ) -> Result { let is_chat_message = headers.get_header_value(HeaderDef::ChatVersion)?.is_some(); - let is_reply_to_chat_message = prefetch_is_reply_to_chat_message(context, &headers)?; + let is_reply_to_chat_message = prefetch_is_reply_to_chat_message(context, &headers).await?; // Autocrypt Setup Message should be shown even if it is from non-chat client. let is_autocrypt_setup_message = headers @@ -1403,7 +1413,8 @@ fn prefetch_should_download( .get_header_value(HeaderDef::From_)? .unwrap_or_default(); - let (_contact_id, blocked_contact, origin) = from_field_to_contact_id(context, &from_field)?; + let (_contact_id, blocked_contact, origin) = + from_field_to_contact_id(context, &from_field).await?; let accepted_contact = origin.is_known(); let show = is_autocrypt_setup_message diff --git a/src/imap/session.rs b/src/imap/session.rs index 8fb146874..9cac58515 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -29,15 +29,10 @@ impl Session { mailbox_pattern: Option<&str>, ) -> ImapResult> + '_ + Send + Unpin> { match self { - Session::Secure(i) => { - i.list(reference_name, mailbox_pattern).await - // list.collect::>().await? - } - Session::Insecure(i) => { + Session::Secure(i) => i.list(reference_name, mailbox_pattern).await, + Session::Insecure(_i) => { unimplemented!() // i.list(reference_name, mailbox_pattern).await - // .collect::>() - // .await? } } } @@ -86,7 +81,7 @@ impl Session { { let res = match self { Session::Secure(i) => i.fetch(sequence_set, query).await?, - Session::Insecure(i) => { + Session::Insecure(_i) => { unimplemented!() // i.fetch(sequence_set, query).await? } @@ -105,7 +100,7 @@ impl Session { { let res = match self { Session::Secure(i) => i.uid_fetch(uid_set, query).await?, - Session::Insecure(i) => { + Session::Insecure(_i) => { unimplemented!() // i.uid_fetch(uid_set, query).await? } @@ -125,7 +120,7 @@ impl Session { { let res = match self { Session::Secure(i) => i.uid_store(uid_set, query).await?, - Session::Insecure(i) => { + Session::Insecure(_i) => { unimplemented!() // i.uid_store(uid_set, query).await? } diff --git a/src/imex.rs b/src/imex.rs index 0084038bb..3ca1a2a89 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -115,9 +115,9 @@ pub async fn has_backup(context: &Context, dir_name: impl AsRef) -> Result } pub async fn initiate_key_transfer(context: &Context) -> Result { - ensure!(context.alloc_ongoing(), "could not allocate ongoing"); + ensure!(context.alloc_ongoing().await, "could not allocate ongoing"); let res = do_initiate_key_transfer(context).await; - context.free_ongoing(); + context.free_ongoing().await; res } @@ -125,10 +125,10 @@ async fn do_initiate_key_transfer(context: &Context) -> Result { let mut msg: Message; let setup_code = create_setup_code(context); /* this may require a keypair to be created. this may take a second ... */ - ensure!(!context.shall_stop_ongoing(), "canceled"); + ensure!(!context.shall_stop_ongoing().await, "canceled"); let setup_file_content = render_setup_file(context, &setup_code).await?; /* encrypting may also take a while ... */ - ensure!(!context.shall_stop_ongoing(), "canceled"); + ensure!(!context.shall_stop_ongoing().await, "canceled"); let setup_file_blob = BlobObject::create( context, "autocrypt-setup-message.html", @@ -148,11 +148,11 @@ async fn do_initiate_key_transfer(context: &Context) -> Result { ForcePlaintext::NoAutocryptHeader as i32, ); - ensure!(!context.shall_stop_ongoing(), "canceled"); + ensure!(!context.shall_stop_ongoing().await, "canceled"); let msg_id = chat::send_msg(context, chat_id, &mut msg).await?; info!(context, "Wait for setup message being sent ...",); - while !context.shall_stop_ongoing() { - std::thread::sleep(std::time::Duration::from_secs(1)); + while !context.shall_stop_ongoing().await { + async_std::task::sleep(std::time::Duration::from_secs(1)).await; if let Ok(msg) = Message::load_from_db(context, msg_id).await { if msg.is_sent() { info!(context, "... setup message sent.",); @@ -197,8 +197,8 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result"); Ok(format!( concat!( @@ -368,7 +368,7 @@ pub fn normalize_setup_code(s: &str) -> String { } pub async fn job_imex_imap(context: &Context, job: &Job) -> Result<()> { - ensure!(context.alloc_ongoing(), "could not allocate ongoing"); + ensure!(context.alloc_ongoing().await, "could not allocate ongoing"); let what: Option = job.param.get_int(Param::Cmd).and_then(ImexMode::from_i32); let param = job.param.get(Param::Arg).unwrap_or_default(); @@ -380,7 +380,7 @@ pub async fn job_imex_imap(context: &Context, job: &Job) -> Result<()> { if what == Some(ImexMode::ExportBackup) || what == Some(ImexMode::ExportSelfKeys) { // before we export anything, make sure the private key exists if e2ee::ensure_secret_key_exists(context).await.is_err() { - context.free_ongoing(); + context.free_ongoing().await; bail!("Cannot create private key or private key not available."); } else { dc_create_folder(context, ¶m)?; @@ -396,7 +396,7 @@ pub async fn job_imex_imap(context: &Context, job: &Job) -> Result<()> { bail!("unknown IMEX type"); } }; - context.free_ongoing(); + context.free_ongoing().await; match success { Ok(()) => { info!(context, "IMEX successfully completed"); @@ -423,7 +423,7 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef) -> !context.is_configured().await, "Cannot import backups to accounts in use." ); - context.sql.close(&context); + context.sql.close(&context).await; dc_delete_file(context, context.get_dbfile()); ensure!( !context.get_dbfile().exists(), @@ -448,7 +448,7 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef) -> let total_files_cnt = context .sql - .query_get_value::<_, isize>(context, "SELECT COUNT(*) FROM backup_blobs;", params![]) + .query_get_value::(context, "SELECT COUNT(*) FROM backup_blobs;", paramsv![]) .await .unwrap_or_default() as usize; info!( @@ -456,11 +456,11 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef) -> "***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt, ); - let res = context + let files = context .sql .query_map( "SELECT file_name, file_content FROM backup_blobs ORDER BY id;", - params![], + paramsv![], |row| { let name: String = row.get(0)?; let blob: Vec = row.get(1)?; @@ -468,46 +468,45 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef) -> Ok((name, blob)) }, |files| { - for (processed_files_cnt, file) in files.enumerate() { - let (file_name, file_blob) = file?; - if context.shall_stop_ongoing() { - return Ok(false); - } - let mut permille = processed_files_cnt * 1000 / total_files_cnt; - if permille < 10 { - permille = 10 - } - if permille > 990 { - permille = 990 - } - context.call_cb(Event::ImexProgress(permille)); - if file_blob.is_empty() { - continue; - } - - let path_filename = context.get_blobdir().join(file_name); - dc_write_file(context, &path_filename, &file_blob)?; - } - Ok(true) + files + .collect::, _>>() + .map_err(Into::into) }, ) - .await; + .await?; - match res { - Ok(all_files_extracted) => { - if all_files_extracted { - // only delete backup_blobs if all files were successfully extracted - context - .sql - .execute("DROP TABLE backup_blobs;", params![]) - .await?; - context.sql.execute("VACUUM;", params![]).await.ok(); - Ok(()) - } else { - bail!("received stop signal"); - } + let mut all_files_extracted = true; + for (processed_files_cnt, (file_name, file_blob)) in files.into_iter().enumerate() { + if context.shall_stop_ongoing().await { + all_files_extracted = false; + break; } - Err(err) => Err(err.into()), + let mut permille = processed_files_cnt * 1000 / total_files_cnt; + if permille < 10 { + permille = 10 + } + if permille > 990 { + permille = 990 + } + context.call_cb(Event::ImexProgress(permille)); + if file_blob.is_empty() { + continue; + } + + let path_filename = context.get_blobdir().join(file_name); + dc_write_file(context, &path_filename, &file_blob)?; + } + + if all_files_extracted { + // only delete backup_blobs if all files were successfully extracted + context + .sql + .execute("DROP TABLE backup_blobs;", paramsv![]) + .await?; + context.sql.execute("VACUUM;", paramsv![]).await.ok(); + Ok(()) + } else { + bail!("received stop signal"); } } @@ -526,10 +525,10 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { sql::housekeeping(context).await; - context.sql.execute("VACUUM;", params![]).await.ok(); + context.sql.execute("VACUUM;", paramsv![]).await.ok(); // we close the database during the copy of the dbfile - context.sql.close(context); + context.sql.close(context).await; info!( context, "Backup '{}' to '{}'.", @@ -537,7 +536,10 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { dest_path_filename.display(), ); let copied = dc_copy_file(context, context.get_dbfile(), &dest_path_filename); - context.sql.open(&context, &context.get_dbfile(), false); + context + .sql + .open(&context, &context.get_dbfile(), false) + .await; if !copied { bail!( @@ -566,7 +568,7 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { Ok(()) } }; - dest_sql.close(context); + dest_sql.close(context).await; Ok(res?) } @@ -577,7 +579,7 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { if !sql.table_exists("backup_blobs").await? { sql.execute( "CREATE TABLE backup_blobs (id INTEGER PRIMARY KEY, file_name, file_content);", - params![], + paramsv![], ) .await?; } @@ -589,17 +591,14 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { info!(context, "EXPORT: total_files_cnt={}", total_files_cnt); - sql.with_conn(|conn| { + sql.with_conn_async(|conn| async move { // scan directory, pass 2: copy files let dir_handle = std::fs::read_dir(&dir)?; - let mut stmt = conn - .prepare_cached("INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);")?; - let mut processed_files_cnt = 0; for entry in dir_handle { let entry = entry?; - if context.shall_stop_ongoing() { + if context.shall_stop_ongoing().await { return Ok(()); } processed_files_cnt += 1; @@ -618,7 +617,10 @@ async fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { continue; } // bail out if we can't insert - stmt.execute(params![name, buf])?; + let mut stmt = conn.prepare_cached( + "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);", + )?; + stmt.execute(paramsv![name, buf])?; } } Ok(()) @@ -690,7 +692,7 @@ async fn export_self_keys(context: &Context, dir: impl AsRef) -> Result<() .sql .query_map( "SELECT id, public_key, private_key, is_default FROM keypairs;", - params![], + paramsv![], |row| { let id = row.get(0)?; let public_key_blob: Vec = row.get(1)?; @@ -766,7 +768,7 @@ mod tests { #[async_std::test] async fn test_render_setup_file() { - let t = test_context(Some(Box::new(logging_cb))); + let t = test_context(Some(Box::new(logging_cb))).await; configure_alice_keypair(&t.ctx).await; let msg = render_setup_file(&t.ctx, "hello").await.unwrap(); @@ -786,9 +788,10 @@ mod tests { #[async_std::test] async fn test_render_setup_file_newline_replace() { - let t = dummy_context(); + let t = dummy_context().await; t.ctx .set_stock_translation(StockMessage::AcSetupMsgBody, "hello\r\nthere".to_string()) + .await .unwrap(); configure_alice_keypair(&t.ctx).await; let msg = render_setup_file(&t.ctx, "pw").await.unwrap(); @@ -796,9 +799,9 @@ mod tests { assert!(msg.contains("

hello
there

")); } - #[test] - fn test_create_setup_code() { - let t = dummy_context(); + #[async_std::test] + async fn test_create_setup_code() { + let t = dummy_context().await; let setupcode = create_setup_code(&t.ctx); assert_eq!(setupcode.len(), 44); assert_eq!(setupcode.chars().nth(4).unwrap(), '-'); @@ -811,9 +814,9 @@ mod tests { assert_eq!(setupcode.chars().nth(39).unwrap(), '-'); } - #[test] - fn test_export_key_to_asc_file() { - let context = dummy_context(); + #[async_std::test] + async fn test_export_key_to_asc_file() { + let context = dummy_context().await; let key = Key::from(alice_keypair().public); let blobdir = "$BLOBDIR"; assert!(export_key_to_asc_file(&context.ctx, blobdir, None, &key).is_ok()); @@ -839,9 +842,9 @@ mod tests { const S_EM_SETUPCODE: &str = "1742-0185-6197-1303-7016-8412-3581-4441-0597"; const S_EM_SETUPFILE: &str = include_str!("../test-data/message/stress.txt"); - #[test] - fn test_split_and_decrypt() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_split_and_decrypt() { + let ctx = dummy_context().await; let context = &ctx.ctx; let buf_1 = S_EM_SETUPFILE.as_bytes().to_vec(); diff --git a/src/job.rs b/src/job.rs index 19ebc75e0..1274ce946 100644 --- a/src/job.rs +++ b/src/job.rs @@ -149,7 +149,7 @@ impl Job { async fn delete(&self, context: &Context) -> bool { context .sql - .execute("DELETE FROM jobs WHERE id=?;", params![self.job_id as i32]) + .execute("DELETE FROM jobs WHERE id=?;", paramsv![self.job_id as i32]) .await .is_ok() } @@ -162,7 +162,7 @@ impl Job { .sql .execute( "UPDATE jobs SET desired_timestamp=?, tries=?, param=? WHERE id=?;", - params![ + paramsv![ self.desired_timestamp, self.tries as i64, self.param.to_string(), @@ -283,7 +283,7 @@ impl Job { /* if there is a msg-id and it does not exist in the db, cancel sending. this happends if dc_delete_msgs() was called before the generated mime was sent out */ - if 0 != self.foreign_id && !message::exists(context, MsgId::new(self.foreign_id)) { + if 0 != self.foreign_id && !message::exists(context, MsgId::new(self.foreign_id)).await { return Status::Finished(Err(format_err!( "Not sending Message {} as it was deleted", self.foreign_id @@ -295,7 +295,7 @@ impl Job { async move { // smtp success, update db ASAP, then delete smtp file if 0 != foreign_id { - set_delivered(context, MsgId::new(foreign_id)); + set_delivered(context, MsgId::new(foreign_id)).await; } // now also delete the generated file dc_delete_file(context, filename); @@ -316,7 +316,7 @@ impl Job { .sql .query_map( "SELECT id, param FROM jobs WHERE foreign_id=? AND id!=?", - params![contact_id, self.job_id], + paramsv![contact_id, self.job_id], |row| { let job_id: u32 = row.get(0)?; let params_str: String = row.get(1)?; @@ -385,8 +385,9 @@ impl Job { } let msg = job_try!(Message::load_from_db(context, msg_id).await); - let mimefactory = job_try!(MimeFactory::from_mdn(context, &msg, additional_rfc724_mids)); - let rendered_msg = job_try!(mimefactory.render()); + let mimefactory = + job_try!(MimeFactory::from_mdn(context, &msg, additional_rfc724_mids).await); + let rendered_msg = job_try!(mimefactory.render().await); let body = rendered_msg.message; let addr = contact.get_addr(); @@ -443,7 +444,8 @@ impl Job { { ImapActionResult::RetryLater => Status::RetryLater, ImapActionResult::Success => { - message::update_server_uid(context, &msg.rfc724_mid, &dest_folder, dest_uid); + message::update_server_uid(context, &msg.rfc724_mid, &dest_folder, dest_uid) + .await; Status::Finished(Ok(())) } ImapActionResult::Failed => { @@ -462,7 +464,7 @@ impl Job { let mut msg = job_try!(Message::load_from_db(context, MsgId::new(self.foreign_id)).await); if !msg.rfc724_mid.is_empty() { - if message::rfc724_mid_cnt(context, &msg.rfc724_mid) > 1 { + if message::rfc724_mid_cnt(context, &msg.rfc724_mid).await > 1 { info!( context, "The message is deleted from the server when all parts are deleted.", @@ -480,7 +482,7 @@ impl Job { return Status::RetryNow; } } - Message::delete_from_db(context, msg.id); + Message::delete_from_db(context, msg.id).await; Status::Finished(Ok(())) } else { /* eg. device messages have no Message-ID */ @@ -576,7 +578,7 @@ impl Job { pub async fn kill_action(context: &Context, action: Action) -> bool { context .sql - .execute("DELETE FROM jobs WHERE action=?;", params![action]) + .execute("DELETE FROM jobs WHERE action=?;", paramsv![action]) .await .is_ok() } @@ -590,7 +592,7 @@ pub async fn kill_ids(context: &Context, job_ids: &[u32]) -> sql::Result<()> { "DELETE FROM jobs WHERE id IN({})", job_ids.iter().map(|_| "?").join(",") ), - job_ids, + job_ids.iter().map(|i| i as &dyn crate::ToSql).collect(), ) .await?; Ok(()) @@ -715,7 +717,7 @@ async fn get_next_wakeup_time(context: &Context, thread: Thread) -> time::Durati .query_get_value( context, "SELECT MIN(desired_timestamp) FROM jobs WHERE thread=?;", - params![thread], + paramsv![thread], ) .await .unwrap_or_default(); @@ -750,19 +752,19 @@ pub async fn maybe_network(context: &Context) { pub async fn action_exists(context: &Context, action: Action) -> bool { context .sql - .exists("SELECT id FROM jobs WHERE action=?;", params![action]) + .exists("SELECT id FROM jobs WHERE action=?;", paramsv![action]) .await .unwrap_or_default() } async fn set_delivered(context: &Context, msg_id: MsgId) { - message::update_msg_state(context, msg_id, MessageState::OutDelivered); + message::update_msg_state(context, msg_id, MessageState::OutDelivered).await; let chat_id: ChatId = context .sql .query_get_value( context, "SELECT chat_id FROM msgs WHERE id=?", - params![msg_id], + paramsv![msg_id], ) .await .unwrap_or_default(); @@ -772,7 +774,7 @@ async fn set_delivered(context: &Context, msg_id: MsgId) { // special case for DC_JOB_SEND_MSG_TO_SMTP pub async fn send_msg(context: &Context, msg_id: MsgId) -> Result<()> { let mut msg = Message::load_from_db(context, msg_id).await?; - msg.try_calc_and_set_dimensions(context).ok(); + msg.try_calc_and_set_dimensions(context).await.ok(); /* create message */ let needs_encryption = msg.param.get_bool(Param::GuaranteeE2ee).unwrap_or_default(); @@ -785,7 +787,7 @@ pub async fn send_msg(context: &Context, msg_id: MsgId) -> Result<()> { } }; - let mimefactory = MimeFactory::from_msg(context, &msg, attach_selfavatar)?; + let mimefactory = MimeFactory::from_msg(context, &msg, attach_selfavatar).await?; let mut recipients = mimefactory.recipients(); @@ -808,14 +810,17 @@ pub async fn send_msg(context: &Context, msg_id: MsgId) -> Result<()> { context, "message {} has no recipient, skipping smtp-send", msg_id ); - set_delivered(context, msg_id); + set_delivered(context, msg_id).await; return Ok(()); } - let rendered_msg = mimefactory.render().map_err(|err| { - message::set_msg_failed(context, msg_id, Some(err.to_string())); - err - })?; + let rendered_msg = match mimefactory.render().await { + Ok(res) => Ok(res), + Err(err) => { + message::set_msg_failed(context, msg_id, Some(err.to_string())).await; + Err(err) + } + }?; if needs_encryption && !rendered_msg.is_encrypted { /* unrecoverable */ @@ -823,7 +828,8 @@ pub async fn send_msg(context: &Context, msg_id: MsgId) -> Result<()> { context, msg_id, Some("End-to-end-encryption unavailable unexpectedly."), - ); + ) + .await; bail!( "e2e encryption unavailable {} - {:?}", msg_id, @@ -857,7 +863,7 @@ pub async fn send_msg(context: &Context, msg_id: MsgId) -> Result<()> { if rendered_msg.is_encrypted && !needs_encryption { msg.param.set_int(Param::GuaranteeE2ee, 1); - msg.save_param_to_disk(context); + msg.save_param_to_disk(context).await; } add_smtp_job( @@ -917,77 +923,78 @@ async fn job_perform(context: &Context, thread: Thread, probe_network: bool) { x => x, }; - // if Action::ConfigureImap == job.action || Action::ImexImap == job.action { - // context.sentbox_thread.unsuspend(context).await; - // context.mvbox_thread.unsuspend(context).await; - // suspend_smtp_thread(context, false).await; - // break; - // } + if Action::ConfigureImap == job.action || Action::ImexImap == job.action { + context.sentbox_thread.unsuspend(context).await; + context.mvbox_thread.unsuspend(context).await; + suspend_smtp_thread(context, false).await; + break; + } - // match try_res { - // Status::RetryNow | Status::RetryLater => { - // let tries = job.tries + 1; + match try_res { + Status::RetryNow | Status::RetryLater => { + let tries = job.tries + 1; - // if tries < JOB_RETRIES { - // info!( - // context, - // "{} thread increases job {} tries to {}", thread, job, tries - // ); - // job.tries = tries; - // let time_offset = get_backoff_time_offset(tries); - // job.desired_timestamp = time() + time_offset; - // job.update(context).await; - // info!( - // context, - // "{}-job #{} not succeeded on try #{}, retry in {} seconds.", - // thread, - // job.job_id as u32, - // tries, - // time_offset - // ); - // if thread == Thread::Smtp && tries < JOB_RETRIES - 1 { - // context.smtp.state.write().await.perform_jobs_needed = - // PerformJobsNeeded::AvoidDos; - // } - // } else { - // info!( - // context, - // "{} thread removes job {} as it exhausted {} retries", - // thread, - // job, - // JOB_RETRIES - // ); - // if job.action == Action::SendMsgToSmtp { - // message::set_msg_failed( - // context, - // MsgId::new(job.foreign_id), - // job.pending_error.as_ref(), - // ); - // } - // job.delete(context).await; - // } - // if !probe_network { - // continue; - // } - // // on dc_maybe_network() we stop trying here; - // // these jobs are already tried once. - // // otherwise, we just continue with the next job - // // to give other jobs a chance being tried at least once. - // break; - // } - // Status::Finished(res) => { - // if let Err(err) = res { - // warn!( - // context, - // "{} removes job {} as it failed with error {:?}", thread, job, err - // ); - // } else { - // info!(context, "{} removes job {} as it succeeded", thread, job); - // } + if tries < JOB_RETRIES { + info!( + context, + "{} thread increases job {} tries to {}", thread, job, tries + ); + job.tries = tries; + let time_offset = get_backoff_time_offset(tries); + job.desired_timestamp = time() + time_offset; + job.update(context).await; + info!( + context, + "{}-job #{} not succeeded on try #{}, retry in {} seconds.", + thread, + job.job_id as u32, + tries, + time_offset + ); + if thread == Thread::Smtp && tries < JOB_RETRIES - 1 { + context.smtp.state.write().await.perform_jobs_needed = + PerformJobsNeeded::AvoidDos; + } + } else { + info!( + context, + "{} thread removes job {} as it exhausted {} retries", + thread, + job, + JOB_RETRIES + ); + if job.action == Action::SendMsgToSmtp { + message::set_msg_failed( + context, + MsgId::new(job.foreign_id), + job.pending_error.as_ref(), + ) + .await; + } + job.delete(context).await; + } + if !probe_network { + continue; + } + // on dc_maybe_network() we stop trying here; + // these jobs are already tried once. + // otherwise, we just continue with the next job + // to give other jobs a chance being tried at least once. + break; + } + Status::Finished(res) => { + if let Err(err) = res { + warn!( + context, + "{} removes job {} as it failed with error {:?}", thread, job, err + ); + } else { + info!(context, "{} removes job {} as it succeeded", thread, job); + } - // job.delete(context).await; - // } - // } + job.delete(context).await; + } + } } } @@ -1024,7 +1031,7 @@ async fn perform_job_action( location::job_maybe_send_locations_ended(context, &mut job).await } Action::Housekeeping => { - sql::housekeeping(context); + sql::housekeeping(context).await; Status::Finished(Ok(())) } }; @@ -1108,7 +1115,7 @@ pub async fn add( context.sql.execute( "INSERT INTO jobs (added_timestamp, thread, action, foreign_id, param, desired_timestamp) VALUES (?,?,?,?,?,?);", - params![ + paramsv![ timestamp, thread, action, @@ -1154,9 +1161,11 @@ async fn load_next_job(context: &Context, thread: Thread, probe_network: bool) - FROM jobs WHERE thread=? AND tries>0 ORDER BY desired_timestamp, action DESC;" }; - let params_no_probe = params![thread as i64, time()]; - let params_probe = params![thread as i64]; - let params: &[&dyn rusqlite::ToSql] = if !probe_network { + let thread_i = thread as i64; + let t = time(); + let params_no_probe = paramsv![thread_i, t]; + let params_probe = paramsv![thread_i]; + let params = if !probe_network { params_no_probe } else { params_probe @@ -1201,7 +1210,7 @@ mod tests { use crate::test_utils::*; - fn insert_job(context: &Context, foreign_id: i64) { + async fn insert_job(context: &Context, foreign_id: i64) { let now = time(); context .sql @@ -1209,7 +1218,7 @@ mod tests { "INSERT INTO jobs (added_timestamp, thread, action, foreign_id, param, desired_timestamp) VALUES (?, ?, ?, ?, ?, ?);", - params![ + paramsv![ now, Thread::from(Action::MoveMsg), Action::MoveMsg, @@ -1218,21 +1227,22 @@ mod tests { now ], ) + .await .unwrap(); } - #[test] - fn test_load_next_job() { + #[async_std::test] + async fn test_load_next_job() { // We want to ensure that loading jobs skips over jobs which // fails to load from the database instead of failing to load // all jobs. - let t = dummy_context(); - insert_job(&t.ctx, -1); // This can not be loaded into Job struct. - let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false); + let t = dummy_context().await; + insert_job(&t.ctx, -1).await; // This can not be loaded into Job struct. + let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false).await; assert!(jobs.is_none()); - insert_job(&t.ctx, 1); - let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false); + insert_job(&t.ctx, 1).await; + let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false).await; assert!(jobs.is_some()); } } diff --git a/src/key.rs b/src/key.rs index 0429f3a58..0ab6bcc23 100644 --- a/src/key.rs +++ b/src/key.rs @@ -207,7 +207,7 @@ impl Key { sql.query_get_value( context, "SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;", - &[addr], + paramsv![addr], ) .await .and_then(|blob: Vec| Self::from_slice(&blob, KeyType::Public)) @@ -221,7 +221,7 @@ impl Key { sql.query_get_value( context, "SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;", - &[self_addr.as_ref()], + paramsv![self_addr.as_ref()], ) .await .and_then(|blob: Vec| Self::from_slice(&blob, KeyType::Private)) @@ -368,37 +368,37 @@ pub async fn store_self_keypair( .sql .execute( "DELETE FROM keypairs WHERE public_key=? OR private_key=?;", - params![public_key, secret_key], + paramsv![public_key, secret_key], ) .await .map_err(|err| SaveKeyError::new("failed to remove old use of key", err))?; if default == KeyPairUse::Default { context .sql - .execute("UPDATE keypairs SET is_default=0;", params![]) + .execute("UPDATE keypairs SET is_default=0;", paramsv![]) .await .map_err(|err| SaveKeyError::new("failed to clear default", err))?; } let is_default = match default { - KeyPairUse::Default => true, - KeyPairUse::ReadOnly => false, + KeyPairUse::Default => true as i32, + KeyPairUse::ReadOnly => false as i32, }; + + let addr = keypair.addr.to_string(); + let t = time(); + + let params = paramsv![addr, is_default, public_key, secret_key, t]; context .sql .execute( "INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);", - params![ - keypair.addr.to_string(), - is_default as i32, - public_key, - secret_key, - time() - ], + params, ) .await - .map(|_| ()) - .map_err(|err| SaveKeyError::new("failed to insert keypair", err)) + .map_err(|err| SaveKeyError::new("failed to insert keypair", err))?; + + Ok(()) } /// Make a fingerprint human-readable, in hex format. @@ -433,6 +433,7 @@ mod tests { use crate::test_utils::*; use std::convert::TryFrom; + use async_std::sync::Arc; use lazy_static::lazy_static; lazy_static! { @@ -583,22 +584,29 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD assert_eq!(public.primary_key, KEYPAIR.public.primary_key); } - #[test] - fn test_save_self_key_twice() { + #[async_std::test] + async fn test_save_self_key_twice() { // Saving the same key twice should result in only one row in // the keypairs table. - let t = dummy_context(); - let nrows = || { - t.ctx - .sql - .query_get_value::<_, u32>(&t.ctx, "SELECT COUNT(*) FROM keypairs;", params![]) + let t = dummy_context().await; + let ctx = Arc::new(t.ctx); + + let ctx1 = ctx.clone(); + let nrows = || async { + ctx1.sql + .query_get_value::(&ctx1, "SELECT COUNT(*) FROM keypairs;", paramsv![]) + .await .unwrap() }; - assert_eq!(nrows(), 0); - store_self_keypair(&t.ctx, &KEYPAIR, KeyPairUse::Default).unwrap(); - assert_eq!(nrows(), 1); - store_self_keypair(&t.ctx, &KEYPAIR, KeyPairUse::Default).unwrap(); - assert_eq!(nrows(), 1); + assert_eq!(nrows().await, 0); + store_self_keypair(&ctx, &KEYPAIR, KeyPairUse::Default) + .await + .unwrap(); + assert_eq!(nrows().await, 1); + store_self_keypair(&ctx, &KEYPAIR, KeyPairUse::Default) + .await + .unwrap(); + assert_eq!(nrows().await, 1); } // Convenient way to create a new key if you need one, run with diff --git a/src/keyring.rs b/src/keyring.rs index 4009a0591..f1da55e13 100644 --- a/src/keyring.rs +++ b/src/keyring.rs @@ -36,7 +36,7 @@ impl<'a> Keyring<'a> { sql.query_get_value( context, "SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;", - &[self_addr.as_ref()], + paramsv![self_addr.as_ref().to_string()], ) .await .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Private)) diff --git a/src/lib.rs b/src/lib.rs index 3bd459ad9..c9f8aafe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,19 @@ extern crate strum_macros; #[macro_use] extern crate debug_stub_derive; +pub trait ToSql: rusqlite::ToSql + Send + Sync {} + +impl ToSql for T {} + +macro_rules! paramsv { + () => { + Vec::new() + }; + ($($param:expr),+ $(,)?) => { + vec![$(&$param as &dyn $crate::ToSql),+] + }; +} + #[macro_use] pub mod log; #[macro_use] @@ -58,7 +71,7 @@ pub mod qr; pub mod securejoin; mod simplify; mod smtp; -pub mod sql; +mod sql; pub mod stock; mod token; #[macro_use] diff --git a/src/location.rs b/src/location.rs index 1ca5cd3f4..728f8c654 100644 --- a/src/location.rs +++ b/src/location.rs @@ -202,7 +202,7 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: SET locations_send_begin=?, \ locations_send_until=? \ WHERE id=?", - params![ + paramsv![ if 0 != seconds { now } else { 0 }, if 0 != seconds { now + seconds } else { 0 }, chat_id, @@ -213,16 +213,20 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: { if 0 != seconds && !is_sending_locations_before { let mut msg = Message::new(Viewtype::Text); - msg.text = - Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)); + msg.text = Some( + context + .stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0) + .await, + ); msg.param.set_cmd(SystemMessage::LocationStreamingEnabled); chat::send_msg(context, chat_id, &mut msg) .await .unwrap_or_default(); } else if 0 == seconds && is_sending_locations_before { - let stock_str = - context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); - chat::add_info_msg(context, chat_id, stock_str); + let stock_str = context + .stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0) + .await; + chat::add_info_msg(context, chat_id, stock_str).await; } context.call_cb(Event::ChatModified(chat_id)); if 0 != seconds { @@ -258,7 +262,7 @@ pub async fn is_sending_locations_to_chat(context: &Context, chat_id: ChatId) -> .sql .exists( "SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;", - params![if chat_id.is_unset() { 1 } else { 0 }, chat_id, time()], + paramsv![if chat_id.is_unset() { 1 } else { 0 }, chat_id, time()], ) .await .unwrap_or_default() @@ -274,7 +278,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64 .sql .query_map( "SELECT id FROM chats WHERE locations_send_until>?;", - params![time()], + paramsv![time()], |row| row.get::<_, i32>(0), |chats| chats.collect::, _>>().map_err(Into::into), ) @@ -284,7 +288,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64 if let Err(err) = context.sql.execute( "INSERT INTO locations \ (latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);", - params![ + paramsv![ latitude, longitude, accuracy, @@ -326,7 +330,7 @@ pub async fn get_range( AND (? OR l.from_id=?) \ AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \ ORDER BY l.timestamp DESC, l.id DESC, msg_id DESC;", - params![ + paramsv![ if chat_id.is_unset() { 1 } else { 0 }, chat_id, if contact_id == 0 { 1 } else { 0 }, @@ -377,7 +381,7 @@ fn is_marker(txt: &str) -> bool { pub async fn delete_all(context: &Context) -> Result<(), Error> { context .sql - .execute("DELETE FROM locations;", params![]) + .execute("DELETE FROM locations;", paramsv![]) .await?; context.call_cb(Event::LocationChanged(None)); Ok(()) @@ -393,7 +397,7 @@ pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32) let (locations_send_begin, locations_send_until, locations_last_sent) = context.sql.query_row( "SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;", - params![chat_id], |row| { + paramsv![chat_id], |row| { let send_begin: i64 = row.get(0)?; let send_until: i64 = row.get(1)?; let last_sent: i64 = row.get(2)?; @@ -419,7 +423,7 @@ pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32) AND independent=0 \ GROUP BY timestamp \ ORDER BY timestamp;", - params![DC_CONTACT_ID_SELF, locations_send_begin, locations_last_sent, DC_CONTACT_ID_SELF], + paramsv![DC_CONTACT_ID_SELF, locations_send_begin, locations_last_sent, DC_CONTACT_ID_SELF], |row| { let location_id: i32 = row.get(0)?; let latitude: f64 = row.get(1)?; @@ -486,7 +490,7 @@ pub async fn set_kml_sent_timestamp( .sql .execute( "UPDATE chats SET locations_last_sent=? WHERE id=?;", - params![timestamp, chat_id], + paramsv![timestamp, chat_id], ) .await?; Ok(()) @@ -501,7 +505,7 @@ pub async fn set_msg_location_id( .sql .execute( "UPDATE msgs SET location_id=? WHERE id=?;", - params![location_id, msg_id], + paramsv![location_id, msg_id], ) .await?; @@ -532,10 +536,10 @@ pub async fn save( VALUES (?,?,?,?,?,?,?);", )?; - let exists = stmt_test.exists(params![location.timestamp, contact_id as i32])?; + let exists = stmt_test.exists(paramsv![location.timestamp, contact_id as i32])?; if independent || !exists { - stmt_insert.execute(params![ + stmt_insert.execute(paramsv![ location.timestamp, contact_id as i32, chat_id, @@ -582,7 +586,7 @@ pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> j "SELECT id, locations_send_begin, locations_last_sent \ FROM chats \ WHERE locations_send_until>?;", - params![now], + paramsv![now], |row| { let chat_id: ChatId = row.get(0)?; let locations_send_begin: i64 = row.get(1)?; @@ -621,10 +625,10 @@ pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> j .iter() .filter_map(|(chat_id, locations_send_begin, locations_last_sent)| { if !stmt_locations - .exists(params![ + .exists(paramsv![ DC_CONTACT_ID_SELF, - locations_send_begin, - locations_last_sent, + *locations_send_begin, + *locations_last_sent, ]) .unwrap_or_default() { @@ -682,7 +686,7 @@ pub(crate) async fn job_maybe_send_locations_ended( .sql .query_row( "SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?", - params![chat_id], + paramsv![chat_id], |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)), ) .await @@ -696,11 +700,13 @@ pub(crate) async fn job_maybe_send_locations_ended( // not streaming, device-message already sent job_try!(context.sql.execute( "UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?", - params![chat_id], + paramsv![chat_id], ).await); - let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); - chat::add_info_msg(context, chat_id, stock_str); + let stock_str = context + .stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0) + .await; + chat::add_info_msg(context, chat_id, stock_str).await; context.call_cb(Event::ChatModified(chat_id)); } } @@ -712,9 +718,9 @@ mod tests { use super::*; use crate::test_utils::dummy_context; - #[test] - fn test_kml_parse() { - let context = dummy_context(); + #[async_std::test] + async fn test_kml_parse() { + let context = dummy_context().await; let xml = b"\n\n\n2019-03-06T21:09:57Z9.423110,53.790302\n\n \n\t2018-12-13T22:11:12Z\t 19.423110 \t , \n 63.790302\n \n\n"; diff --git a/src/message.rs b/src/message.rs index 401f93be7..8ae5cd2ec 100644 --- a/src/message.rs +++ b/src/message.rs @@ -251,7 +251,7 @@ impl Message { " FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id", " WHERE m.id=?;" ), - params![id], + paramsv![id], |row| { let mut msg = Message::default(); // msg.id = row.get::<_, AnyMsgId>("id")?; @@ -310,12 +310,12 @@ impl Message { if let Ok(msg) = Message::load_from_db(context, msg_id).await { context .sql - .execute("DELETE FROM msgs WHERE id=?;", params![msg.id]) + .execute("DELETE FROM msgs WHERE id=?;", paramsv![msg.id]) .await .ok(); context .sql - .execute("DELETE FROM msgs_mdns WHERE msg_id=?;", params![msg.id]) + .execute("DELETE FROM msgs_mdns WHERE msg_id=?;", paramsv![msg.id]) .await .ok(); } @@ -339,7 +339,7 @@ impl Message { self.param.get_path(Param::File, context).unwrap_or(None) } - pub fn try_calc_and_set_dimensions(&mut self, context: &Context) -> Result<(), Error> { + pub async fn try_calc_and_set_dimensions(&mut self, context: &Context) -> Result<(), Error> { if chat::msgtype_has_file(self.viewtype) { let file_param = self.param.get_path(Param::File, context)?; if let Some(path_and_filename) = file_param { @@ -357,7 +357,7 @@ impl Message { } if !self.id.is_unset() { - self.save_param_to_disk(context); + self.save_param_to_disk(context).await; } } } @@ -495,12 +495,12 @@ impl Message { None }; - ret.fill(self, chat, contact.as_ref(), context); + ret.fill(self, chat, contact.as_ref(), context).await; ret } - pub fn get_summarytext(&self, context: &Context, approx_characters: usize) -> String { + pub async fn get_summarytext(&self, context: &Context, approx_characters: usize) -> String { get_summarytext_by_raw( self.viewtype, self.text.as_ref(), @@ -508,6 +508,7 @@ impl Message { approx_characters, context, ) + .await } pub fn has_deviating_timestamp(&self) -> bool { @@ -595,7 +596,7 @@ impl Message { self.param.set_int(Param::Duration, duration); } - pub fn latefiling_mediasize( + pub async fn latefiling_mediasize( &mut self, context: &Context, width: i32, @@ -609,7 +610,7 @@ impl Message { if duration > 0 { self.param.set_int(Param::Duration, duration); } - self.save_param_to_disk(context); + self.save_param_to_disk(context).await; } pub async fn save_param_to_disk(&mut self, context: &Context) -> bool { @@ -617,7 +618,7 @@ impl Message { .sql .execute( "UPDATE msgs SET param=? WHERE id=?;", - params![self.param.to_string(), self.id], + paramsv![self.param.to_string(), self.id], ) .await .is_ok() @@ -740,7 +741,7 @@ impl MessageState { impl Lot { /* library-internal */ /* in practice, the user additionally cuts the string himself pixel-accurate */ - pub fn fill( + pub async fn fill( &mut self, msg: &mut Message, chat: &Chat, @@ -748,14 +749,26 @@ impl Lot { context: &Context, ) { if msg.state == MessageState::OutDraft { - self.text1 = Some(context.stock_str(StockMessage::Draft).to_owned().into()); + self.text1 = Some( + context + .stock_str(StockMessage::Draft) + .await + .to_owned() + .into(), + ); self.text1_meaning = Meaning::Text1Draft; } else if msg.from_id == DC_CONTACT_ID_SELF { if msg.is_info() || chat.is_self_talk() { self.text1 = None; self.text1_meaning = Meaning::None; } else { - self.text1 = Some(context.stock_str(StockMessage::SelfMsg).to_owned().into()); + self.text1 = Some( + context + .stock_str(StockMessage::SelfMsg) + .await + .to_owned() + .into(), + ); self.text1_meaning = Meaning::Text1Self; } } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { @@ -778,13 +791,16 @@ impl Lot { } } - self.text2 = Some(get_summarytext_by_raw( - msg.viewtype, - msg.text.as_ref(), - &msg.param, - SUMMARY_CHARACTERS, - context, - )); + self.text2 = Some( + get_summarytext_by_raw( + msg.viewtype, + msg.text.as_ref(), + &msg.param, + SUMMARY_CHARACTERS, + context, + ) + .await, + ); self.timestamp = msg.get_timestamp(); self.state = msg.state.into(); @@ -806,7 +822,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> String { .query_get_value( context, "SELECT txt_raw FROM msgs WHERE id=?;", - params![msg_id], + paramsv![msg_id], ) .await; @@ -843,21 +859,26 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> String { return ret; } - if let Ok(rows) = context.sql.query_map( - "SELECT contact_id, timestamp_sent FROM msgs_mdns WHERE msg_id=?;", - params![msg_id], - |row| { - let contact_id: i32 = row.get(0)?; - let ts: i64 = row.get(1)?; - Ok((contact_id, ts)) - }, - |rows| rows.collect::, _>>().map_err(Into::into), - ) { + if let Ok(rows) = context + .sql + .query_map( + "SELECT contact_id, timestamp_sent FROM msgs_mdns WHERE msg_id=?;", + paramsv![msg_id], + |row| { + let contact_id: i32 = row.get(0)?; + let ts: i64 = row.get(1)?; + Ok((contact_id, ts)) + }, + |rows| rows.collect::, _>>().map_err(Into::into), + ) + .await + { for (contact_id, ts) in rows { let fts = dc_timestamp_to_str(ts); ret += &format!("Read: {}", fts); let name = Contact::load_from_db(context, contact_id as u32) + .await .map(|contact| contact.get_name_n_addr()) .unwrap_or_default(); @@ -944,22 +965,25 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> { Some(info) } -pub fn get_mime_headers(context: &Context, msg_id: MsgId) -> Option { - context.sql.query_get_value( - context, - "SELECT mime_headers FROM msgs WHERE id=?;", - params![msg_id], - ) +pub async fn get_mime_headers(context: &Context, msg_id: MsgId) -> Option { + context + .sql + .query_get_value( + context, + "SELECT mime_headers FROM msgs WHERE id=?;", + paramsv![msg_id], + ) + .await } pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) { for msg_id in msg_ids.iter() { if let Ok(msg) = Message::load_from_db(context, *msg_id).await { if msg.location_id > 0 { - delete_poi_location(context, msg.location_id); + delete_poi_location(context, msg.location_id).await; } } - update_msg_chat_id(context, *msg_id, ChatId::new(DC_CHAT_ID_TRASH)); + update_msg_chat_id(context, *msg_id, ChatId::new(DC_CHAT_ID_TRASH)).await; job::add( context, Action::DeleteMsgOnImap, @@ -985,7 +1009,7 @@ async fn update_msg_chat_id(context: &Context, msg_id: MsgId, chat_id: ChatId) - .sql .execute( "UPDATE msgs SET chat_id=? WHERE id=?;", - params![chat_id, msg_id], + paramsv![chat_id, msg_id], ) .await .is_ok() @@ -996,7 +1020,7 @@ async fn delete_poi_location(context: &Context, location_id: u32) -> bool { .sql .execute( "DELETE FROM locations WHERE independent = 1 AND id=?;", - params![location_id as i32], + paramsv![location_id as i32], ) .await .is_ok() @@ -1020,7 +1044,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: &[MsgId]) -> bool { let mut msgs = Vec::with_capacity(msg_ids.len()); for id in msg_ids.iter() { - let query_res = stmt.query_row(params![*id], |row| { + let query_res = stmt.query_row(paramsv![*id], |row| { Ok(( row.get::<_, MessageState>("state")?, row.get::<_, Option>("blocked")? @@ -1046,7 +1070,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: &[MsgId]) -> bool { for (id, curr_state, curr_blocked) in msgs.into_iter() { if curr_blocked == Blocked::Not { if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed { - update_msg_state(context, *id, MessageState::InSeen); + update_msg_state(context, *id, MessageState::InSeen).await; info!(context, "Seen message {}.", id); job::add( @@ -1060,7 +1084,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: &[MsgId]) -> bool { send_event = true; } } else if curr_state == MessageState::InFresh { - update_msg_state(context, *id, MessageState::InNoticed); + update_msg_state(context, *id, MessageState::InNoticed).await; send_event = true; } } @@ -1080,29 +1104,31 @@ pub async fn update_msg_state(context: &Context, msg_id: MsgId, state: MessageSt .sql .execute( "UPDATE msgs SET state=? WHERE id=?;", - params![state, msg_id], + paramsv![state, msg_id], ) .await .is_ok() } -pub fn star_msgs(context: &Context, msg_ids: &[MsgId], star: bool) -> bool { +pub async fn star_msgs(context: &Context, msg_ids: &[MsgId], star: bool) -> bool { if msg_ids.is_empty() { return false; } context .sql - .prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt, _| { + .with_conn(|conn| { + let mut stmt = conn.prepare("UPDATE msgs SET starred=? WHERE id=?;")?; for msg_id in msg_ids.iter() { - stmt.execute(params![star as i32, *msg_id])?; + stmt.execute(paramsv![star as i32, *msg_id])?; } Ok(()) }) + .await .is_ok() } /// Returns a summary test. -pub fn get_summarytext_by_raw( +pub async fn get_summarytext_by_raw( viewtype: Viewtype, text: Option>, param: &Params, @@ -1111,16 +1137,20 @@ pub fn get_summarytext_by_raw( ) -> String { let mut append_text = true; let prefix = match viewtype { - Viewtype::Image => context.stock_str(StockMessage::Image).into_owned(), - Viewtype::Gif => context.stock_str(StockMessage::Gif).into_owned(), - Viewtype::Sticker => context.stock_str(StockMessage::Sticker).into_owned(), - Viewtype::Video => context.stock_str(StockMessage::Video).into_owned(), - Viewtype::Voice => context.stock_str(StockMessage::VoiceMessage).into_owned(), + Viewtype::Image => context.stock_str(StockMessage::Image).await.into_owned(), + Viewtype::Gif => context.stock_str(StockMessage::Gif).await.into_owned(), + Viewtype::Sticker => context.stock_str(StockMessage::Sticker).await.into_owned(), + Viewtype::Video => context.stock_str(StockMessage::Video).await.into_owned(), + Viewtype::Voice => context + .stock_str(StockMessage::VoiceMessage) + .await + .into_owned(), Viewtype::Audio | Viewtype::File => { if param.get_cmd() == SystemMessage::AutocryptSetupMessage { append_text = false; context .stock_str(StockMessage::AcSetupMsgSubject) + .await .to_string() } else { let file_name: String = param @@ -1131,11 +1161,13 @@ pub fn get_summarytext_by_raw( .map(|fname| fname.to_string_lossy().into_owned()) }) .unwrap_or_else(|| String::from("ErrFileName")); - let label = context.stock_str(if viewtype == Viewtype::Audio { - StockMessage::Audio - } else { - StockMessage::File - }); + let label = context + .stock_str(if viewtype == Viewtype::Audio { + StockMessage::Audio + } else { + StockMessage::File + }) + .await; format!("{} – {}", label, file_name) } } @@ -1144,7 +1176,7 @@ pub fn get_summarytext_by_raw( "".to_string() } else { append_text = false; - context.stock_str(StockMessage::Location).to_string() + context.stock_str(StockMessage::Location).await.to_string() } } }; @@ -1176,16 +1208,19 @@ pub fn get_summarytext_by_raw( // Context functions to work with messages -pub fn exists(context: &Context, msg_id: MsgId) -> bool { +pub async fn exists(context: &Context, msg_id: MsgId) -> bool { if msg_id.is_special() { return false; } - let chat_id: Option = context.sql.query_get_value( - context, - "SELECT chat_id FROM msgs WHERE id=?;", - params![msg_id], - ); + let chat_id: Option = context + .sql + .query_get_value( + context, + "SELECT chat_id FROM msgs WHERE id=?;", + paramsv![msg_id], + ) + .await; if let Some(chat_id) = chat_id { !chat_id.is_trash() @@ -1208,7 +1243,7 @@ pub async fn set_msg_failed(context: &Context, msg_id: MsgId, error: Option("msg_id")?, - row.get::<_, ChatId>("chat_id")?, - row.get::<_, Chattype>("type")?, - row.get::<_, MessageState>("state")?, - )) - }, - ); + let res = context + .sql + .query_row( + concat!( + "SELECT", + " m.id AS msg_id,", + " c.id AS chat_id,", + " c.type AS type,", + " m.state AS state", + " FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id", + " WHERE rfc724_mid=? AND from_id=1", + " ORDER BY m.id;" + ), + paramsv![rfc724_mid], + |row| { + Ok(( + row.get::<_, MsgId>("msg_id")?, + row.get::<_, ChatId>("chat_id")?, + row.get::<_, Chattype>("type")?, + row.get::<_, MessageState>("state")?, + )) + }, + ) + .await; if let Err(ref err) = res { info!(context, "Failed to select MDN {:?}", err); } @@ -1268,30 +1306,34 @@ pub fn mdn_from_ext( .sql .exists( "SELECT contact_id FROM msgs_mdns WHERE msg_id=? AND contact_id=?;", - params![msg_id, from_id as i32,], + paramsv![msg_id, from_id as i32,], ) + .await .unwrap_or_default(); if !mdn_already_in_table { context.sql.execute( "INSERT INTO msgs_mdns (msg_id, contact_id, timestamp_sent) VALUES (?, ?, ?);", - params![msg_id, from_id as i32, timestamp_sent], - ).unwrap_or_default(); // TODO: better error handling + paramsv![msg_id, from_id as i32, timestamp_sent], + ) + .await + .unwrap_or_default(); // TODO: better error handling } // Normal chat? that's quite easy. if chat_type == Chattype::Single { - update_msg_state(context, msg_id, MessageState::OutMdnRcvd); + update_msg_state(context, msg_id, MessageState::OutMdnRcvd).await; read_by_all = true; } else { // send event about new state let ist_cnt = context .sql - .query_get_value::<_, isize>( + .query_get_value::( context, "SELECT COUNT(*) FROM msgs_mdns WHERE msg_id=?;", - params![msg_id], + paramsv![msg_id], ) + .await .unwrap_or_default() as usize; /* Groupsize: Min. MDNs @@ -1306,9 +1348,9 @@ pub fn mdn_from_ext( (S=Sender, R=Recipient) */ // for rounding, SELF is already included! - let soll_cnt = (chat::get_chat_contact_cnt(context, chat_id) + 1) / 2; + let soll_cnt = (chat::get_chat_contact_cnt(context, chat_id).await + 1) / 2; if ist_cnt >= soll_cnt { - update_msg_state(context, msg_id, MessageState::OutMdnRcvd); + update_msg_state(context, msg_id, MessageState::OutMdnRcvd).await; read_by_all = true; } // else wait for more receipts } @@ -1323,14 +1365,18 @@ pub fn mdn_from_ext( } /// The number of messages assigned to real chat (!=deaddrop, !=trash) -pub fn get_real_msg_cnt(context: &Context) -> i32 { - match context.sql.query_row( - "SELECT COUNT(*) \ +pub async fn get_real_msg_cnt(context: &Context) -> i32 { + match context + .sql + .query_row( + "SELECT COUNT(*) \ FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ WHERE m.id>9 AND m.chat_id>9 AND c.blocked=0;", - rusqlite::NO_PARAMS, - |row| row.get(0), - ) { + paramsv![], + |row| row.get(0), + ) + .await + { Ok(res) => res, Err(err) => { error!(context, "dc_get_real_msg_cnt() failed. {}", err); @@ -1339,14 +1385,18 @@ pub fn get_real_msg_cnt(context: &Context) -> i32 { } } -pub fn get_deaddrop_msg_cnt(context: &Context) -> usize { - match context.sql.query_row( - "SELECT COUNT(*) \ +pub async fn get_deaddrop_msg_cnt(context: &Context) -> usize { + match context + .sql + .query_row( + "SELECT COUNT(*) \ FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ WHERE c.blocked=2;", - rusqlite::NO_PARAMS, - |row| row.get::<_, isize>(0), - ) { + paramsv![], + |row| row.get::<_, isize>(0), + ) + .await + { Ok(res) => res as usize, Err(err) => { error!(context, "dc_get_deaddrop_msg_cnt() failed. {}", err); @@ -1355,13 +1405,17 @@ pub fn get_deaddrop_msg_cnt(context: &Context) -> usize { } } -pub fn rfc724_mid_cnt(context: &Context, rfc724_mid: &str) -> i32 { +pub async fn rfc724_mid_cnt(context: &Context, rfc724_mid: &str) -> i32 { // check the number of messages with the same rfc724_mid - match context.sql.query_row( - "SELECT COUNT(*) FROM msgs WHERE rfc724_mid=?;", - &[rfc724_mid], - |row| row.get(0), - ) { + match context + .sql + .query_row( + "SELECT COUNT(*) FROM msgs WHERE rfc724_mid=?;", + paramsv![rfc724_mid], + |row| row.get(0), + ) + .await + { Ok(res) => res, Err(err) => { error!(context, "dc_get_rfc724_mid_cnt() failed. {}", err); @@ -1370,17 +1424,17 @@ pub fn rfc724_mid_cnt(context: &Context, rfc724_mid: &str) -> i32 { } } -pub(crate) fn rfc724_mid_exists( +pub(crate) async fn rfc724_mid_exists( context: &Context, rfc724_mid: &str, ) -> Result<(String, u32, MsgId), Error> { ensure!(!rfc724_mid.is_empty(), "empty rfc724_mid"); - context + let res = context .sql .query_row( "SELECT server_folder, server_uid, id FROM msgs WHERE rfc724_mid=?", - &[rfc724_mid], + paramsv![rfc724_mid], |row| { let server_folder = row.get::<_, Option>(0)?.unwrap_or_default(); let server_uid = row.get(1)?; @@ -1389,19 +1443,25 @@ pub(crate) fn rfc724_mid_exists( Ok((server_folder, server_uid, msg_id)) }, ) - .map_err(Into::into) + .await?; + + Ok(res) } -pub fn update_server_uid( +pub async fn update_server_uid( context: &Context, rfc724_mid: &str, server_folder: impl AsRef, server_uid: u32, ) { - match context.sql.execute( - "UPDATE msgs SET server_folder=?, server_uid=? WHERE rfc724_mid=?;", - params![server_folder.as_ref(), server_uid, rfc724_mid], - ) { + match context + .sql + .execute( + "UPDATE msgs SET server_folder=?, server_uid=? WHERE rfc724_mid=?;", + paramsv![server_folder.as_ref().to_string(), server_uid, rfc724_mid], + ) + .await + { Ok(_) => {} Err(err) => { warn!(context, "msg: failed to update server_uid: {}", err); @@ -1432,7 +1492,7 @@ mod tests { async fn test_prepare_message_and_send() { use crate::config::Config; - let d = test::dummy_context(); + let d = test::dummy_context().await; let ctx = &d.ctx; let contact = Contact::create(ctx, "", "dest@example.com") @@ -1454,9 +1514,9 @@ mod tests { assert_eq!(_msg2.get_filemime(), None); } - #[test] - pub fn test_get_summarytext_by_raw() { - let d = test::dummy_context(); + #[async_std::test] + async fn test_get_summarytext_by_raw() { + let d = test::dummy_context().await; let ctx = &d.ctx; let some_text = Some("bla bla".to_string()); @@ -1467,62 +1527,69 @@ mod tests { some_file.set(Param::File, "foo.bar"); assert_eq!( - get_summarytext_by_raw(Viewtype::Text, some_text.as_ref(), &Params::new(), 50, &ctx), + get_summarytext_by_raw(Viewtype::Text, some_text.as_ref(), &Params::new(), 50, &ctx) + .await, "bla bla" // for simple text, the type is not added to the summary ); assert_eq!( - get_summarytext_by_raw(Viewtype::Image, no_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Image, no_text.as_ref(), &some_file, 50, &ctx).await, "Image" // file names are not added for images ); assert_eq!( - get_summarytext_by_raw(Viewtype::Video, no_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Video, no_text.as_ref(), &some_file, 50, &ctx).await, "Video" // file names are not added for videos ); assert_eq!( - get_summarytext_by_raw(Viewtype::Gif, no_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Gif, no_text.as_ref(), &some_file, 50, &ctx,).await, "GIF" // file names are not added for GIFs ); assert_eq!( - get_summarytext_by_raw(Viewtype::Sticker, no_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Sticker, no_text.as_ref(), &some_file, 50, &ctx,) + .await, "Sticker" // file names are not added for stickers ); assert_eq!( - get_summarytext_by_raw(Viewtype::Voice, empty_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Voice, empty_text.as_ref(), &some_file, 50, &ctx,) + .await, "Voice message" // file names are not added for voice messages, empty text is skipped ); assert_eq!( - get_summarytext_by_raw(Viewtype::Voice, no_text.as_ref(), &mut some_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::Voice, no_text.as_ref(), &mut some_file, 50, &ctx) + .await, "Voice message" // file names are not added for voice messages ); assert_eq!( - get_summarytext_by_raw(Viewtype::Voice, some_text.as_ref(), &some_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::Voice, some_text.as_ref(), &some_file, 50, &ctx).await, "Voice message \u{2013} bla bla" // `\u{2013}` explicitly checks for "EN DASH" ); assert_eq!( - get_summarytext_by_raw(Viewtype::Audio, no_text.as_ref(), &mut some_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::Audio, no_text.as_ref(), &mut some_file, 50, &ctx) + .await, "Audio \u{2013} foo.bar" // file name is added for audio ); assert_eq!( - get_summarytext_by_raw(Viewtype::Audio, empty_text.as_ref(), &some_file, 50, &ctx,), + get_summarytext_by_raw(Viewtype::Audio, empty_text.as_ref(), &some_file, 50, &ctx,) + .await, "Audio \u{2013} foo.bar" // file name is added for audio, empty text is not added ); assert_eq!( - get_summarytext_by_raw(Viewtype::Audio, some_text.as_ref(), &some_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::Audio, some_text.as_ref(), &some_file, 50, &ctx).await, "Audio \u{2013} foo.bar \u{2013} bla bla" // file name and text added for audio ); assert_eq!( - get_summarytext_by_raw(Viewtype::File, some_text.as_ref(), &mut some_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::File, some_text.as_ref(), &mut some_file, 50, &ctx) + .await, "File \u{2013} foo.bar \u{2013} bla bla" // file name is added for files ); @@ -1530,7 +1597,7 @@ mod tests { asm_file.set(Param::File, "foo.bar"); asm_file.set_cmd(SystemMessage::AutocryptSetupMessage); assert_eq!( - get_summarytext_by_raw(Viewtype::File, no_text.as_ref(), &mut asm_file, 50, &ctx), + get_summarytext_by_raw(Viewtype::File, no_text.as_ref(), &mut asm_file, 50, &ctx).await, "Autocrypt Setup Message" // file name is not added for autocrypt setup messages ); } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 9841108c3..b831f7c7b 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -65,44 +65,51 @@ pub struct RenderedEmail { } impl<'a, 'b> MimeFactory<'a, 'b> { - pub fn from_msg( + pub async fn from_msg( context: &'a Context, msg: &'b Message, attach_selfavatar: bool, ) -> Result, Error> { - let chat = Chat::load_from_db(context, msg.chat_id)?; + let chat = Chat::load_from_db(context, msg.chat_id).await?; let from_addr = context .get_config(Config::ConfiguredAddr) + .await + .unwrap_or_default(); + let from_displayname = context + .get_config(Config::Displayname) + .await .unwrap_or_default(); - let from_displayname = context.get_config(Config::Displayname).unwrap_or_default(); let mut recipients = Vec::with_capacity(5); let mut req_mdn = false; if chat.is_self_talk() { recipients.push((from_displayname.to_string(), from_addr.to_string())); } else { - context.sql.query_map( - "SELECT c.authname, c.addr \ + context + .sql + .query_map( + "SELECT c.authname, c.addr \ FROM chats_contacts cc \ LEFT JOIN contacts c ON cc.contact_id=c.id \ WHERE cc.chat_id=? AND cc.contact_id>9;", - params![msg.chat_id], - |row| { - let authname: String = row.get(0)?; - let addr: String = row.get(1)?; - Ok((authname, addr)) - }, - |rows| { - for row in rows { - let (authname, addr) = row?; - if !recipients_contain_addr(&recipients, &addr) { - recipients.push((authname, addr)); + paramsv![msg.chat_id], + |row| { + let authname: String = row.get(0)?; + let addr: String = row.get(1)?; + Ok((authname, addr)) + }, + |rows| { + for row in rows { + let (authname, addr) = row?; + if !recipients_contain_addr(&recipients, &addr) { + recipients.push((authname, addr)); + } } - } - Ok(()) - }, - )?; + Ok(()) + }, + ) + .await?; let command = msg.param.get_cmd(); @@ -112,6 +119,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { let self_addr = context .get_config(Config::ConfiguredAddr) + .await .unwrap_or_default(); if !email_to_remove.is_empty() @@ -123,31 +131,39 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } if command != SystemMessage::AutocryptSetupMessage && command != SystemMessage::SecurejoinMessage - && context.get_config_bool(Config::MdnsEnabled) + && context.get_config_bool(Config::MdnsEnabled).await { req_mdn = true; } } - let (in_reply_to, references) = context.sql.query_row( - "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", - params![msg.id], - |row| { - let in_reply_to: String = row.get(0)?; - let references: String = row.get(1)?; + let (in_reply_to, references) = context + .sql + .query_row( + "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", + paramsv![msg.id], + |row| { + let in_reply_to: String = row.get(0)?; + let references: String = row.get(1)?; - Ok(( - render_rfc724_mid_list(&in_reply_to), - render_rfc724_mid_list(&references), - )) - }, - )?; + Ok(( + render_rfc724_mid_list(&in_reply_to), + render_rfc724_mid_list(&references), + )) + }, + ) + .await?; + let default_str = context + .stock_str(StockMessage::StatusLine) + .await + .to_string(); let factory = MimeFactory { from_addr, from_displayname, selfstatus: context .get_config(Config::Selfstatus) - .unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()), + .await + .unwrap_or_else(|| default_str), recipients, timestamp: msg.timestamp_sort, loaded: Loaded::Message { chat }, @@ -162,29 +178,42 @@ impl<'a, 'b> MimeFactory<'a, 'b> { Ok(factory) } - pub fn from_mdn( + pub async fn from_mdn( context: &'a Context, msg: &'b Message, additional_msg_ids: Vec, - ) -> Result { + ) -> Result, Error> { ensure!(!msg.chat_id.is_special(), "Invalid chat id"); - let contact = Contact::load_from_db(context, msg.from_id)?; + let contact = Contact::load_from_db(context, msg.from_id).await?; + let from_addr = context + .get_config(Config::ConfiguredAddr) + .await + .unwrap_or_default(); + let from_displayname = context + .get_config(Config::Displayname) + .await + .unwrap_or_default(); + let default_str = context + .stock_str(StockMessage::StatusLine) + .await + .to_string(); + let selfstatus = context + .get_config(Config::Selfstatus) + .await + .unwrap_or_else(|| default_str); + let timestamp = dc_create_smeared_timestamp(context).await; - Ok(MimeFactory { + let res = MimeFactory::<'a, 'b> { context, - from_addr: context - .get_config(Config::ConfiguredAddr) - .unwrap_or_default(), - from_displayname: context.get_config(Config::Displayname).unwrap_or_default(), - selfstatus: context - .get_config(Config::Selfstatus) - .unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()), + from_addr, + from_displayname, + selfstatus, recipients: vec![( contact.get_authname().to_string(), contact.get_addr().to_string(), )], - timestamp: dc_create_smeared_timestamp(context), + timestamp, loaded: Loaded::MDN { additional_msg_ids }, msg, in_reply_to: String::default(), @@ -192,7 +221,9 @@ impl<'a, 'b> MimeFactory<'a, 'b> { req_mdn: false, last_added_location_id: 0, attach_selfavatar: false, - }) + }; + + Ok(res) } async fn peerstates_for_recipients(&self) -> Result>, &str)>, Error> { @@ -202,17 +233,19 @@ impl<'a, 'b> MimeFactory<'a, 'b> { .await .ok_or_else(|| format_err!("Not configured"))?; - Ok(self + let mut res = Vec::new(); + for (_, addr) in self .recipients .iter() .filter(|(_, addr)| addr != &self_addr) - .map(|(_, addr)| { - ( - Peerstate::from_addr(self.context, &self.context.sql, addr), - addr.as_str(), - ) - }) - .collect()) + { + res.push(( + Peerstate::from_addr(self.context, &self.context.sql, addr).await, + addr.as_str(), + )); + } + + Ok(res) } fn is_e2ee_guaranteed(&self) -> bool { @@ -272,11 +305,11 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } } - fn should_do_gossip(&self) -> bool { + async fn should_do_gossip(&self) -> bool { match &self.loaded { Loaded::Message { chat } => { // beside key- and member-changes, force re-gossip every 48 hours - let gossiped_timestamp = chat.get_gossiped_timestamp(self.context); + let gossiped_timestamp = chat.get_gossiped_timestamp(self.context).await; if time() > gossiped_timestamp + (2 * 24 * 60 * 60) { return true; } @@ -317,12 +350,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } } - fn subject_str(&self) -> String { + async fn subject_str(&self) -> String { match self.loaded { Loaded::Message { ref chat } => { if self.msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage { self.context .stock_str(StockMessage::AcSetupMsgSubject) + .await .into_owned() } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { let re = if self.in_reply_to.is_empty() { @@ -338,12 +372,17 @@ impl<'a, 'b> MimeFactory<'a, 'b> { &self.msg.param, 32, self.context, - ); + ) + .await; let raw_subject = raw.lines().next().unwrap_or_default(); format!("Chat: {}", raw_subject) } } - Loaded::MDN { .. } => self.context.stock_str(StockMessage::ReadRcpt).into_owned(), + Loaded::MDN { .. } => self + .context + .stock_str(StockMessage::ReadRcpt) + .await + .into_owned(), } } @@ -354,7 +393,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { .collect() } - pub fn render(mut self) -> Result { + pub async fn render(mut self) -> Result { // Headers that are encrypted // - Chat-*, except Chat-Version // - Secure-Join* @@ -433,17 +472,18 @@ impl<'a, 'b> MimeFactory<'a, 'b> { let min_verified = self.min_verified(); let grpimage = self.grpimage(); let force_plaintext = self.should_force_plaintext(); - let subject_str = self.subject_str(); + let subject_str = self.subject_str().await; let e2ee_guaranteed = self.is_e2ee_guaranteed(); - let mut encrypt_helper = EncryptHelper::new(self.context)?; + let mut encrypt_helper = EncryptHelper::new(self.context).await?; let subject = encode_words(&subject_str); let mut message = match self.loaded { Loaded::Message { .. } => { - self.render_message(&mut protected_headers, &mut unprotected_headers, &grpimage)? + self.render_message(&mut protected_headers, &mut unprotected_headers, &grpimage) + .await? } - Loaded::MDN { .. } => self.render_mdn()?, + Loaded::MDN { .. } => self.render_mdn().await?, }; if force_plaintext != ForcePlaintext::NoAutocryptHeader as i32 { @@ -454,7 +494,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { protected_headers.push(Header::new("Subject".into(), subject)); - let peerstates = self.peerstates_for_recipients()?; + let peerstates = self.peerstates_for_recipients().await?; let should_encrypt = encrypt_helper.should_encrypt(self.context, e2ee_guaranteed, &peerstates)?; let is_encrypted = should_encrypt && force_plaintext == 0; @@ -482,7 +522,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { let outer_message = if is_encrypted { // Add gossip headers in chats with multiple recipients - if peerstates.len() > 1 && self.should_do_gossip() { + if peerstates.len() > 1 && self.should_do_gossip().await { for peerstate in peerstates.iter().filter_map(|(state, _)| state.as_ref()) { if peerstate.peek_key(min_verified).is_some() { if let Some(header) = peerstate.render_gossip_header(min_verified) { @@ -530,8 +570,9 @@ impl<'a, 'b> MimeFactory<'a, 'b> { println!("{}", raw_message); } - let encrypted = - encrypt_helper.encrypt(self.context, min_verified, message, &peerstates)?; + let encrypted = encrypt_helper + .encrypt(self.context, min_verified, message, &peerstates) + .await?; outer_message = outer_message .child( @@ -603,9 +644,9 @@ impl<'a, 'b> MimeFactory<'a, 'b> { Some(part) } - fn get_location_kml_part(&mut self) -> Result { + async fn get_location_kml_part(&mut self) -> Result { let (kml_content, last_added_location_id) = - location::get_kml(self.context, self.msg.chat_id)?; + location::get_kml(self.context, self.msg.chat_id).await?; let part = PartBuilder::new() .content_type( &"application/vnd.google-earth.kml+xml" @@ -625,7 +666,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } #[allow(clippy::cognitive_complexity)] - fn render_message( + async fn render_message( &mut self, protected_headers: &mut Vec
, unprotected_headers: &mut Vec
, @@ -720,6 +761,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { placeholdertext = Some( self.context .stock_str(StockMessage::AcSetupMsgBody) + .await .to_string(), ); } @@ -856,8 +898,8 @@ impl<'a, 'b> MimeFactory<'a, 'b> { parts.push(msg_kml_part); } - if location::is_sending_locations_to_chat(context, self.msg.chat_id) { - match self.get_location_kml_part() { + if location::is_sending_locations_to_chat(context, self.msg.chat_id).await { + match self.get_location_kml_part().await { Ok(part) => parts.push(part), Err(err) => { warn!(context, "mimefactory: could not send location: {}", err); @@ -866,7 +908,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } if self.attach_selfavatar { - match context.get_config(Config::Selfavatar) { + match context.get_config(Config::Selfavatar).await { Some(path) => match build_selfavatar_file(context, &path) { Ok((part, filename)) => { parts.push(part); @@ -893,7 +935,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> { } /// Render an MDN - fn render_mdn(&mut self) -> Result { + async fn render_mdn(&mut self) -> Result { // RFC 6522, this also requires the `report-type` parameter which is equal // to the MIME subtype of the second body part of the multipart/report // @@ -928,13 +970,15 @@ impl<'a, 'b> MimeFactory<'a, 'b> { { self.context .stock_str(StockMessage::EncryptedMsg) + .await .into_owned() } else { - self.msg.get_summarytext(self.context, 32) + self.msg.get_summarytext(self.context, 32).await }; let p2 = self .context - .stock_string_repl_str(StockMessage::ReadRcptMailBody, p1); + .stock_string_repl_str(StockMessage::ReadRcptMailBody, p1) + .await; let message_text = format!("{}\r\n", p2); message = message.child( PartBuilder::new() diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 51f20bdef..0836ff6bf 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1,4 +1,6 @@ use std::collections::{HashMap, HashSet}; +use std::future::Future; +use std::pin::Pin; use deltachat_derive::{FromSql, ToSql}; use lettre_email::mime::{self, Mime}; @@ -156,7 +158,7 @@ impl MimeMessage { user_avatar: None, group_avatar: None, }; - parser.parse_mime_recursive(context, &mail)?; + parser.parse_mime_recursive(context, &mail).await?; parser.parse_headers(context)?; Ok(parser) @@ -405,63 +407,69 @@ impl MimeMessage { None } - fn parse_mime_recursive( - &mut self, - context: &Context, - mail: &mailparse::ParsedMail<'_>, - ) -> Result { - if mail.ctype.params.get("protected-headers").is_some() { - if mail.ctype.mimetype == "text/rfc822-headers" { - warn!( + fn parse_mime_recursive<'a>( + &'a mut self, + context: &'a Context, + mail: &'a mailparse::ParsedMail<'a>, + ) -> Pin> + 'a + Send>> { + use futures::future::FutureExt; + + // Boxed future to deal with recursion + async move { + 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"); - } - - enum MimeS { - Multiple, - Single, - Message, - } - - let mimetype = mail.ctype.mimetype.to_lowercase(); - - let m = if mimetype.starts_with("multipart") { - if mail.ctype.params.get("boundary").is_some() { - MimeS::Multiple - } else { - MimeS::Single - } - } else if mimetype.starts_with("message") { - if mimetype == "message/rfc822" { - MimeS::Message - } else { - MimeS::Single - } - } else { - MimeS::Single - }; - - match m { - MimeS::Multiple => self.handle_multiple(context, mail), - MimeS::Message => { - let raw = mail.get_body_raw()?; - if raw.is_empty() { return Ok(false); } - let mail = mailparse::parse_mail(&raw).unwrap(); - self.parse_mime_recursive(context, &mail) + warn!(context, "Ignoring nested protected headers"); + } + + enum MimeS { + Multiple, + Single, + Message, + } + + let mimetype = mail.ctype.mimetype.to_lowercase(); + + let m = if mimetype.starts_with("multipart") { + if mail.ctype.params.get("boundary").is_some() { + MimeS::Multiple + } else { + MimeS::Single + } + } else if mimetype.starts_with("message") { + if mimetype == "message/rfc822" { + MimeS::Message + } else { + MimeS::Single + } + } else { + MimeS::Single + }; + + match m { + MimeS::Multiple => self.handle_multiple(context, mail).await, + MimeS::Message => { + let raw = mail.get_body_raw()?; + if raw.is_empty() { + return Ok(false); + } + let mail = mailparse::parse_mail(&raw).unwrap(); + + self.parse_mime_recursive(context, &mail).await + } + MimeS::Single => self.add_single_part_if_known(context, mail), } - MimeS::Single => self.add_single_part_if_known(context, mail), } + .boxed() } - fn handle_multiple( + async fn handle_multiple( &mut self, context: &Context, mail: &mailparse::ParsedMail<'_>, @@ -476,7 +484,7 @@ impl MimeMessage { (mime::MULTIPART, "alternative") => { for cur_data in &mail.subparts { if get_mime_type(cur_data)?.0 == "multipart/mixed" { - any_part_added = self.parse_mime_recursive(context, cur_data)?; + any_part_added = self.parse_mime_recursive(context, cur_data).await?; break; } } @@ -484,7 +492,7 @@ impl MimeMessage { /* search for text/plain and add this */ for cur_data in &mail.subparts { if get_mime_type(cur_data)?.0.type_() == mime::TEXT { - any_part_added = self.parse_mime_recursive(context, cur_data)?; + any_part_added = self.parse_mime_recursive(context, cur_data).await?; break; } } @@ -492,7 +500,7 @@ impl MimeMessage { if !any_part_added { /* `text/plain` not found - use the first part */ for cur_part in &mail.subparts { - if self.parse_mime_recursive(context, cur_part)? { + if self.parse_mime_recursive(context, cur_part).await? { any_part_added = true; break; } @@ -505,14 +513,14 @@ impl MimeMessage { being the first one, which may not be always true ... however, most times it seems okay. */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(context, first)?; + any_part_added = self.parse_mime_recursive(context, first).await?; } } (mime::MULTIPART, "encrypted") => { // we currently do not try to decrypt non-autocrypt messages // at all. If we see an encrypted part, we set // decrypting_failed. - let msg_body = context.stock_str(StockMessage::CantDecryptMsgBody); + let msg_body = context.stock_str(StockMessage::CantDecryptMsgBody).await; let txt = format!("[{}]", msg_body); let mut part = Part::default(); @@ -535,7 +543,7 @@ impl MimeMessage { https://k9mail.github.io/2016/11/24/OpenPGP-Considerations-Part-I.html for background information why we use encrypted+signed) */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(context, first)?; + any_part_added = self.parse_mime_recursive(context, first).await?; } } (mime::MULTIPART, "report") => { @@ -550,7 +558,7 @@ impl MimeMessage { /* eg. `report-type=delivery-status`; maybe we should show them as a little error icon */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(context, first)?; + any_part_added = self.parse_mime_recursive(context, first).await?; } } } @@ -560,7 +568,7 @@ impl MimeMessage { // Add all parts (in fact, AddSinglePartIfKnown() later check if // the parts are really supported) for cur_data in mail.subparts.iter() { - if self.parse_mime_recursive(context, cur_data)? { + if self.parse_mime_recursive(context, cur_data).await? { any_part_added = true; } } @@ -827,6 +835,7 @@ impl MimeMessage { { if let Some((chat_id, msg_id)) = message::mdn_from_ext(context, from_id, original_message_id, sent_timestamp) + .await { context.call_cb(Event::MsgRead { chat_id, msg_id }); mdn_recognized = true; @@ -876,7 +885,7 @@ async fn update_gossip_peerstates( .unwrap() .contains(&header.addr.to_lowercase()) { - let mut peerstate = Peerstate::from_addr(context, &context.sql, &header.addr); + let mut peerstate = Peerstate::from_addr(context, &context.sql, &header.addr).await; if let Some(ref mut peerstate) = peerstate { peerstate.apply_gossip(header, message_time); peerstate.save_to_db(&context.sql, false).await?; @@ -1103,21 +1112,25 @@ mod tests { } } - #[test] - fn test_dc_mimeparser_crash() { - let context = dummy_context(); + #[async_std::test] + async fn test_dc_mimeparser_crash() { + let context = dummy_context().await; let raw = include_bytes!("../test-data/message/issue_523.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!(mimeparser.get_subject(), None); assert_eq!(mimeparser.parts.len(), 1); } - #[test] - fn test_get_rfc724_mid_exists() { - let context = dummy_context(); + #[async_std::test] + async fn test_get_rfc724_mid_exists() { + let context = dummy_context().await; let raw = include_bytes!("../test-data/message/mail_with_message_id.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( mimeparser.get_rfc724_mid(), @@ -1125,19 +1138,23 @@ mod tests { ); } - #[test] - fn test_get_rfc724_mid_not_exists() { - let context = dummy_context(); + #[async_std::test] + async fn test_get_rfc724_mid_not_exists() { + let context = dummy_context().await; let raw = include_bytes!("../test-data/message/issue_523.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!(mimeparser.get_rfc724_mid(), None); } - #[test] - fn test_get_recipients() { - let context = dummy_context(); + #[async_std::test] + async fn test_get_recipients() { + let context = dummy_context().await; let raw = include_bytes!("../test-data/message/mail_with_cc.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); let recipients = get_recipients(mimeparser.header.iter()); assert!(recipients.contains("abc@bcd.com")); assert!(recipients.contains("def@def.de")); @@ -1182,9 +1199,9 @@ mod tests { ); } - #[test] - fn test_parse_first_addr() { - let context = dummy_context(); + #[async_std::test] + async fn test_parse_first_addr() { + let context = dummy_context().await; let raw = b"From: hello@one.org, world@two.org\n\ Chat-Disposition-Notification-To: wrong\n\ Content-Type: text/plain\n\ @@ -1193,7 +1210,9 @@ mod tests { test1\n\ "; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); let of = mimeparser .parse_first_addr(&context.ctx, HeaderDef::From_) @@ -1205,9 +1224,9 @@ mod tests { assert!(of.is_none()); } - #[test] - fn test_mimeparser_with_context() { - let context = dummy_context(); + #[async_std::test] + async fn test_mimeparser_with_context() { + let context = dummy_context().await; let raw = b"From: hello\n\ Content-Type: multipart/mixed; boundary=\"==break==\";\n\ Subject: outer-subject\n\ @@ -1226,7 +1245,9 @@ mod tests { --==break==--\n\ \n"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); // non-overwritten headers do not bubble up let of = mimeparser.get(HeaderDef::SecureJoinGroup).unwrap(); @@ -1246,31 +1267,31 @@ mod tests { assert_eq!(mimeparser.parts.len(), 1); } - #[test] - fn test_mimeparser_with_avatars() { - let t = dummy_context(); + #[async_std::test] + async fn test_mimeparser_with_avatars() { + let t = dummy_context().await; let raw = include_bytes!("../test-data/message/mail_attach_txt.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap(); assert_eq!(mimeparser.user_avatar, None); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../test-data/message/mail_with_user_avatar.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert!(mimeparser.user_avatar.unwrap().is_change()); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../test-data/message/mail_with_user_avatar_deleted.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert_eq!(mimeparser.user_avatar, Some(AvatarAction::Delete)); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../test-data/message/mail_with_user_and_group_avatars.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert!(mimeparser.user_avatar.unwrap().is_change()); @@ -1280,16 +1301,18 @@ mod tests { let raw = include_bytes!("../test-data/message/mail_with_user_and_group_avatars.eml"); let raw = String::from_utf8_lossy(raw).to_string(); let raw = raw.replace("Chat-User-Avatar:", "Xhat-Xser-Xvatar:"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw.as_bytes()).unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw.as_bytes()) + .await + .unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Image); assert_eq!(mimeparser.user_avatar, None); assert!(mimeparser.group_avatar.unwrap().is_change()); } - #[test] - fn test_mimeparser_message_kml() { - let context = dummy_context(); + #[async_std::test] + async fn test_mimeparser_message_kml() { + let context = dummy_context().await; let raw = b"Chat-Version: 1.0\n\ From: foo \n\ To: bar \n\ @@ -1317,7 +1340,9 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\ --==break==--\n\ ;"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( mimeparser.get_subject(), Some("Location streaming".to_string()) @@ -1330,9 +1355,9 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\ assert_eq!(mimeparser.parts.len(), 1); } - #[test] - fn test_parse_mdn() { - let context = dummy_context(); + #[async_std::test] + async fn test_parse_mdn() { + let context = dummy_context().await; let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ Date: Mon, 10 Jan 2020 00:00:00 +0000\n\ Chat-Version: 1.0\n\ @@ -1364,7 +1389,9 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ --kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( message.get_subject(), Some("Chat: Message opened".to_string()) @@ -1378,9 +1405,9 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ /// /// RFC 6522 specifically allows MDNs to be nested inside /// multipart MIME messages. - #[test] - fn test_parse_multiple_mdns() { - let context = dummy_context(); + #[async_std::test] + async fn test_parse_multiple_mdns() { + let context = dummy_context().await; let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ Date: Mon, 10 Jan 2020 00:00:00 +0000\n\ Chat-Version: 1.0\n\ @@ -1442,7 +1469,9 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ --outer--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( message.get_subject(), Some("Chat: Message opened".to_string()) @@ -1452,9 +1481,9 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ assert_eq!(message.reports.len(), 2); } - #[test] - fn test_parse_mdn_with_additional_message_ids() { - let context = dummy_context(); + #[async_std::test] + async fn test_parse_mdn_with_additional_message_ids() { + let context = dummy_context().await; let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ Date: Mon, 10 Jan 2020 00:00:00 +0000\n\ Chat-Version: 1.0\n\ @@ -1487,7 +1516,9 @@ Additional-Message-IDs: \n\ --kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( message.get_subject(), Some("Chat: Message opened".to_string()) @@ -1502,9 +1533,9 @@ Additional-Message-IDs: \n\ ); } - #[test] - fn test_parse_inline_attachment() { - let context = dummy_context(); + #[async_std::test] + async fn test_parse_inline_attachment() { + let context = dummy_context().await; let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC) From: sender@example.com To: receiver@example.com @@ -1529,7 +1560,9 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg== ------=_Part_25_46172632.1581201680436-- "#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); assert_eq!( message.get_subject(), Some("Mail with inline attachment".to_string()) diff --git a/src/oauth2.rs b/src/oauth2.rs index e7a7c8c53..8eb0fc286 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -48,7 +48,7 @@ struct Response { scope: Option, } -pub fn dc_get_oauth2_url( +pub async fn dc_get_oauth2_url( context: &Context, addr: impl AsRef, redirect_uri: impl AsRef, @@ -61,6 +61,7 @@ pub fn dc_get_oauth2_url( "oauth2_pending_redirect_uri", Some(redirect_uri.as_ref()), ) + .await .is_err() { return None; @@ -76,7 +77,7 @@ pub fn dc_get_oauth2_url( // The following function may block due http-requests; // must not be called from the main thread or by the ui! -pub fn dc_get_oauth2_access_token( +pub async fn dc_get_oauth2_access_token( context: &Context, addr: impl AsRef, code: impl AsRef, @@ -84,11 +85,14 @@ pub fn dc_get_oauth2_access_token( ) -> Option { if let Some(oauth2) = Oauth2::from_address(addr) { let lock = context.oauth2_critical.clone(); - let _l = lock.lock().unwrap(); + let _l = lock.lock().await; // read generated token - if !regenerate && !is_expired(context) { - let access_token = context.sql.get_raw_config(context, "oauth2_access_token"); + if !regenerate && !is_expired(context).await { + let access_token = context + .sql + .get_raw_config(context, "oauth2_access_token") + .await; if access_token.is_some() { // success return access_token; @@ -96,10 +100,14 @@ pub fn dc_get_oauth2_access_token( } // generate new token: build & call auth url - let refresh_token = context.sql.get_raw_config(context, "oauth2_refresh_token"); + let refresh_token = context + .sql + .get_raw_config(context, "oauth2_refresh_token") + .await; let refresh_token_for = context .sql .get_raw_config(context, "oauth2_refresh_token_for") + .await .unwrap_or_else(|| "unset".into()); let (redirect_uri, token_url, update_redirect_uri_on_success) = @@ -109,6 +117,7 @@ pub fn dc_get_oauth2_access_token( context .sql .get_raw_config(context, "oauth2_pending_redirect_uri") + .await .unwrap_or_else(|| "unset".into()), oauth2.init_token, true, @@ -122,6 +131,7 @@ pub fn dc_get_oauth2_access_token( context .sql .get_raw_config(context, "oauth2_redirect_uri") + .await .unwrap_or_else(|| "unset".into()), oauth2.refresh_token, false, @@ -193,10 +203,12 @@ pub fn dc_get_oauth2_access_token( context .sql .set_raw_config(context, "oauth2_refresh_token", Some(token)) + .await .ok(); context .sql .set_raw_config(context, "oauth2_refresh_token_for", Some(code.as_ref())) + .await .ok(); } @@ -206,6 +218,7 @@ pub fn dc_get_oauth2_access_token( context .sql .set_raw_config(context, "oauth2_access_token", Some(token)) + .await .ok(); let expires_in = response .expires_in @@ -215,12 +228,14 @@ pub fn dc_get_oauth2_access_token( context .sql .set_raw_config_int64(context, "oauth2_timestamp_expires", expires_in) + .await .ok(); if update_redirect_uri_on_success { context .sql .set_raw_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref())) + .await .ok(); } } else { @@ -235,7 +250,7 @@ pub fn dc_get_oauth2_access_token( } } -pub fn dc_get_oauth2_addr( +pub async fn dc_get_oauth2_addr( context: &Context, addr: impl AsRef, code: impl AsRef, @@ -244,12 +259,13 @@ pub fn dc_get_oauth2_addr( oauth2.get_userinfo?; if let Some(access_token) = - dc_get_oauth2_access_token(context, addr.as_ref(), code.as_ref(), false) + dc_get_oauth2_access_token(context, addr.as_ref(), code.as_ref(), false).await { let addr_out = oauth2.get_addr(context, access_token); if addr_out.is_none() { // regenerate - if let Some(access_token) = dc_get_oauth2_access_token(context, addr, code, true) { + if let Some(access_token) = dc_get_oauth2_access_token(context, addr, code, true).await + { oauth2.get_addr(context, access_token) } else { None @@ -331,10 +347,11 @@ impl Oauth2 { } } -fn is_expired(context: &Context) -> bool { +async fn is_expired(context: &Context) -> bool { let expire_timestamp = context .sql .get_raw_config_int64(context, "oauth2_timestamp_expires") + .await .unwrap_or_default(); if expire_timestamp <= 0 { @@ -393,32 +410,32 @@ mod tests { assert_eq!(Oauth2::from_address("hello@web.de"), None); } - #[test] - fn test_dc_get_oauth2_addr() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_dc_get_oauth2_addr() { + let ctx = dummy_context().await; let addr = "dignifiedquire@gmail.com"; let code = "fail"; - let res = dc_get_oauth2_addr(&ctx.ctx, addr, code); + let res = dc_get_oauth2_addr(&ctx.ctx, addr, code).await; // this should fail as it is an invalid password assert_eq!(res, None); } - #[test] - fn test_dc_get_oauth2_url() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_dc_get_oauth2_url() { + let ctx = dummy_context().await; let addr = "dignifiedquire@gmail.com"; let redirect_uri = "chat.delta:/com.b44t.messenger"; - let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri); + let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri).await; assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into())); } - #[test] - fn test_dc_get_oauth2_token() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_dc_get_oauth2_token() { + let ctx = dummy_context().await; let addr = "dignifiedquire@gmail.com"; let code = "fail"; - let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, false); + let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, false).await; // this should fail as it is an invalid password assert_eq!(res, None); } diff --git a/src/param.rs b/src/param.rs index 48df7ff82..ccd02549f 100644 --- a/src/param.rs +++ b/src/param.rs @@ -417,9 +417,9 @@ mod tests { assert_eq!(p1.get(Param::Forwarded).unwrap(), "cli%40deltachat.de"); } - #[test] - fn test_params_file_fs_path() { - let t = dummy_context(); + #[async_std::test] + async fn test_params_file_fs_path() { + let t = dummy_context().await; if let ParamsFile::FsPath(p) = ParamsFile::from_param(&t.ctx, "/foo/bar/baz").unwrap() { assert_eq!(p, Path::new("/foo/bar/baz")); } else { @@ -427,9 +427,9 @@ mod tests { } } - #[test] - fn test_params_file_blob() { - let t = dummy_context(); + #[async_std::test] + async fn test_params_file_blob() { + let t = dummy_context().await; if let ParamsFile::Blob(b) = ParamsFile::from_param(&t.ctx, "$BLOBDIR/foo").unwrap() { assert_eq!(b.as_name(), "$BLOBDIR/foo"); } else { @@ -438,9 +438,9 @@ mod tests { } // Tests for Params::get_file(), Params::get_path() and Params::get_blob(). - #[test] - fn test_params_get_fileparam() { - let t = dummy_context(); + #[async_std::test] + async fn test_params_get_fileparam() { + let t = dummy_context().await; let fname = t.dir.path().join("foo"); let mut p = Params::new(); p.set(Param::File, fname.to_str().unwrap()); diff --git a/src/peerstate.rs b/src/peerstate.rs index 52a591dcb..a2cbc6ef1 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -144,12 +144,16 @@ impl<'a> Peerstate<'a> { res } - pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option { + pub async fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option> { let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;"; - Self::from_stmt(context, query, &[addr]) + Self::from_stmt(context, query, paramsv![addr]).await } - pub fn from_fingerprint(context: &'a Context, _sql: &Sql, fingerprint: &str) -> Option { + pub async fn from_fingerprint( + context: &'a Context, + _sql: &Sql, + fingerprint: &str, + ) -> Option> { let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ verified_key, verified_key_fingerprint \ @@ -161,15 +165,16 @@ impl<'a> Peerstate<'a> { Self::from_stmt( context, query, - params![fingerprint, fingerprint, fingerprint], + paramsv![fingerprint, fingerprint, fingerprint], ) + .await } - fn from_stmt

(context: &'a Context, query: &str, params: P) -> Option - where - P: IntoIterator, - P::Item: rusqlite::ToSql, - { + async fn from_stmt( + context: &'a Context, + query: &str, + params: Vec<&dyn crate::ToSql>, + ) -> Option> { context .sql .query_row(query, params, |row| { @@ -227,6 +232,7 @@ impl<'a> Peerstate<'a> { Ok(res) }) + .await .ok() } @@ -413,7 +419,7 @@ impl<'a> Peerstate<'a> { if create { sql.execute( "INSERT INTO acpeerstates (addr) VALUES(?);", - params![self.addr], + paramsv![self.addr], ) .await?; } @@ -425,29 +431,29 @@ impl<'a> Peerstate<'a> { public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, \ verified_key=?, verified_key_fingerprint=? \ WHERE addr=?;", - params![ + paramsv![ self.last_seen, self.last_seen_autocrypt, self.prefer_encrypt as i64, self.public_key.as_ref().map(|k| k.to_bytes()), self.gossip_timestamp, self.gossip_key.as_ref().map(|k| k.to_bytes()), - &self.public_key_fingerprint, - &self.gossip_key_fingerprint, + self.public_key_fingerprint, + self.gossip_key_fingerprint, self.verified_key.as_ref().map(|k| k.to_bytes()), - &self.verified_key_fingerprint, - &self.addr, + self.verified_key_fingerprint, + self.addr, ], ).await?; } else if self.to_save == Some(ToSave::Timestamps) { sql.execute( "UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \ WHERE addr=?;", - params![ + paramsv![ self.last_seen, self.last_seen_autocrypt, self.gossip_timestamp, - &self.addr + self.addr ], ) .await?; @@ -475,9 +481,9 @@ mod tests { use pretty_assertions::assert_eq; use tempfile::TempDir; - #[test] - fn test_peerstate_save_to_db() { - let ctx = crate::test_utils::dummy_context(); + #[async_std::test] + async fn test_peerstate_save_to_db() { + let ctx = crate::test_utils::dummy_context().await; let addr = "hello@mail.com"; let pub_key = crate::key::Key::from(alice_keypair().public); @@ -500,11 +506,12 @@ mod tests { }; assert!( - peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + peerstate.save_to_db(&ctx.ctx.sql, true).await.is_ok(), "failed to save to db" ); let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr) + .await .expect("failed to load peerstate from db"); // clear to_save, as that is not persissted @@ -512,13 +519,14 @@ mod tests { assert_eq!(peerstate, peerstate_new); let peerstate_new2 = Peerstate::from_fingerprint(&ctx.ctx, &ctx.ctx.sql, &pub_key.fingerprint()) + .await .expect("failed to load peerstate from db"); assert_eq!(peerstate, peerstate_new2); } - #[test] - fn test_peerstate_double_create() { - let ctx = crate::test_utils::dummy_context(); + #[async_std::test] + async fn test_peerstate_double_create() { + let ctx = crate::test_utils::dummy_context().await; let addr = "hello@mail.com"; let pub_key = crate::key::Key::from(alice_keypair().public); @@ -540,18 +548,18 @@ mod tests { }; assert!( - peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + peerstate.save_to_db(&ctx.ctx.sql, true).await.is_ok(), "failed to save" ); assert!( - peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + peerstate.save_to_db(&ctx.ctx.sql, true).await.is_ok(), "double-call with create failed" ); } - #[test] - fn test_peerstate_with_empty_gossip_key_save_to_db() { - let ctx = crate::test_utils::dummy_context(); + #[async_std::test] + async fn test_peerstate_with_empty_gossip_key_save_to_db() { + let ctx = crate::test_utils::dummy_context().await; let addr = "hello@mail.com"; let pub_key = crate::key::Key::from(alice_keypair().public); @@ -574,11 +582,12 @@ mod tests { }; assert!( - peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + peerstate.save_to_db(&ctx.ctx.sql, true).await.is_ok(), "failed to save" ); let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr) + .await .expect("failed to load peerstate from db"); // clear to_save, as that is not persissted diff --git a/src/qr.rs b/src/qr.rs index 8c69655df..59d00b8c9 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -50,13 +50,13 @@ pub async fn check_qr(context: &Context, qr: impl AsRef) -> Lot { } else if qr.starts_with(DCACCOUNT_SCHEME) { decode_account(context, qr) } else if qr.starts_with(MAILTO_SCHEME) { - decode_mailto(context, qr) + decode_mailto(context, qr).await } else if qr.starts_with(SMTP_SCHEME) { - decode_smtp(context, qr) + decode_smtp(context, qr).await } else if qr.starts_with(MATMSG_SCHEME) { - decode_matmsg(context, qr) + decode_matmsg(context, qr).await } else if qr.starts_with(VCARD_SCHEME) { - decode_vcard(context, qr) + decode_vcard(context, qr).await } else if qr.starts_with(HTTP_SCHEME) || qr.starts_with(HTTPS_SCHEME) { Lot::from_url(qr) } else { @@ -140,7 +140,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { let mut lot = Lot::new(); // retrieve known state for this fingerprint - let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint); + let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint).await; if invitenumber.is_none() || auth.is_none() { if let Some(peerstate) = peerstate { @@ -160,7 +160,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { .await .unwrap_or_default(); - chat::add_info_msg(context, id, format!("{} verified.", peerstate.addr)); + chat::add_info_msg(context, id, format!("{} verified.", peerstate.addr)).await; } else { lot.state = LotState::QrFprWithoutAddr; lot.text1 = Some(dc_format_fingerprint(&fingerprint)); @@ -262,7 +262,7 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error /// Extract address for the mailto scheme. /// /// Scheme: `mailto:addr...?subject=...&body=..` -fn decode_mailto(context: &Context, qr: &str) -> Lot { +async fn decode_mailto(context: &Context, qr: &str) -> Lot { let payload = &qr[MAILTO_SCHEME.len()..]; let addr = if let Some(query_index) = payload.find('?') { @@ -277,13 +277,13 @@ fn decode_mailto(context: &Context, qr: &str) -> Lot { }; let name = "".to_string(); - Lot::from_address(context, name, addr) + Lot::from_address(context, name, addr).await } /// Extract address for the smtp scheme. /// /// Scheme: `SMTP:addr...:subject...:body...` -fn decode_smtp(context: &Context, qr: &str) -> Lot { +async fn decode_smtp(context: &Context, qr: &str) -> Lot { let payload = &qr[SMTP_SCHEME.len()..]; let addr = if let Some(query_index) = payload.find(':') { @@ -298,7 +298,7 @@ fn decode_smtp(context: &Context, qr: &str) -> Lot { }; let name = "".to_string(); - Lot::from_address(context, name, addr) + Lot::from_address(context, name, addr).await } /// Extract address for the matmsg scheme. @@ -306,7 +306,7 @@ fn decode_smtp(context: &Context, qr: &str) -> Lot { /// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` /// /// There may or may not be linebreaks after the fields. -fn decode_matmsg(context: &Context, qr: &str) -> Lot { +async fn decode_matmsg(context: &Context, qr: &str) -> Lot { // Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. // we ignore this case. let addr = if let Some(to_index) = qr.find("TO:") { @@ -326,7 +326,7 @@ fn decode_matmsg(context: &Context, qr: &str) -> Lot { }; let name = "".to_string(); - Lot::from_address(context, name, addr) + Lot::from_address(context, name, addr).await } lazy_static! { @@ -339,7 +339,7 @@ lazy_static! { /// Extract address for the matmsg scheme. /// /// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;:addr...; -fn decode_vcard(context: &Context, qr: &str) -> Lot { +async fn decode_vcard(context: &Context, qr: &str) -> Lot { let name = VCARD_NAME_RE .captures(qr) .map(|caps| { @@ -359,7 +359,7 @@ fn decode_vcard(context: &Context, qr: &str) -> Lot { return format_err!("Bad e-mail address").into(); }; - Lot::from_address(context, name, addr) + Lot::from_address(context, name, addr).await } impl Lot { @@ -379,10 +379,10 @@ impl Lot { l } - pub fn from_address(context: &Context, name: String, addr: String) -> Self { + pub async fn from_address(context: &Context, name: String, addr: String) -> Self { let mut l = Lot::new(); l.state = LotState::QrAddr; - l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan) { + l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan).await { Ok((id, _)) => id, Err(err) => return err.into(), }; @@ -408,11 +408,11 @@ mod tests { use crate::test_utils::dummy_context; - #[test] - fn test_decode_http() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_http() { + let ctx = dummy_context().await; - let res = check_qr(&ctx.ctx, "http://www.hello.com"); + let res = check_qr(&ctx.ctx, "http://www.hello.com").await; assert_eq!(res.get_state(), LotState::QrUrl); assert_eq!(res.get_id(), 0); @@ -420,11 +420,11 @@ mod tests { assert!(res.get_text2().is_none()); } - #[test] - fn test_decode_https() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_https() { + let ctx = dummy_context().await; - let res = check_qr(&ctx.ctx, "https://www.hello.com"); + let res = check_qr(&ctx.ctx, "https://www.hello.com").await; assert_eq!(res.get_state(), LotState::QrUrl); assert_eq!(res.get_id(), 0); @@ -432,11 +432,11 @@ mod tests { assert!(res.get_text2().is_none()); } - #[test] - fn test_decode_text() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_text() { + let ctx = dummy_context().await; - let res = check_qr(&ctx.ctx, "I am so cool"); + let res = check_qr(&ctx.ctx, "I am so cool").await; assert_eq!(res.get_state(), LotState::QrText); assert_eq!(res.get_id(), 0); @@ -444,124 +444,127 @@ mod tests { assert!(res.get_text2().is_none()); } - #[test] - fn test_decode_vcard() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_vcard() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD" - ); + ).await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAddr); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "stress@test.local"); assert_eq!(contact.get_name(), "First Last"); } - #[test] - fn test_decode_matmsg() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_matmsg() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "MATMSG:TO:\n\nstress@test.local ; \n\nSUB:\n\nSubject here\n\nBODY:\n\nhelloworld\n;;", - ); + ) + .await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAddr); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "stress@test.local"); } - #[test] - fn test_decode_mailto() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_mailto() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "mailto:stress@test.local?subject=hello&body=world", - ); + ) + .await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAddr); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "stress@test.local"); - let res = check_qr(&ctx.ctx, "mailto:no-questionmark@example.org"); + let res = check_qr(&ctx.ctx, "mailto:no-questionmark@example.org").await; assert_eq!(res.get_state(), LotState::QrAddr); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "no-questionmark@example.org"); - let res = check_qr(&ctx.ctx, "mailto:no-addr"); + let res = check_qr(&ctx.ctx, "mailto:no-addr").await; assert_eq!(res.get_state(), LotState::QrError); assert!(res.get_text1().is_some()); } - #[test] - fn test_decode_smtp() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_smtp() { + let ctx = dummy_context().await; - let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld"); + let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld").await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAddr); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "stress@test.local"); } - #[test] - fn test_decode_openpgp_group() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_openpgp_group() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=test%20%3F+test%20%21&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL9cxRL" - ); + ).await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAskVerifyGroup); assert_ne!(res.get_id(), 0); assert_eq!(res.get_text1().unwrap(), "test ? test !"); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "cli@deltachat.de"); } - #[test] - fn test_decode_openpgp_secure_join() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_openpgp_secure_join() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&n=J%C3%B6rn%20P.+P.&i=TbnwJ6lSvD5&s=0ejvbdFSQxB" - ); + ).await; println!("{:?}", res); assert_eq!(res.get_state(), LotState::QrAskVerifyContact); assert_ne!(res.get_id(), 0); - let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap(); + let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).await.unwrap(); assert_eq!(contact.get_addr(), "cli@deltachat.de"); assert_eq!(contact.get_name(), "Jörn P. P."); } - #[test] - fn test_decode_openpgp_without_addr() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_openpgp_without_addr() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "OPENPGP4FPR:1234567890123456789012345678901234567890", - ); + ) + .await; assert_eq!(res.get_state(), LotState::QrFprWithoutAddr); assert_eq!( res.get_text1().unwrap(), @@ -569,31 +572,32 @@ mod tests { ); assert_eq!(res.get_id(), 0); - let res = check_qr(&ctx.ctx, "OPENPGP4FPR:12345678901234567890"); + let res = check_qr(&ctx.ctx, "OPENPGP4FPR:12345678901234567890").await; assert_eq!(res.get_state(), LotState::QrError); assert_eq!(res.get_id(), 0); } - #[test] - fn test_decode_account() { - let ctx = dummy_context(); + #[async_std::test] + async fn test_decode_account() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ); + ) + .await; assert_eq!(res.get_state(), LotState::QrAccount); assert_eq!(res.get_text1().unwrap(), "example.org"); } - #[test] - fn test_decode_account_bad_scheme() { - let ctx = dummy_context(); - + #[async_std::test] + async fn test_decode_account_bad_scheme() { + let ctx = dummy_context().await; let res = check_qr( &ctx.ctx, "DCACCOUNT:http://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ); + ) + .await; assert_eq!(res.get_state(), LotState::QrError); assert!(res.get_text1().is_some()); } diff --git a/src/securejoin.rs b/src/securejoin.rs index feda5657f..6998c4f7b 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -55,7 +55,7 @@ macro_rules! get_qr_attr { $context .bob .read() - .unwrap() + .await .qr_scan .as_ref() .unwrap() @@ -65,7 +65,7 @@ macro_rules! get_qr_attr { }; } -pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option { +pub async fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option { /*======================================================= ==== Alice - the inviter side ==== ==== Step 1 in "Setup verified contact" protocol ==== @@ -73,13 +73,14 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option< let fingerprint: String; - ensure_secret_key_exists(context).ok(); + ensure_secret_key_exists(context).await.ok(); // invitenumber will be used to allow starting the handshake, // auth will be used to verify the fingerprint - let invitenumber = token::lookup_or_new(context, token::Namespace::InviteNumber, group_chat_id); - let auth = token::lookup_or_new(context, token::Namespace::Auth, group_chat_id); - let self_addr = match context.get_config(Config::ConfiguredAddr) { + let invitenumber = + token::lookup_or_new(context, token::Namespace::InviteNumber, group_chat_id).await; + let auth = token::lookup_or_new(context, token::Namespace::Auth, group_chat_id).await; + let self_addr = match context.get_config(Config::ConfiguredAddr).await { Some(addr) => addr, None => { error!(context, "Not configured, cannot generate QR code.",); @@ -87,9 +88,12 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option< } }; - let self_name = context.get_config(Config::Displayname).unwrap_or_default(); + let self_name = context + .get_config(Config::Displayname) + .await + .unwrap_or_default(); - fingerprint = match get_self_fingerprint(context) { + fingerprint = match get_self_fingerprint(context).await { Some(fp) => fp, None => { return None; @@ -103,7 +107,7 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option< let qr = if !group_chat_id.is_unset() { // parameters used: a=g=x=i=s= - if let Ok(chat) = Chat::load_from_db(context, group_chat_id) { + if let Ok(chat) = Chat::load_from_db(context, group_chat_id).await { let group_name = chat.get_name(); let group_name_urlencoded = utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string(); @@ -134,9 +138,9 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option< qr } -fn get_self_fingerprint(context: &Context) -> Option { - if let Some(self_addr) = context.get_config(Config::ConfiguredAddr) { - if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) { +async fn get_self_fingerprint(context: &Context) -> Option { + if let Some(self_addr) = context.get_config(Config::ConfiguredAddr).await { + if let Some(key) = Key::from_self_public(context, self_addr, &context.sql).await { return Some(key.fingerprint()); } } @@ -149,7 +153,7 @@ async fn cleanup( ongoing_allocated: bool, join_vg: bool, ) -> ChatId { - let mut bob = context.bob.write().unwrap(); + let mut bob = context.bob.write().await; bob.expects = 0; let ret_chat_id: ChatId = if bob.status == DC_BOB_SUCCESS { if join_vg { @@ -169,7 +173,7 @@ async fn cleanup( bob.qr_scan = None; if ongoing_allocated { - context.free_ongoing(); + context.free_ongoing().await; } ret_chat_id } @@ -187,7 +191,7 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { info!(context, "Requesting secure-join ...",); ensure_secret_key_exists(context).await.ok(); - if !context.alloc_ongoing() { + if !context.alloc_ongoing().await { return cleanup(&context, contact_chat_id, false, join_vg).await; } let qr_scan = check_qr(context, &qr).await; @@ -203,12 +207,12 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { return cleanup(&context, contact_chat_id, true, join_vg).await; } }; - if context.shall_stop_ongoing() { + if context.shall_stop_ongoing().await { return cleanup(&context, contact_chat_id, true, join_vg).await; } join_vg = qr_scan.get_state() == LotState::QrAskVerifyGroup; { - let mut bob = context.bob.write().unwrap(); + let mut bob = context.bob.write().await; bob.status = 0; bob.qr_scan = Some(qr_scan); } @@ -217,7 +221,7 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { context .bob .read() - .unwrap() + .await .qr_scan .as_ref() .unwrap() @@ -225,13 +229,19 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { .as_ref() .unwrap(), contact_chat_id, - ) { + ) + .await + { // the scanned fingerprint matches Alice's key, // we can proceed to step 4b) directly and save two mails info!(context, "Taking protocol shortcut."); - context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; - joiner_progress!(context, chat_id_2_contact_id(context, contact_chat_id), 400); - let own_fingerprint = get_self_fingerprint(context).unwrap_or_default(); + context.bob.write().await.expects = DC_VC_CONTACT_CONFIRM; + joiner_progress!( + context, + chat_id_2_contact_id(context, contact_chat_id).await, + 400 + ); + let own_fingerprint = get_self_fingerprint(context).await.unwrap_or_default(); // Bob -> Alice send_handshake_msg( @@ -252,7 +262,7 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { ) .await; } else { - context.bob.write().unwrap().expects = DC_VC_AUTH_REQUIRED; + context.bob.write().await.expects = DC_VC_AUTH_REQUIRED; // Bob -> Alice send_handshake_msg( @@ -268,14 +278,14 @@ pub async fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId { if join_vg { // for a group-join, wait until the secure-join is done and the group is created - while !context.shall_stop_ongoing() { + while !context.shall_stop_ongoing().await { std::thread::sleep(std::time::Duration::from_millis(200)); } cleanup(&context, contact_chat_id, true, join_vg).await } else { // for a one-to-one-chat, the chat is already known, return the chat-id, // the verification runs in background - context.free_ongoing(); + context.free_ongoing().await; contact_chat_id } } @@ -321,8 +331,8 @@ async fn send_handshake_msg( .unwrap_or_default(); } -fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 { - let contacts = chat::get_chat_contacts(context, contact_chat_id); +async fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 { + let contacts = chat::get_chat_contacts(context, contact_chat_id).await; if contacts.len() == 1 { contacts[0] } else { @@ -330,16 +340,17 @@ fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 { } } -fn fingerprint_equals_sender( +async fn fingerprint_equals_sender( context: &Context, fingerprint: impl AsRef, contact_chat_id: ChatId, ) -> bool { - let contacts = chat::get_chat_contacts(context, contact_chat_id); + let contacts = chat::get_chat_contacts(context, contact_chat_id).await; if contacts.len() == 1 { - if let Ok(contact) = Contact::load_from_db(context, contacts[0]) { - if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr()) + if let Ok(contact) = Contact::load_from_db(context, contacts[0]).await { + if let Some(peerstate) = + Peerstate::from_addr(context, &context.sql, contact.get_addr()).await { let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref()); if peerstate.public_key_fingerprint.is_some() @@ -420,7 +431,7 @@ pub(crate) async fn handle_securejoin_handshake( match chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).await { Ok((chat_id, blocked)) => { if blocked != Blocked::Not { - chat_id.unblock(context); + chat_id.unblock(context).await; } chat_id } @@ -480,7 +491,7 @@ pub(crate) async fn handle_securejoin_handshake( // verify that Alice's Autocrypt key and fingerprint matches the QR-code let cond = { - let bob = context.bob.read().unwrap(); + let bob = context.bob.read().await; let scan = bob.qr_scan.as_ref(); scan.is_none() || bob.expects != DC_VC_AUTH_REQUIRED @@ -504,25 +515,29 @@ pub(crate) async fn handle_securejoin_handshake( } else { "Not encrypted." }, - ); - context.bob.write().unwrap().status = 0; // secure-join failed - context.stop_ongoing(); + ) + .await; + context.bob.write().await.status = 0; // secure-join failed + context.stop_ongoing().await; return Ok(HandshakeMessage::Ignore); } - if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) { + if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) + .await + { could_not_establish_secure_connection( context, contact_chat_id, "Fingerprint mismatch on joiner-side.", - ); - context.bob.write().unwrap().status = 0; // secure-join failed - context.stop_ongoing(); + ) + .await; + context.bob.write().await.status = 0; // secure-join failed + context.stop_ongoing().await; return Ok(HandshakeMessage::Ignore); } info!(context, "Fingerprint verified.",); - own_fingerprint = get_self_fingerprint(context).unwrap(); + own_fingerprint = get_self_fingerprint(context).await.unwrap(); joiner_progress!(context, contact_id, 400); - context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; + context.bob.write().await.expects = DC_VC_CONTACT_CONFIRM; // Bob -> Alice send_handshake_msg( @@ -555,7 +570,8 @@ pub(crate) async fn handle_securejoin_handshake( context, contact_chat_id, "Fingerprint not provided.", - ); + ) + .await; return Ok(HandshakeMessage::Ignore); } }; @@ -564,15 +580,17 @@ pub(crate) async fn handle_securejoin_handshake( context, contact_chat_id, "Auth not encrypted.", - ); + ) + .await; return Ok(HandshakeMessage::Ignore); } - if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) { + if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id).await { could_not_establish_secure_connection( context, contact_chat_id, "Fingerprint mismatch on inviter-side.", - ); + ) + .await; return Ok(HandshakeMessage::Ignore); } info!(context, "Fingerprint verified.",); @@ -584,25 +602,28 @@ pub(crate) async fn handle_securejoin_handshake( context, contact_chat_id, "Auth not provided.", - ); + ) + .await; return Ok(HandshakeMessage::Ignore); } }; if !token::exists(context, token::Namespace::Auth, &auth_0).await { - could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid."); + could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid.") + .await; return Ok(HandshakeMessage::Ignore); } - if mark_peer_as_verified(context, fingerprint).is_err() { + if mark_peer_as_verified(context, fingerprint).await.is_err() { could_not_establish_secure_connection( context, contact_chat_id, "Fingerprint mismatch on inviter-side.", - ); + ) + .await; return Ok(HandshakeMessage::Ignore); } - Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited); + Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited).await; info!(context, "Auth verified.",); - secure_connection_established(context, contact_chat_id); + secure_connection_established(context, contact_chat_id).await; emit_event!(context, Event::ContactsChanged(Some(contact_id))); inviter_progress!(context, contact_id, 600); if join_vg { @@ -651,12 +672,12 @@ pub(crate) async fn handle_securejoin_handshake( HandshakeMessage::Ignore }; - if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM { + if context.bob.read().await.expects != DC_VC_CONTACT_CONFIRM { info!(context, "Message belongs to a different handshake.",); return Ok(abort_retval); } let cond = { - let bob = context.bob.read().unwrap(); + let bob = context.bob.read().await; let scan = bob.qr_scan.as_ref(); scan.is_none() || (join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup) }; @@ -695,20 +716,25 @@ pub(crate) async fn handle_securejoin_handshake( context, contact_chat_id, "Contact confirm message not encrypted.", - ); - context.bob.write().unwrap().status = 0; + ) + .await; + context.bob.write().await.status = 0; return Ok(abort_retval); } - if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() { + if mark_peer_as_verified(context, &scanned_fingerprint_of_alice) + .await + .is_err() + { could_not_establish_secure_connection( context, contact_chat_id, "Fingerprint mismatch on joiner-side.", - ); + ) + .await; return Ok(abort_retval); } - Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); + Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined).await; emit_event!(context, Event::ContactsChanged(None)); let cg_member_added = mime_message .get(HeaderDef::ChatGroupMemberAdded) @@ -723,8 +749,8 @@ pub(crate) async fn handle_securejoin_handshake( info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."); return Ok(abort_retval); } - secure_connection_established(context, contact_chat_id); - context.bob.write().unwrap().expects = 0; + secure_connection_established(context, contact_chat_id).await; + context.bob.write().await.expects = 0; if join_vg { // Bob -> Alice send_handshake_msg( @@ -737,8 +763,8 @@ pub(crate) async fn handle_securejoin_handshake( ) .await; } - context.bob.write().unwrap().status = 1; - context.stop_ongoing(); + context.bob.write().await.status = 1; + context.stop_ongoing().await; Ok(if join_vg { HandshakeMessage::Propagate } else { @@ -752,7 +778,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) == VerifiedStatus::Unverified { + if contact.is_verified(context).await == VerifiedStatus::Unverified { warn!(context, "vg-member-added-received invalid.",); return Ok(HandshakeMessage::Ignore); } @@ -787,42 +813,49 @@ pub(crate) async fn handle_securejoin_handshake( } } -fn secure_connection_established(context: &Context, contact_chat_id: ChatId) { - let contact_id: u32 = chat_id_2_contact_id(context, contact_chat_id); - let contact = Contact::get_by_id(context, contact_id); +async fn secure_connection_established(context: &Context, contact_chat_id: ChatId) { + let contact_id: u32 = chat_id_2_contact_id(context, contact_chat_id).await; + let contact = Contact::get_by_id(context, contact_id).await; let addr = if let Ok(ref contact) = contact { contact.get_addr() } else { "?" }; - let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr); - chat::add_info_msg(context, contact_chat_id, msg); + let msg = context + .stock_string_repl_str(StockMessage::ContactVerified, addr) + .await; + chat::add_info_msg(context, contact_chat_id, msg).await; emit_event!(context, Event::ChatModified(contact_chat_id)); } -fn could_not_establish_secure_connection( +async fn could_not_establish_secure_connection( context: &Context, contact_chat_id: ChatId, details: &str, ) { - let contact_id = chat_id_2_contact_id(context, contact_chat_id); - let contact = Contact::get_by_id(context, contact_id); - let msg = context.stock_string_repl_str( - StockMessage::ContactNotVerified, - if let Ok(ref contact) = contact { - contact.get_addr() - } else { - "?" - }, - ); + let contact_id = chat_id_2_contact_id(context, contact_chat_id).await; + let contact = Contact::get_by_id(context, contact_id).await; + let msg = context + .stock_string_repl_str( + StockMessage::ContactNotVerified, + if let Ok(ref contact) = contact { + contact.get_addr() + } else { + "?" + }, + ) + .await; - chat::add_info_msg(context, contact_chat_id, &msg); + chat::add_info_msg(context, contact_chat_id, &msg).await; error!(context, "{} ({})", &msg, details); } -fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Result<(), Error> { +async fn mark_peer_as_verified( + context: &Context, + fingerprint: impl AsRef, +) -> Result<(), Error> { if let Some(ref mut peerstate) = - Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref()) + Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref()).await { if peerstate.set_verified( PeerstateKeyType::PublicKey, @@ -833,6 +866,7 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Res peerstate.to_save = Some(ToSave::All); peerstate .save_to_db(&context.sql, false) + .await .unwrap_or_default(); return Ok(()); } @@ -891,7 +925,7 @@ pub async fn handle_degrade_event( .query_get_value( context, "SELECT id FROM contacts WHERE addr=?;", - params![&peerstate.addr], + paramsv![peerstate.addr], ) .await { @@ -908,9 +942,10 @@ pub async fn handle_degrade_event( .unwrap_or_default(); let msg = context - .stock_string_repl_str(StockMessage::ContactSetupChanged, peerstate.addr.clone()); + .stock_string_repl_str(StockMessage::ContactSetupChanged, peerstate.addr.clone()) + .await; - chat::add_info_msg(context, contact_chat_id, msg); + chat::add_info_msg(context, contact_chat_id, msg).await; emit_event!(context, Event::ChatModified(contact_chat_id)); } } diff --git a/src/smtp/mod.rs b/src/smtp/mod.rs index bcb98a25f..0734c717b 100644 --- a/src/smtp/mod.rs +++ b/src/smtp/mod.rs @@ -159,7 +159,7 @@ impl Smtp { // oauth2 let addr = &lp.addr; let send_pw = &lp.send_pw; - let access_token = dc_get_oauth2_access_token(context, addr, send_pw, false); + let access_token = dc_get_oauth2_access_token(context, addr, send_pw, false).await; if access_token.is_none() { return Err(Error::Oauth2Error { address: addr.to_string(), diff --git a/src/sql.rs b/src/sql.rs index f5a08f1d3..30abf1da4 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -7,7 +7,7 @@ use std::collections::HashSet; use std::path::Path; use std::time::Duration; -use rusqlite::{Connection, Error as SqlError, OpenFlags, NO_PARAMS}; +use rusqlite::{Connection, Error as SqlError, OpenFlags}; use thread_local_object::ThreadLocal; use crate::chat::{update_device_icon, update_saved_messages_icon}; @@ -107,34 +107,34 @@ impl Sql { } } - pub async fn execute>(&self, sql: S, params: P) -> Result - where - P: IntoIterator, - P::Item: rusqlite::ToSql, - { + pub async fn execute>( + &self, + sql: S, + params: Vec<&dyn crate::ToSql>, + ) -> Result { self.start_stmt(sql.as_ref()); let res = { let conn = self.get_conn().await?; - conn.execute(sql.as_ref(), params)? + let res = conn.execute(sql.as_ref(), params); + self.in_use.remove(); + res }; - Ok(res) + res.map_err(Into::into) } /// Prepares and executes the statement and maps a function over the resulting rows. /// Then executes the second function over the returned iterator and returns the /// result of that function. - pub async fn query_map( + pub async fn query_map( &self, sql: impl AsRef, - params: P, + params: Vec<&dyn crate::ToSql>, f: F, mut g: G, ) -> Result where - P: IntoIterator, - P::Item: rusqlite::ToSql, F: FnMut(&rusqlite::Row) -> rusqlite::Result, G: FnMut(rusqlite::MappedRows) -> Result, { @@ -144,41 +144,12 @@ impl Sql { let res = { let conn = self.get_conn().await?; let mut stmt = conn.prepare(sql)?; - let res = stmt.query_map(params, f)?; - g(res)? + let res = stmt.query_map(¶ms, f)?; + self.in_use.remove(); + g(res) }; - Ok(res) - } - - /// Prepares and executes the statement and maps a function over the resulting rows. - /// Then executes the second function over the returned iterator and returns the - /// result of that function. - pub async fn query_map_async<'a, T, P, F, G, H, Fut>( - &self, - sql: impl AsRef, - params: P, - f: F, - g: G, - ) -> Result - where - P: IntoIterator, - P::Item: rusqlite::ToSql, - F: FnMut(&rusqlite::Row) -> rusqlite::Result, - G: FnMut(rusqlite::MappedRows<'a, F>) -> Fut, - Fut: Future> + 'a, - { - self.start_stmt(sql.as_ref().to_string()); - unimplemented!() - // let sql = sql.as_ref(); - // self.with_conn_async(|conn| async move { - // let mut stmt = conn.prepare(sql)?; - // { - // let res = stmt.query_map(params, f)?; - // g(res).await - // } - // }) - // .await + res } pub async fn get_conn( @@ -226,36 +197,39 @@ impl Sql { /// Return `true` if a query in the SQL statement it executes returns one or more /// rows and false if the SQL returns an empty set. - pub async fn exists

(&self, sql: &str, params: P) -> Result - where - P: IntoIterator, - P::Item: rusqlite::ToSql, - { + pub async fn exists(&self, sql: &str, params: Vec<&dyn crate::ToSql>) -> Result { self.start_stmt(sql.to_string()); let res = { let conn = self.get_conn().await?; let mut stmt = conn.prepare(sql)?; - stmt.exists(params)? + let res = stmt.exists(¶ms); + self.in_use.remove(); + res }; - Ok(res) + res.map_err(Into::into) } /// Execute a query which is expected to return one row. - pub async fn query_row(&self, sql: impl AsRef, params: P, f: F) -> Result + pub async fn query_row( + &self, + sql: impl AsRef, + params: Vec<&dyn crate::ToSql>, + f: F, + ) -> Result where - P: IntoIterator + Copy, - P::Item: rusqlite::ToSql, F: FnOnce(&rusqlite::Row) -> rusqlite::Result, { self.start_stmt(sql.as_ref().to_string()); let sql = sql.as_ref(); let res = { let conn = self.get_conn().await?; - conn.query_row(sql, params, f)? + let res = conn.query_row(sql, params, f); + self.in_use.remove(); + res }; - Ok(res) + res.map_err(Into::into) } pub async fn table_exists(&self, name: impl AsRef) -> Result { @@ -276,10 +250,12 @@ impl Sql { /// Executes a query which is expected to return one row and one /// column. If the query does not return a value or returns SQL /// `NULL`, returns `Ok(None)`. - pub async fn query_get_value_result(&self, query: &str, params: P) -> Result> + pub async fn query_get_value_result( + &self, + query: &str, + params: Vec<&dyn crate::ToSql>, + ) -> Result> where - P: IntoIterator + Copy, - P::Item: rusqlite::ToSql, T: rusqlite::types::FromSql, { match self @@ -299,15 +275,13 @@ impl Sql { /// Not resultified version of `query_get_value_result`. Returns /// `None` on error. - pub async fn query_get_value( + pub async fn query_get_value( &self, context: &Context, query: &str, - params: P, + params: Vec<&dyn crate::ToSql>, ) -> Option where - P: IntoIterator + Copy, - P::Item: rusqlite::ToSql, T: rusqlite::types::FromSql, { match self.query_get_value_result(query, params).await { @@ -337,23 +311,23 @@ impl Sql { let key = key.as_ref(); let res = if let Some(ref value) = value { let exists = self - .exists("SELECT value FROM config WHERE keyname=?;", params![key]) + .exists("SELECT value FROM config WHERE keyname=?;", paramsv![key]) .await?; if exists { self.execute( "UPDATE config SET value=? WHERE keyname=?;", - params![value, key], + paramsv![value.to_string(), key.to_string()], ) .await } else { self.execute( "INSERT INTO config (keyname, value) VALUES (?, ?);", - params![key, value], + paramsv![key.to_string(), value.to_string()], ) .await } } else { - self.execute("DELETE FROM config WHERE keyname=?;", params![key]) + self.execute("DELETE FROM config WHERE keyname=?;", paramsv![key]) .await }; @@ -374,7 +348,7 @@ impl Sql { self.query_get_value( context, "SELECT value FROM config WHERE keyname=?;", - params![key.as_ref()], + paramsv![key.as_ref().to_string()], ) .await } @@ -455,18 +429,14 @@ impl Sql { ) -> Result { self.start_stmt("get rowid".to_string()); - let query = format!( - "SELECT id FROM {} WHERE {}=? ORDER BY id DESC", - table.as_ref(), - field.as_ref(), - ); - let res = { let mut conn = self.get_conn().await?; - get_rowid(context, &mut conn, table, field, value)? + let res = get_rowid(context, &mut conn, table, field, value); + self.in_use.remove(); + res }; - Ok(res) + res.map_err(Into::into) } pub async fn get_rowid2( @@ -482,15 +452,17 @@ impl Sql { let res = { let mut conn = self.get_conn().await?; - get_rowid2(context, &mut conn, table, field, value, field2, value2)? + let res = get_rowid2(context, &mut conn, table, field, value, field2, value2); + self.in_use.remove(); + res }; - Ok(res) + res.map_err(Into::into) } } pub fn get_rowid( - context: &Context, + _context: &Context, conn: &mut Connection, table: impl AsRef, field: impl AsRef, @@ -509,7 +481,7 @@ pub fn get_rowid( } pub fn get_rowid2( - context: &Context, + _context: &Context, conn: &mut Connection, table: impl AsRef, field: impl AsRef, @@ -526,7 +498,7 @@ pub fn get_rowid2( field2.as_ref(), value2, ), - NO_PARAMS, + params![], |row| row.get::<_, u32>(0), ) } @@ -569,7 +541,7 @@ pub async fn housekeeping(context: &Context) { .sql .query_map( "SELECT value FROM config;", - params![], + paramsv![], |row| row.get::<_, String>(0), |rows| { for row in rows { @@ -683,7 +655,7 @@ async fn maybe_add_from_param( .sql .query_map( query, - NO_PARAMS, + paramsv![], |row| row.get::<_, String>(0), |rows| { for row in rows { @@ -762,11 +734,14 @@ async fn open( ); sql.execute( "CREATE TABLE config (id INTEGER PRIMARY KEY, keyname TEXT, value TEXT);", - NO_PARAMS, + paramsv![], + ) + .await?; + sql.execute( + "CREATE INDEX config_index1 ON config (keyname);", + paramsv![], ) .await?; - sql.execute("CREATE INDEX config_index1 ON config (keyname);", NO_PARAMS) - .await?; sql.execute( "CREATE TABLE contacts (\ id INTEGER PRIMARY KEY AUTOINCREMENT, \ @@ -776,17 +751,17 @@ async fn open( blocked INTEGER DEFAULT 0, \ last_seen INTEGER DEFAULT 0, \ param TEXT DEFAULT '');", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX contacts_index1 ON contacts (name COLLATE NOCASE);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX contacts_index2 ON contacts (addr COLLATE NOCASE);", - params![], + paramsv![], ) .await?; sql.execute( @@ -794,7 +769,7 @@ async fn open( (1,'self',262144), (2,'info',262144), (3,'rsvd',262144), \ (4,'rsvd',262144), (5,'device',262144), (6,'rsvd',262144), \ (7,'rsvd',262144), (8,'rsvd',262144), (9,'rsvd',262144);", - params![], + paramsv![], ) .await?; sql.execute( @@ -807,19 +782,19 @@ async fn open( blocked INTEGER DEFAULT 0, \ grpid TEXT DEFAULT '', \ param TEXT DEFAULT '');", - params![], + paramsv![], ) .await?; - sql.execute("CREATE INDEX chats_index1 ON chats (grpid);", params![]) + sql.execute("CREATE INDEX chats_index1 ON chats (grpid);", paramsv![]) .await?; sql.execute( "CREATE TABLE chats_contacts (chat_id INTEGER, contact_id INTEGER);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);", - params![], + paramsv![], ) .await?; sql.execute( @@ -827,7 +802,7 @@ async fn open( (1,120,'deaddrop'), (2,120,'rsvd'), (3,120,'trash'), \ (4,120,'msgs_in_creation'), (5,120,'starred'), (6,120,'archivedlink'), \ (7,100,'rsvd'), (8,100,'rsvd'), (9,100,'rsvd');", - params![], + paramsv![], ) .await?; sql.execute( @@ -847,23 +822,23 @@ async fn open( txt TEXT DEFAULT '', \ txt_raw TEXT DEFAULT '', \ param TEXT DEFAULT '');", - params![], + paramsv![], ) .await?; - sql.execute("CREATE INDEX msgs_index1 ON msgs (rfc724_mid);", params![]) + sql.execute("CREATE INDEX msgs_index1 ON msgs (rfc724_mid);", paramsv![]) .await?; - sql.execute("CREATE INDEX msgs_index2 ON msgs (chat_id);", params![]) + sql.execute("CREATE INDEX msgs_index2 ON msgs (chat_id);", paramsv![]) .await?; - sql.execute("CREATE INDEX msgs_index3 ON msgs (timestamp);", params![]) + sql.execute("CREATE INDEX msgs_index3 ON msgs (timestamp);", paramsv![]) .await?; - sql.execute("CREATE INDEX msgs_index4 ON msgs (state);", params![]) + sql.execute("CREATE INDEX msgs_index4 ON msgs (state);", paramsv![]) .await?; sql.execute( "INSERT INTO msgs (id,msgrmsg,txt) VALUES \ (1,0,'marker1'), (2,0,'rsvd'), (3,0,'rsvd'), \ (4,0,'rsvd'), (5,0,'rsvd'), (6,0,'rsvd'), (7,0,'rsvd'), \ (8,0,'rsvd'), (9,0,'daymarker');", - params![], + paramsv![], ) .await?; sql.execute( @@ -874,12 +849,12 @@ async fn open( action INTEGER, \ foreign_id INTEGER, \ param TEXT DEFAULT '');", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX jobs_index1 ON jobs (desired_timestamp);", - params![], + paramsv![], ) .await?; if !sql.table_exists("config").await? @@ -920,12 +895,12 @@ async fn open( info!(context, "[migration] v1"); sql.execute( "CREATE TABLE leftgrps ( id INTEGER PRIMARY KEY, grpid TEXT DEFAULT '');", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX leftgrps_index1 ON leftgrps (grpid);", - params![], + paramsv![], ) .await?; dbversion = 1; @@ -935,7 +910,7 @@ async fn open( info!(context, "[migration] v2"); sql.execute( "ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';", - params![], + paramsv![], ) .await?; dbversion = 2; @@ -951,7 +926,7 @@ async fn open( private_key, \ public_key, \ created INTEGER DEFAULT 0);", - params![], + paramsv![], ) .await?; dbversion = 7; @@ -967,12 +942,12 @@ async fn open( last_seen_autocrypt INTEGER DEFAULT 0, \ public_key, \ prefer_encrypted INTEGER DEFAULT 0);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);", - params![], + paramsv![], ) .await?; dbversion = 10; @@ -982,12 +957,12 @@ async fn open( info!(context, "[migration] v12"); sql.execute( "CREATE TABLE msgs_mdns ( msg_id INTEGER, contact_id INTEGER);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);", - params![], + paramsv![], ) .await?; dbversion = 12; @@ -997,17 +972,17 @@ async fn open( info!(context, "[migration] v17"); sql.execute( "ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; - sql.execute("CREATE INDEX chats_index2 ON chats (archived);", params![]) + sql.execute("CREATE INDEX chats_index2 ON chats (archived);", paramsv![]) .await?; sql.execute( "ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; - sql.execute("CREATE INDEX msgs_index5 ON msgs (starred);", params![]) + sql.execute("CREATE INDEX msgs_index5 ON msgs (starred);", paramsv![]) .await?; dbversion = 17; sql.set_raw_config_int(context, "dbversion", 17).await?; @@ -1016,11 +991,14 @@ async fn open( info!(context, "[migration] v18"); sql.execute( "ALTER TABLE acpeerstates ADD COLUMN gossip_timestamp INTEGER DEFAULT 0;", - params![], + paramsv![], + ) + .await?; + sql.execute( + "ALTER TABLE acpeerstates ADD COLUMN gossip_key;", + paramsv![], ) .await?; - sql.execute("ALTER TABLE acpeerstates ADD COLUMN gossip_key;", params![]) - .await?; dbversion = 18; sql.set_raw_config_int(context, "dbversion", 18).await?; } @@ -1028,21 +1006,21 @@ async fn open( info!(context, "[migration] v27"); // chat.id=1 and chat.id=2 are the old deaddrops, // the current ones are defined by chats.blocked=2 - sql.execute("DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;", params![]) + sql.execute("DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;", paramsv![]) .await?; sql.execute( "CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE msgs ADD COLUMN timestamp_sent INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE msgs ADD COLUMN timestamp_rcvd INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; dbversion = 27; @@ -1052,32 +1030,32 @@ async fn open( info!(context, "[migration] v34"); sql.execute( "ALTER TABLE msgs ADD COLUMN hidden INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE msgs_mdns ADD COLUMN timestamp_sent INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE acpeerstates ADD COLUMN public_key_fingerprint TEXT DEFAULT '';", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE acpeerstates ADD COLUMN gossip_key_fingerprint TEXT DEFAULT '';", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX acpeerstates_index3 ON acpeerstates (public_key_fingerprint);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX acpeerstates_index4 ON acpeerstates (gossip_key_fingerprint);", - params![], + paramsv![], ) .await?; recalc_fingerprints = true; @@ -1088,21 +1066,21 @@ async fn open( info!(context, "[migration] v39"); sql.execute( "CREATE TABLE tokens ( id INTEGER PRIMARY KEY, namespc INTEGER DEFAULT 0, foreign_id INTEGER DEFAULT 0, token TEXT DEFAULT '', timestamp INTEGER DEFAULT 0);", - params![] + paramsv![] ).await?; sql.execute( "ALTER TABLE acpeerstates ADD COLUMN verified_key;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE acpeerstates ADD COLUMN verified_key_fingerprint TEXT DEFAULT '';", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX acpeerstates_index5 ON acpeerstates (verified_key_fingerprint);", - params![], + paramsv![], ) .await?; dbversion = 39; @@ -1112,7 +1090,7 @@ async fn open( info!(context, "[migration] v40"); sql.execute( "ALTER TABLE jobs ADD COLUMN thread INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; dbversion = 40; @@ -1120,7 +1098,7 @@ async fn open( } if dbversion < 44 { info!(context, "[migration] v44"); - sql.execute("ALTER TABLE msgs ADD COLUMN mime_headers TEXT;", params![]) + sql.execute("ALTER TABLE msgs ADD COLUMN mime_headers TEXT;", paramsv![]) .await?; dbversion = 44; sql.set_raw_config_int(context, "dbversion", 44).await?; @@ -1129,12 +1107,12 @@ async fn open( info!(context, "[migration] v46"); sql.execute( "ALTER TABLE msgs ADD COLUMN mime_in_reply_to TEXT;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE msgs ADD COLUMN mime_references TEXT;", - params![], + paramsv![], ) .await?; dbversion = 46; @@ -1144,7 +1122,7 @@ async fn open( info!(context, "[migration] v47"); sql.execute( "ALTER TABLE jobs ADD COLUMN tries INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; dbversion = 47; @@ -1155,7 +1133,7 @@ async fn open( // NOTE: move_state is not used anymore sql.execute( "ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;", - params![], + paramsv![], ) .await?; @@ -1166,7 +1144,7 @@ async fn open( info!(context, "[migration] v49"); sql.execute( "ALTER TABLE chats ADD COLUMN gossiped_timestamp INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; dbversion = 49; @@ -1190,36 +1168,36 @@ async fn open( // are also added to the database as _hidden_. sql.execute( "CREATE TABLE locations ( id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL DEFAULT 0.0, longitude REAL DEFAULT 0.0, accuracy REAL DEFAULT 0.0, timestamp INTEGER DEFAULT 0, chat_id INTEGER DEFAULT 0, from_id INTEGER DEFAULT 0);", - params![] + paramsv![] ).await?; sql.execute( "CREATE INDEX locations_index1 ON locations (from_id);", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX locations_index2 ON locations (timestamp);", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE chats ADD COLUMN locations_send_begin INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE chats ADD COLUMN locations_send_until INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "ALTER TABLE chats ADD COLUMN locations_last_sent INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.execute( "CREATE INDEX chats_index3 ON chats (locations_send_until);", - params![], + paramsv![], ) .await?; dbversion = 53; @@ -1229,11 +1207,14 @@ async fn open( info!(context, "[migration] v54"); sql.execute( "ALTER TABLE msgs ADD COLUMN location_id INTEGER DEFAULT 0;", - params![], + paramsv![], + ) + .await?; + sql.execute( + "CREATE INDEX msgs_index6 ON msgs (location_id);", + paramsv![], ) .await?; - sql.execute("CREATE INDEX msgs_index6 ON msgs (location_id);", params![]) - .await?; dbversion = 54; sql.set_raw_config_int(context, "dbversion", 54).await?; } @@ -1241,7 +1222,7 @@ async fn open( info!(context, "[migration] v55"); sql.execute( "ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;", - params![], + paramsv![], ) .await?; sql.set_raw_config_int(context, "dbversion", 55).await?; @@ -1252,11 +1233,11 @@ async fn open( // so, msg_id may or may not exist. sql.execute( "CREATE TABLE devmsglabels (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT, msg_id INTEGER DEFAULT 0);", - NO_PARAMS, + paramsv![], ).await?; sql.execute( "CREATE INDEX devmsglabels_index1 ON devmsglabels (label);", - NO_PARAMS, + paramsv![], ) .await?; if exists_before_update && sql.get_raw_config_int(context, "bcc_self").await.is_none() { @@ -1268,7 +1249,7 @@ async fn open( info!(context, "[migration] v60"); sql.execute( "ALTER TABLE chats ADD COLUMN created_timestamp INTEGER DEFAULT 0;", - NO_PARAMS, + paramsv![], ) .await?; sql.set_raw_config_int(context, "dbversion", 60).await?; @@ -1277,7 +1258,7 @@ async fn open( info!(context, "[migration] v61"); sql.execute( "ALTER TABLE contacts ADD COLUMN selfavatar_sent INTEGER DEFAULT 0;", - NO_PARAMS, + paramsv![], ) .await?; update_icons = true; @@ -1287,14 +1268,14 @@ async fn open( info!(context, "[migration] v62"); sql.execute( "ALTER TABLE chats ADD COLUMN muted_until INTEGER DEFAULT 0;", - NO_PARAMS, + paramsv![], ) .await?; sql.set_raw_config_int(context, "dbversion", 62).await?; } if dbversion < 63 { info!(context, "[migration] v63"); - sql.execute("UPDATE chats SET grpid='' WHERE type=100", NO_PARAMS) + sql.execute("UPDATE chats SET grpid='' WHERE type=100", paramsv![]) .await?; sql.set_raw_config_int(context, "dbversion", 63).await?; } @@ -1305,22 +1286,24 @@ async fn open( if recalc_fingerprints { info!(context, "[migration] recalc fingerprints"); - sql.query_map_async( - "SELECT addr FROM acpeerstates;", - params![], - |row| row.get::<_, String>(0), - |addrs| async move { - for addr in addrs { - if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, &addr?) - { - peerstate.recalc_fingerprint(); - peerstate.save_to_db(sql, false).await?; - } - } - Ok(()) - }, - ) - .await?; + let addrs = sql + .query_map( + "SELECT addr FROM acpeerstates;", + paramsv![], + |row| row.get::<_, String>(0), + |addrs| { + addrs + .collect::, _>>() + .map_err(Into::into) + }, + ) + .await?; + for addr in &addrs { + if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, addr).await { + peerstate.recalc_fingerprint(); + peerstate.save_to_db(sql, false).await?; + } + } } if update_icons { update_saved_messages_icon(context).await?; diff --git a/src/stock.rs b/src/stock.rs index 8ce11cc33..0e30fc794 100644 --- a/src/stock.rs +++ b/src/stock.rs @@ -206,7 +206,7 @@ impl StockMessage { impl Context { /// Set the stock string for the [StockMessage]. /// - pub fn set_stock_translation( + pub async fn set_stock_translation( &self, id: StockMessage, stockstring: String, @@ -227,7 +227,7 @@ impl Context { } self.translated_stockstrings .write() - .unwrap() + .await .insert(id as usize, stockstring); Ok(()) } @@ -236,11 +236,11 @@ impl Context { /// /// Return a translation (if it was set with set_stock_translation before) /// or a default (English) string. - pub fn stock_str(&self, id: StockMessage) -> Cow { + pub async fn stock_str(&self, id: StockMessage) -> Cow<'_, str> { match self .translated_stockstrings .read() - .unwrap() + .await .get(&(id as usize)) { Some(ref x) => Cow::Owned((*x).to_string()), @@ -253,8 +253,9 @@ impl Context { /// This replaces both the *first* `%1$s`, `%1$d` and `%1$@` /// placeholders with the provided string. /// (the `%1$@` variant is used on iOS, the other are used on Android and Desktop) - pub fn stock_string_repl_str(&self, id: StockMessage, insert: impl AsRef) -> String { + pub async fn stock_string_repl_str(&self, id: StockMessage, insert: impl AsRef) -> String { self.stock_str(id) + .await .replacen("%1$s", insert.as_ref(), 1) .replacen("%1$d", insert.as_ref(), 1) .replacen("%1$@", insert.as_ref(), 1) @@ -264,8 +265,9 @@ impl Context { /// /// Like [Context::stock_string_repl_str] but substitute the placeholders /// with an integer. - pub fn stock_string_repl_int(&self, id: StockMessage, insert: i32) -> String { + pub async fn stock_string_repl_int(&self, id: StockMessage, insert: i32) -> String { self.stock_string_repl_str(id, format!("{}", insert).as_str()) + .await } /// Return stock string, replacing 2 placeholders with provided string. @@ -274,13 +276,14 @@ impl Context { /// placeholders with the string in `insert` and does the same for /// `%2$s`, `%2$d` and `%2$@` for `insert2`. /// (the `%1$@` variant is used on iOS, the other are used on Android and Desktop) - pub fn stock_string_repl_str2( + pub async fn stock_string_repl_str2( &self, id: StockMessage, insert: impl AsRef, insert2: impl AsRef, ) -> String { self.stock_str(id) + .await .replacen("%1$s", insert.as_ref(), 1) .replacen("%1$d", insert.as_ref(), 1) .replacen("%1$@", insert.as_ref(), 1) @@ -306,7 +309,7 @@ impl Context { /// used as the second parameter to [StockMessage::MsgActionByUser] with /// again the original stock string being used as the first parameter, /// resulting in a string like "Member Alice added by Bob.". - pub fn stock_system_msg( + pub async fn stock_system_msg( &self, id: StockMessage, param1: impl AsRef, @@ -314,9 +317,10 @@ impl Context { from_id: u32, ) -> String { let insert1 = if id == StockMessage::MsgAddMember || id == StockMessage::MsgDelMember { - let contact_id = Contact::lookup_id_by_addr(self, param1.as_ref()); + let contact_id = Contact::lookup_id_by_addr(self, param1.as_ref()).await; if contact_id != 0 { Contact::get_by_id(self, contact_id) + .await .map(|contact| contact.get_name_n_addr()) .unwrap_or_default() } else { @@ -326,52 +330,60 @@ impl Context { param1.as_ref().to_string() }; - let action = self.stock_string_repl_str2(id, insert1, param2.as_ref().to_string()); + let action = self + .stock_string_repl_str2(id, insert1, param2.as_ref().to_string()) + .await; let action1 = action.trim_end_matches('.'); match from_id { 0 => action, - 1 => self.stock_string_repl_str(StockMessage::MsgActionByMe, action1), // DC_CONTACT_ID_SELF + 1 => { + self.stock_string_repl_str(StockMessage::MsgActionByMe, action1) + .await + } // DC_CONTACT_ID_SELF _ => { let displayname = Contact::get_by_id(self, from_id) + .await .map(|contact| contact.get_name_n_addr()) .unwrap_or_default(); self.stock_string_repl_str2(StockMessage::MsgActionByUser, action1, &displayname) + .await } } } - pub fn update_device_chats(&self) -> Result<(), Error> { + pub async fn update_device_chats(&self) -> Result<(), Error> { // check for the LAST added device message - if it is present, we can skip message creation. // this is worthwhile as this function is typically called // by the ui on every probram start or even on every opening of the chatlist. - if chat::was_device_msg_ever_added(&self, "core-welcome")? { + if chat::was_device_msg_ever_added(&self, "core-welcome").await? { return Ok(()); } // create saved-messages chat; // we do this only once, if the user has deleted the chat, he can recreate it manually. - if !self.sql.get_raw_config_bool(&self, "self-chat-added") { + if !self.sql.get_raw_config_bool(&self, "self-chat-added").await { self.sql - .set_raw_config_bool(&self, "self-chat-added", true)?; - chat::create_by_contact_id(&self, DC_CONTACT_ID_SELF)?; + .set_raw_config_bool(&self, "self-chat-added", true) + .await?; + chat::create_by_contact_id(&self, DC_CONTACT_ID_SELF).await?; } // add welcome-messages. by the label, this is done only once, // if the user has deleted the message or the chat, it is not added again. let mut msg = Message::new(Viewtype::Text); - msg.text = Some(self.stock_str(DeviceMessagesHint).to_string()); - chat::add_device_msg(&self, Some("core-about-device-chat"), Some(&mut msg))?; + msg.text = Some(self.stock_str(DeviceMessagesHint).await.to_string()); + chat::add_device_msg(&self, Some("core-about-device-chat"), Some(&mut msg)).await?; let image = include_bytes!("../assets/welcome-image.jpg"); let blob = BlobObject::create(&self, "welcome-image.jpg".to_string(), image)?; let mut msg = Message::new(Viewtype::Image); msg.param.set(Param::File, blob.as_name()); - chat::add_device_msg(&self, Some("core-welcome-image"), Some(&mut msg))?; + chat::add_device_msg(&self, Some("core-welcome-image"), Some(&mut msg)).await?; let mut msg = Message::new(Viewtype::Text); - msg.text = Some(self.stock_str(WelcomeMessage).to_string()); - chat::add_device_msg(&self, Some("core-welcome"), Some(&mut msg))?; + msg.text = Some(self.stock_str(WelcomeMessage).await.to_string()); + chat::add_device_msg(&self, Some("core-welcome"), Some(&mut msg)).await?; Ok(()) } } @@ -397,153 +409,178 @@ mod tests { assert_eq!(StockMessage::NoMessages.fallback(), "No messages."); } - #[test] - fn test_set_stock_translation() { - let t = dummy_context(); + #[async_std::test] + async fn test_set_stock_translation() { + let t = dummy_context().await; t.ctx .set_stock_translation(StockMessage::NoMessages, "xyz".to_string()) + .await .unwrap(); - assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "xyz") + assert_eq!(t.ctx.stock_str(StockMessage::NoMessages).await, "xyz") } - #[test] - fn test_set_stock_translation_wrong_replacements() { - let t = dummy_context(); + #[async_std::test] + async fn test_set_stock_translation_wrong_replacements() { + let t = dummy_context().await; assert!(t .ctx .set_stock_translation(StockMessage::NoMessages, "xyz %1$s ".to_string()) + .await .is_err()); assert!(t .ctx .set_stock_translation(StockMessage::NoMessages, "xyz %2$s ".to_string()) + .await .is_err()); } - #[test] - fn test_stock_str() { - let t = dummy_context(); - assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "No messages."); + #[async_std::test] + async fn test_stock_str() { + let t = dummy_context().await; + assert_eq!( + t.ctx.stock_str(StockMessage::NoMessages).await, + "No messages." + ); } - #[test] - fn test_stock_string_repl_str() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_string_repl_str() { + let t = dummy_context().await; // uses %1$s substitution assert_eq!( - t.ctx.stock_string_repl_str(StockMessage::Member, "42"), + t.ctx + .stock_string_repl_str(StockMessage::Member, "42") + .await, "42 member(s)" ); // We have no string using %1$d to test... } - #[test] - fn test_stock_string_repl_int() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_string_repl_int() { + let t = dummy_context().await; assert_eq!( - t.ctx.stock_string_repl_int(StockMessage::Member, 42), + t.ctx.stock_string_repl_int(StockMessage::Member, 42).await, "42 member(s)" ); } - #[test] - fn test_stock_string_repl_str2() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_string_repl_str2() { + let t = dummy_context().await; assert_eq!( t.ctx - .stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"), + .stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar") + .await, "Could not connect to foo: bar" ); } - #[test] - fn test_stock_system_msg_simple() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_system_msg_simple() { + let t = dummy_context().await; assert_eq!( t.ctx - .stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0), + .stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0) + .await, "Location streaming enabled." ) } - #[test] - fn test_stock_system_msg_add_member_by_me() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_system_msg_add_member_by_me() { + let t = dummy_context().await; assert_eq!( - t.ctx.stock_system_msg( - StockMessage::MsgAddMember, - "alice@example.com", - "", - DC_CONTACT_ID_SELF - ), + t.ctx + .stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + DC_CONTACT_ID_SELF + ) + .await, "Member alice@example.com added by me." ) } - #[test] - fn test_stock_system_msg_add_member_by_me_with_displayname() { - let t = dummy_context(); - Contact::create(&t.ctx, "Alice", "alice@example.com").expect("failed to create contact"); + #[async_std::test] + async fn test_stock_system_msg_add_member_by_me_with_displayname() { + let t = dummy_context().await; + Contact::create(&t.ctx, "Alice", "alice@example.com") + .await + .expect("failed to create contact"); assert_eq!( - t.ctx.stock_system_msg( - StockMessage::MsgAddMember, - "alice@example.com", - "", - DC_CONTACT_ID_SELF - ), + t.ctx + .stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + DC_CONTACT_ID_SELF + ) + .await, "Member Alice (alice@example.com) added by me." ); } - #[test] - fn test_stock_system_msg_add_member_by_other_with_displayname() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_system_msg_add_member_by_other_with_displayname() { + let t = dummy_context().await; let contact_id = { Contact::create(&t.ctx, "Alice", "alice@example.com") + .await .expect("Failed to create contact Alice"); - Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob") + Contact::create(&t.ctx, "Bob", "bob@example.com") + .await + .expect("failed to create bob") }; assert_eq!( - t.ctx.stock_system_msg( - StockMessage::MsgAddMember, - "alice@example.com", - "", - contact_id, - ), + t.ctx + .stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + contact_id, + ) + .await, "Member Alice (alice@example.com) added by Bob (bob@example.com)." ); } - #[test] - fn test_stock_system_msg_grp_name() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_system_msg_grp_name() { + let t = dummy_context().await; assert_eq!( - t.ctx.stock_system_msg( - StockMessage::MsgGrpName, - "Some chat", - "Other chat", - DC_CONTACT_ID_SELF - ), + t.ctx + .stock_system_msg( + StockMessage::MsgGrpName, + "Some chat", + "Other chat", + DC_CONTACT_ID_SELF + ) + .await, "Group name changed from \"Some chat\" to \"Other chat\" by me." ) } - #[test] - fn test_stock_system_msg_grp_name_other() { - let t = dummy_context(); + #[async_std::test] + async fn test_stock_system_msg_grp_name_other() { + let t = dummy_context().await; let id = Contact::create(&t.ctx, "Alice", "alice@example.com") + .await .expect("failed to create contact"); assert_eq!( t.ctx - .stock_system_msg(StockMessage::MsgGrpName, "Some chat", "Other chat", id,), + .stock_system_msg(StockMessage::MsgGrpName, "Some chat", "Other chat", id) + .await, "Group name changed from \"Some chat\" to \"Other chat\" by Alice (alice@example.com)." ) } #[async_std::test] async fn test_update_device_chats() { - let t = dummy_context(); - t.ctx.update_device_chats().ok(); + let t = dummy_context().await; + t.ctx.update_device_chats().await.ok(); let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); assert_eq!(chats.len(), 2); @@ -553,7 +590,7 @@ mod tests { assert_eq!(chats.len(), 0); // a subsequent call to update_device_chats() must not re-add manally deleted messages or chats - t.ctx.update_device_chats().ok(); + t.ctx.update_device_chats().await.ok(); let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); assert_eq!(chats.len(), 0); } diff --git a/src/test_utils.rs b/src/test_utils.rs index 681f57fa1..f384df70f 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -25,14 +25,14 @@ pub(crate) struct TestContext { /// "db.sqlite" in the [TestContext.dir] directory. /// /// [Context]: crate::context::Context -pub(crate) fn test_context(callback: Option>) -> TestContext { +pub(crate) async fn test_context(callback: Option>) -> TestContext { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); let cb: Box = match callback { Some(cb) => cb, None => Box::new(|_, _| ()), }; - let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap(); + let ctx = Context::new(cb, "FakeOs".into(), dbfile).await.unwrap(); TestContext { ctx, dir } } @@ -41,8 +41,8 @@ pub(crate) fn test_context(callback: Option>) -> TestContex /// The context will be opened and use the SQLite database as /// specified in [test_context] but there is no callback hooked up, /// i.e. [Context::call_cb] will always return `0`. -pub(crate) fn dummy_context() -> TestContext { - test_context(None) +pub(crate) async fn dummy_context() -> TestContext { + test_context(None).await } pub(crate) fn logging_cb(_ctx: &Context, evt: Event) { diff --git a/src/token.rs b/src/token.rs index ac2b4af02..b3a89d5b4 100644 --- a/src/token.rs +++ b/src/token.rs @@ -34,7 +34,7 @@ pub async fn save(context: &Context, namespace: Namespace, foreign_id: ChatId) - .sql .execute( "INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);", - params![namespace, foreign_id, &token, time()], + paramsv![namespace, foreign_id, token, time()], ) .await .ok(); @@ -44,10 +44,10 @@ pub async fn save(context: &Context, namespace: Namespace, foreign_id: ChatId) - pub async fn lookup(context: &Context, namespace: Namespace, foreign_id: ChatId) -> Option { context .sql - .query_get_value::<_, String>( + .query_get_value::( context, "SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;", - params![namespace, foreign_id], + paramsv![namespace, foreign_id], ) .await } @@ -65,7 +65,7 @@ pub async fn exists(context: &Context, namespace: Namespace, token: &str) -> boo .sql .exists( "SELECT id FROM tokens WHERE namespc=? AND token=?;", - params![namespace, token], + paramsv![namespace, token], ) .await .unwrap_or_default() diff --git a/tests/stress.rs b/tests/stress.rs index 55e9e35a8..e4ec5e873 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -8,8 +8,11 @@ use tempfile::{tempdir, TempDir}; /* some data used for testing ******************************************************************************/ -fn stress_functions(context: &Context) { - let res = context.get_config(config::Config::SysConfigKeys).unwrap(); +async fn stress_functions(context: &Context) { + let res = context + .get_config(config::Config::SysConfigKeys) + .await + .unwrap(); assert!(!res.contains(" probably_never_a_key ")); assert!(res.contains(" addr ")); @@ -98,15 +101,17 @@ struct TestContext { dir: TempDir, } -fn create_test_context() -> TestContext { +async fn create_test_context() -> TestContext { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); - let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap(); + let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile) + .await + .unwrap(); TestContext { ctx, dir } } -#[test] -fn test_stress_tests() { - let context = create_test_context(); - stress_functions(&context.ctx); +#[async_std::test] +async fn test_stress_tests() { + let context = create_test_context().await; + stress_functions(&context.ctx).await; }